editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    inline_completion_tests::FakeInlineCompletionProvider,
    5    linked_editing_ranges::LinkedEditingRanges,
    6    scroll::scroll_amount::ScrollAmount,
    7    test::{
    8        assert_text_with_selections, build_editor,
    9        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
   10        editor_test_context::EditorTestContext,
   11        select_ranges,
   12    },
   13};
   14use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   15use futures::StreamExt;
   16use gpui::{
   17    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   18    VisualTestContext, WindowBounds, WindowOptions, div,
   19};
   20use indoc::indoc;
   21use language::{
   22    BracketPairConfig,
   23    Capability::ReadWrite,
   24    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   25    Override, Point,
   26    language_settings::{
   27        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   28        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   29    },
   30    tree_sitter_python,
   31};
   32use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   33use lsp::CompletionParams;
   34use multi_buffer::{IndentGuide, PathKey};
   35use parking_lot::Mutex;
   36use pretty_assertions::{assert_eq, assert_ne};
   37use project::{
   38    FakeFs,
   39    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   40    project_settings::{LspSettings, ProjectSettings},
   41};
   42use serde_json::{self, json};
   43use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   44use std::{
   45    iter,
   46    sync::atomic::{self, AtomicUsize},
   47};
   48use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   49use text::ToPoint as _;
   50use unindent::Unindent;
   51use util::{
   52    assert_set_eq, path,
   53    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   54    uri,
   55};
   56use workspace::{
   57    CloseActiveItem, CloseAllItems, CloseInactiveItems, NavigationEntry, OpenOptions, ViewId,
   58    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   59};
   60
   61#[gpui::test]
   62fn test_edit_events(cx: &mut TestAppContext) {
   63    init_test(cx, |_| {});
   64
   65    let buffer = cx.new(|cx| {
   66        let mut buffer = language::Buffer::local("123456", cx);
   67        buffer.set_group_interval(Duration::from_secs(1));
   68        buffer
   69    });
   70
   71    let events = Rc::new(RefCell::new(Vec::new()));
   72    let editor1 = cx.add_window({
   73        let events = events.clone();
   74        |window, cx| {
   75            let entity = cx.entity().clone();
   76            cx.subscribe_in(
   77                &entity,
   78                window,
   79                move |_, _, event: &EditorEvent, _, _| match event {
   80                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   81                    EditorEvent::BufferEdited => {
   82                        events.borrow_mut().push(("editor1", "buffer edited"))
   83                    }
   84                    _ => {}
   85                },
   86            )
   87            .detach();
   88            Editor::for_buffer(buffer.clone(), None, window, cx)
   89        }
   90    });
   91
   92    let editor2 = cx.add_window({
   93        let events = events.clone();
   94        |window, cx| {
   95            cx.subscribe_in(
   96                &cx.entity().clone(),
   97                window,
   98                move |_, _, event: &EditorEvent, _, _| match event {
   99                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
  100                    EditorEvent::BufferEdited => {
  101                        events.borrow_mut().push(("editor2", "buffer edited"))
  102                    }
  103                    _ => {}
  104                },
  105            )
  106            .detach();
  107            Editor::for_buffer(buffer.clone(), None, window, cx)
  108        }
  109    });
  110
  111    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  112
  113    // Mutating editor 1 will emit an `Edited` event only for that editor.
  114    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  115    assert_eq!(
  116        mem::take(&mut *events.borrow_mut()),
  117        [
  118            ("editor1", "edited"),
  119            ("editor1", "buffer edited"),
  120            ("editor2", "buffer edited"),
  121        ]
  122    );
  123
  124    // Mutating editor 2 will emit an `Edited` event only for that editor.
  125    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  126    assert_eq!(
  127        mem::take(&mut *events.borrow_mut()),
  128        [
  129            ("editor2", "edited"),
  130            ("editor1", "buffer edited"),
  131            ("editor2", "buffer edited"),
  132        ]
  133    );
  134
  135    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  136    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  137    assert_eq!(
  138        mem::take(&mut *events.borrow_mut()),
  139        [
  140            ("editor1", "edited"),
  141            ("editor1", "buffer edited"),
  142            ("editor2", "buffer edited"),
  143        ]
  144    );
  145
  146    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  147    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  148    assert_eq!(
  149        mem::take(&mut *events.borrow_mut()),
  150        [
  151            ("editor1", "edited"),
  152            ("editor1", "buffer edited"),
  153            ("editor2", "buffer edited"),
  154        ]
  155    );
  156
  157    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  158    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  159    assert_eq!(
  160        mem::take(&mut *events.borrow_mut()),
  161        [
  162            ("editor2", "edited"),
  163            ("editor1", "buffer edited"),
  164            ("editor2", "buffer edited"),
  165        ]
  166    );
  167
  168    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  169    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  170    assert_eq!(
  171        mem::take(&mut *events.borrow_mut()),
  172        [
  173            ("editor2", "edited"),
  174            ("editor1", "buffer edited"),
  175            ("editor2", "buffer edited"),
  176        ]
  177    );
  178
  179    // No event is emitted when the mutation is a no-op.
  180    _ = editor2.update(cx, |editor, window, cx| {
  181        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  182
  183        editor.backspace(&Backspace, window, cx);
  184    });
  185    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  186}
  187
  188#[gpui::test]
  189fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  190    init_test(cx, |_| {});
  191
  192    let mut now = Instant::now();
  193    let group_interval = Duration::from_millis(1);
  194    let buffer = cx.new(|cx| {
  195        let mut buf = language::Buffer::local("123456", cx);
  196        buf.set_group_interval(group_interval);
  197        buf
  198    });
  199    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  200    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  201
  202    _ = editor.update(cx, |editor, window, cx| {
  203        editor.start_transaction_at(now, window, cx);
  204        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  205
  206        editor.insert("cd", window, cx);
  207        editor.end_transaction_at(now, cx);
  208        assert_eq!(editor.text(cx), "12cd56");
  209        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  210
  211        editor.start_transaction_at(now, window, cx);
  212        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  213        editor.insert("e", window, cx);
  214        editor.end_transaction_at(now, cx);
  215        assert_eq!(editor.text(cx), "12cde6");
  216        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  217
  218        now += group_interval + Duration::from_millis(1);
  219        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  220
  221        // Simulate an edit in another editor
  222        buffer.update(cx, |buffer, cx| {
  223            buffer.start_transaction_at(now, cx);
  224            buffer.edit([(0..1, "a")], None, cx);
  225            buffer.edit([(1..1, "b")], None, cx);
  226            buffer.end_transaction_at(now, cx);
  227        });
  228
  229        assert_eq!(editor.text(cx), "ab2cde6");
  230        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  231
  232        // Last transaction happened past the group interval in a different editor.
  233        // Undo it individually and don't restore selections.
  234        editor.undo(&Undo, window, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  237
  238        // First two transactions happened within the group interval in this editor.
  239        // Undo them together and restore selections.
  240        editor.undo(&Undo, window, cx);
  241        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  242        assert_eq!(editor.text(cx), "123456");
  243        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  244
  245        // Redo the first two transactions together.
  246        editor.redo(&Redo, window, cx);
  247        assert_eq!(editor.text(cx), "12cde6");
  248        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  249
  250        // Redo the last transaction on its own.
  251        editor.redo(&Redo, window, cx);
  252        assert_eq!(editor.text(cx), "ab2cde6");
  253        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  254
  255        // Test empty transactions.
  256        editor.start_transaction_at(now, window, cx);
  257        editor.end_transaction_at(now, cx);
  258        editor.undo(&Undo, window, cx);
  259        assert_eq!(editor.text(cx), "12cde6");
  260    });
  261}
  262
  263#[gpui::test]
  264fn test_ime_composition(cx: &mut TestAppContext) {
  265    init_test(cx, |_| {});
  266
  267    let buffer = cx.new(|cx| {
  268        let mut buffer = language::Buffer::local("abcde", cx);
  269        // Ensure automatic grouping doesn't occur.
  270        buffer.set_group_interval(Duration::ZERO);
  271        buffer
  272    });
  273
  274    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  275    cx.add_window(|window, cx| {
  276        let mut editor = build_editor(buffer.clone(), window, cx);
  277
  278        // Start a new IME composition.
  279        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  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        assert_eq!(editor.text(cx), "äbcde");
  283        assert_eq!(
  284            editor.marked_text_ranges(cx),
  285            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  286        );
  287
  288        // Finalize IME composition.
  289        editor.replace_text_in_range(None, "ā", window, cx);
  290        assert_eq!(editor.text(cx), "ābcde");
  291        assert_eq!(editor.marked_text_ranges(cx), None);
  292
  293        // IME composition edits are grouped and are undone/redone at once.
  294        editor.undo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "abcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297        editor.redo(&Default::default(), window, cx);
  298        assert_eq!(editor.text(cx), "ābcde");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition.
  302        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  303        assert_eq!(
  304            editor.marked_text_ranges(cx),
  305            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  306        );
  307
  308        // Undoing during an IME composition cancels it.
  309        editor.undo(&Default::default(), window, cx);
  310        assert_eq!(editor.text(cx), "ābcde");
  311        assert_eq!(editor.marked_text_ranges(cx), None);
  312
  313        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  314        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  315        assert_eq!(editor.text(cx), "ābcdè");
  316        assert_eq!(
  317            editor.marked_text_ranges(cx),
  318            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  319        );
  320
  321        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  322        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  323        assert_eq!(editor.text(cx), "ābcdę");
  324        assert_eq!(editor.marked_text_ranges(cx), None);
  325
  326        // Start a new IME composition with multiple cursors.
  327        editor.change_selections(None, window, cx, |s| {
  328            s.select_ranges([
  329                OffsetUtf16(1)..OffsetUtf16(1),
  330                OffsetUtf16(3)..OffsetUtf16(3),
  331                OffsetUtf16(5)..OffsetUtf16(5),
  332            ])
  333        });
  334        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  335        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  336        assert_eq!(
  337            editor.marked_text_ranges(cx),
  338            Some(vec![
  339                OffsetUtf16(0)..OffsetUtf16(3),
  340                OffsetUtf16(4)..OffsetUtf16(7),
  341                OffsetUtf16(8)..OffsetUtf16(11)
  342            ])
  343        );
  344
  345        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  346        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  347        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  348        assert_eq!(
  349            editor.marked_text_ranges(cx),
  350            Some(vec![
  351                OffsetUtf16(1)..OffsetUtf16(2),
  352                OffsetUtf16(5)..OffsetUtf16(6),
  353                OffsetUtf16(9)..OffsetUtf16(10)
  354            ])
  355        );
  356
  357        // Finalize IME composition with multiple cursors.
  358        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  359        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  360        assert_eq!(editor.marked_text_ranges(cx), None);
  361
  362        editor
  363    });
  364}
  365
  366#[gpui::test]
  367fn test_selection_with_mouse(cx: &mut TestAppContext) {
  368    init_test(cx, |_| {});
  369
  370    let editor = cx.add_window(|window, cx| {
  371        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  372        build_editor(buffer, window, cx)
  373    });
  374
  375    _ = editor.update(cx, |editor, window, cx| {
  376        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  377    });
  378    assert_eq!(
  379        editor
  380            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  381            .unwrap(),
  382        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  383    );
  384
  385    _ = editor.update(cx, |editor, window, cx| {
  386        editor.update_selection(
  387            DisplayPoint::new(DisplayRow(3), 3),
  388            0,
  389            gpui::Point::<f32>::default(),
  390            window,
  391            cx,
  392        );
  393    });
  394
  395    assert_eq!(
  396        editor
  397            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  398            .unwrap(),
  399        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  400    );
  401
  402    _ = editor.update(cx, |editor, window, cx| {
  403        editor.update_selection(
  404            DisplayPoint::new(DisplayRow(1), 1),
  405            0,
  406            gpui::Point::<f32>::default(),
  407            window,
  408            cx,
  409        );
  410    });
  411
  412    assert_eq!(
  413        editor
  414            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  415            .unwrap(),
  416        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  417    );
  418
  419    _ = editor.update(cx, |editor, window, cx| {
  420        editor.end_selection(window, cx);
  421        editor.update_selection(
  422            DisplayPoint::new(DisplayRow(3), 3),
  423            0,
  424            gpui::Point::<f32>::default(),
  425            window,
  426            cx,
  427        );
  428    });
  429
  430    assert_eq!(
  431        editor
  432            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  433            .unwrap(),
  434        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  435    );
  436
  437    _ = editor.update(cx, |editor, window, cx| {
  438        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  439        editor.update_selection(
  440            DisplayPoint::new(DisplayRow(0), 0),
  441            0,
  442            gpui::Point::<f32>::default(),
  443            window,
  444            cx,
  445        );
  446    });
  447
  448    assert_eq!(
  449        editor
  450            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  451            .unwrap(),
  452        [
  453            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  454            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  455        ]
  456    );
  457
  458    _ = editor.update(cx, |editor, window, cx| {
  459        editor.end_selection(window, cx);
  460    });
  461
  462    assert_eq!(
  463        editor
  464            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  465            .unwrap(),
  466        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  467    );
  468}
  469
  470#[gpui::test]
  471fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  472    init_test(cx, |_| {});
  473
  474    let editor = cx.add_window(|window, cx| {
  475        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  476        build_editor(buffer, window, cx)
  477    });
  478
  479    _ = editor.update(cx, |editor, window, cx| {
  480        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  481    });
  482
  483    _ = editor.update(cx, |editor, window, cx| {
  484        editor.end_selection(window, cx);
  485    });
  486
  487    _ = editor.update(cx, |editor, window, cx| {
  488        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  489    });
  490
  491    _ = editor.update(cx, |editor, window, cx| {
  492        editor.end_selection(window, cx);
  493    });
  494
  495    assert_eq!(
  496        editor
  497            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  498            .unwrap(),
  499        [
  500            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  501            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  502        ]
  503    );
  504
  505    _ = editor.update(cx, |editor, window, cx| {
  506        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  507    });
  508
  509    _ = editor.update(cx, |editor, window, cx| {
  510        editor.end_selection(window, cx);
  511    });
  512
  513    assert_eq!(
  514        editor
  515            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  516            .unwrap(),
  517        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  518    );
  519}
  520
  521#[gpui::test]
  522fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  523    init_test(cx, |_| {});
  524
  525    let editor = cx.add_window(|window, cx| {
  526        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  527        build_editor(buffer, window, cx)
  528    });
  529
  530    _ = editor.update(cx, |editor, window, cx| {
  531        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  532        assert_eq!(
  533            editor.selections.display_ranges(cx),
  534            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  535        );
  536    });
  537
  538    _ = editor.update(cx, |editor, window, cx| {
  539        editor.update_selection(
  540            DisplayPoint::new(DisplayRow(3), 3),
  541            0,
  542            gpui::Point::<f32>::default(),
  543            window,
  544            cx,
  545        );
  546        assert_eq!(
  547            editor.selections.display_ranges(cx),
  548            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  549        );
  550    });
  551
  552    _ = editor.update(cx, |editor, window, cx| {
  553        editor.cancel(&Cancel, window, cx);
  554        editor.update_selection(
  555            DisplayPoint::new(DisplayRow(1), 1),
  556            0,
  557            gpui::Point::<f32>::default(),
  558            window,
  559            cx,
  560        );
  561        assert_eq!(
  562            editor.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  564        );
  565    });
  566}
  567
  568#[gpui::test]
  569fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  570    init_test(cx, |_| {});
  571
  572    let editor = cx.add_window(|window, cx| {
  573        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  574        build_editor(buffer, window, cx)
  575    });
  576
  577    _ = editor.update(cx, |editor, window, cx| {
  578        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  579        assert_eq!(
  580            editor.selections.display_ranges(cx),
  581            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  582        );
  583
  584        editor.move_down(&Default::default(), window, cx);
  585        assert_eq!(
  586            editor.selections.display_ranges(cx),
  587            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  588        );
  589
  590        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  591        assert_eq!(
  592            editor.selections.display_ranges(cx),
  593            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  594        );
  595
  596        editor.move_up(&Default::default(), window, cx);
  597        assert_eq!(
  598            editor.selections.display_ranges(cx),
  599            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  600        );
  601    });
  602}
  603
  604#[gpui::test]
  605fn test_clone(cx: &mut TestAppContext) {
  606    init_test(cx, |_| {});
  607
  608    let (text, selection_ranges) = marked_text_ranges(
  609        indoc! {"
  610            one
  611            two
  612            threeˇ
  613            four
  614            fiveˇ
  615        "},
  616        true,
  617    );
  618
  619    let editor = cx.add_window(|window, cx| {
  620        let buffer = MultiBuffer::build_simple(&text, cx);
  621        build_editor(buffer, window, cx)
  622    });
  623
  624    _ = editor.update(cx, |editor, window, cx| {
  625        editor.change_selections(None, window, cx, |s| {
  626            s.select_ranges(selection_ranges.clone())
  627        });
  628        editor.fold_creases(
  629            vec![
  630                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  631                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  632            ],
  633            true,
  634            window,
  635            cx,
  636        );
  637    });
  638
  639    let cloned_editor = editor
  640        .update(cx, |editor, _, cx| {
  641            cx.open_window(Default::default(), |window, cx| {
  642                cx.new(|cx| editor.clone(window, cx))
  643            })
  644        })
  645        .unwrap()
  646        .unwrap();
  647
  648    let snapshot = editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651    let cloned_snapshot = cloned_editor
  652        .update(cx, |e, window, cx| e.snapshot(window, cx))
  653        .unwrap();
  654
  655    assert_eq!(
  656        cloned_editor
  657            .update(cx, |e, _, cx| e.display_text(cx))
  658            .unwrap(),
  659        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  660    );
  661    assert_eq!(
  662        cloned_snapshot
  663            .folds_in_range(0..text.len())
  664            .collect::<Vec<_>>(),
  665        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  666    );
  667    assert_set_eq!(
  668        cloned_editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  670            .unwrap(),
  671        editor
  672            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  673            .unwrap()
  674    );
  675    assert_set_eq!(
  676        cloned_editor
  677            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  678            .unwrap(),
  679        editor
  680            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  681            .unwrap()
  682    );
  683}
  684
  685#[gpui::test]
  686async fn test_navigation_history(cx: &mut TestAppContext) {
  687    init_test(cx, |_| {});
  688
  689    use workspace::item::Item;
  690
  691    let fs = FakeFs::new(cx.executor());
  692    let project = Project::test(fs, [], cx).await;
  693    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  694    let pane = workspace
  695        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  696        .unwrap();
  697
  698    _ = workspace.update(cx, |_v, window, cx| {
  699        cx.new(|cx| {
  700            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  701            let mut editor = build_editor(buffer.clone(), window, cx);
  702            let handle = cx.entity();
  703            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  704
  705            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  706                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  707            }
  708
  709            // Move the cursor a small distance.
  710            // Nothing is added to the navigation history.
  711            editor.change_selections(None, window, cx, |s| {
  712                s.select_display_ranges([
  713                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  714                ])
  715            });
  716            editor.change_selections(None, window, cx, |s| {
  717                s.select_display_ranges([
  718                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  719                ])
  720            });
  721            assert!(pop_history(&mut editor, cx).is_none());
  722
  723            // Move the cursor a large distance.
  724            // The history can jump back to the previous position.
  725            editor.change_selections(None, window, cx, |s| {
  726                s.select_display_ranges([
  727                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  728                ])
  729            });
  730            let nav_entry = pop_history(&mut editor, cx).unwrap();
  731            editor.navigate(nav_entry.data.unwrap(), window, cx);
  732            assert_eq!(nav_entry.item.id(), cx.entity_id());
  733            assert_eq!(
  734                editor.selections.display_ranges(cx),
  735                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  736            );
  737            assert!(pop_history(&mut editor, cx).is_none());
  738
  739            // Move the cursor a small distance via the mouse.
  740            // Nothing is added to the navigation history.
  741            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  742            editor.end_selection(window, cx);
  743            assert_eq!(
  744                editor.selections.display_ranges(cx),
  745                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  746            );
  747            assert!(pop_history(&mut editor, cx).is_none());
  748
  749            // Move the cursor a large distance via the mouse.
  750            // The history can jump back to the previous position.
  751            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  752            editor.end_selection(window, cx);
  753            assert_eq!(
  754                editor.selections.display_ranges(cx),
  755                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  756            );
  757            let nav_entry = pop_history(&mut editor, cx).unwrap();
  758            editor.navigate(nav_entry.data.unwrap(), window, cx);
  759            assert_eq!(nav_entry.item.id(), cx.entity_id());
  760            assert_eq!(
  761                editor.selections.display_ranges(cx),
  762                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  763            );
  764            assert!(pop_history(&mut editor, cx).is_none());
  765
  766            // Set scroll position to check later
  767            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  768            let original_scroll_position = editor.scroll_manager.anchor();
  769
  770            // Jump to the end of the document and adjust scroll
  771            editor.move_to_end(&MoveToEnd, window, cx);
  772            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  773            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  774
  775            let nav_entry = pop_history(&mut editor, cx).unwrap();
  776            editor.navigate(nav_entry.data.unwrap(), window, cx);
  777            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  778
  779            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  780            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  781            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  782            let invalid_point = Point::new(9999, 0);
  783            editor.navigate(
  784                Box::new(NavigationData {
  785                    cursor_anchor: invalid_anchor,
  786                    cursor_position: invalid_point,
  787                    scroll_anchor: ScrollAnchor {
  788                        anchor: invalid_anchor,
  789                        offset: Default::default(),
  790                    },
  791                    scroll_top_row: invalid_point.row,
  792                }),
  793                window,
  794                cx,
  795            );
  796            assert_eq!(
  797                editor.selections.display_ranges(cx),
  798                &[editor.max_point(cx)..editor.max_point(cx)]
  799            );
  800            assert_eq!(
  801                editor.scroll_position(cx),
  802                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  803            );
  804
  805            editor
  806        })
  807    });
  808}
  809
  810#[gpui::test]
  811fn test_cancel(cx: &mut TestAppContext) {
  812    init_test(cx, |_| {});
  813
  814    let editor = cx.add_window(|window, cx| {
  815        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  816        build_editor(buffer, window, cx)
  817    });
  818
  819    _ = editor.update(cx, |editor, window, cx| {
  820        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  821        editor.update_selection(
  822            DisplayPoint::new(DisplayRow(1), 1),
  823            0,
  824            gpui::Point::<f32>::default(),
  825            window,
  826            cx,
  827        );
  828        editor.end_selection(window, cx);
  829
  830        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  831        editor.update_selection(
  832            DisplayPoint::new(DisplayRow(0), 3),
  833            0,
  834            gpui::Point::<f32>::default(),
  835            window,
  836            cx,
  837        );
  838        editor.end_selection(window, cx);
  839        assert_eq!(
  840            editor.selections.display_ranges(cx),
  841            [
  842                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  843                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  844            ]
  845        );
  846    });
  847
  848    _ = editor.update(cx, |editor, window, cx| {
  849        editor.cancel(&Cancel, window, cx);
  850        assert_eq!(
  851            editor.selections.display_ranges(cx),
  852            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  853        );
  854    });
  855
  856    _ = editor.update(cx, |editor, window, cx| {
  857        editor.cancel(&Cancel, window, cx);
  858        assert_eq!(
  859            editor.selections.display_ranges(cx),
  860            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  861        );
  862    });
  863}
  864
  865#[gpui::test]
  866fn test_fold_action(cx: &mut TestAppContext) {
  867    init_test(cx, |_| {});
  868
  869    let editor = cx.add_window(|window, cx| {
  870        let buffer = MultiBuffer::build_simple(
  871            &"
  872                impl Foo {
  873                    // Hello!
  874
  875                    fn a() {
  876                        1
  877                    }
  878
  879                    fn b() {
  880                        2
  881                    }
  882
  883                    fn c() {
  884                        3
  885                    }
  886                }
  887            "
  888            .unindent(),
  889            cx,
  890        );
  891        build_editor(buffer.clone(), window, cx)
  892    });
  893
  894    _ = editor.update(cx, |editor, window, cx| {
  895        editor.change_selections(None, window, cx, |s| {
  896            s.select_display_ranges([
  897                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  898            ]);
  899        });
  900        editor.fold(&Fold, window, cx);
  901        assert_eq!(
  902            editor.display_text(cx),
  903            "
  904                impl Foo {
  905                    // Hello!
  906
  907                    fn a() {
  908                        1
  909                    }
  910
  911                    fn b() {⋯
  912                    }
  913
  914                    fn c() {⋯
  915                    }
  916                }
  917            "
  918            .unindent(),
  919        );
  920
  921        editor.fold(&Fold, window, cx);
  922        assert_eq!(
  923            editor.display_text(cx),
  924            "
  925                impl Foo {⋯
  926                }
  927            "
  928            .unindent(),
  929        );
  930
  931        editor.unfold_lines(&UnfoldLines, window, cx);
  932        assert_eq!(
  933            editor.display_text(cx),
  934            "
  935                impl Foo {
  936                    // Hello!
  937
  938                    fn a() {
  939                        1
  940                    }
  941
  942                    fn b() {⋯
  943                    }
  944
  945                    fn c() {⋯
  946                    }
  947                }
  948            "
  949            .unindent(),
  950        );
  951
  952        editor.unfold_lines(&UnfoldLines, window, cx);
  953        assert_eq!(
  954            editor.display_text(cx),
  955            editor.buffer.read(cx).read(cx).text()
  956        );
  957    });
  958}
  959
  960#[gpui::test]
  961fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  962    init_test(cx, |_| {});
  963
  964    let editor = cx.add_window(|window, cx| {
  965        let buffer = MultiBuffer::build_simple(
  966            &"
  967                class Foo:
  968                    # Hello!
  969
  970                    def a():
  971                        print(1)
  972
  973                    def b():
  974                        print(2)
  975
  976                    def c():
  977                        print(3)
  978            "
  979            .unindent(),
  980            cx,
  981        );
  982        build_editor(buffer.clone(), window, cx)
  983    });
  984
  985    _ = editor.update(cx, |editor, window, cx| {
  986        editor.change_selections(None, window, cx, |s| {
  987            s.select_display_ranges([
  988                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  989            ]);
  990        });
  991        editor.fold(&Fold, window, cx);
  992        assert_eq!(
  993            editor.display_text(cx),
  994            "
  995                class Foo:
  996                    # Hello!
  997
  998                    def a():
  999                        print(1)
 1000
 1001                    def b():⋯
 1002
 1003                    def c():⋯
 1004            "
 1005            .unindent(),
 1006        );
 1007
 1008        editor.fold(&Fold, window, cx);
 1009        assert_eq!(
 1010            editor.display_text(cx),
 1011            "
 1012                class Foo:⋯
 1013            "
 1014            .unindent(),
 1015        );
 1016
 1017        editor.unfold_lines(&UnfoldLines, window, cx);
 1018        assert_eq!(
 1019            editor.display_text(cx),
 1020            "
 1021                class Foo:
 1022                    # Hello!
 1023
 1024                    def a():
 1025                        print(1)
 1026
 1027                    def b():⋯
 1028
 1029                    def c():⋯
 1030            "
 1031            .unindent(),
 1032        );
 1033
 1034        editor.unfold_lines(&UnfoldLines, window, cx);
 1035        assert_eq!(
 1036            editor.display_text(cx),
 1037            editor.buffer.read(cx).read(cx).text()
 1038        );
 1039    });
 1040}
 1041
 1042#[gpui::test]
 1043fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1044    init_test(cx, |_| {});
 1045
 1046    let editor = cx.add_window(|window, cx| {
 1047        let buffer = MultiBuffer::build_simple(
 1048            &"
 1049                class Foo:
 1050                    # Hello!
 1051
 1052                    def a():
 1053                        print(1)
 1054
 1055                    def b():
 1056                        print(2)
 1057
 1058
 1059                    def c():
 1060                        print(3)
 1061
 1062
 1063            "
 1064            .unindent(),
 1065            cx,
 1066        );
 1067        build_editor(buffer.clone(), window, cx)
 1068    });
 1069
 1070    _ = editor.update(cx, |editor, window, cx| {
 1071        editor.change_selections(None, window, cx, |s| {
 1072            s.select_display_ranges([
 1073                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1074            ]);
 1075        });
 1076        editor.fold(&Fold, window, cx);
 1077        assert_eq!(
 1078            editor.display_text(cx),
 1079            "
 1080                class Foo:
 1081                    # Hello!
 1082
 1083                    def a():
 1084                        print(1)
 1085
 1086                    def b():⋯
 1087
 1088
 1089                    def c():⋯
 1090
 1091
 1092            "
 1093            .unindent(),
 1094        );
 1095
 1096        editor.fold(&Fold, window, cx);
 1097        assert_eq!(
 1098            editor.display_text(cx),
 1099            "
 1100                class Foo:⋯
 1101
 1102
 1103            "
 1104            .unindent(),
 1105        );
 1106
 1107        editor.unfold_lines(&UnfoldLines, window, cx);
 1108        assert_eq!(
 1109            editor.display_text(cx),
 1110            "
 1111                class Foo:
 1112                    # Hello!
 1113
 1114                    def a():
 1115                        print(1)
 1116
 1117                    def b():⋯
 1118
 1119
 1120                    def c():⋯
 1121
 1122
 1123            "
 1124            .unindent(),
 1125        );
 1126
 1127        editor.unfold_lines(&UnfoldLines, window, cx);
 1128        assert_eq!(
 1129            editor.display_text(cx),
 1130            editor.buffer.read(cx).read(cx).text()
 1131        );
 1132    });
 1133}
 1134
 1135#[gpui::test]
 1136fn test_fold_at_level(cx: &mut TestAppContext) {
 1137    init_test(cx, |_| {});
 1138
 1139    let editor = cx.add_window(|window, cx| {
 1140        let buffer = MultiBuffer::build_simple(
 1141            &"
 1142                class Foo:
 1143                    # Hello!
 1144
 1145                    def a():
 1146                        print(1)
 1147
 1148                    def b():
 1149                        print(2)
 1150
 1151
 1152                class Bar:
 1153                    # World!
 1154
 1155                    def a():
 1156                        print(1)
 1157
 1158                    def b():
 1159                        print(2)
 1160
 1161
 1162            "
 1163            .unindent(),
 1164            cx,
 1165        );
 1166        build_editor(buffer.clone(), window, cx)
 1167    });
 1168
 1169    _ = editor.update(cx, |editor, window, cx| {
 1170        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1171        assert_eq!(
 1172            editor.display_text(cx),
 1173            "
 1174                class Foo:
 1175                    # Hello!
 1176
 1177                    def a():⋯
 1178
 1179                    def b():⋯
 1180
 1181
 1182                class Bar:
 1183                    # World!
 1184
 1185                    def a():⋯
 1186
 1187                    def b():⋯
 1188
 1189
 1190            "
 1191            .unindent(),
 1192        );
 1193
 1194        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1195        assert_eq!(
 1196            editor.display_text(cx),
 1197            "
 1198                class Foo:⋯
 1199
 1200
 1201                class Bar:⋯
 1202
 1203
 1204            "
 1205            .unindent(),
 1206        );
 1207
 1208        editor.unfold_all(&UnfoldAll, window, cx);
 1209        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1210        assert_eq!(
 1211            editor.display_text(cx),
 1212            "
 1213                class Foo:
 1214                    # Hello!
 1215
 1216                    def a():
 1217                        print(1)
 1218
 1219                    def b():
 1220                        print(2)
 1221
 1222
 1223                class Bar:
 1224                    # World!
 1225
 1226                    def a():
 1227                        print(1)
 1228
 1229                    def b():
 1230                        print(2)
 1231
 1232
 1233            "
 1234            .unindent(),
 1235        );
 1236
 1237        assert_eq!(
 1238            editor.display_text(cx),
 1239            editor.buffer.read(cx).read(cx).text()
 1240        );
 1241    });
 1242}
 1243
 1244#[gpui::test]
 1245fn test_move_cursor(cx: &mut TestAppContext) {
 1246    init_test(cx, |_| {});
 1247
 1248    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1249    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1250
 1251    buffer.update(cx, |buffer, cx| {
 1252        buffer.edit(
 1253            vec![
 1254                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1255                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1256            ],
 1257            None,
 1258            cx,
 1259        );
 1260    });
 1261    _ = editor.update(cx, |editor, window, cx| {
 1262        assert_eq!(
 1263            editor.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1265        );
 1266
 1267        editor.move_down(&MoveDown, window, cx);
 1268        assert_eq!(
 1269            editor.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1271        );
 1272
 1273        editor.move_right(&MoveRight, window, cx);
 1274        assert_eq!(
 1275            editor.selections.display_ranges(cx),
 1276            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1277        );
 1278
 1279        editor.move_left(&MoveLeft, window, cx);
 1280        assert_eq!(
 1281            editor.selections.display_ranges(cx),
 1282            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1283        );
 1284
 1285        editor.move_up(&MoveUp, window, cx);
 1286        assert_eq!(
 1287            editor.selections.display_ranges(cx),
 1288            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1289        );
 1290
 1291        editor.move_to_end(&MoveToEnd, window, cx);
 1292        assert_eq!(
 1293            editor.selections.display_ranges(cx),
 1294            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1295        );
 1296
 1297        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1298        assert_eq!(
 1299            editor.selections.display_ranges(cx),
 1300            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1301        );
 1302
 1303        editor.change_selections(None, window, cx, |s| {
 1304            s.select_display_ranges([
 1305                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1306            ]);
 1307        });
 1308        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1309        assert_eq!(
 1310            editor.selections.display_ranges(cx),
 1311            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1312        );
 1313
 1314        editor.select_to_end(&SelectToEnd, window, cx);
 1315        assert_eq!(
 1316            editor.selections.display_ranges(cx),
 1317            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1318        );
 1319    });
 1320}
 1321
 1322#[gpui::test]
 1323fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1324    init_test(cx, |_| {});
 1325
 1326    let editor = cx.add_window(|window, cx| {
 1327        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1328        build_editor(buffer.clone(), window, cx)
 1329    });
 1330
 1331    assert_eq!('🟥'.len_utf8(), 4);
 1332    assert_eq!('α'.len_utf8(), 2);
 1333
 1334    _ = editor.update(cx, |editor, window, cx| {
 1335        editor.fold_creases(
 1336            vec![
 1337                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1339                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1340            ],
 1341            true,
 1342            window,
 1343            cx,
 1344        );
 1345        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1346
 1347        editor.move_right(&MoveRight, window, cx);
 1348        assert_eq!(
 1349            editor.selections.display_ranges(cx),
 1350            &[empty_range(0, "🟥".len())]
 1351        );
 1352        editor.move_right(&MoveRight, window, cx);
 1353        assert_eq!(
 1354            editor.selections.display_ranges(cx),
 1355            &[empty_range(0, "🟥🟧".len())]
 1356        );
 1357        editor.move_right(&MoveRight, window, cx);
 1358        assert_eq!(
 1359            editor.selections.display_ranges(cx),
 1360            &[empty_range(0, "🟥🟧⋯".len())]
 1361        );
 1362
 1363        editor.move_down(&MoveDown, window, cx);
 1364        assert_eq!(
 1365            editor.selections.display_ranges(cx),
 1366            &[empty_range(1, "ab⋯e".len())]
 1367        );
 1368        editor.move_left(&MoveLeft, window, cx);
 1369        assert_eq!(
 1370            editor.selections.display_ranges(cx),
 1371            &[empty_range(1, "ab⋯".len())]
 1372        );
 1373        editor.move_left(&MoveLeft, window, cx);
 1374        assert_eq!(
 1375            editor.selections.display_ranges(cx),
 1376            &[empty_range(1, "ab".len())]
 1377        );
 1378        editor.move_left(&MoveLeft, window, cx);
 1379        assert_eq!(
 1380            editor.selections.display_ranges(cx),
 1381            &[empty_range(1, "a".len())]
 1382        );
 1383
 1384        editor.move_down(&MoveDown, window, cx);
 1385        assert_eq!(
 1386            editor.selections.display_ranges(cx),
 1387            &[empty_range(2, "α".len())]
 1388        );
 1389        editor.move_right(&MoveRight, window, cx);
 1390        assert_eq!(
 1391            editor.selections.display_ranges(cx),
 1392            &[empty_range(2, "αβ".len())]
 1393        );
 1394        editor.move_right(&MoveRight, window, cx);
 1395        assert_eq!(
 1396            editor.selections.display_ranges(cx),
 1397            &[empty_range(2, "αβ⋯".len())]
 1398        );
 1399        editor.move_right(&MoveRight, window, cx);
 1400        assert_eq!(
 1401            editor.selections.display_ranges(cx),
 1402            &[empty_range(2, "αβ⋯ε".len())]
 1403        );
 1404
 1405        editor.move_up(&MoveUp, window, cx);
 1406        assert_eq!(
 1407            editor.selections.display_ranges(cx),
 1408            &[empty_range(1, "ab⋯e".len())]
 1409        );
 1410        editor.move_down(&MoveDown, window, cx);
 1411        assert_eq!(
 1412            editor.selections.display_ranges(cx),
 1413            &[empty_range(2, "αβ⋯ε".len())]
 1414        );
 1415        editor.move_up(&MoveUp, window, cx);
 1416        assert_eq!(
 1417            editor.selections.display_ranges(cx),
 1418            &[empty_range(1, "ab⋯e".len())]
 1419        );
 1420
 1421        editor.move_up(&MoveUp, window, cx);
 1422        assert_eq!(
 1423            editor.selections.display_ranges(cx),
 1424            &[empty_range(0, "🟥🟧".len())]
 1425        );
 1426        editor.move_left(&MoveLeft, window, cx);
 1427        assert_eq!(
 1428            editor.selections.display_ranges(cx),
 1429            &[empty_range(0, "🟥".len())]
 1430        );
 1431        editor.move_left(&MoveLeft, window, cx);
 1432        assert_eq!(
 1433            editor.selections.display_ranges(cx),
 1434            &[empty_range(0, "".len())]
 1435        );
 1436    });
 1437}
 1438
 1439#[gpui::test]
 1440fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1441    init_test(cx, |_| {});
 1442
 1443    let editor = cx.add_window(|window, cx| {
 1444        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1445        build_editor(buffer.clone(), window, cx)
 1446    });
 1447    _ = editor.update(cx, |editor, window, cx| {
 1448        editor.change_selections(None, window, cx, |s| {
 1449            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1450        });
 1451
 1452        // moving above start of document should move selection to start of document,
 1453        // but the next move down should still be at the original goal_x
 1454        editor.move_up(&MoveUp, window, cx);
 1455        assert_eq!(
 1456            editor.selections.display_ranges(cx),
 1457            &[empty_range(0, "".len())]
 1458        );
 1459
 1460        editor.move_down(&MoveDown, window, cx);
 1461        assert_eq!(
 1462            editor.selections.display_ranges(cx),
 1463            &[empty_range(1, "abcd".len())]
 1464        );
 1465
 1466        editor.move_down(&MoveDown, window, cx);
 1467        assert_eq!(
 1468            editor.selections.display_ranges(cx),
 1469            &[empty_range(2, "αβγ".len())]
 1470        );
 1471
 1472        editor.move_down(&MoveDown, window, cx);
 1473        assert_eq!(
 1474            editor.selections.display_ranges(cx),
 1475            &[empty_range(3, "abcd".len())]
 1476        );
 1477
 1478        editor.move_down(&MoveDown, window, cx);
 1479        assert_eq!(
 1480            editor.selections.display_ranges(cx),
 1481            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1482        );
 1483
 1484        // moving past end of document should not change goal_x
 1485        editor.move_down(&MoveDown, window, cx);
 1486        assert_eq!(
 1487            editor.selections.display_ranges(cx),
 1488            &[empty_range(5, "".len())]
 1489        );
 1490
 1491        editor.move_down(&MoveDown, window, cx);
 1492        assert_eq!(
 1493            editor.selections.display_ranges(cx),
 1494            &[empty_range(5, "".len())]
 1495        );
 1496
 1497        editor.move_up(&MoveUp, window, cx);
 1498        assert_eq!(
 1499            editor.selections.display_ranges(cx),
 1500            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1501        );
 1502
 1503        editor.move_up(&MoveUp, window, cx);
 1504        assert_eq!(
 1505            editor.selections.display_ranges(cx),
 1506            &[empty_range(3, "abcd".len())]
 1507        );
 1508
 1509        editor.move_up(&MoveUp, window, cx);
 1510        assert_eq!(
 1511            editor.selections.display_ranges(cx),
 1512            &[empty_range(2, "αβγ".len())]
 1513        );
 1514    });
 1515}
 1516
 1517#[gpui::test]
 1518fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1519    init_test(cx, |_| {});
 1520    let move_to_beg = MoveToBeginningOfLine {
 1521        stop_at_soft_wraps: true,
 1522        stop_at_indent: true,
 1523    };
 1524
 1525    let delete_to_beg = DeleteToBeginningOfLine {
 1526        stop_at_indent: false,
 1527    };
 1528
 1529    let move_to_end = MoveToEndOfLine {
 1530        stop_at_soft_wraps: true,
 1531    };
 1532
 1533    let editor = cx.add_window(|window, cx| {
 1534        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1535        build_editor(buffer, window, cx)
 1536    });
 1537    _ = editor.update(cx, |editor, window, cx| {
 1538        editor.change_selections(None, window, cx, |s| {
 1539            s.select_display_ranges([
 1540                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1541                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1542            ]);
 1543        });
 1544    });
 1545
 1546    _ = editor.update(cx, |editor, window, cx| {
 1547        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1548        assert_eq!(
 1549            editor.selections.display_ranges(cx),
 1550            &[
 1551                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1552                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1553            ]
 1554        );
 1555    });
 1556
 1557    _ = editor.update(cx, |editor, window, cx| {
 1558        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1559        assert_eq!(
 1560            editor.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = editor.update(cx, |editor, window, cx| {
 1569        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1570        assert_eq!(
 1571            editor.selections.display_ranges(cx),
 1572            &[
 1573                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1574                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1575            ]
 1576        );
 1577    });
 1578
 1579    _ = editor.update(cx, |editor, window, cx| {
 1580        editor.move_to_end_of_line(&move_to_end, window, cx);
 1581        assert_eq!(
 1582            editor.selections.display_ranges(cx),
 1583            &[
 1584                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1585                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1586            ]
 1587        );
 1588    });
 1589
 1590    // Moving to the end of line again is a no-op.
 1591    _ = editor.update(cx, |editor, window, cx| {
 1592        editor.move_to_end_of_line(&move_to_end, window, cx);
 1593        assert_eq!(
 1594            editor.selections.display_ranges(cx),
 1595            &[
 1596                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1597                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1598            ]
 1599        );
 1600    });
 1601
 1602    _ = editor.update(cx, |editor, window, cx| {
 1603        editor.move_left(&MoveLeft, window, cx);
 1604        editor.select_to_beginning_of_line(
 1605            &SelectToBeginningOfLine {
 1606                stop_at_soft_wraps: true,
 1607                stop_at_indent: true,
 1608            },
 1609            window,
 1610            cx,
 1611        );
 1612        assert_eq!(
 1613            editor.selections.display_ranges(cx),
 1614            &[
 1615                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1616                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1617            ]
 1618        );
 1619    });
 1620
 1621    _ = editor.update(cx, |editor, window, cx| {
 1622        editor.select_to_beginning_of_line(
 1623            &SelectToBeginningOfLine {
 1624                stop_at_soft_wraps: true,
 1625                stop_at_indent: true,
 1626            },
 1627            window,
 1628            cx,
 1629        );
 1630        assert_eq!(
 1631            editor.selections.display_ranges(cx),
 1632            &[
 1633                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1634                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1635            ]
 1636        );
 1637    });
 1638
 1639    _ = editor.update(cx, |editor, window, cx| {
 1640        editor.select_to_beginning_of_line(
 1641            &SelectToBeginningOfLine {
 1642                stop_at_soft_wraps: true,
 1643                stop_at_indent: true,
 1644            },
 1645            window,
 1646            cx,
 1647        );
 1648        assert_eq!(
 1649            editor.selections.display_ranges(cx),
 1650            &[
 1651                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1652                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1653            ]
 1654        );
 1655    });
 1656
 1657    _ = editor.update(cx, |editor, window, cx| {
 1658        editor.select_to_end_of_line(
 1659            &SelectToEndOfLine {
 1660                stop_at_soft_wraps: true,
 1661            },
 1662            window,
 1663            cx,
 1664        );
 1665        assert_eq!(
 1666            editor.selections.display_ranges(cx),
 1667            &[
 1668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1669                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1670            ]
 1671        );
 1672    });
 1673
 1674    _ = editor.update(cx, |editor, window, cx| {
 1675        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1676        assert_eq!(editor.display_text(cx), "ab\n  de");
 1677        assert_eq!(
 1678            editor.selections.display_ranges(cx),
 1679            &[
 1680                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1681                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1682            ]
 1683        );
 1684    });
 1685
 1686    _ = editor.update(cx, |editor, window, cx| {
 1687        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1688        assert_eq!(editor.display_text(cx), "\n");
 1689        assert_eq!(
 1690            editor.selections.display_ranges(cx),
 1691            &[
 1692                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1693                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1694            ]
 1695        );
 1696    });
 1697}
 1698
 1699#[gpui::test]
 1700fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1701    init_test(cx, |_| {});
 1702    let move_to_beg = MoveToBeginningOfLine {
 1703        stop_at_soft_wraps: false,
 1704        stop_at_indent: false,
 1705    };
 1706
 1707    let move_to_end = MoveToEndOfLine {
 1708        stop_at_soft_wraps: false,
 1709    };
 1710
 1711    let editor = cx.add_window(|window, cx| {
 1712        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1713        build_editor(buffer, window, cx)
 1714    });
 1715
 1716    _ = editor.update(cx, |editor, window, cx| {
 1717        editor.set_wrap_width(Some(140.0.into()), cx);
 1718
 1719        // We expect the following lines after wrapping
 1720        // ```
 1721        // thequickbrownfox
 1722        // jumpedoverthelazydo
 1723        // gs
 1724        // ```
 1725        // The final `gs` was soft-wrapped onto a new line.
 1726        assert_eq!(
 1727            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1728            editor.display_text(cx),
 1729        );
 1730
 1731        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1732        // Start the cursor at the `k` on the first line
 1733        editor.change_selections(None, window, cx, |s| {
 1734            s.select_display_ranges([
 1735                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1736            ]);
 1737        });
 1738
 1739        // Moving to the beginning of the line should put us at the beginning of the line.
 1740        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1741        assert_eq!(
 1742            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1743            editor.selections.display_ranges(cx)
 1744        );
 1745
 1746        // Moving to the end of the line should put us at the end of the line.
 1747        editor.move_to_end_of_line(&move_to_end, window, cx);
 1748        assert_eq!(
 1749            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1750            editor.selections.display_ranges(cx)
 1751        );
 1752
 1753        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1754        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1755        editor.change_selections(None, window, cx, |s| {
 1756            s.select_display_ranges([
 1757                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1758            ]);
 1759        });
 1760
 1761        // Moving to the beginning of the line should put us at the start of the second line of
 1762        // display text, i.e., the `j`.
 1763        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1764        assert_eq!(
 1765            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1766            editor.selections.display_ranges(cx)
 1767        );
 1768
 1769        // Moving to the beginning of the line again should be a no-op.
 1770        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1771        assert_eq!(
 1772            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1773            editor.selections.display_ranges(cx)
 1774        );
 1775
 1776        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1777        // next display line.
 1778        editor.move_to_end_of_line(&move_to_end, window, cx);
 1779        assert_eq!(
 1780            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1781            editor.selections.display_ranges(cx)
 1782        );
 1783
 1784        // Moving to the end of the line again should be a no-op.
 1785        editor.move_to_end_of_line(&move_to_end, window, cx);
 1786        assert_eq!(
 1787            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1788            editor.selections.display_ranges(cx)
 1789        );
 1790    });
 1791}
 1792
 1793#[gpui::test]
 1794fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1795    init_test(cx, |_| {});
 1796
 1797    let move_to_beg = MoveToBeginningOfLine {
 1798        stop_at_soft_wraps: true,
 1799        stop_at_indent: true,
 1800    };
 1801
 1802    let select_to_beg = SelectToBeginningOfLine {
 1803        stop_at_soft_wraps: true,
 1804        stop_at_indent: true,
 1805    };
 1806
 1807    let delete_to_beg = DeleteToBeginningOfLine {
 1808        stop_at_indent: true,
 1809    };
 1810
 1811    let move_to_end = MoveToEndOfLine {
 1812        stop_at_soft_wraps: false,
 1813    };
 1814
 1815    let editor = cx.add_window(|window, cx| {
 1816        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1817        build_editor(buffer, window, cx)
 1818    });
 1819
 1820    _ = editor.update(cx, |editor, window, cx| {
 1821        editor.change_selections(None, window, cx, |s| {
 1822            s.select_display_ranges([
 1823                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1824                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1825            ]);
 1826        });
 1827
 1828        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1829        // and the second cursor at the first non-whitespace character in the line.
 1830        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1831        assert_eq!(
 1832            editor.selections.display_ranges(cx),
 1833            &[
 1834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1835                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1836            ]
 1837        );
 1838
 1839        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1840        // and should move the second cursor to the beginning of the line.
 1841        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1842        assert_eq!(
 1843            editor.selections.display_ranges(cx),
 1844            &[
 1845                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1846                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1847            ]
 1848        );
 1849
 1850        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1851        // and should move the second cursor back to the first non-whitespace character in the line.
 1852        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1853        assert_eq!(
 1854            editor.selections.display_ranges(cx),
 1855            &[
 1856                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1857                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1858            ]
 1859        );
 1860
 1861        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1862        // and to the first non-whitespace character in the line for the second cursor.
 1863        editor.move_to_end_of_line(&move_to_end, window, cx);
 1864        editor.move_left(&MoveLeft, window, cx);
 1865        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1866        assert_eq!(
 1867            editor.selections.display_ranges(cx),
 1868            &[
 1869                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1870                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1871            ]
 1872        );
 1873
 1874        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1875        // and should select to the beginning of the line for the second cursor.
 1876        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1877        assert_eq!(
 1878            editor.selections.display_ranges(cx),
 1879            &[
 1880                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1881                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1882            ]
 1883        );
 1884
 1885        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1886        // and should delete to the first non-whitespace character in the line for the second cursor.
 1887        editor.move_to_end_of_line(&move_to_end, window, cx);
 1888        editor.move_left(&MoveLeft, window, cx);
 1889        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1890        assert_eq!(editor.text(cx), "c\n  f");
 1891    });
 1892}
 1893
 1894#[gpui::test]
 1895fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1896    init_test(cx, |_| {});
 1897
 1898    let editor = cx.add_window(|window, cx| {
 1899        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1900        build_editor(buffer, window, cx)
 1901    });
 1902    _ = editor.update(cx, |editor, window, cx| {
 1903        editor.change_selections(None, window, cx, |s| {
 1904            s.select_display_ranges([
 1905                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1906                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1907            ])
 1908        });
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1923        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1930
 1931        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1932        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1933
 1934        editor.move_right(&MoveRight, window, cx);
 1935        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1936        assert_selection_ranges(
 1937            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1938            editor,
 1939            cx,
 1940        );
 1941
 1942        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1943        assert_selection_ranges(
 1944            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1945            editor,
 1946            cx,
 1947        );
 1948
 1949        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1950        assert_selection_ranges(
 1951            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1952            editor,
 1953            cx,
 1954        );
 1955    });
 1956}
 1957
 1958#[gpui::test]
 1959fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1960    init_test(cx, |_| {});
 1961
 1962    let editor = cx.add_window(|window, cx| {
 1963        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1964        build_editor(buffer, window, cx)
 1965    });
 1966
 1967    _ = editor.update(cx, |editor, window, cx| {
 1968        editor.set_wrap_width(Some(140.0.into()), cx);
 1969        assert_eq!(
 1970            editor.display_text(cx),
 1971            "use one::{\n    two::three::\n    four::five\n};"
 1972        );
 1973
 1974        editor.change_selections(None, window, cx, |s| {
 1975            s.select_display_ranges([
 1976                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1977            ]);
 1978        });
 1979
 1980        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1981        assert_eq!(
 1982            editor.selections.display_ranges(cx),
 1983            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1984        );
 1985
 1986        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1987        assert_eq!(
 1988            editor.selections.display_ranges(cx),
 1989            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1990        );
 1991
 1992        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1993        assert_eq!(
 1994            editor.selections.display_ranges(cx),
 1995            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1996        );
 1997
 1998        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1999        assert_eq!(
 2000            editor.selections.display_ranges(cx),
 2001            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2002        );
 2003
 2004        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2005        assert_eq!(
 2006            editor.selections.display_ranges(cx),
 2007            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2008        );
 2009
 2010        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2011        assert_eq!(
 2012            editor.selections.display_ranges(cx),
 2013            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2014        );
 2015    });
 2016}
 2017
 2018#[gpui::test]
 2019async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2020    init_test(cx, |_| {});
 2021    let mut cx = EditorTestContext::new(cx).await;
 2022
 2023    let line_height = cx.editor(|editor, window, _| {
 2024        editor
 2025            .style()
 2026            .unwrap()
 2027            .text
 2028            .line_height_in_pixels(window.rem_size())
 2029    });
 2030    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2031
 2032    cx.set_state(
 2033        &r#"ˇone
 2034        two
 2035
 2036        three
 2037        fourˇ
 2038        five
 2039
 2040        six"#
 2041            .unindent(),
 2042    );
 2043
 2044    cx.update_editor(|editor, window, cx| {
 2045        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2046    });
 2047    cx.assert_editor_state(
 2048        &r#"one
 2049        two
 2050        ˇ
 2051        three
 2052        four
 2053        five
 2054        ˇ
 2055        six"#
 2056            .unindent(),
 2057    );
 2058
 2059    cx.update_editor(|editor, window, cx| {
 2060        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2061    });
 2062    cx.assert_editor_state(
 2063        &r#"one
 2064        two
 2065
 2066        three
 2067        four
 2068        five
 2069        ˇ
 2070        sixˇ"#
 2071            .unindent(),
 2072    );
 2073
 2074    cx.update_editor(|editor, window, cx| {
 2075        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2076    });
 2077    cx.assert_editor_state(
 2078        &r#"one
 2079        two
 2080
 2081        three
 2082        four
 2083        five
 2084
 2085        sixˇ"#
 2086            .unindent(),
 2087    );
 2088
 2089    cx.update_editor(|editor, window, cx| {
 2090        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2091    });
 2092    cx.assert_editor_state(
 2093        &r#"one
 2094        two
 2095
 2096        three
 2097        four
 2098        five
 2099        ˇ
 2100        six"#
 2101            .unindent(),
 2102    );
 2103
 2104    cx.update_editor(|editor, window, cx| {
 2105        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2106    });
 2107    cx.assert_editor_state(
 2108        &r#"one
 2109        two
 2110        ˇ
 2111        three
 2112        four
 2113        five
 2114
 2115        six"#
 2116            .unindent(),
 2117    );
 2118
 2119    cx.update_editor(|editor, window, cx| {
 2120        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2121    });
 2122    cx.assert_editor_state(
 2123        &r#"ˇone
 2124        two
 2125
 2126        three
 2127        four
 2128        five
 2129
 2130        six"#
 2131            .unindent(),
 2132    );
 2133}
 2134
 2135#[gpui::test]
 2136async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2137    init_test(cx, |_| {});
 2138    let mut cx = EditorTestContext::new(cx).await;
 2139    let line_height = cx.editor(|editor, window, _| {
 2140        editor
 2141            .style()
 2142            .unwrap()
 2143            .text
 2144            .line_height_in_pixels(window.rem_size())
 2145    });
 2146    let window = cx.window;
 2147    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2148
 2149    cx.set_state(
 2150        r#"ˇone
 2151        two
 2152        three
 2153        four
 2154        five
 2155        six
 2156        seven
 2157        eight
 2158        nine
 2159        ten
 2160        "#,
 2161    );
 2162
 2163    cx.update_editor(|editor, window, cx| {
 2164        assert_eq!(
 2165            editor.snapshot(window, cx).scroll_position(),
 2166            gpui::Point::new(0., 0.)
 2167        );
 2168        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2169        assert_eq!(
 2170            editor.snapshot(window, cx).scroll_position(),
 2171            gpui::Point::new(0., 3.)
 2172        );
 2173        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2174        assert_eq!(
 2175            editor.snapshot(window, cx).scroll_position(),
 2176            gpui::Point::new(0., 6.)
 2177        );
 2178        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2179        assert_eq!(
 2180            editor.snapshot(window, cx).scroll_position(),
 2181            gpui::Point::new(0., 3.)
 2182        );
 2183
 2184        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2185        assert_eq!(
 2186            editor.snapshot(window, cx).scroll_position(),
 2187            gpui::Point::new(0., 1.)
 2188        );
 2189        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2190        assert_eq!(
 2191            editor.snapshot(window, cx).scroll_position(),
 2192            gpui::Point::new(0., 3.)
 2193        );
 2194    });
 2195}
 2196
 2197#[gpui::test]
 2198async fn test_autoscroll(cx: &mut TestAppContext) {
 2199    init_test(cx, |_| {});
 2200    let mut cx = EditorTestContext::new(cx).await;
 2201
 2202    let line_height = cx.update_editor(|editor, window, cx| {
 2203        editor.set_vertical_scroll_margin(2, cx);
 2204        editor
 2205            .style()
 2206            .unwrap()
 2207            .text
 2208            .line_height_in_pixels(window.rem_size())
 2209    });
 2210    let window = cx.window;
 2211    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2212
 2213    cx.set_state(
 2214        r#"ˇone
 2215            two
 2216            three
 2217            four
 2218            five
 2219            six
 2220            seven
 2221            eight
 2222            nine
 2223            ten
 2224        "#,
 2225    );
 2226    cx.update_editor(|editor, window, cx| {
 2227        assert_eq!(
 2228            editor.snapshot(window, cx).scroll_position(),
 2229            gpui::Point::new(0., 0.0)
 2230        );
 2231    });
 2232
 2233    // Add a cursor below the visible area. Since both cursors cannot fit
 2234    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2235    // allows the vertical scroll margin below that cursor.
 2236    cx.update_editor(|editor, window, cx| {
 2237        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2238            selections.select_ranges([
 2239                Point::new(0, 0)..Point::new(0, 0),
 2240                Point::new(6, 0)..Point::new(6, 0),
 2241            ]);
 2242        })
 2243    });
 2244    cx.update_editor(|editor, window, cx| {
 2245        assert_eq!(
 2246            editor.snapshot(window, cx).scroll_position(),
 2247            gpui::Point::new(0., 3.0)
 2248        );
 2249    });
 2250
 2251    // Move down. The editor cursor scrolls down to track the newest cursor.
 2252    cx.update_editor(|editor, window, cx| {
 2253        editor.move_down(&Default::default(), window, cx);
 2254    });
 2255    cx.update_editor(|editor, window, cx| {
 2256        assert_eq!(
 2257            editor.snapshot(window, cx).scroll_position(),
 2258            gpui::Point::new(0., 4.0)
 2259        );
 2260    });
 2261
 2262    // Add a cursor above the visible area. Since both cursors fit on screen,
 2263    // the editor scrolls to show both.
 2264    cx.update_editor(|editor, window, cx| {
 2265        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2266            selections.select_ranges([
 2267                Point::new(1, 0)..Point::new(1, 0),
 2268                Point::new(6, 0)..Point::new(6, 0),
 2269            ]);
 2270        })
 2271    });
 2272    cx.update_editor(|editor, window, cx| {
 2273        assert_eq!(
 2274            editor.snapshot(window, cx).scroll_position(),
 2275            gpui::Point::new(0., 1.0)
 2276        );
 2277    });
 2278}
 2279
 2280#[gpui::test]
 2281async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2282    init_test(cx, |_| {});
 2283    let mut cx = EditorTestContext::new(cx).await;
 2284
 2285    let line_height = cx.editor(|editor, window, _cx| {
 2286        editor
 2287            .style()
 2288            .unwrap()
 2289            .text
 2290            .line_height_in_pixels(window.rem_size())
 2291    });
 2292    let window = cx.window;
 2293    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2294    cx.set_state(
 2295        &r#"
 2296        ˇone
 2297        two
 2298        threeˇ
 2299        four
 2300        five
 2301        six
 2302        seven
 2303        eight
 2304        nine
 2305        ten
 2306        "#
 2307        .unindent(),
 2308    );
 2309
 2310    cx.update_editor(|editor, window, cx| {
 2311        editor.move_page_down(&MovePageDown::default(), window, cx)
 2312    });
 2313    cx.assert_editor_state(
 2314        &r#"
 2315        one
 2316        two
 2317        three
 2318        ˇfour
 2319        five
 2320        sixˇ
 2321        seven
 2322        eight
 2323        nine
 2324        ten
 2325        "#
 2326        .unindent(),
 2327    );
 2328
 2329    cx.update_editor(|editor, window, cx| {
 2330        editor.move_page_down(&MovePageDown::default(), window, cx)
 2331    });
 2332    cx.assert_editor_state(
 2333        &r#"
 2334        one
 2335        two
 2336        three
 2337        four
 2338        five
 2339        six
 2340        ˇseven
 2341        eight
 2342        nineˇ
 2343        ten
 2344        "#
 2345        .unindent(),
 2346    );
 2347
 2348    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2349    cx.assert_editor_state(
 2350        &r#"
 2351        one
 2352        two
 2353        three
 2354        ˇfour
 2355        five
 2356        sixˇ
 2357        seven
 2358        eight
 2359        nine
 2360        ten
 2361        "#
 2362        .unindent(),
 2363    );
 2364
 2365    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2366    cx.assert_editor_state(
 2367        &r#"
 2368        ˇone
 2369        two
 2370        threeˇ
 2371        four
 2372        five
 2373        six
 2374        seven
 2375        eight
 2376        nine
 2377        ten
 2378        "#
 2379        .unindent(),
 2380    );
 2381
 2382    // Test select collapsing
 2383    cx.update_editor(|editor, window, cx| {
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386        editor.move_page_down(&MovePageDown::default(), window, cx);
 2387    });
 2388    cx.assert_editor_state(
 2389        &r#"
 2390        one
 2391        two
 2392        three
 2393        four
 2394        five
 2395        six
 2396        seven
 2397        eight
 2398        nine
 2399        ˇten
 2400        ˇ"#
 2401        .unindent(),
 2402    );
 2403}
 2404
 2405#[gpui::test]
 2406async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2407    init_test(cx, |_| {});
 2408    let mut cx = EditorTestContext::new(cx).await;
 2409    cx.set_state("one «two threeˇ» four");
 2410    cx.update_editor(|editor, window, cx| {
 2411        editor.delete_to_beginning_of_line(
 2412            &DeleteToBeginningOfLine {
 2413                stop_at_indent: false,
 2414            },
 2415            window,
 2416            cx,
 2417        );
 2418        assert_eq!(editor.text(cx), " four");
 2419    });
 2420}
 2421
 2422#[gpui::test]
 2423fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2424    init_test(cx, |_| {});
 2425
 2426    let editor = cx.add_window(|window, cx| {
 2427        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2428        build_editor(buffer.clone(), window, cx)
 2429    });
 2430
 2431    _ = editor.update(cx, |editor, window, cx| {
 2432        editor.change_selections(None, window, cx, |s| {
 2433            s.select_display_ranges([
 2434                // an empty selection - the preceding word fragment is deleted
 2435                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2436                // characters selected - they are deleted
 2437                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2438            ])
 2439        });
 2440        editor.delete_to_previous_word_start(
 2441            &DeleteToPreviousWordStart {
 2442                ignore_newlines: false,
 2443            },
 2444            window,
 2445            cx,
 2446        );
 2447        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2448    });
 2449
 2450    _ = editor.update(cx, |editor, window, cx| {
 2451        editor.change_selections(None, window, cx, |s| {
 2452            s.select_display_ranges([
 2453                // an empty selection - the following word fragment is deleted
 2454                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2455                // characters selected - they are deleted
 2456                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2457            ])
 2458        });
 2459        editor.delete_to_next_word_end(
 2460            &DeleteToNextWordEnd {
 2461                ignore_newlines: false,
 2462            },
 2463            window,
 2464            cx,
 2465        );
 2466        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2467    });
 2468}
 2469
 2470#[gpui::test]
 2471fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2472    init_test(cx, |_| {});
 2473
 2474    let editor = cx.add_window(|window, cx| {
 2475        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2476        build_editor(buffer.clone(), window, cx)
 2477    });
 2478    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2479        ignore_newlines: false,
 2480    };
 2481    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2482        ignore_newlines: true,
 2483    };
 2484
 2485    _ = editor.update(cx, |editor, window, cx| {
 2486        editor.change_selections(None, window, cx, |s| {
 2487            s.select_display_ranges([
 2488                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2489            ])
 2490        });
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2501        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2502        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2503    });
 2504}
 2505
 2506#[gpui::test]
 2507fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2508    init_test(cx, |_| {});
 2509
 2510    let editor = cx.add_window(|window, cx| {
 2511        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2512        build_editor(buffer.clone(), window, cx)
 2513    });
 2514    let del_to_next_word_end = DeleteToNextWordEnd {
 2515        ignore_newlines: false,
 2516    };
 2517    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2518        ignore_newlines: true,
 2519    };
 2520
 2521    _ = editor.update(cx, |editor, window, cx| {
 2522        editor.change_selections(None, window, cx, |s| {
 2523            s.select_display_ranges([
 2524                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2525            ])
 2526        });
 2527        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2528        assert_eq!(
 2529            editor.buffer.read(cx).read(cx).text(),
 2530            "one\n   two\nthree\n   four"
 2531        );
 2532        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2533        assert_eq!(
 2534            editor.buffer.read(cx).read(cx).text(),
 2535            "\n   two\nthree\n   four"
 2536        );
 2537        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2538        assert_eq!(
 2539            editor.buffer.read(cx).read(cx).text(),
 2540            "two\nthree\n   four"
 2541        );
 2542        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2546        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2547        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2548    });
 2549}
 2550
 2551#[gpui::test]
 2552fn test_newline(cx: &mut TestAppContext) {
 2553    init_test(cx, |_| {});
 2554
 2555    let editor = cx.add_window(|window, cx| {
 2556        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2557        build_editor(buffer.clone(), window, cx)
 2558    });
 2559
 2560    _ = editor.update(cx, |editor, window, cx| {
 2561        editor.change_selections(None, window, cx, |s| {
 2562            s.select_display_ranges([
 2563                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2564                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2565                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2566            ])
 2567        });
 2568
 2569        editor.newline(&Newline, window, cx);
 2570        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2571    });
 2572}
 2573
 2574#[gpui::test]
 2575fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2576    init_test(cx, |_| {});
 2577
 2578    let editor = cx.add_window(|window, cx| {
 2579        let buffer = MultiBuffer::build_simple(
 2580            "
 2581                a
 2582                b(
 2583                    X
 2584                )
 2585                c(
 2586                    X
 2587                )
 2588            "
 2589            .unindent()
 2590            .as_str(),
 2591            cx,
 2592        );
 2593        let mut editor = build_editor(buffer.clone(), window, cx);
 2594        editor.change_selections(None, window, cx, |s| {
 2595            s.select_ranges([
 2596                Point::new(2, 4)..Point::new(2, 5),
 2597                Point::new(5, 4)..Point::new(5, 5),
 2598            ])
 2599        });
 2600        editor
 2601    });
 2602
 2603    _ = editor.update(cx, |editor, window, cx| {
 2604        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2605        editor.buffer.update(cx, |buffer, cx| {
 2606            buffer.edit(
 2607                [
 2608                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2609                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2610                ],
 2611                None,
 2612                cx,
 2613            );
 2614            assert_eq!(
 2615                buffer.read(cx).text(),
 2616                "
 2617                    a
 2618                    b()
 2619                    c()
 2620                "
 2621                .unindent()
 2622            );
 2623        });
 2624        assert_eq!(
 2625            editor.selections.ranges(cx),
 2626            &[
 2627                Point::new(1, 2)..Point::new(1, 2),
 2628                Point::new(2, 2)..Point::new(2, 2),
 2629            ],
 2630        );
 2631
 2632        editor.newline(&Newline, window, cx);
 2633        assert_eq!(
 2634            editor.text(cx),
 2635            "
 2636                a
 2637                b(
 2638                )
 2639                c(
 2640                )
 2641            "
 2642            .unindent()
 2643        );
 2644
 2645        // The selections are moved after the inserted newlines
 2646        assert_eq!(
 2647            editor.selections.ranges(cx),
 2648            &[
 2649                Point::new(2, 0)..Point::new(2, 0),
 2650                Point::new(4, 0)..Point::new(4, 0),
 2651            ],
 2652        );
 2653    });
 2654}
 2655
 2656#[gpui::test]
 2657async fn test_newline_above(cx: &mut TestAppContext) {
 2658    init_test(cx, |settings| {
 2659        settings.defaults.tab_size = NonZeroU32::new(4)
 2660    });
 2661
 2662    let language = Arc::new(
 2663        Language::new(
 2664            LanguageConfig::default(),
 2665            Some(tree_sitter_rust::LANGUAGE.into()),
 2666        )
 2667        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2668        .unwrap(),
 2669    );
 2670
 2671    let mut cx = EditorTestContext::new(cx).await;
 2672    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2673    cx.set_state(indoc! {"
 2674        const a: ˇA = (
 2675 2676                «const_functionˇ»(ˇ),
 2677                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2678 2679        ˇ);ˇ
 2680    "});
 2681
 2682    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2683    cx.assert_editor_state(indoc! {"
 2684        ˇ
 2685        const a: A = (
 2686            ˇ
 2687            (
 2688                ˇ
 2689                ˇ
 2690                const_function(),
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                ˇ
 2695                something_else,
 2696                ˇ
 2697            )
 2698            ˇ
 2699            ˇ
 2700        );
 2701    "});
 2702}
 2703
 2704#[gpui::test]
 2705async fn test_newline_below(cx: &mut TestAppContext) {
 2706    init_test(cx, |settings| {
 2707        settings.defaults.tab_size = NonZeroU32::new(4)
 2708    });
 2709
 2710    let language = Arc::new(
 2711        Language::new(
 2712            LanguageConfig::default(),
 2713            Some(tree_sitter_rust::LANGUAGE.into()),
 2714        )
 2715        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2716        .unwrap(),
 2717    );
 2718
 2719    let mut cx = EditorTestContext::new(cx).await;
 2720    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2721    cx.set_state(indoc! {"
 2722        const a: ˇA = (
 2723 2724                «const_functionˇ»(ˇ),
 2725                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2726 2727        ˇ);ˇ
 2728    "});
 2729
 2730    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2731    cx.assert_editor_state(indoc! {"
 2732        const a: A = (
 2733            ˇ
 2734            (
 2735                ˇ
 2736                const_function(),
 2737                ˇ
 2738                ˇ
 2739                something_else,
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743                ˇ
 2744            )
 2745            ˇ
 2746        );
 2747        ˇ
 2748        ˇ
 2749    "});
 2750}
 2751
 2752#[gpui::test]
 2753async fn test_newline_comments(cx: &mut TestAppContext) {
 2754    init_test(cx, |settings| {
 2755        settings.defaults.tab_size = NonZeroU32::new(4)
 2756    });
 2757
 2758    let language = Arc::new(Language::new(
 2759        LanguageConfig {
 2760            line_comments: vec!["// ".into()],
 2761            ..LanguageConfig::default()
 2762        },
 2763        None,
 2764    ));
 2765    {
 2766        let mut cx = EditorTestContext::new(cx).await;
 2767        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2768        cx.set_state(indoc! {"
 2769        // Fooˇ
 2770    "});
 2771
 2772        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2773        cx.assert_editor_state(indoc! {"
 2774        // Foo
 2775        // ˇ
 2776    "});
 2777        // Ensure that we add comment prefix when existing line contains space
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(
 2780            indoc! {"
 2781        // Foo
 2782        //s
 2783        // ˇ
 2784    "}
 2785            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2786            .as_str(),
 2787        );
 2788        // Ensure that we add comment prefix when existing line does not contain space
 2789        cx.set_state(indoc! {"
 2790        // Foo
 2791        //ˇ
 2792    "});
 2793        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794        cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        //
 2797        // ˇ
 2798    "});
 2799        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2800        cx.set_state(indoc! {"
 2801        ˇ// Foo
 2802    "});
 2803        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2804        cx.assert_editor_state(indoc! {"
 2805
 2806        ˇ// Foo
 2807    "});
 2808    }
 2809    // Ensure that comment continuations can be disabled.
 2810    update_test_language_settings(cx, |settings| {
 2811        settings.defaults.extend_comment_on_newline = Some(false);
 2812    });
 2813    let mut cx = EditorTestContext::new(cx).await;
 2814    cx.set_state(indoc! {"
 2815        // Fooˇ
 2816    "});
 2817    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2818    cx.assert_editor_state(indoc! {"
 2819        // Foo
 2820        ˇ
 2821    "});
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.tab_size = NonZeroU32::new(4)
 2828    });
 2829
 2830    let language = Arc::new(Language::new(
 2831        LanguageConfig {
 2832            line_comments: vec!["// ".into(), "/// ".into()],
 2833            ..LanguageConfig::default()
 2834        },
 2835        None,
 2836    ));
 2837    {
 2838        let mut cx = EditorTestContext::new(cx).await;
 2839        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2840        cx.set_state(indoc! {"
 2841        //ˇ
 2842    "});
 2843        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2844        cx.assert_editor_state(indoc! {"
 2845        //
 2846        // ˇ
 2847    "});
 2848
 2849        cx.set_state(indoc! {"
 2850        ///ˇ
 2851    "});
 2852        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2853        cx.assert_editor_state(indoc! {"
 2854        ///
 2855        /// ˇ
 2856    "});
 2857    }
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 2862    init_test(cx, |settings| {
 2863        settings.defaults.tab_size = NonZeroU32::new(4)
 2864    });
 2865
 2866    let language = Arc::new(Language::new(
 2867        LanguageConfig {
 2868            documentation: Some(language::DocumentationConfig {
 2869                start: "/**".into(),
 2870                end: "*/".into(),
 2871                prefix: "* ".into(),
 2872                tab_size: NonZeroU32::new(1).unwrap(),
 2873            }),
 2874            ..LanguageConfig::default()
 2875        },
 2876        None,
 2877    ));
 2878    {
 2879        let mut cx = EditorTestContext::new(cx).await;
 2880        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2881        cx.set_state(indoc! {"
 2882        /**ˇ
 2883    "});
 2884
 2885        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2886        cx.assert_editor_state(indoc! {"
 2887        /**
 2888         * ˇ
 2889    "});
 2890        // Ensure that if cursor is before the comment start,
 2891        // we do not actually insert a comment prefix.
 2892        cx.set_state(indoc! {"
 2893        ˇ/**
 2894    "});
 2895        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2896        cx.assert_editor_state(indoc! {"
 2897
 2898        ˇ/**
 2899    "});
 2900        // Ensure that if cursor is between it doesn't add comment prefix.
 2901        cx.set_state(indoc! {"
 2902        /*ˇ*
 2903    "});
 2904        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2905        cx.assert_editor_state(indoc! {"
 2906        /*
 2907        ˇ*
 2908    "});
 2909        // Ensure that if suffix exists on same line after cursor it adds new line.
 2910        cx.set_state(indoc! {"
 2911        /**ˇ*/
 2912    "});
 2913        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2914        cx.assert_editor_state(indoc! {"
 2915        /**
 2916         * ˇ
 2917         */
 2918    "});
 2919        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2920        cx.set_state(indoc! {"
 2921        /**ˇ */
 2922    "});
 2923        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2924        cx.assert_editor_state(indoc! {"
 2925        /**
 2926         * ˇ
 2927         */
 2928    "});
 2929        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2930        cx.set_state(indoc! {"
 2931        /** ˇ*/
 2932    "});
 2933        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2934        cx.assert_editor_state(
 2935            indoc! {"
 2936        /**s
 2937         * ˇ
 2938         */
 2939    "}
 2940            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2941            .as_str(),
 2942        );
 2943        // Ensure that delimiter space is preserved when newline on already
 2944        // spaced delimiter.
 2945        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2946        cx.assert_editor_state(
 2947            indoc! {"
 2948        /**s
 2949         *s
 2950         * ˇ
 2951         */
 2952    "}
 2953            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2954            .as_str(),
 2955        );
 2956        // Ensure that delimiter space is preserved when space is not
 2957        // on existing delimiter.
 2958        cx.set_state(indoc! {"
 2959        /**
 2960 2961         */
 2962    "});
 2963        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2964        cx.assert_editor_state(indoc! {"
 2965        /**
 2966         *
 2967         * ˇ
 2968         */
 2969    "});
 2970        // Ensure that if suffix exists on same line after cursor it
 2971        // doesn't add extra new line if prefix is not on same line.
 2972        cx.set_state(indoc! {"
 2973        /**
 2974        ˇ*/
 2975    "});
 2976        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2977        cx.assert_editor_state(indoc! {"
 2978        /**
 2979
 2980        ˇ*/
 2981    "});
 2982        // Ensure that it detects suffix after existing prefix.
 2983        cx.set_state(indoc! {"
 2984        /**ˇ/
 2985    "});
 2986        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2987        cx.assert_editor_state(indoc! {"
 2988        /**
 2989        ˇ/
 2990    "});
 2991        // Ensure that if suffix exists on same line before
 2992        // cursor it does not add comment prefix.
 2993        cx.set_state(indoc! {"
 2994        /** */ˇ
 2995    "});
 2996        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2997        cx.assert_editor_state(indoc! {"
 2998        /** */
 2999        ˇ
 3000    "});
 3001        // Ensure that if suffix exists on same line before
 3002        // cursor it does not add comment prefix.
 3003        cx.set_state(indoc! {"
 3004        /**
 3005         *
 3006         */ˇ
 3007    "});
 3008        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3009        cx.assert_editor_state(indoc! {"
 3010        /**
 3011         *
 3012         */
 3013         ˇ
 3014    "});
 3015
 3016        // Ensure that inline comment followed by code
 3017        // doesn't add comment prefix on newline
 3018        cx.set_state(indoc! {"
 3019        /** */ textˇ
 3020    "});
 3021        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3022        cx.assert_editor_state(indoc! {"
 3023        /** */ text
 3024        ˇ
 3025    "});
 3026
 3027        // Ensure that text after comment end tag
 3028        // doesn't add comment prefix on newline
 3029        cx.set_state(indoc! {"
 3030        /**
 3031         *
 3032         */ˇtext
 3033    "});
 3034        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3035        cx.assert_editor_state(indoc! {"
 3036        /**
 3037         *
 3038         */
 3039         ˇtext
 3040    "});
 3041    }
 3042    // Ensure that comment continuations can be disabled.
 3043    update_test_language_settings(cx, |settings| {
 3044        settings.defaults.extend_comment_on_newline = Some(false);
 3045    });
 3046    let mut cx = EditorTestContext::new(cx).await;
 3047    cx.set_state(indoc! {"
 3048        /**ˇ
 3049    "});
 3050    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        /**
 3053        ˇ
 3054    "});
 3055}
 3056
 3057#[gpui::test]
 3058fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 3059    init_test(cx, |_| {});
 3060
 3061    let editor = cx.add_window(|window, cx| {
 3062        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3063        let mut editor = build_editor(buffer.clone(), window, cx);
 3064        editor.change_selections(None, window, cx, |s| {
 3065            s.select_ranges([3..4, 11..12, 19..20])
 3066        });
 3067        editor
 3068    });
 3069
 3070    _ = editor.update(cx, |editor, window, cx| {
 3071        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3072        editor.buffer.update(cx, |buffer, cx| {
 3073            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3074            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3075        });
 3076        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3077
 3078        editor.insert("Z", window, cx);
 3079        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3080
 3081        // The selections are moved after the inserted characters
 3082        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3083    });
 3084}
 3085
 3086#[gpui::test]
 3087async fn test_tab(cx: &mut TestAppContext) {
 3088    init_test(cx, |settings| {
 3089        settings.defaults.tab_size = NonZeroU32::new(3)
 3090    });
 3091
 3092    let mut cx = EditorTestContext::new(cx).await;
 3093    cx.set_state(indoc! {"
 3094        ˇabˇc
 3095        ˇ🏀ˇ🏀ˇefg
 3096 3097    "});
 3098    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3099    cx.assert_editor_state(indoc! {"
 3100           ˇab ˇc
 3101           ˇ🏀  ˇ🏀  ˇefg
 3102        d  ˇ
 3103    "});
 3104
 3105    cx.set_state(indoc! {"
 3106        a
 3107        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        a
 3112           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3113    "});
 3114}
 3115
 3116#[gpui::test]
 3117async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3118    init_test(cx, |_| {});
 3119
 3120    let mut cx = EditorTestContext::new(cx).await;
 3121    let language = Arc::new(
 3122        Language::new(
 3123            LanguageConfig::default(),
 3124            Some(tree_sitter_rust::LANGUAGE.into()),
 3125        )
 3126        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3127        .unwrap(),
 3128    );
 3129    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3130
 3131    // test when all cursors are not at suggested indent
 3132    // then simply move to their suggested indent location
 3133    cx.set_state(indoc! {"
 3134        const a: B = (
 3135            c(
 3136        ˇ
 3137        ˇ    )
 3138        );
 3139    "});
 3140    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3141    cx.assert_editor_state(indoc! {"
 3142        const a: B = (
 3143            c(
 3144                ˇ
 3145            ˇ)
 3146        );
 3147    "});
 3148
 3149    // test cursor already at suggested indent not moving when
 3150    // other cursors are yet to reach their suggested indents
 3151    cx.set_state(indoc! {"
 3152        ˇ
 3153        const a: B = (
 3154            c(
 3155                d(
 3156        ˇ
 3157                )
 3158        ˇ
 3159        ˇ    )
 3160        );
 3161    "});
 3162    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        ˇ
 3165        const a: B = (
 3166            c(
 3167                d(
 3168                    ˇ
 3169                )
 3170                ˇ
 3171            ˇ)
 3172        );
 3173    "});
 3174    // test when all cursors are at suggested indent then tab is inserted
 3175    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3176    cx.assert_editor_state(indoc! {"
 3177            ˇ
 3178        const a: B = (
 3179            c(
 3180                d(
 3181                        ˇ
 3182                )
 3183                    ˇ
 3184                ˇ)
 3185        );
 3186    "});
 3187
 3188    // test when current indent is less than suggested indent,
 3189    // we adjust line to match suggested indent and move cursor to it
 3190    //
 3191    // when no other cursor is at word boundary, all of them should move
 3192    cx.set_state(indoc! {"
 3193        const a: B = (
 3194            c(
 3195                d(
 3196        ˇ
 3197        ˇ   )
 3198        ˇ   )
 3199        );
 3200    "});
 3201    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3202    cx.assert_editor_state(indoc! {"
 3203        const a: B = (
 3204            c(
 3205                d(
 3206                    ˇ
 3207                ˇ)
 3208            ˇ)
 3209        );
 3210    "});
 3211
 3212    // test when current indent is less than suggested indent,
 3213    // we adjust line to match suggested indent and move cursor to it
 3214    //
 3215    // when some other cursor is at word boundary, it should not move
 3216    cx.set_state(indoc! {"
 3217        const a: B = (
 3218            c(
 3219                d(
 3220        ˇ
 3221        ˇ   )
 3222           ˇ)
 3223        );
 3224    "});
 3225    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3226    cx.assert_editor_state(indoc! {"
 3227        const a: B = (
 3228            c(
 3229                d(
 3230                    ˇ
 3231                ˇ)
 3232            ˇ)
 3233        );
 3234    "});
 3235
 3236    // test when current indent is more than suggested indent,
 3237    // we just move cursor to current indent instead of suggested indent
 3238    //
 3239    // when no other cursor is at word boundary, all of them should move
 3240    cx.set_state(indoc! {"
 3241        const a: B = (
 3242            c(
 3243                d(
 3244        ˇ
 3245        ˇ                )
 3246        ˇ   )
 3247        );
 3248    "});
 3249    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3250    cx.assert_editor_state(indoc! {"
 3251        const a: B = (
 3252            c(
 3253                d(
 3254                    ˇ
 3255                        ˇ)
 3256            ˇ)
 3257        );
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        const a: B = (
 3262            c(
 3263                d(
 3264                        ˇ
 3265                            ˇ)
 3266                ˇ)
 3267        );
 3268    "});
 3269
 3270    // test when current indent is more than suggested indent,
 3271    // we just move cursor to current indent instead of suggested indent
 3272    //
 3273    // when some other cursor is at word boundary, it doesn't move
 3274    cx.set_state(indoc! {"
 3275        const a: B = (
 3276            c(
 3277                d(
 3278        ˇ
 3279        ˇ                )
 3280            ˇ)
 3281        );
 3282    "});
 3283    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3284    cx.assert_editor_state(indoc! {"
 3285        const a: B = (
 3286            c(
 3287                d(
 3288                    ˇ
 3289                        ˇ)
 3290            ˇ)
 3291        );
 3292    "});
 3293
 3294    // handle auto-indent when there are multiple cursors on the same line
 3295    cx.set_state(indoc! {"
 3296        const a: B = (
 3297            c(
 3298        ˇ    ˇ
 3299        ˇ    )
 3300        );
 3301    "});
 3302    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3303    cx.assert_editor_state(indoc! {"
 3304        const a: B = (
 3305            c(
 3306                ˇ
 3307            ˇ)
 3308        );
 3309    "});
 3310}
 3311
 3312#[gpui::test]
 3313async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3314    init_test(cx, |settings| {
 3315        settings.defaults.tab_size = NonZeroU32::new(3)
 3316    });
 3317
 3318    let mut cx = EditorTestContext::new(cx).await;
 3319    cx.set_state(indoc! {"
 3320         ˇ
 3321        \t ˇ
 3322        \t  ˇ
 3323        \t   ˇ
 3324         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3325    "});
 3326
 3327    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3328    cx.assert_editor_state(indoc! {"
 3329           ˇ
 3330        \t   ˇ
 3331        \t   ˇ
 3332        \t      ˇ
 3333         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3334    "});
 3335}
 3336
 3337#[gpui::test]
 3338async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3339    init_test(cx, |settings| {
 3340        settings.defaults.tab_size = NonZeroU32::new(4)
 3341    });
 3342
 3343    let language = Arc::new(
 3344        Language::new(
 3345            LanguageConfig::default(),
 3346            Some(tree_sitter_rust::LANGUAGE.into()),
 3347        )
 3348        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3349        .unwrap(),
 3350    );
 3351
 3352    let mut cx = EditorTestContext::new(cx).await;
 3353    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3354    cx.set_state(indoc! {"
 3355        fn a() {
 3356            if b {
 3357        \t ˇc
 3358            }
 3359        }
 3360    "});
 3361
 3362    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        fn a() {
 3365            if b {
 3366                ˇc
 3367            }
 3368        }
 3369    "});
 3370}
 3371
 3372#[gpui::test]
 3373async fn test_indent_outdent(cx: &mut TestAppContext) {
 3374    init_test(cx, |settings| {
 3375        settings.defaults.tab_size = NonZeroU32::new(4);
 3376    });
 3377
 3378    let mut cx = EditorTestContext::new(cx).await;
 3379
 3380    cx.set_state(indoc! {"
 3381          «oneˇ» «twoˇ»
 3382        three
 3383         four
 3384    "});
 3385    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3386    cx.assert_editor_state(indoc! {"
 3387            «oneˇ» «twoˇ»
 3388        three
 3389         four
 3390    "});
 3391
 3392    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3393    cx.assert_editor_state(indoc! {"
 3394        «oneˇ» «twoˇ»
 3395        three
 3396         four
 3397    "});
 3398
 3399    // select across line ending
 3400    cx.set_state(indoc! {"
 3401        one two
 3402        t«hree
 3403        ˇ» four
 3404    "});
 3405    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3406    cx.assert_editor_state(indoc! {"
 3407        one two
 3408            t«hree
 3409        ˇ» four
 3410    "});
 3411
 3412    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3413    cx.assert_editor_state(indoc! {"
 3414        one two
 3415        t«hree
 3416        ˇ» four
 3417    "});
 3418
 3419    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3420    cx.set_state(indoc! {"
 3421        one two
 3422        ˇthree
 3423            four
 3424    "});
 3425    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3426    cx.assert_editor_state(indoc! {"
 3427        one two
 3428            ˇthree
 3429            four
 3430    "});
 3431
 3432    cx.set_state(indoc! {"
 3433        one two
 3434        ˇ    three
 3435            four
 3436    "});
 3437    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3438    cx.assert_editor_state(indoc! {"
 3439        one two
 3440        ˇthree
 3441            four
 3442    "});
 3443}
 3444
 3445#[gpui::test]
 3446async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3447    init_test(cx, |settings| {
 3448        settings.defaults.hard_tabs = Some(true);
 3449    });
 3450
 3451    let mut cx = EditorTestContext::new(cx).await;
 3452
 3453    // select two ranges on one line
 3454    cx.set_state(indoc! {"
 3455        «oneˇ» «twoˇ»
 3456        three
 3457        four
 3458    "});
 3459    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3460    cx.assert_editor_state(indoc! {"
 3461        \t«oneˇ» «twoˇ»
 3462        three
 3463        four
 3464    "});
 3465    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3466    cx.assert_editor_state(indoc! {"
 3467        \t\t«oneˇ» «twoˇ»
 3468        three
 3469        four
 3470    "});
 3471    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3472    cx.assert_editor_state(indoc! {"
 3473        \t«oneˇ» «twoˇ»
 3474        three
 3475        four
 3476    "});
 3477    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3478    cx.assert_editor_state(indoc! {"
 3479        «oneˇ» «twoˇ»
 3480        three
 3481        four
 3482    "});
 3483
 3484    // select across a line ending
 3485    cx.set_state(indoc! {"
 3486        one two
 3487        t«hree
 3488        ˇ»four
 3489    "});
 3490    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3491    cx.assert_editor_state(indoc! {"
 3492        one two
 3493        \tt«hree
 3494        ˇ»four
 3495    "});
 3496    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3497    cx.assert_editor_state(indoc! {"
 3498        one two
 3499        \t\tt«hree
 3500        ˇ»four
 3501    "});
 3502    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3503    cx.assert_editor_state(indoc! {"
 3504        one two
 3505        \tt«hree
 3506        ˇ»four
 3507    "});
 3508    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3509    cx.assert_editor_state(indoc! {"
 3510        one two
 3511        t«hree
 3512        ˇ»four
 3513    "});
 3514
 3515    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3516    cx.set_state(indoc! {"
 3517        one two
 3518        ˇthree
 3519        four
 3520    "});
 3521    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3522    cx.assert_editor_state(indoc! {"
 3523        one two
 3524        ˇthree
 3525        four
 3526    "});
 3527    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3528    cx.assert_editor_state(indoc! {"
 3529        one two
 3530        \tˇthree
 3531        four
 3532    "});
 3533    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3534    cx.assert_editor_state(indoc! {"
 3535        one two
 3536        ˇthree
 3537        four
 3538    "});
 3539}
 3540
 3541#[gpui::test]
 3542fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3543    init_test(cx, |settings| {
 3544        settings.languages.extend([
 3545            (
 3546                "TOML".into(),
 3547                LanguageSettingsContent {
 3548                    tab_size: NonZeroU32::new(2),
 3549                    ..Default::default()
 3550                },
 3551            ),
 3552            (
 3553                "Rust".into(),
 3554                LanguageSettingsContent {
 3555                    tab_size: NonZeroU32::new(4),
 3556                    ..Default::default()
 3557                },
 3558            ),
 3559        ]);
 3560    });
 3561
 3562    let toml_language = Arc::new(Language::new(
 3563        LanguageConfig {
 3564            name: "TOML".into(),
 3565            ..Default::default()
 3566        },
 3567        None,
 3568    ));
 3569    let rust_language = Arc::new(Language::new(
 3570        LanguageConfig {
 3571            name: "Rust".into(),
 3572            ..Default::default()
 3573        },
 3574        None,
 3575    ));
 3576
 3577    let toml_buffer =
 3578        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3579    let rust_buffer =
 3580        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3581    let multibuffer = cx.new(|cx| {
 3582        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3583        multibuffer.push_excerpts(
 3584            toml_buffer.clone(),
 3585            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3586            cx,
 3587        );
 3588        multibuffer.push_excerpts(
 3589            rust_buffer.clone(),
 3590            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3591            cx,
 3592        );
 3593        multibuffer
 3594    });
 3595
 3596    cx.add_window(|window, cx| {
 3597        let mut editor = build_editor(multibuffer, window, cx);
 3598
 3599        assert_eq!(
 3600            editor.text(cx),
 3601            indoc! {"
 3602                a = 1
 3603                b = 2
 3604
 3605                const c: usize = 3;
 3606            "}
 3607        );
 3608
 3609        select_ranges(
 3610            &mut editor,
 3611            indoc! {"
 3612                «aˇ» = 1
 3613                b = 2
 3614
 3615                «const c:ˇ» usize = 3;
 3616            "},
 3617            window,
 3618            cx,
 3619        );
 3620
 3621        editor.tab(&Tab, window, cx);
 3622        assert_text_with_selections(
 3623            &mut editor,
 3624            indoc! {"
 3625                  «aˇ» = 1
 3626                b = 2
 3627
 3628                    «const c:ˇ» usize = 3;
 3629            "},
 3630            cx,
 3631        );
 3632        editor.backtab(&Backtab, window, cx);
 3633        assert_text_with_selections(
 3634            &mut editor,
 3635            indoc! {"
 3636                «aˇ» = 1
 3637                b = 2
 3638
 3639                «const c:ˇ» usize = 3;
 3640            "},
 3641            cx,
 3642        );
 3643
 3644        editor
 3645    });
 3646}
 3647
 3648#[gpui::test]
 3649async fn test_backspace(cx: &mut TestAppContext) {
 3650    init_test(cx, |_| {});
 3651
 3652    let mut cx = EditorTestContext::new(cx).await;
 3653
 3654    // Basic backspace
 3655    cx.set_state(indoc! {"
 3656        onˇe two three
 3657        fou«rˇ» five six
 3658        seven «ˇeight nine
 3659        »ten
 3660    "});
 3661    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3662    cx.assert_editor_state(indoc! {"
 3663        oˇe two three
 3664        fouˇ five six
 3665        seven ˇten
 3666    "});
 3667
 3668    // Test backspace inside and around indents
 3669    cx.set_state(indoc! {"
 3670        zero
 3671            ˇone
 3672                ˇtwo
 3673            ˇ ˇ ˇ  three
 3674        ˇ  ˇ  four
 3675    "});
 3676    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3677    cx.assert_editor_state(indoc! {"
 3678        zero
 3679        ˇone
 3680            ˇtwo
 3681        ˇ  threeˇ  four
 3682    "});
 3683}
 3684
 3685#[gpui::test]
 3686async fn test_delete(cx: &mut TestAppContext) {
 3687    init_test(cx, |_| {});
 3688
 3689    let mut cx = EditorTestContext::new(cx).await;
 3690    cx.set_state(indoc! {"
 3691        onˇe two three
 3692        fou«rˇ» five six
 3693        seven «ˇeight nine
 3694        »ten
 3695    "});
 3696    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3697    cx.assert_editor_state(indoc! {"
 3698        onˇ two three
 3699        fouˇ five six
 3700        seven ˇten
 3701    "});
 3702}
 3703
 3704#[gpui::test]
 3705fn test_delete_line(cx: &mut TestAppContext) {
 3706    init_test(cx, |_| {});
 3707
 3708    let editor = cx.add_window(|window, cx| {
 3709        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3710        build_editor(buffer, window, cx)
 3711    });
 3712    _ = editor.update(cx, |editor, window, cx| {
 3713        editor.change_selections(None, window, cx, |s| {
 3714            s.select_display_ranges([
 3715                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3716                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3717                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3718            ])
 3719        });
 3720        editor.delete_line(&DeleteLine, window, cx);
 3721        assert_eq!(editor.display_text(cx), "ghi");
 3722        assert_eq!(
 3723            editor.selections.display_ranges(cx),
 3724            vec![
 3725                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3726                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3727            ]
 3728        );
 3729    });
 3730
 3731    let editor = cx.add_window(|window, cx| {
 3732        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3733        build_editor(buffer, window, cx)
 3734    });
 3735    _ = editor.update(cx, |editor, window, cx| {
 3736        editor.change_selections(None, window, cx, |s| {
 3737            s.select_display_ranges([
 3738                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3739            ])
 3740        });
 3741        editor.delete_line(&DeleteLine, window, cx);
 3742        assert_eq!(editor.display_text(cx), "ghi\n");
 3743        assert_eq!(
 3744            editor.selections.display_ranges(cx),
 3745            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3746        );
 3747    });
 3748}
 3749
 3750#[gpui::test]
 3751fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3752    init_test(cx, |_| {});
 3753
 3754    cx.add_window(|window, cx| {
 3755        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3756        let mut editor = build_editor(buffer.clone(), window, cx);
 3757        let buffer = buffer.read(cx).as_singleton().unwrap();
 3758
 3759        assert_eq!(
 3760            editor.selections.ranges::<Point>(cx),
 3761            &[Point::new(0, 0)..Point::new(0, 0)]
 3762        );
 3763
 3764        // When on single line, replace newline at end by space
 3765        editor.join_lines(&JoinLines, window, cx);
 3766        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3767        assert_eq!(
 3768            editor.selections.ranges::<Point>(cx),
 3769            &[Point::new(0, 3)..Point::new(0, 3)]
 3770        );
 3771
 3772        // When multiple lines are selected, remove newlines that are spanned by the selection
 3773        editor.change_selections(None, window, cx, |s| {
 3774            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3775        });
 3776        editor.join_lines(&JoinLines, window, cx);
 3777        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3778        assert_eq!(
 3779            editor.selections.ranges::<Point>(cx),
 3780            &[Point::new(0, 11)..Point::new(0, 11)]
 3781        );
 3782
 3783        // Undo should be transactional
 3784        editor.undo(&Undo, window, cx);
 3785        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3786        assert_eq!(
 3787            editor.selections.ranges::<Point>(cx),
 3788            &[Point::new(0, 5)..Point::new(2, 2)]
 3789        );
 3790
 3791        // When joining an empty line don't insert a space
 3792        editor.change_selections(None, window, cx, |s| {
 3793            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3794        });
 3795        editor.join_lines(&JoinLines, window, cx);
 3796        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3797        assert_eq!(
 3798            editor.selections.ranges::<Point>(cx),
 3799            [Point::new(2, 3)..Point::new(2, 3)]
 3800        );
 3801
 3802        // We can remove trailing newlines
 3803        editor.join_lines(&JoinLines, window, cx);
 3804        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3805        assert_eq!(
 3806            editor.selections.ranges::<Point>(cx),
 3807            [Point::new(2, 3)..Point::new(2, 3)]
 3808        );
 3809
 3810        // We don't blow up on the last line
 3811        editor.join_lines(&JoinLines, window, cx);
 3812        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3813        assert_eq!(
 3814            editor.selections.ranges::<Point>(cx),
 3815            [Point::new(2, 3)..Point::new(2, 3)]
 3816        );
 3817
 3818        // reset to test indentation
 3819        editor.buffer.update(cx, |buffer, cx| {
 3820            buffer.edit(
 3821                [
 3822                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3823                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3824                ],
 3825                None,
 3826                cx,
 3827            )
 3828        });
 3829
 3830        // We remove any leading spaces
 3831        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3832        editor.change_selections(None, window, cx, |s| {
 3833            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3834        });
 3835        editor.join_lines(&JoinLines, window, cx);
 3836        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3837
 3838        // We don't insert a space for a line containing only spaces
 3839        editor.join_lines(&JoinLines, window, cx);
 3840        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3841
 3842        // We ignore any leading tabs
 3843        editor.join_lines(&JoinLines, window, cx);
 3844        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3845
 3846        editor
 3847    });
 3848}
 3849
 3850#[gpui::test]
 3851fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3852    init_test(cx, |_| {});
 3853
 3854    cx.add_window(|window, cx| {
 3855        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3856        let mut editor = build_editor(buffer.clone(), window, cx);
 3857        let buffer = buffer.read(cx).as_singleton().unwrap();
 3858
 3859        editor.change_selections(None, window, cx, |s| {
 3860            s.select_ranges([
 3861                Point::new(0, 2)..Point::new(1, 1),
 3862                Point::new(1, 2)..Point::new(1, 2),
 3863                Point::new(3, 1)..Point::new(3, 2),
 3864            ])
 3865        });
 3866
 3867        editor.join_lines(&JoinLines, window, cx);
 3868        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3869
 3870        assert_eq!(
 3871            editor.selections.ranges::<Point>(cx),
 3872            [
 3873                Point::new(0, 7)..Point::new(0, 7),
 3874                Point::new(1, 3)..Point::new(1, 3)
 3875            ]
 3876        );
 3877        editor
 3878    });
 3879}
 3880
 3881#[gpui::test]
 3882async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3883    init_test(cx, |_| {});
 3884
 3885    let mut cx = EditorTestContext::new(cx).await;
 3886
 3887    let diff_base = r#"
 3888        Line 0
 3889        Line 1
 3890        Line 2
 3891        Line 3
 3892        "#
 3893    .unindent();
 3894
 3895    cx.set_state(
 3896        &r#"
 3897        ˇLine 0
 3898        Line 1
 3899        Line 2
 3900        Line 3
 3901        "#
 3902        .unindent(),
 3903    );
 3904
 3905    cx.set_head_text(&diff_base);
 3906    executor.run_until_parked();
 3907
 3908    // Join lines
 3909    cx.update_editor(|editor, window, cx| {
 3910        editor.join_lines(&JoinLines, window, cx);
 3911    });
 3912    executor.run_until_parked();
 3913
 3914    cx.assert_editor_state(
 3915        &r#"
 3916        Line 0ˇ Line 1
 3917        Line 2
 3918        Line 3
 3919        "#
 3920        .unindent(),
 3921    );
 3922    // Join again
 3923    cx.update_editor(|editor, window, cx| {
 3924        editor.join_lines(&JoinLines, window, cx);
 3925    });
 3926    executor.run_until_parked();
 3927
 3928    cx.assert_editor_state(
 3929        &r#"
 3930        Line 0 Line 1ˇ Line 2
 3931        Line 3
 3932        "#
 3933        .unindent(),
 3934    );
 3935}
 3936
 3937#[gpui::test]
 3938async fn test_custom_newlines_cause_no_false_positive_diffs(
 3939    executor: BackgroundExecutor,
 3940    cx: &mut TestAppContext,
 3941) {
 3942    init_test(cx, |_| {});
 3943    let mut cx = EditorTestContext::new(cx).await;
 3944    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3945    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3946    executor.run_until_parked();
 3947
 3948    cx.update_editor(|editor, window, cx| {
 3949        let snapshot = editor.snapshot(window, cx);
 3950        assert_eq!(
 3951            snapshot
 3952                .buffer_snapshot
 3953                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3954                .collect::<Vec<_>>(),
 3955            Vec::new(),
 3956            "Should not have any diffs for files with custom newlines"
 3957        );
 3958    });
 3959}
 3960
 3961#[gpui::test]
 3962async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3963    init_test(cx, |_| {});
 3964
 3965    let mut cx = EditorTestContext::new(cx).await;
 3966
 3967    // Test sort_lines_case_insensitive()
 3968    cx.set_state(indoc! {"
 3969        «z
 3970        y
 3971        x
 3972        Z
 3973        Y
 3974        Xˇ»
 3975    "});
 3976    cx.update_editor(|e, window, cx| {
 3977        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3978    });
 3979    cx.assert_editor_state(indoc! {"
 3980        «x
 3981        X
 3982        y
 3983        Y
 3984        z
 3985        Zˇ»
 3986    "});
 3987
 3988    // Test reverse_lines()
 3989    cx.set_state(indoc! {"
 3990        «5
 3991        4
 3992        3
 3993        2
 3994        1ˇ»
 3995    "});
 3996    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3997    cx.assert_editor_state(indoc! {"
 3998        «1
 3999        2
 4000        3
 4001        4
 4002        5ˇ»
 4003    "});
 4004
 4005    // Skip testing shuffle_line()
 4006
 4007    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 4008    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 4009
 4010    // Don't manipulate when cursor is on single line, but expand the selection
 4011    cx.set_state(indoc! {"
 4012        ddˇdd
 4013        ccc
 4014        bb
 4015        a
 4016    "});
 4017    cx.update_editor(|e, window, cx| {
 4018        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4019    });
 4020    cx.assert_editor_state(indoc! {"
 4021        «ddddˇ»
 4022        ccc
 4023        bb
 4024        a
 4025    "});
 4026
 4027    // Basic manipulate case
 4028    // Start selection moves to column 0
 4029    // End of selection shrinks to fit shorter line
 4030    cx.set_state(indoc! {"
 4031        dd«d
 4032        ccc
 4033        bb
 4034        aaaaaˇ»
 4035    "});
 4036    cx.update_editor(|e, window, cx| {
 4037        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4038    });
 4039    cx.assert_editor_state(indoc! {"
 4040        «aaaaa
 4041        bb
 4042        ccc
 4043        dddˇ»
 4044    "});
 4045
 4046    // Manipulate case with newlines
 4047    cx.set_state(indoc! {"
 4048        dd«d
 4049        ccc
 4050
 4051        bb
 4052        aaaaa
 4053
 4054        ˇ»
 4055    "});
 4056    cx.update_editor(|e, window, cx| {
 4057        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4058    });
 4059    cx.assert_editor_state(indoc! {"
 4060        «
 4061
 4062        aaaaa
 4063        bb
 4064        ccc
 4065        dddˇ»
 4066
 4067    "});
 4068
 4069    // Adding new line
 4070    cx.set_state(indoc! {"
 4071        aa«a
 4072        bbˇ»b
 4073    "});
 4074    cx.update_editor(|e, window, cx| {
 4075        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4076    });
 4077    cx.assert_editor_state(indoc! {"
 4078        «aaa
 4079        bbb
 4080        added_lineˇ»
 4081    "});
 4082
 4083    // Removing line
 4084    cx.set_state(indoc! {"
 4085        aa«a
 4086        bbbˇ»
 4087    "});
 4088    cx.update_editor(|e, window, cx| {
 4089        e.manipulate_lines(window, cx, |lines| {
 4090            lines.pop();
 4091        })
 4092    });
 4093    cx.assert_editor_state(indoc! {"
 4094        «aaaˇ»
 4095    "});
 4096
 4097    // Removing all lines
 4098    cx.set_state(indoc! {"
 4099        aa«a
 4100        bbbˇ»
 4101    "});
 4102    cx.update_editor(|e, window, cx| {
 4103        e.manipulate_lines(window, cx, |lines| {
 4104            lines.drain(..);
 4105        })
 4106    });
 4107    cx.assert_editor_state(indoc! {"
 4108        ˇ
 4109    "});
 4110}
 4111
 4112#[gpui::test]
 4113async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4114    init_test(cx, |_| {});
 4115
 4116    let mut cx = EditorTestContext::new(cx).await;
 4117
 4118    // Consider continuous selection as single selection
 4119    cx.set_state(indoc! {"
 4120        Aaa«aa
 4121        cˇ»c«c
 4122        bb
 4123        aaaˇ»aa
 4124    "});
 4125    cx.update_editor(|e, window, cx| {
 4126        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4127    });
 4128    cx.assert_editor_state(indoc! {"
 4129        «Aaaaa
 4130        ccc
 4131        bb
 4132        aaaaaˇ»
 4133    "});
 4134
 4135    cx.set_state(indoc! {"
 4136        Aaa«aa
 4137        cˇ»c«c
 4138        bb
 4139        aaaˇ»aa
 4140    "});
 4141    cx.update_editor(|e, window, cx| {
 4142        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4143    });
 4144    cx.assert_editor_state(indoc! {"
 4145        «Aaaaa
 4146        ccc
 4147        bbˇ»
 4148    "});
 4149
 4150    // Consider non continuous selection as distinct dedup operations
 4151    cx.set_state(indoc! {"
 4152        «aaaaa
 4153        bb
 4154        aaaaa
 4155        aaaaaˇ»
 4156
 4157        aaa«aaˇ»
 4158    "});
 4159    cx.update_editor(|e, window, cx| {
 4160        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4161    });
 4162    cx.assert_editor_state(indoc! {"
 4163        «aaaaa
 4164        bbˇ»
 4165
 4166        «aaaaaˇ»
 4167    "});
 4168}
 4169
 4170#[gpui::test]
 4171async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4172    init_test(cx, |_| {});
 4173
 4174    let mut cx = EditorTestContext::new(cx).await;
 4175
 4176    cx.set_state(indoc! {"
 4177        «Aaa
 4178        aAa
 4179        Aaaˇ»
 4180    "});
 4181    cx.update_editor(|e, window, cx| {
 4182        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4183    });
 4184    cx.assert_editor_state(indoc! {"
 4185        «Aaa
 4186        aAaˇ»
 4187    "});
 4188
 4189    cx.set_state(indoc! {"
 4190        «Aaa
 4191        aAa
 4192        aaAˇ»
 4193    "});
 4194    cx.update_editor(|e, window, cx| {
 4195        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4196    });
 4197    cx.assert_editor_state(indoc! {"
 4198        «Aaaˇ»
 4199    "});
 4200}
 4201
 4202#[gpui::test]
 4203async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4204    init_test(cx, |_| {});
 4205
 4206    let mut cx = EditorTestContext::new(cx).await;
 4207
 4208    // Manipulate with multiple selections on a single line
 4209    cx.set_state(indoc! {"
 4210        dd«dd
 4211        cˇ»c«c
 4212        bb
 4213        aaaˇ»aa
 4214    "});
 4215    cx.update_editor(|e, window, cx| {
 4216        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4217    });
 4218    cx.assert_editor_state(indoc! {"
 4219        «aaaaa
 4220        bb
 4221        ccc
 4222        ddddˇ»
 4223    "});
 4224
 4225    // Manipulate with multiple disjoin selections
 4226    cx.set_state(indoc! {"
 4227 4228        4
 4229        3
 4230        2
 4231        1ˇ»
 4232
 4233        dd«dd
 4234        ccc
 4235        bb
 4236        aaaˇ»aa
 4237    "});
 4238    cx.update_editor(|e, window, cx| {
 4239        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4240    });
 4241    cx.assert_editor_state(indoc! {"
 4242        «1
 4243        2
 4244        3
 4245        4
 4246        5ˇ»
 4247
 4248        «aaaaa
 4249        bb
 4250        ccc
 4251        ddddˇ»
 4252    "});
 4253
 4254    // Adding lines on each selection
 4255    cx.set_state(indoc! {"
 4256 4257        1ˇ»
 4258
 4259        bb«bb
 4260        aaaˇ»aa
 4261    "});
 4262    cx.update_editor(|e, window, cx| {
 4263        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4264    });
 4265    cx.assert_editor_state(indoc! {"
 4266        «2
 4267        1
 4268        added lineˇ»
 4269
 4270        «bbbb
 4271        aaaaa
 4272        added lineˇ»
 4273    "});
 4274
 4275    // Removing lines on each selection
 4276    cx.set_state(indoc! {"
 4277 4278        1ˇ»
 4279
 4280        bb«bb
 4281        aaaˇ»aa
 4282    "});
 4283    cx.update_editor(|e, window, cx| {
 4284        e.manipulate_lines(window, cx, |lines| {
 4285            lines.pop();
 4286        })
 4287    });
 4288    cx.assert_editor_state(indoc! {"
 4289        «2ˇ»
 4290
 4291        «bbbbˇ»
 4292    "});
 4293}
 4294
 4295#[gpui::test]
 4296async fn test_toggle_case(cx: &mut TestAppContext) {
 4297    init_test(cx, |_| {});
 4298
 4299    let mut cx = EditorTestContext::new(cx).await;
 4300
 4301    // If all lower case -> upper case
 4302    cx.set_state(indoc! {"
 4303        «hello worldˇ»
 4304    "});
 4305    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4306    cx.assert_editor_state(indoc! {"
 4307        «HELLO WORLDˇ»
 4308    "});
 4309
 4310    // If all upper case -> lower case
 4311    cx.set_state(indoc! {"
 4312        «HELLO WORLDˇ»
 4313    "});
 4314    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4315    cx.assert_editor_state(indoc! {"
 4316        «hello worldˇ»
 4317    "});
 4318
 4319    // If any upper case characters are identified -> lower case
 4320    // This matches JetBrains IDEs
 4321    cx.set_state(indoc! {"
 4322        «hEllo worldˇ»
 4323    "});
 4324    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4325    cx.assert_editor_state(indoc! {"
 4326        «hello worldˇ»
 4327    "});
 4328}
 4329
 4330#[gpui::test]
 4331async fn test_manipulate_text(cx: &mut TestAppContext) {
 4332    init_test(cx, |_| {});
 4333
 4334    let mut cx = EditorTestContext::new(cx).await;
 4335
 4336    // Test convert_to_upper_case()
 4337    cx.set_state(indoc! {"
 4338        «hello worldˇ»
 4339    "});
 4340    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4341    cx.assert_editor_state(indoc! {"
 4342        «HELLO WORLDˇ»
 4343    "});
 4344
 4345    // Test convert_to_lower_case()
 4346    cx.set_state(indoc! {"
 4347        «HELLO WORLDˇ»
 4348    "});
 4349    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4350    cx.assert_editor_state(indoc! {"
 4351        «hello worldˇ»
 4352    "});
 4353
 4354    // Test multiple line, single selection case
 4355    cx.set_state(indoc! {"
 4356        «The quick brown
 4357        fox jumps over
 4358        the lazy dogˇ»
 4359    "});
 4360    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4361    cx.assert_editor_state(indoc! {"
 4362        «The Quick Brown
 4363        Fox Jumps Over
 4364        The Lazy Dogˇ»
 4365    "});
 4366
 4367    // Test multiple line, single selection case
 4368    cx.set_state(indoc! {"
 4369        «The quick brown
 4370        fox jumps over
 4371        the lazy dogˇ»
 4372    "});
 4373    cx.update_editor(|e, window, cx| {
 4374        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4375    });
 4376    cx.assert_editor_state(indoc! {"
 4377        «TheQuickBrown
 4378        FoxJumpsOver
 4379        TheLazyDogˇ»
 4380    "});
 4381
 4382    // From here on out, test more complex cases of manipulate_text()
 4383
 4384    // Test no selection case - should affect words cursors are in
 4385    // Cursor at beginning, middle, and end of word
 4386    cx.set_state(indoc! {"
 4387        ˇhello big beauˇtiful worldˇ
 4388    "});
 4389    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4390    cx.assert_editor_state(indoc! {"
 4391        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4392    "});
 4393
 4394    // Test multiple selections on a single line and across multiple lines
 4395    cx.set_state(indoc! {"
 4396        «Theˇ» quick «brown
 4397        foxˇ» jumps «overˇ»
 4398        the «lazyˇ» dog
 4399    "});
 4400    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4401    cx.assert_editor_state(indoc! {"
 4402        «THEˇ» quick «BROWN
 4403        FOXˇ» jumps «OVERˇ»
 4404        the «LAZYˇ» dog
 4405    "});
 4406
 4407    // Test case where text length grows
 4408    cx.set_state(indoc! {"
 4409        «tschüߡ»
 4410    "});
 4411    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4412    cx.assert_editor_state(indoc! {"
 4413        «TSCHÜSSˇ»
 4414    "});
 4415
 4416    // Test to make sure we don't crash when text shrinks
 4417    cx.set_state(indoc! {"
 4418        aaa_bbbˇ
 4419    "});
 4420    cx.update_editor(|e, window, cx| {
 4421        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4422    });
 4423    cx.assert_editor_state(indoc! {"
 4424        «aaaBbbˇ»
 4425    "});
 4426
 4427    // Test to make sure we all aware of the fact that each word can grow and shrink
 4428    // Final selections should be aware of this fact
 4429    cx.set_state(indoc! {"
 4430        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4431    "});
 4432    cx.update_editor(|e, window, cx| {
 4433        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4434    });
 4435    cx.assert_editor_state(indoc! {"
 4436        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4437    "});
 4438
 4439    cx.set_state(indoc! {"
 4440        «hElLo, WoRld!ˇ»
 4441    "});
 4442    cx.update_editor(|e, window, cx| {
 4443        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4444    });
 4445    cx.assert_editor_state(indoc! {"
 4446        «HeLlO, wOrLD!ˇ»
 4447    "});
 4448}
 4449
 4450#[gpui::test]
 4451fn test_duplicate_line(cx: &mut TestAppContext) {
 4452    init_test(cx, |_| {});
 4453
 4454    let editor = cx.add_window(|window, cx| {
 4455        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4456        build_editor(buffer, window, cx)
 4457    });
 4458    _ = editor.update(cx, |editor, window, cx| {
 4459        editor.change_selections(None, window, cx, |s| {
 4460            s.select_display_ranges([
 4461                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4462                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4463                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4464                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4465            ])
 4466        });
 4467        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4468        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4469        assert_eq!(
 4470            editor.selections.display_ranges(cx),
 4471            vec![
 4472                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4473                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4474                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4475                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4476            ]
 4477        );
 4478    });
 4479
 4480    let editor = cx.add_window(|window, cx| {
 4481        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4482        build_editor(buffer, window, cx)
 4483    });
 4484    _ = editor.update(cx, |editor, window, cx| {
 4485        editor.change_selections(None, window, cx, |s| {
 4486            s.select_display_ranges([
 4487                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4488                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4489            ])
 4490        });
 4491        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4492        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4493        assert_eq!(
 4494            editor.selections.display_ranges(cx),
 4495            vec![
 4496                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4497                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4498            ]
 4499        );
 4500    });
 4501
 4502    // With `move_upwards` the selections stay in place, except for
 4503    // the lines inserted above them
 4504    let editor = cx.add_window(|window, cx| {
 4505        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4506        build_editor(buffer, window, cx)
 4507    });
 4508    _ = editor.update(cx, |editor, window, cx| {
 4509        editor.change_selections(None, window, cx, |s| {
 4510            s.select_display_ranges([
 4511                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4512                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4513                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4514                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4515            ])
 4516        });
 4517        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4518        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4519        assert_eq!(
 4520            editor.selections.display_ranges(cx),
 4521            vec![
 4522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4523                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4524                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4525                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4526            ]
 4527        );
 4528    });
 4529
 4530    let editor = cx.add_window(|window, cx| {
 4531        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4532        build_editor(buffer, window, cx)
 4533    });
 4534    _ = editor.update(cx, |editor, window, cx| {
 4535        editor.change_selections(None, window, cx, |s| {
 4536            s.select_display_ranges([
 4537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4538                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4539            ])
 4540        });
 4541        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4542        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4543        assert_eq!(
 4544            editor.selections.display_ranges(cx),
 4545            vec![
 4546                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4547                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4548            ]
 4549        );
 4550    });
 4551
 4552    let editor = cx.add_window(|window, cx| {
 4553        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4554        build_editor(buffer, window, cx)
 4555    });
 4556    _ = editor.update(cx, |editor, window, cx| {
 4557        editor.change_selections(None, window, cx, |s| {
 4558            s.select_display_ranges([
 4559                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4560                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4561            ])
 4562        });
 4563        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4564        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4565        assert_eq!(
 4566            editor.selections.display_ranges(cx),
 4567            vec![
 4568                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4569                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4570            ]
 4571        );
 4572    });
 4573}
 4574
 4575#[gpui::test]
 4576fn test_move_line_up_down(cx: &mut TestAppContext) {
 4577    init_test(cx, |_| {});
 4578
 4579    let editor = cx.add_window(|window, cx| {
 4580        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4581        build_editor(buffer, window, cx)
 4582    });
 4583    _ = editor.update(cx, |editor, window, cx| {
 4584        editor.fold_creases(
 4585            vec![
 4586                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4587                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4588                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4589            ],
 4590            true,
 4591            window,
 4592            cx,
 4593        );
 4594        editor.change_selections(None, window, cx, |s| {
 4595            s.select_display_ranges([
 4596                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4597                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4598                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4599                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4600            ])
 4601        });
 4602        assert_eq!(
 4603            editor.display_text(cx),
 4604            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4605        );
 4606
 4607        editor.move_line_up(&MoveLineUp, window, cx);
 4608        assert_eq!(
 4609            editor.display_text(cx),
 4610            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4611        );
 4612        assert_eq!(
 4613            editor.selections.display_ranges(cx),
 4614            vec![
 4615                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4616                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4617                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4618                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4619            ]
 4620        );
 4621    });
 4622
 4623    _ = editor.update(cx, |editor, window, cx| {
 4624        editor.move_line_down(&MoveLineDown, window, cx);
 4625        assert_eq!(
 4626            editor.display_text(cx),
 4627            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4628        );
 4629        assert_eq!(
 4630            editor.selections.display_ranges(cx),
 4631            vec![
 4632                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4633                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4634                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4635                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4636            ]
 4637        );
 4638    });
 4639
 4640    _ = editor.update(cx, |editor, window, cx| {
 4641        editor.move_line_down(&MoveLineDown, window, cx);
 4642        assert_eq!(
 4643            editor.display_text(cx),
 4644            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4645        );
 4646        assert_eq!(
 4647            editor.selections.display_ranges(cx),
 4648            vec![
 4649                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4650                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4651                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4652                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4653            ]
 4654        );
 4655    });
 4656
 4657    _ = editor.update(cx, |editor, window, cx| {
 4658        editor.move_line_up(&MoveLineUp, window, cx);
 4659        assert_eq!(
 4660            editor.display_text(cx),
 4661            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4662        );
 4663        assert_eq!(
 4664            editor.selections.display_ranges(cx),
 4665            vec![
 4666                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4667                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4668                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4669                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4670            ]
 4671        );
 4672    });
 4673}
 4674
 4675#[gpui::test]
 4676fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4677    init_test(cx, |_| {});
 4678
 4679    let editor = cx.add_window(|window, cx| {
 4680        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4681        build_editor(buffer, window, cx)
 4682    });
 4683    _ = editor.update(cx, |editor, window, cx| {
 4684        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4685        editor.insert_blocks(
 4686            [BlockProperties {
 4687                style: BlockStyle::Fixed,
 4688                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4689                height: Some(1),
 4690                render: Arc::new(|_| div().into_any()),
 4691                priority: 0,
 4692                render_in_minimap: true,
 4693            }],
 4694            Some(Autoscroll::fit()),
 4695            cx,
 4696        );
 4697        editor.change_selections(None, window, cx, |s| {
 4698            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4699        });
 4700        editor.move_line_down(&MoveLineDown, window, cx);
 4701    });
 4702}
 4703
 4704#[gpui::test]
 4705async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4706    init_test(cx, |_| {});
 4707
 4708    let mut cx = EditorTestContext::new(cx).await;
 4709    cx.set_state(
 4710        &"
 4711            ˇzero
 4712            one
 4713            two
 4714            three
 4715            four
 4716            five
 4717        "
 4718        .unindent(),
 4719    );
 4720
 4721    // Create a four-line block that replaces three lines of text.
 4722    cx.update_editor(|editor, window, cx| {
 4723        let snapshot = editor.snapshot(window, cx);
 4724        let snapshot = &snapshot.buffer_snapshot;
 4725        let placement = BlockPlacement::Replace(
 4726            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4727        );
 4728        editor.insert_blocks(
 4729            [BlockProperties {
 4730                placement,
 4731                height: Some(4),
 4732                style: BlockStyle::Sticky,
 4733                render: Arc::new(|_| gpui::div().into_any_element()),
 4734                priority: 0,
 4735                render_in_minimap: true,
 4736            }],
 4737            None,
 4738            cx,
 4739        );
 4740    });
 4741
 4742    // Move down so that the cursor touches the block.
 4743    cx.update_editor(|editor, window, cx| {
 4744        editor.move_down(&Default::default(), window, cx);
 4745    });
 4746    cx.assert_editor_state(
 4747        &"
 4748            zero
 4749            «one
 4750            two
 4751            threeˇ»
 4752            four
 4753            five
 4754        "
 4755        .unindent(),
 4756    );
 4757
 4758    // Move down past the block.
 4759    cx.update_editor(|editor, window, cx| {
 4760        editor.move_down(&Default::default(), window, cx);
 4761    });
 4762    cx.assert_editor_state(
 4763        &"
 4764            zero
 4765            one
 4766            two
 4767            three
 4768            ˇfour
 4769            five
 4770        "
 4771        .unindent(),
 4772    );
 4773}
 4774
 4775#[gpui::test]
 4776fn test_transpose(cx: &mut TestAppContext) {
 4777    init_test(cx, |_| {});
 4778
 4779    _ = cx.add_window(|window, cx| {
 4780        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4781        editor.set_style(EditorStyle::default(), window, cx);
 4782        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4783        editor.transpose(&Default::default(), window, cx);
 4784        assert_eq!(editor.text(cx), "bac");
 4785        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4786
 4787        editor.transpose(&Default::default(), window, cx);
 4788        assert_eq!(editor.text(cx), "bca");
 4789        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4790
 4791        editor.transpose(&Default::default(), window, cx);
 4792        assert_eq!(editor.text(cx), "bac");
 4793        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4794
 4795        editor
 4796    });
 4797
 4798    _ = cx.add_window(|window, cx| {
 4799        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4800        editor.set_style(EditorStyle::default(), window, cx);
 4801        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4802        editor.transpose(&Default::default(), window, cx);
 4803        assert_eq!(editor.text(cx), "acb\nde");
 4804        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4805
 4806        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4807        editor.transpose(&Default::default(), window, cx);
 4808        assert_eq!(editor.text(cx), "acbd\ne");
 4809        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4810
 4811        editor.transpose(&Default::default(), window, cx);
 4812        assert_eq!(editor.text(cx), "acbde\n");
 4813        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4814
 4815        editor.transpose(&Default::default(), window, cx);
 4816        assert_eq!(editor.text(cx), "acbd\ne");
 4817        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4818
 4819        editor
 4820    });
 4821
 4822    _ = cx.add_window(|window, cx| {
 4823        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4824        editor.set_style(EditorStyle::default(), window, cx);
 4825        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4826        editor.transpose(&Default::default(), window, cx);
 4827        assert_eq!(editor.text(cx), "bacd\ne");
 4828        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4829
 4830        editor.transpose(&Default::default(), window, cx);
 4831        assert_eq!(editor.text(cx), "bcade\n");
 4832        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4833
 4834        editor.transpose(&Default::default(), window, cx);
 4835        assert_eq!(editor.text(cx), "bcda\ne");
 4836        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4837
 4838        editor.transpose(&Default::default(), window, cx);
 4839        assert_eq!(editor.text(cx), "bcade\n");
 4840        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4841
 4842        editor.transpose(&Default::default(), window, cx);
 4843        assert_eq!(editor.text(cx), "bcaed\n");
 4844        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4845
 4846        editor
 4847    });
 4848
 4849    _ = cx.add_window(|window, cx| {
 4850        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4851        editor.set_style(EditorStyle::default(), window, cx);
 4852        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4853        editor.transpose(&Default::default(), window, cx);
 4854        assert_eq!(editor.text(cx), "🏀🍐✋");
 4855        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4856
 4857        editor.transpose(&Default::default(), window, cx);
 4858        assert_eq!(editor.text(cx), "🏀✋🍐");
 4859        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4860
 4861        editor.transpose(&Default::default(), window, cx);
 4862        assert_eq!(editor.text(cx), "🏀🍐✋");
 4863        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4864
 4865        editor
 4866    });
 4867}
 4868
 4869#[gpui::test]
 4870async fn test_rewrap(cx: &mut TestAppContext) {
 4871    init_test(cx, |settings| {
 4872        settings.languages.extend([
 4873            (
 4874                "Markdown".into(),
 4875                LanguageSettingsContent {
 4876                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4877                    ..Default::default()
 4878                },
 4879            ),
 4880            (
 4881                "Plain Text".into(),
 4882                LanguageSettingsContent {
 4883                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4884                    ..Default::default()
 4885                },
 4886            ),
 4887        ])
 4888    });
 4889
 4890    let mut cx = EditorTestContext::new(cx).await;
 4891
 4892    let language_with_c_comments = Arc::new(Language::new(
 4893        LanguageConfig {
 4894            line_comments: vec!["// ".into()],
 4895            ..LanguageConfig::default()
 4896        },
 4897        None,
 4898    ));
 4899    let language_with_pound_comments = Arc::new(Language::new(
 4900        LanguageConfig {
 4901            line_comments: vec!["# ".into()],
 4902            ..LanguageConfig::default()
 4903        },
 4904        None,
 4905    ));
 4906    let markdown_language = Arc::new(Language::new(
 4907        LanguageConfig {
 4908            name: "Markdown".into(),
 4909            ..LanguageConfig::default()
 4910        },
 4911        None,
 4912    ));
 4913    let language_with_doc_comments = Arc::new(Language::new(
 4914        LanguageConfig {
 4915            line_comments: vec!["// ".into(), "/// ".into()],
 4916            ..LanguageConfig::default()
 4917        },
 4918        Some(tree_sitter_rust::LANGUAGE.into()),
 4919    ));
 4920
 4921    let plaintext_language = Arc::new(Language::new(
 4922        LanguageConfig {
 4923            name: "Plain Text".into(),
 4924            ..LanguageConfig::default()
 4925        },
 4926        None,
 4927    ));
 4928
 4929    assert_rewrap(
 4930        indoc! {"
 4931            // ˇ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.
 4932        "},
 4933        indoc! {"
 4934            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4935            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4936            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4937            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4938            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4939            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4940            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4941            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4942            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4943            // porttitor id. Aliquam id accumsan eros.
 4944        "},
 4945        language_with_c_comments.clone(),
 4946        &mut cx,
 4947    );
 4948
 4949    // Test that rewrapping works inside of a selection
 4950    assert_rewrap(
 4951        indoc! {"
 4952            «// 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.ˇ»
 4953        "},
 4954        indoc! {"
 4955            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4956            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4957            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4958            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4959            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4960            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4961            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4962            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4963            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4964            // porttitor id. Aliquam id accumsan eros.ˇ»
 4965        "},
 4966        language_with_c_comments.clone(),
 4967        &mut cx,
 4968    );
 4969
 4970    // Test that cursors that expand to the same region are collapsed.
 4971    assert_rewrap(
 4972        indoc! {"
 4973            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4974            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4975            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4976            // ˇ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.
 4977        "},
 4978        indoc! {"
 4979            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4980            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4981            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4982            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4983            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4984            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4985            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4986            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4987            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4988            // porttitor id. Aliquam id accumsan eros.
 4989        "},
 4990        language_with_c_comments.clone(),
 4991        &mut cx,
 4992    );
 4993
 4994    // Test that non-contiguous selections are treated separately.
 4995    assert_rewrap(
 4996        indoc! {"
 4997            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4998            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4999            //
 5000            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5001            // ˇ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.
 5002        "},
 5003        indoc! {"
 5004            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 5005            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 5006            // auctor, eu lacinia sapien scelerisque.
 5007            //
 5008            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 5009            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5010            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 5011            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 5012            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 5013            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 5014            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 5015        "},
 5016        language_with_c_comments.clone(),
 5017        &mut cx,
 5018    );
 5019
 5020    // Test that different comment prefixes are supported.
 5021    assert_rewrap(
 5022        indoc! {"
 5023            # ˇ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.
 5024        "},
 5025        indoc! {"
 5026            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 5027            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5028            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5029            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5030            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 5031            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 5032            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 5033            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 5034            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 5035            # accumsan eros.
 5036        "},
 5037        language_with_pound_comments.clone(),
 5038        &mut cx,
 5039    );
 5040
 5041    // Test that rewrapping is ignored outside of comments in most languages.
 5042    assert_rewrap(
 5043        indoc! {"
 5044            /// Adds two numbers.
 5045            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5046            fn add(a: u32, b: u32) -> u32 {
 5047                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ˇ
 5048            }
 5049        "},
 5050        indoc! {"
 5051            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 5052            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5053            fn add(a: u32, b: u32) -> u32 {
 5054                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ˇ
 5055            }
 5056        "},
 5057        language_with_doc_comments.clone(),
 5058        &mut cx,
 5059    );
 5060
 5061    // Test that rewrapping works in Markdown and Plain Text languages.
 5062    assert_rewrap(
 5063        indoc! {"
 5064            # Hello
 5065
 5066            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.
 5067        "},
 5068        indoc! {"
 5069            # Hello
 5070
 5071            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5072            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5073            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5074            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5075            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5076            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5077            Integer sit amet scelerisque nisi.
 5078        "},
 5079        markdown_language,
 5080        &mut cx,
 5081    );
 5082
 5083    assert_rewrap(
 5084        indoc! {"
 5085            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.
 5086        "},
 5087        indoc! {"
 5088            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5089            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5090            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5091            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5092            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5093            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5094            Integer sit amet scelerisque nisi.
 5095        "},
 5096        plaintext_language,
 5097        &mut cx,
 5098    );
 5099
 5100    // Test rewrapping unaligned comments in a selection.
 5101    assert_rewrap(
 5102        indoc! {"
 5103            fn foo() {
 5104                if true {
 5105            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5106            // Praesent semper egestas tellus id dignissim.ˇ»
 5107                    do_something();
 5108                } else {
 5109                    //
 5110                }
 5111            }
 5112        "},
 5113        indoc! {"
 5114            fn foo() {
 5115                if true {
 5116            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5117                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5118                    // egestas tellus id dignissim.ˇ»
 5119                    do_something();
 5120                } else {
 5121                    //
 5122                }
 5123            }
 5124        "},
 5125        language_with_doc_comments.clone(),
 5126        &mut cx,
 5127    );
 5128
 5129    assert_rewrap(
 5130        indoc! {"
 5131            fn foo() {
 5132                if true {
 5133            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5134            // Praesent semper egestas tellus id dignissim.»
 5135                    do_something();
 5136                } else {
 5137                    //
 5138                }
 5139
 5140            }
 5141        "},
 5142        indoc! {"
 5143            fn foo() {
 5144                if true {
 5145            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5146                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5147                    // egestas tellus id dignissim.»
 5148                    do_something();
 5149                } else {
 5150                    //
 5151                }
 5152
 5153            }
 5154        "},
 5155        language_with_doc_comments.clone(),
 5156        &mut cx,
 5157    );
 5158
 5159    #[track_caller]
 5160    fn assert_rewrap(
 5161        unwrapped_text: &str,
 5162        wrapped_text: &str,
 5163        language: Arc<Language>,
 5164        cx: &mut EditorTestContext,
 5165    ) {
 5166        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5167        cx.set_state(unwrapped_text);
 5168        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5169        cx.assert_editor_state(wrapped_text);
 5170    }
 5171}
 5172
 5173#[gpui::test]
 5174async fn test_hard_wrap(cx: &mut TestAppContext) {
 5175    init_test(cx, |_| {});
 5176    let mut cx = EditorTestContext::new(cx).await;
 5177
 5178    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5179    cx.update_editor(|editor, _, cx| {
 5180        editor.set_hard_wrap(Some(14), cx);
 5181    });
 5182
 5183    cx.set_state(indoc!(
 5184        "
 5185        one two three ˇ
 5186        "
 5187    ));
 5188    cx.simulate_input("four");
 5189    cx.run_until_parked();
 5190
 5191    cx.assert_editor_state(indoc!(
 5192        "
 5193        one two three
 5194        fourˇ
 5195        "
 5196    ));
 5197
 5198    cx.update_editor(|editor, window, cx| {
 5199        editor.newline(&Default::default(), window, cx);
 5200    });
 5201    cx.run_until_parked();
 5202    cx.assert_editor_state(indoc!(
 5203        "
 5204        one two three
 5205        four
 5206        ˇ
 5207        "
 5208    ));
 5209
 5210    cx.simulate_input("five");
 5211    cx.run_until_parked();
 5212    cx.assert_editor_state(indoc!(
 5213        "
 5214        one two three
 5215        four
 5216        fiveˇ
 5217        "
 5218    ));
 5219
 5220    cx.update_editor(|editor, window, cx| {
 5221        editor.newline(&Default::default(), window, cx);
 5222    });
 5223    cx.run_until_parked();
 5224    cx.simulate_input("# ");
 5225    cx.run_until_parked();
 5226    cx.assert_editor_state(indoc!(
 5227        "
 5228        one two three
 5229        four
 5230        five
 5231        # ˇ
 5232        "
 5233    ));
 5234
 5235    cx.update_editor(|editor, window, cx| {
 5236        editor.newline(&Default::default(), window, cx);
 5237    });
 5238    cx.run_until_parked();
 5239    cx.assert_editor_state(indoc!(
 5240        "
 5241        one two three
 5242        four
 5243        five
 5244        #\x20
 5245 5246        "
 5247    ));
 5248
 5249    cx.simulate_input(" 6");
 5250    cx.run_until_parked();
 5251    cx.assert_editor_state(indoc!(
 5252        "
 5253        one two three
 5254        four
 5255        five
 5256        #
 5257        # 6ˇ
 5258        "
 5259    ));
 5260}
 5261
 5262#[gpui::test]
 5263async fn test_clipboard(cx: &mut TestAppContext) {
 5264    init_test(cx, |_| {});
 5265
 5266    let mut cx = EditorTestContext::new(cx).await;
 5267
 5268    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5269    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5270    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5271
 5272    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5273    cx.set_state("two ˇfour ˇsix ˇ");
 5274    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5275    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5276
 5277    // Paste again but with only two cursors. Since the number of cursors doesn't
 5278    // match the number of slices in the clipboard, the entire clipboard text
 5279    // is pasted at each cursor.
 5280    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5281    cx.update_editor(|e, window, cx| {
 5282        e.handle_input("( ", window, cx);
 5283        e.paste(&Paste, window, cx);
 5284        e.handle_input(") ", window, cx);
 5285    });
 5286    cx.assert_editor_state(
 5287        &([
 5288            "( one✅ ",
 5289            "three ",
 5290            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5291            "three ",
 5292            "five ) ˇ",
 5293        ]
 5294        .join("\n")),
 5295    );
 5296
 5297    // Cut with three selections, one of which is full-line.
 5298    cx.set_state(indoc! {"
 5299        1«2ˇ»3
 5300        4ˇ567
 5301        «8ˇ»9"});
 5302    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5303    cx.assert_editor_state(indoc! {"
 5304        1ˇ3
 5305        ˇ9"});
 5306
 5307    // Paste with three selections, noticing how the copied selection that was full-line
 5308    // gets inserted before the second cursor.
 5309    cx.set_state(indoc! {"
 5310        1ˇ3
 5311 5312        «oˇ»ne"});
 5313    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5314    cx.assert_editor_state(indoc! {"
 5315        12ˇ3
 5316        4567
 5317 5318        8ˇne"});
 5319
 5320    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5321    cx.set_state(indoc! {"
 5322        The quick brown
 5323        fox juˇmps over
 5324        the lazy dog"});
 5325    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5326    assert_eq!(
 5327        cx.read_from_clipboard()
 5328            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5329        Some("fox jumps over\n".to_string())
 5330    );
 5331
 5332    // Paste with three selections, noticing how the copied full-line selection is inserted
 5333    // before the empty selections but replaces the selection that is non-empty.
 5334    cx.set_state(indoc! {"
 5335        Tˇhe quick brown
 5336        «foˇ»x jumps over
 5337        tˇhe lazy dog"});
 5338    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5339    cx.assert_editor_state(indoc! {"
 5340        fox jumps over
 5341        Tˇhe quick brown
 5342        fox jumps over
 5343        ˇx jumps over
 5344        fox jumps over
 5345        tˇhe lazy dog"});
 5346}
 5347
 5348#[gpui::test]
 5349async fn test_copy_trim(cx: &mut TestAppContext) {
 5350    init_test(cx, |_| {});
 5351
 5352    let mut cx = EditorTestContext::new(cx).await;
 5353    cx.set_state(
 5354        r#"            «for selection in selections.iter() {
 5355            let mut start = selection.start;
 5356            let mut end = selection.end;
 5357            let is_entire_line = selection.is_empty();
 5358            if is_entire_line {
 5359                start = Point::new(start.row, 0);ˇ»
 5360                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5361            }
 5362        "#,
 5363    );
 5364    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5365    assert_eq!(
 5366        cx.read_from_clipboard()
 5367            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5368        Some(
 5369            "for selection in selections.iter() {
 5370            let mut start = selection.start;
 5371            let mut end = selection.end;
 5372            let is_entire_line = selection.is_empty();
 5373            if is_entire_line {
 5374                start = Point::new(start.row, 0);"
 5375                .to_string()
 5376        ),
 5377        "Regular copying preserves all indentation selected",
 5378    );
 5379    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5380    assert_eq!(
 5381        cx.read_from_clipboard()
 5382            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5383        Some(
 5384            "for selection in selections.iter() {
 5385let mut start = selection.start;
 5386let mut end = selection.end;
 5387let is_entire_line = selection.is_empty();
 5388if is_entire_line {
 5389    start = Point::new(start.row, 0);"
 5390                .to_string()
 5391        ),
 5392        "Copying with stripping should strip all leading whitespaces"
 5393    );
 5394
 5395    cx.set_state(
 5396        r#"       «     for selection in selections.iter() {
 5397            let mut start = selection.start;
 5398            let mut end = selection.end;
 5399            let is_entire_line = selection.is_empty();
 5400            if is_entire_line {
 5401                start = Point::new(start.row, 0);ˇ»
 5402                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5403            }
 5404        "#,
 5405    );
 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(
 5411            "     for selection in selections.iter() {
 5412            let mut start = selection.start;
 5413            let mut end = selection.end;
 5414            let is_entire_line = selection.is_empty();
 5415            if is_entire_line {
 5416                start = Point::new(start.row, 0);"
 5417                .to_string()
 5418        ),
 5419        "Regular copying preserves all indentation selected",
 5420    );
 5421    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5422    assert_eq!(
 5423        cx.read_from_clipboard()
 5424            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5425        Some(
 5426            "for selection in selections.iter() {
 5427let mut start = selection.start;
 5428let mut end = selection.end;
 5429let is_entire_line = selection.is_empty();
 5430if is_entire_line {
 5431    start = Point::new(start.row, 0);"
 5432                .to_string()
 5433        ),
 5434        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5435    );
 5436
 5437    cx.set_state(
 5438        r#"       «ˇ     for selection in selections.iter() {
 5439            let mut start = selection.start;
 5440            let mut end = selection.end;
 5441            let is_entire_line = selection.is_empty();
 5442            if is_entire_line {
 5443                start = Point::new(start.row, 0);»
 5444                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5445            }
 5446        "#,
 5447    );
 5448    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5449    assert_eq!(
 5450        cx.read_from_clipboard()
 5451            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5452        Some(
 5453            "     for selection in selections.iter() {
 5454            let mut start = selection.start;
 5455            let mut end = selection.end;
 5456            let is_entire_line = selection.is_empty();
 5457            if is_entire_line {
 5458                start = Point::new(start.row, 0);"
 5459                .to_string()
 5460        ),
 5461        "Regular copying for reverse selection works the same",
 5462    );
 5463    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5464    assert_eq!(
 5465        cx.read_from_clipboard()
 5466            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5467        Some(
 5468            "for selection in selections.iter() {
 5469let mut start = selection.start;
 5470let mut end = selection.end;
 5471let is_entire_line = selection.is_empty();
 5472if is_entire_line {
 5473    start = Point::new(start.row, 0);"
 5474                .to_string()
 5475        ),
 5476        "Copying with stripping for reverse selection works the same"
 5477    );
 5478
 5479    cx.set_state(
 5480        r#"            for selection «in selections.iter() {
 5481            let mut start = selection.start;
 5482            let mut end = selection.end;
 5483            let is_entire_line = selection.is_empty();
 5484            if is_entire_line {
 5485                start = Point::new(start.row, 0);ˇ»
 5486                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5487            }
 5488        "#,
 5489    );
 5490    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5491    assert_eq!(
 5492        cx.read_from_clipboard()
 5493            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5494        Some(
 5495            "in selections.iter() {
 5496            let mut start = selection.start;
 5497            let mut end = selection.end;
 5498            let is_entire_line = selection.is_empty();
 5499            if is_entire_line {
 5500                start = Point::new(start.row, 0);"
 5501                .to_string()
 5502        ),
 5503        "When selecting past the indent, the copying works as usual",
 5504    );
 5505    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5506    assert_eq!(
 5507        cx.read_from_clipboard()
 5508            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5509        Some(
 5510            "in selections.iter() {
 5511            let mut start = selection.start;
 5512            let mut end = selection.end;
 5513            let is_entire_line = selection.is_empty();
 5514            if is_entire_line {
 5515                start = Point::new(start.row, 0);"
 5516                .to_string()
 5517        ),
 5518        "When selecting past the indent, nothing is trimmed"
 5519    );
 5520
 5521    cx.set_state(
 5522        r#"            «for selection in selections.iter() {
 5523            let mut start = selection.start;
 5524
 5525            let mut end = selection.end;
 5526            let is_entire_line = selection.is_empty();
 5527            if is_entire_line {
 5528                start = Point::new(start.row, 0);
 5529ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5530            }
 5531        "#,
 5532    );
 5533    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5534    assert_eq!(
 5535        cx.read_from_clipboard()
 5536            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5537        Some(
 5538            "for selection in selections.iter() {
 5539let mut start = selection.start;
 5540
 5541let mut end = selection.end;
 5542let is_entire_line = selection.is_empty();
 5543if is_entire_line {
 5544    start = Point::new(start.row, 0);
 5545"
 5546            .to_string()
 5547        ),
 5548        "Copying with stripping should ignore empty lines"
 5549    );
 5550}
 5551
 5552#[gpui::test]
 5553async fn test_paste_multiline(cx: &mut TestAppContext) {
 5554    init_test(cx, |_| {});
 5555
 5556    let mut cx = EditorTestContext::new(cx).await;
 5557    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5558
 5559    // Cut an indented block, without the leading whitespace.
 5560    cx.set_state(indoc! {"
 5561        const a: B = (
 5562            c(),
 5563            «d(
 5564                e,
 5565                f
 5566            )ˇ»
 5567        );
 5568    "});
 5569    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5570    cx.assert_editor_state(indoc! {"
 5571        const a: B = (
 5572            c(),
 5573            ˇ
 5574        );
 5575    "});
 5576
 5577    // Paste it at the same position.
 5578    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5579    cx.assert_editor_state(indoc! {"
 5580        const a: B = (
 5581            c(),
 5582            d(
 5583                e,
 5584                f
 5585 5586        );
 5587    "});
 5588
 5589    // Paste it at a line with a lower indent level.
 5590    cx.set_state(indoc! {"
 5591        ˇ
 5592        const a: B = (
 5593            c(),
 5594        );
 5595    "});
 5596    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5597    cx.assert_editor_state(indoc! {"
 5598        d(
 5599            e,
 5600            f
 5601 5602        const a: B = (
 5603            c(),
 5604        );
 5605    "});
 5606
 5607    // Cut an indented block, with the leading whitespace.
 5608    cx.set_state(indoc! {"
 5609        const a: B = (
 5610            c(),
 5611        «    d(
 5612                e,
 5613                f
 5614            )
 5615        ˇ»);
 5616    "});
 5617    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5618    cx.assert_editor_state(indoc! {"
 5619        const a: B = (
 5620            c(),
 5621        ˇ);
 5622    "});
 5623
 5624    // Paste it at the same position.
 5625    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5626    cx.assert_editor_state(indoc! {"
 5627        const a: B = (
 5628            c(),
 5629            d(
 5630                e,
 5631                f
 5632            )
 5633        ˇ);
 5634    "});
 5635
 5636    // Paste it at a line with a higher indent level.
 5637    cx.set_state(indoc! {"
 5638        const a: B = (
 5639            c(),
 5640            d(
 5641                e,
 5642 5643            )
 5644        );
 5645    "});
 5646    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5647    cx.assert_editor_state(indoc! {"
 5648        const a: B = (
 5649            c(),
 5650            d(
 5651                e,
 5652                f    d(
 5653                    e,
 5654                    f
 5655                )
 5656        ˇ
 5657            )
 5658        );
 5659    "});
 5660
 5661    // Copy an indented block, starting mid-line
 5662    cx.set_state(indoc! {"
 5663        const a: B = (
 5664            c(),
 5665            somethin«g(
 5666                e,
 5667                f
 5668            )ˇ»
 5669        );
 5670    "});
 5671    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5672
 5673    // Paste it on a line with a lower indent level
 5674    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5675    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5676    cx.assert_editor_state(indoc! {"
 5677        const a: B = (
 5678            c(),
 5679            something(
 5680                e,
 5681                f
 5682            )
 5683        );
 5684        g(
 5685            e,
 5686            f
 5687"});
 5688}
 5689
 5690#[gpui::test]
 5691async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5692    init_test(cx, |_| {});
 5693
 5694    cx.write_to_clipboard(ClipboardItem::new_string(
 5695        "    d(\n        e\n    );\n".into(),
 5696    ));
 5697
 5698    let mut cx = EditorTestContext::new(cx).await;
 5699    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5700
 5701    cx.set_state(indoc! {"
 5702        fn a() {
 5703            b();
 5704            if c() {
 5705                ˇ
 5706            }
 5707        }
 5708    "});
 5709
 5710    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5711    cx.assert_editor_state(indoc! {"
 5712        fn a() {
 5713            b();
 5714            if c() {
 5715                d(
 5716                    e
 5717                );
 5718        ˇ
 5719            }
 5720        }
 5721    "});
 5722
 5723    cx.set_state(indoc! {"
 5724        fn a() {
 5725            b();
 5726            ˇ
 5727        }
 5728    "});
 5729
 5730    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5731    cx.assert_editor_state(indoc! {"
 5732        fn a() {
 5733            b();
 5734            d(
 5735                e
 5736            );
 5737        ˇ
 5738        }
 5739    "});
 5740}
 5741
 5742#[gpui::test]
 5743fn test_select_all(cx: &mut TestAppContext) {
 5744    init_test(cx, |_| {});
 5745
 5746    let editor = cx.add_window(|window, cx| {
 5747        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5748        build_editor(buffer, window, cx)
 5749    });
 5750    _ = editor.update(cx, |editor, window, cx| {
 5751        editor.select_all(&SelectAll, window, cx);
 5752        assert_eq!(
 5753            editor.selections.display_ranges(cx),
 5754            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5755        );
 5756    });
 5757}
 5758
 5759#[gpui::test]
 5760fn test_select_line(cx: &mut TestAppContext) {
 5761    init_test(cx, |_| {});
 5762
 5763    let editor = cx.add_window(|window, cx| {
 5764        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5765        build_editor(buffer, window, cx)
 5766    });
 5767    _ = editor.update(cx, |editor, window, cx| {
 5768        editor.change_selections(None, window, cx, |s| {
 5769            s.select_display_ranges([
 5770                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5771                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5772                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5773                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5774            ])
 5775        });
 5776        editor.select_line(&SelectLine, window, cx);
 5777        assert_eq!(
 5778            editor.selections.display_ranges(cx),
 5779            vec![
 5780                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5781                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5782            ]
 5783        );
 5784    });
 5785
 5786    _ = editor.update(cx, |editor, window, cx| {
 5787        editor.select_line(&SelectLine, window, cx);
 5788        assert_eq!(
 5789            editor.selections.display_ranges(cx),
 5790            vec![
 5791                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5792                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5793            ]
 5794        );
 5795    });
 5796
 5797    _ = editor.update(cx, |editor, window, cx| {
 5798        editor.select_line(&SelectLine, window, cx);
 5799        assert_eq!(
 5800            editor.selections.display_ranges(cx),
 5801            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5802        );
 5803    });
 5804}
 5805
 5806#[gpui::test]
 5807async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5808    init_test(cx, |_| {});
 5809    let mut cx = EditorTestContext::new(cx).await;
 5810
 5811    #[track_caller]
 5812    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5813        cx.set_state(initial_state);
 5814        cx.update_editor(|e, window, cx| {
 5815            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5816        });
 5817        cx.assert_editor_state(expected_state);
 5818    }
 5819
 5820    // Selection starts and ends at the middle of lines, left-to-right
 5821    test(
 5822        &mut cx,
 5823        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5824        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5825    );
 5826    // Same thing, right-to-left
 5827    test(
 5828        &mut cx,
 5829        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5830        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5831    );
 5832
 5833    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5834    test(
 5835        &mut cx,
 5836        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5837        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5838    );
 5839    // Same thing, right-to-left
 5840    test(
 5841        &mut cx,
 5842        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5843        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5844    );
 5845
 5846    // Whole buffer, left-to-right, last line ends with newline
 5847    test(
 5848        &mut cx,
 5849        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5850        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5851    );
 5852    // Same thing, right-to-left
 5853    test(
 5854        &mut cx,
 5855        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5856        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5857    );
 5858
 5859    // Starts at the end of a line, ends at the start of another
 5860    test(
 5861        &mut cx,
 5862        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5863        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5864    );
 5865}
 5866
 5867#[gpui::test]
 5868async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5869    init_test(cx, |_| {});
 5870
 5871    let editor = cx.add_window(|window, cx| {
 5872        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5873        build_editor(buffer, window, cx)
 5874    });
 5875
 5876    // setup
 5877    _ = editor.update(cx, |editor, window, cx| {
 5878        editor.fold_creases(
 5879            vec![
 5880                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5881                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5882                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5883            ],
 5884            true,
 5885            window,
 5886            cx,
 5887        );
 5888        assert_eq!(
 5889            editor.display_text(cx),
 5890            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5891        );
 5892    });
 5893
 5894    _ = editor.update(cx, |editor, window, cx| {
 5895        editor.change_selections(None, window, cx, |s| {
 5896            s.select_display_ranges([
 5897                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5898                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5899                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5900                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5901            ])
 5902        });
 5903        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5904        assert_eq!(
 5905            editor.display_text(cx),
 5906            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5907        );
 5908    });
 5909    EditorTestContext::for_editor(editor, cx)
 5910        .await
 5911        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5912
 5913    _ = editor.update(cx, |editor, window, cx| {
 5914        editor.change_selections(None, window, cx, |s| {
 5915            s.select_display_ranges([
 5916                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5917            ])
 5918        });
 5919        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5920        assert_eq!(
 5921            editor.display_text(cx),
 5922            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5923        );
 5924        assert_eq!(
 5925            editor.selections.display_ranges(cx),
 5926            [
 5927                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5928                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5929                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5930                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5931                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5932                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5933                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5934            ]
 5935        );
 5936    });
 5937    EditorTestContext::for_editor(editor, cx)
 5938        .await
 5939        .assert_editor_state(
 5940            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5941        );
 5942}
 5943
 5944#[gpui::test]
 5945async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5946    init_test(cx, |_| {});
 5947
 5948    let mut cx = EditorTestContext::new(cx).await;
 5949
 5950    cx.set_state(indoc!(
 5951        r#"abc
 5952           defˇghi
 5953
 5954           jk
 5955           nlmo
 5956           "#
 5957    ));
 5958
 5959    cx.update_editor(|editor, window, cx| {
 5960        editor.add_selection_above(&Default::default(), window, cx);
 5961    });
 5962
 5963    cx.assert_editor_state(indoc!(
 5964        r#"abcˇ
 5965           defˇghi
 5966
 5967           jk
 5968           nlmo
 5969           "#
 5970    ));
 5971
 5972    cx.update_editor(|editor, window, cx| {
 5973        editor.add_selection_above(&Default::default(), window, cx);
 5974    });
 5975
 5976    cx.assert_editor_state(indoc!(
 5977        r#"abcˇ
 5978            defˇghi
 5979
 5980            jk
 5981            nlmo
 5982            "#
 5983    ));
 5984
 5985    cx.update_editor(|editor, window, cx| {
 5986        editor.add_selection_below(&Default::default(), window, cx);
 5987    });
 5988
 5989    cx.assert_editor_state(indoc!(
 5990        r#"abc
 5991           defˇghi
 5992
 5993           jk
 5994           nlmo
 5995           "#
 5996    ));
 5997
 5998    cx.update_editor(|editor, window, cx| {
 5999        editor.undo_selection(&Default::default(), window, cx);
 6000    });
 6001
 6002    cx.assert_editor_state(indoc!(
 6003        r#"abcˇ
 6004           defˇghi
 6005
 6006           jk
 6007           nlmo
 6008           "#
 6009    ));
 6010
 6011    cx.update_editor(|editor, window, cx| {
 6012        editor.redo_selection(&Default::default(), window, cx);
 6013    });
 6014
 6015    cx.assert_editor_state(indoc!(
 6016        r#"abc
 6017           defˇghi
 6018
 6019           jk
 6020           nlmo
 6021           "#
 6022    ));
 6023
 6024    cx.update_editor(|editor, window, cx| {
 6025        editor.add_selection_below(&Default::default(), window, cx);
 6026    });
 6027
 6028    cx.assert_editor_state(indoc!(
 6029        r#"abc
 6030           defˇghi
 6031           ˇ
 6032           jk
 6033           nlmo
 6034           "#
 6035    ));
 6036
 6037    cx.update_editor(|editor, window, cx| {
 6038        editor.add_selection_below(&Default::default(), window, cx);
 6039    });
 6040
 6041    cx.assert_editor_state(indoc!(
 6042        r#"abc
 6043           defˇghi
 6044           ˇ
 6045           jkˇ
 6046           nlmo
 6047           "#
 6048    ));
 6049
 6050    cx.update_editor(|editor, window, cx| {
 6051        editor.add_selection_below(&Default::default(), window, cx);
 6052    });
 6053
 6054    cx.assert_editor_state(indoc!(
 6055        r#"abc
 6056           defˇghi
 6057           ˇ
 6058           jkˇ
 6059           nlmˇo
 6060           "#
 6061    ));
 6062
 6063    cx.update_editor(|editor, window, cx| {
 6064        editor.add_selection_below(&Default::default(), window, cx);
 6065    });
 6066
 6067    cx.assert_editor_state(indoc!(
 6068        r#"abc
 6069           defˇghi
 6070           ˇ
 6071           jkˇ
 6072           nlmˇo
 6073           ˇ"#
 6074    ));
 6075
 6076    // change selections
 6077    cx.set_state(indoc!(
 6078        r#"abc
 6079           def«ˇg»hi
 6080
 6081           jk
 6082           nlmo
 6083           "#
 6084    ));
 6085
 6086    cx.update_editor(|editor, window, cx| {
 6087        editor.add_selection_below(&Default::default(), window, cx);
 6088    });
 6089
 6090    cx.assert_editor_state(indoc!(
 6091        r#"abc
 6092           def«ˇg»hi
 6093
 6094           jk
 6095           nlm«ˇo»
 6096           "#
 6097    ));
 6098
 6099    cx.update_editor(|editor, window, cx| {
 6100        editor.add_selection_below(&Default::default(), window, cx);
 6101    });
 6102
 6103    cx.assert_editor_state(indoc!(
 6104        r#"abc
 6105           def«ˇg»hi
 6106
 6107           jk
 6108           nlm«ˇo»
 6109           "#
 6110    ));
 6111
 6112    cx.update_editor(|editor, window, cx| {
 6113        editor.add_selection_above(&Default::default(), window, cx);
 6114    });
 6115
 6116    cx.assert_editor_state(indoc!(
 6117        r#"abc
 6118           def«ˇg»hi
 6119
 6120           jk
 6121           nlmo
 6122           "#
 6123    ));
 6124
 6125    cx.update_editor(|editor, window, cx| {
 6126        editor.add_selection_above(&Default::default(), window, cx);
 6127    });
 6128
 6129    cx.assert_editor_state(indoc!(
 6130        r#"abc
 6131           def«ˇg»hi
 6132
 6133           jk
 6134           nlmo
 6135           "#
 6136    ));
 6137
 6138    // Change selections again
 6139    cx.set_state(indoc!(
 6140        r#"a«bc
 6141           defgˇ»hi
 6142
 6143           jk
 6144           nlmo
 6145           "#
 6146    ));
 6147
 6148    cx.update_editor(|editor, window, cx| {
 6149        editor.add_selection_below(&Default::default(), window, cx);
 6150    });
 6151
 6152    cx.assert_editor_state(indoc!(
 6153        r#"a«bcˇ»
 6154           d«efgˇ»hi
 6155
 6156           j«kˇ»
 6157           nlmo
 6158           "#
 6159    ));
 6160
 6161    cx.update_editor(|editor, window, cx| {
 6162        editor.add_selection_below(&Default::default(), window, cx);
 6163    });
 6164    cx.assert_editor_state(indoc!(
 6165        r#"a«bcˇ»
 6166           d«efgˇ»hi
 6167
 6168           j«kˇ»
 6169           n«lmoˇ»
 6170           "#
 6171    ));
 6172    cx.update_editor(|editor, window, cx| {
 6173        editor.add_selection_above(&Default::default(), window, cx);
 6174    });
 6175
 6176    cx.assert_editor_state(indoc!(
 6177        r#"a«bcˇ»
 6178           d«efgˇ»hi
 6179
 6180           j«kˇ»
 6181           nlmo
 6182           "#
 6183    ));
 6184
 6185    // Change selections again
 6186    cx.set_state(indoc!(
 6187        r#"abc
 6188           d«ˇefghi
 6189
 6190           jk
 6191           nlm»o
 6192           "#
 6193    ));
 6194
 6195    cx.update_editor(|editor, window, cx| {
 6196        editor.add_selection_above(&Default::default(), window, cx);
 6197    });
 6198
 6199    cx.assert_editor_state(indoc!(
 6200        r#"a«ˇbc»
 6201           d«ˇef»ghi
 6202
 6203           j«ˇk»
 6204           n«ˇlm»o
 6205           "#
 6206    ));
 6207
 6208    cx.update_editor(|editor, window, cx| {
 6209        editor.add_selection_below(&Default::default(), window, cx);
 6210    });
 6211
 6212    cx.assert_editor_state(indoc!(
 6213        r#"abc
 6214           d«ˇef»ghi
 6215
 6216           j«ˇk»
 6217           n«ˇlm»o
 6218           "#
 6219    ));
 6220}
 6221
 6222#[gpui::test]
 6223async fn test_select_next(cx: &mut TestAppContext) {
 6224    init_test(cx, |_| {});
 6225
 6226    let mut cx = EditorTestContext::new(cx).await;
 6227    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6228
 6229    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6230        .unwrap();
 6231    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6232
 6233    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6234        .unwrap();
 6235    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6236
 6237    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6238    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6239
 6240    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6241    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6242
 6243    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6244        .unwrap();
 6245    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6246
 6247    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6248        .unwrap();
 6249    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6250
 6251    // Test selection direction should be preserved
 6252    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6253
 6254    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6255        .unwrap();
 6256    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6257}
 6258
 6259#[gpui::test]
 6260async fn test_select_all_matches(cx: &mut TestAppContext) {
 6261    init_test(cx, |_| {});
 6262
 6263    let mut cx = EditorTestContext::new(cx).await;
 6264
 6265    // Test caret-only selections
 6266    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6267    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6268        .unwrap();
 6269    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6270
 6271    // Test left-to-right selections
 6272    cx.set_state("abc\n«abcˇ»\nabc");
 6273    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6274        .unwrap();
 6275    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6276
 6277    // Test right-to-left selections
 6278    cx.set_state("abc\n«ˇabc»\nabc");
 6279    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6280        .unwrap();
 6281    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6282
 6283    // Test selecting whitespace with caret selection
 6284    cx.set_state("abc\nˇ   abc\nabc");
 6285    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6286        .unwrap();
 6287    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6288
 6289    // Test selecting whitespace with left-to-right selection
 6290    cx.set_state("abc\n«ˇ  »abc\nabc");
 6291    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6292        .unwrap();
 6293    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6294
 6295    // Test no matches with right-to-left selection
 6296    cx.set_state("abc\n«  ˇ»abc\nabc");
 6297    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6298        .unwrap();
 6299    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6300}
 6301
 6302#[gpui::test]
 6303async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6304    init_test(cx, |_| {});
 6305
 6306    let mut cx = EditorTestContext::new(cx).await;
 6307
 6308    let large_body_1 = "\nd".repeat(200);
 6309    let large_body_2 = "\ne".repeat(200);
 6310
 6311    cx.set_state(&format!(
 6312        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6313    ));
 6314    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6315        let scroll_position = editor.scroll_position(cx);
 6316        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6317        scroll_position
 6318    });
 6319
 6320    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6321        .unwrap();
 6322    cx.assert_editor_state(&format!(
 6323        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6324    ));
 6325    let scroll_position_after_selection =
 6326        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6327    assert_eq!(
 6328        initial_scroll_position, scroll_position_after_selection,
 6329        "Scroll position should not change after selecting all matches"
 6330    );
 6331}
 6332
 6333#[gpui::test]
 6334async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6335    init_test(cx, |_| {});
 6336
 6337    let mut cx = EditorLspTestContext::new_rust(
 6338        lsp::ServerCapabilities {
 6339            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6340            ..Default::default()
 6341        },
 6342        cx,
 6343    )
 6344    .await;
 6345
 6346    cx.set_state(indoc! {"
 6347        line 1
 6348        line 2
 6349        linˇe 3
 6350        line 4
 6351        line 5
 6352    "});
 6353
 6354    // Make an edit
 6355    cx.update_editor(|editor, window, cx| {
 6356        editor.handle_input("X", window, cx);
 6357    });
 6358
 6359    // Move cursor to a different position
 6360    cx.update_editor(|editor, window, cx| {
 6361        editor.change_selections(None, window, cx, |s| {
 6362            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6363        });
 6364    });
 6365
 6366    cx.assert_editor_state(indoc! {"
 6367        line 1
 6368        line 2
 6369        linXe 3
 6370        line 4
 6371        liˇne 5
 6372    "});
 6373
 6374    cx.lsp
 6375        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6376            Ok(Some(vec![lsp::TextEdit::new(
 6377                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6378                "PREFIX ".to_string(),
 6379            )]))
 6380        });
 6381
 6382    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6383        .unwrap()
 6384        .await
 6385        .unwrap();
 6386
 6387    cx.assert_editor_state(indoc! {"
 6388        PREFIX line 1
 6389        line 2
 6390        linXe 3
 6391        line 4
 6392        liˇne 5
 6393    "});
 6394
 6395    // Undo formatting
 6396    cx.update_editor(|editor, window, cx| {
 6397        editor.undo(&Default::default(), window, cx);
 6398    });
 6399
 6400    // Verify cursor moved back to position after edit
 6401    cx.assert_editor_state(indoc! {"
 6402        line 1
 6403        line 2
 6404        linXˇe 3
 6405        line 4
 6406        line 5
 6407    "});
 6408}
 6409
 6410#[gpui::test]
 6411async fn test_undo_inline_completion_scrolls_to_edit_pos(cx: &mut TestAppContext) {
 6412    init_test(cx, |_| {});
 6413
 6414    let mut cx = EditorTestContext::new(cx).await;
 6415
 6416    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
 6417    cx.update_editor(|editor, window, cx| {
 6418        editor.set_edit_prediction_provider(Some(provider.clone()), window, cx);
 6419    });
 6420
 6421    cx.set_state(indoc! {"
 6422        line 1
 6423        line 2
 6424        linˇe 3
 6425        line 4
 6426        line 5
 6427        line 6
 6428        line 7
 6429        line 8
 6430        line 9
 6431        line 10
 6432    "});
 6433
 6434    let snapshot = cx.buffer_snapshot();
 6435    let edit_position = snapshot.anchor_after(Point::new(2, 4));
 6436
 6437    cx.update(|_, cx| {
 6438        provider.update(cx, |provider, _| {
 6439            provider.set_inline_completion(Some(inline_completion::InlineCompletion {
 6440                id: None,
 6441                edits: vec![(edit_position..edit_position, "X".into())],
 6442                edit_preview: None,
 6443            }))
 6444        })
 6445    });
 6446
 6447    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 6448    cx.update_editor(|editor, window, cx| {
 6449        editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)
 6450    });
 6451
 6452    cx.assert_editor_state(indoc! {"
 6453        line 1
 6454        line 2
 6455        lineXˇ 3
 6456        line 4
 6457        line 5
 6458        line 6
 6459        line 7
 6460        line 8
 6461        line 9
 6462        line 10
 6463    "});
 6464
 6465    cx.update_editor(|editor, window, cx| {
 6466        editor.change_selections(None, window, cx, |s| {
 6467            s.select_ranges([Point::new(9, 2)..Point::new(9, 2)]);
 6468        });
 6469    });
 6470
 6471    cx.assert_editor_state(indoc! {"
 6472        line 1
 6473        line 2
 6474        lineX 3
 6475        line 4
 6476        line 5
 6477        line 6
 6478        line 7
 6479        line 8
 6480        line 9
 6481        liˇne 10
 6482    "});
 6483
 6484    cx.update_editor(|editor, window, cx| {
 6485        editor.undo(&Default::default(), window, cx);
 6486    });
 6487
 6488    cx.assert_editor_state(indoc! {"
 6489        line 1
 6490        line 2
 6491        lineˇ 3
 6492        line 4
 6493        line 5
 6494        line 6
 6495        line 7
 6496        line 8
 6497        line 9
 6498        line 10
 6499    "});
 6500}
 6501
 6502#[gpui::test]
 6503async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6504    init_test(cx, |_| {});
 6505
 6506    let mut cx = EditorTestContext::new(cx).await;
 6507    cx.set_state(
 6508        r#"let foo = 2;
 6509lˇet foo = 2;
 6510let fooˇ = 2;
 6511let foo = 2;
 6512let foo = ˇ2;"#,
 6513    );
 6514
 6515    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6516        .unwrap();
 6517    cx.assert_editor_state(
 6518        r#"let foo = 2;
 6519«letˇ» foo = 2;
 6520let «fooˇ» = 2;
 6521let foo = 2;
 6522let foo = «2ˇ»;"#,
 6523    );
 6524
 6525    // noop for multiple selections with different contents
 6526    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6527        .unwrap();
 6528    cx.assert_editor_state(
 6529        r#"let foo = 2;
 6530«letˇ» foo = 2;
 6531let «fooˇ» = 2;
 6532let foo = 2;
 6533let foo = «2ˇ»;"#,
 6534    );
 6535
 6536    // Test last selection direction should be preserved
 6537    cx.set_state(
 6538        r#"let foo = 2;
 6539let foo = 2;
 6540let «fooˇ» = 2;
 6541let «ˇfoo» = 2;
 6542let foo = 2;"#,
 6543    );
 6544
 6545    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6546        .unwrap();
 6547    cx.assert_editor_state(
 6548        r#"let foo = 2;
 6549let foo = 2;
 6550let «fooˇ» = 2;
 6551let «ˇfoo» = 2;
 6552let «ˇfoo» = 2;"#,
 6553    );
 6554}
 6555
 6556#[gpui::test]
 6557async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6558    init_test(cx, |_| {});
 6559
 6560    let mut cx =
 6561        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6562
 6563    cx.assert_editor_state(indoc! {"
 6564        ˇbbb
 6565        ccc
 6566
 6567        bbb
 6568        ccc
 6569        "});
 6570    cx.dispatch_action(SelectPrevious::default());
 6571    cx.assert_editor_state(indoc! {"
 6572                «bbbˇ»
 6573                ccc
 6574
 6575                bbb
 6576                ccc
 6577                "});
 6578    cx.dispatch_action(SelectPrevious::default());
 6579    cx.assert_editor_state(indoc! {"
 6580                «bbbˇ»
 6581                ccc
 6582
 6583                «bbbˇ»
 6584                ccc
 6585                "});
 6586}
 6587
 6588#[gpui::test]
 6589async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6590    init_test(cx, |_| {});
 6591
 6592    let mut cx = EditorTestContext::new(cx).await;
 6593    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6594
 6595    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6596        .unwrap();
 6597    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6598
 6599    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6600        .unwrap();
 6601    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6602
 6603    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6604    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6605
 6606    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6607    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6608
 6609    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6610        .unwrap();
 6611    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6612
 6613    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6614        .unwrap();
 6615    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6616}
 6617
 6618#[gpui::test]
 6619async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6620    init_test(cx, |_| {});
 6621
 6622    let mut cx = EditorTestContext::new(cx).await;
 6623    cx.set_state("");
 6624
 6625    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6626        .unwrap();
 6627    cx.assert_editor_state("«aˇ»");
 6628    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6629        .unwrap();
 6630    cx.assert_editor_state("«aˇ»");
 6631}
 6632
 6633#[gpui::test]
 6634async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6635    init_test(cx, |_| {});
 6636
 6637    let mut cx = EditorTestContext::new(cx).await;
 6638    cx.set_state(
 6639        r#"let foo = 2;
 6640lˇet foo = 2;
 6641let fooˇ = 2;
 6642let foo = 2;
 6643let foo = ˇ2;"#,
 6644    );
 6645
 6646    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6647        .unwrap();
 6648    cx.assert_editor_state(
 6649        r#"let foo = 2;
 6650«letˇ» foo = 2;
 6651let «fooˇ» = 2;
 6652let foo = 2;
 6653let foo = «2ˇ»;"#,
 6654    );
 6655
 6656    // noop for multiple selections with different contents
 6657    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6658        .unwrap();
 6659    cx.assert_editor_state(
 6660        r#"let foo = 2;
 6661«letˇ» foo = 2;
 6662let «fooˇ» = 2;
 6663let foo = 2;
 6664let foo = «2ˇ»;"#,
 6665    );
 6666}
 6667
 6668#[gpui::test]
 6669async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6670    init_test(cx, |_| {});
 6671
 6672    let mut cx = EditorTestContext::new(cx).await;
 6673    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6674
 6675    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6676        .unwrap();
 6677    // selection direction is preserved
 6678    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6679
 6680    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6681        .unwrap();
 6682    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6683
 6684    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6685    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6686
 6687    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6688    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6689
 6690    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6691        .unwrap();
 6692    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6693
 6694    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6695        .unwrap();
 6696    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6697}
 6698
 6699#[gpui::test]
 6700async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6701    init_test(cx, |_| {});
 6702
 6703    let language = Arc::new(Language::new(
 6704        LanguageConfig::default(),
 6705        Some(tree_sitter_rust::LANGUAGE.into()),
 6706    ));
 6707
 6708    let text = r#"
 6709        use mod1::mod2::{mod3, mod4};
 6710
 6711        fn fn_1(param1: bool, param2: &str) {
 6712            let var1 = "text";
 6713        }
 6714    "#
 6715    .unindent();
 6716
 6717    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6718    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6719    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6720
 6721    editor
 6722        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6723        .await;
 6724
 6725    editor.update_in(cx, |editor, window, cx| {
 6726        editor.change_selections(None, window, cx, |s| {
 6727            s.select_display_ranges([
 6728                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6729                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6730                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6731            ]);
 6732        });
 6733        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6734    });
 6735    editor.update(cx, |editor, cx| {
 6736        assert_text_with_selections(
 6737            editor,
 6738            indoc! {r#"
 6739                use mod1::mod2::{mod3, «mod4ˇ»};
 6740
 6741                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6742                    let var1 = "«ˇtext»";
 6743                }
 6744            "#},
 6745            cx,
 6746        );
 6747    });
 6748
 6749    editor.update_in(cx, |editor, window, cx| {
 6750        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6751    });
 6752    editor.update(cx, |editor, cx| {
 6753        assert_text_with_selections(
 6754            editor,
 6755            indoc! {r#"
 6756                use mod1::mod2::«{mod3, mod4}ˇ»;
 6757
 6758                «ˇfn fn_1(param1: bool, param2: &str) {
 6759                    let var1 = "text";
 6760 6761            "#},
 6762            cx,
 6763        );
 6764    });
 6765
 6766    editor.update_in(cx, |editor, window, cx| {
 6767        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6768    });
 6769    assert_eq!(
 6770        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6771        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6772    );
 6773
 6774    // Trying to expand the selected syntax node one more time has no effect.
 6775    editor.update_in(cx, |editor, window, cx| {
 6776        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6777    });
 6778    assert_eq!(
 6779        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6780        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6781    );
 6782
 6783    editor.update_in(cx, |editor, window, cx| {
 6784        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6785    });
 6786    editor.update(cx, |editor, cx| {
 6787        assert_text_with_selections(
 6788            editor,
 6789            indoc! {r#"
 6790                use mod1::mod2::«{mod3, mod4}ˇ»;
 6791
 6792                «ˇfn fn_1(param1: bool, param2: &str) {
 6793                    let var1 = "text";
 6794 6795            "#},
 6796            cx,
 6797        );
 6798    });
 6799
 6800    editor.update_in(cx, |editor, window, cx| {
 6801        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6802    });
 6803    editor.update(cx, |editor, cx| {
 6804        assert_text_with_selections(
 6805            editor,
 6806            indoc! {r#"
 6807                use mod1::mod2::{mod3, «mod4ˇ»};
 6808
 6809                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6810                    let var1 = "«ˇtext»";
 6811                }
 6812            "#},
 6813            cx,
 6814        );
 6815    });
 6816
 6817    editor.update_in(cx, |editor, window, cx| {
 6818        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6819    });
 6820    editor.update(cx, |editor, cx| {
 6821        assert_text_with_selections(
 6822            editor,
 6823            indoc! {r#"
 6824                use mod1::mod2::{mod3, mo«ˇ»d4};
 6825
 6826                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6827                    let var1 = "te«ˇ»xt";
 6828                }
 6829            "#},
 6830            cx,
 6831        );
 6832    });
 6833
 6834    // Trying to shrink the selected syntax node one more time has no effect.
 6835    editor.update_in(cx, |editor, window, cx| {
 6836        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6837    });
 6838    editor.update_in(cx, |editor, _, cx| {
 6839        assert_text_with_selections(
 6840            editor,
 6841            indoc! {r#"
 6842                use mod1::mod2::{mod3, mo«ˇ»d4};
 6843
 6844                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6845                    let var1 = "te«ˇ»xt";
 6846                }
 6847            "#},
 6848            cx,
 6849        );
 6850    });
 6851
 6852    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6853    // a fold.
 6854    editor.update_in(cx, |editor, window, cx| {
 6855        editor.fold_creases(
 6856            vec![
 6857                Crease::simple(
 6858                    Point::new(0, 21)..Point::new(0, 24),
 6859                    FoldPlaceholder::test(),
 6860                ),
 6861                Crease::simple(
 6862                    Point::new(3, 20)..Point::new(3, 22),
 6863                    FoldPlaceholder::test(),
 6864                ),
 6865            ],
 6866            true,
 6867            window,
 6868            cx,
 6869        );
 6870        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6871    });
 6872    editor.update(cx, |editor, cx| {
 6873        assert_text_with_selections(
 6874            editor,
 6875            indoc! {r#"
 6876                use mod1::mod2::«{mod3, mod4}ˇ»;
 6877
 6878                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6879                    let var1 = "«ˇtext»";
 6880                }
 6881            "#},
 6882            cx,
 6883        );
 6884    });
 6885}
 6886
 6887#[gpui::test]
 6888async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6889    init_test(cx, |_| {});
 6890
 6891    let language = Arc::new(Language::new(
 6892        LanguageConfig::default(),
 6893        Some(tree_sitter_rust::LANGUAGE.into()),
 6894    ));
 6895
 6896    let text = "let a = 2;";
 6897
 6898    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6899    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6900    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6901
 6902    editor
 6903        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6904        .await;
 6905
 6906    // Test case 1: Cursor at end of word
 6907    editor.update_in(cx, |editor, window, cx| {
 6908        editor.change_selections(None, window, cx, |s| {
 6909            s.select_display_ranges([
 6910                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6911            ]);
 6912        });
 6913    });
 6914    editor.update(cx, |editor, cx| {
 6915        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6916    });
 6917    editor.update_in(cx, |editor, window, cx| {
 6918        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6919    });
 6920    editor.update(cx, |editor, cx| {
 6921        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6922    });
 6923    editor.update_in(cx, |editor, window, cx| {
 6924        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6925    });
 6926    editor.update(cx, |editor, cx| {
 6927        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6928    });
 6929
 6930    // Test case 2: Cursor at end of statement
 6931    editor.update_in(cx, |editor, window, cx| {
 6932        editor.change_selections(None, window, cx, |s| {
 6933            s.select_display_ranges([
 6934                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6935            ]);
 6936        });
 6937    });
 6938    editor.update(cx, |editor, cx| {
 6939        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6940    });
 6941    editor.update_in(cx, |editor, window, cx| {
 6942        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6943    });
 6944    editor.update(cx, |editor, cx| {
 6945        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6946    });
 6947}
 6948
 6949#[gpui::test]
 6950async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6951    init_test(cx, |_| {});
 6952
 6953    let language = Arc::new(Language::new(
 6954        LanguageConfig::default(),
 6955        Some(tree_sitter_rust::LANGUAGE.into()),
 6956    ));
 6957
 6958    let text = r#"
 6959        use mod1::mod2::{mod3, mod4};
 6960
 6961        fn fn_1(param1: bool, param2: &str) {
 6962            let var1 = "hello world";
 6963        }
 6964    "#
 6965    .unindent();
 6966
 6967    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6968    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6969    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6970
 6971    editor
 6972        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6973        .await;
 6974
 6975    // Test 1: Cursor on a letter of a string word
 6976    editor.update_in(cx, |editor, window, cx| {
 6977        editor.change_selections(None, window, cx, |s| {
 6978            s.select_display_ranges([
 6979                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6980            ]);
 6981        });
 6982    });
 6983    editor.update_in(cx, |editor, window, cx| {
 6984        assert_text_with_selections(
 6985            editor,
 6986            indoc! {r#"
 6987                use mod1::mod2::{mod3, mod4};
 6988
 6989                fn fn_1(param1: bool, param2: &str) {
 6990                    let var1 = "hˇello world";
 6991                }
 6992            "#},
 6993            cx,
 6994        );
 6995        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6996        assert_text_with_selections(
 6997            editor,
 6998            indoc! {r#"
 6999                use mod1::mod2::{mod3, mod4};
 7000
 7001                fn fn_1(param1: bool, param2: &str) {
 7002                    let var1 = "«ˇhello» world";
 7003                }
 7004            "#},
 7005            cx,
 7006        );
 7007    });
 7008
 7009    // Test 2: Partial selection within a word
 7010    editor.update_in(cx, |editor, window, cx| {
 7011        editor.change_selections(None, window, cx, |s| {
 7012            s.select_display_ranges([
 7013                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 7014            ]);
 7015        });
 7016    });
 7017    editor.update_in(cx, |editor, window, cx| {
 7018        assert_text_with_selections(
 7019            editor,
 7020            indoc! {r#"
 7021                use mod1::mod2::{mod3, mod4};
 7022
 7023                fn fn_1(param1: bool, param2: &str) {
 7024                    let var1 = "h«elˇ»lo world";
 7025                }
 7026            "#},
 7027            cx,
 7028        );
 7029        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7030        assert_text_with_selections(
 7031            editor,
 7032            indoc! {r#"
 7033                use mod1::mod2::{mod3, mod4};
 7034
 7035                fn fn_1(param1: bool, param2: &str) {
 7036                    let var1 = "«ˇhello» world";
 7037                }
 7038            "#},
 7039            cx,
 7040        );
 7041    });
 7042
 7043    // Test 3: Complete word already selected
 7044    editor.update_in(cx, |editor, window, cx| {
 7045        editor.change_selections(None, window, cx, |s| {
 7046            s.select_display_ranges([
 7047                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 7048            ]);
 7049        });
 7050    });
 7051    editor.update_in(cx, |editor, window, cx| {
 7052        assert_text_with_selections(
 7053            editor,
 7054            indoc! {r#"
 7055                use mod1::mod2::{mod3, mod4};
 7056
 7057                fn fn_1(param1: bool, param2: &str) {
 7058                    let var1 = "«helloˇ» world";
 7059                }
 7060            "#},
 7061            cx,
 7062        );
 7063        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7064        assert_text_with_selections(
 7065            editor,
 7066            indoc! {r#"
 7067                use mod1::mod2::{mod3, mod4};
 7068
 7069                fn fn_1(param1: bool, param2: &str) {
 7070                    let var1 = "«hello worldˇ»";
 7071                }
 7072            "#},
 7073            cx,
 7074        );
 7075    });
 7076
 7077    // Test 4: Selection spanning across words
 7078    editor.update_in(cx, |editor, window, cx| {
 7079        editor.change_selections(None, window, cx, |s| {
 7080            s.select_display_ranges([
 7081                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 7082            ]);
 7083        });
 7084    });
 7085    editor.update_in(cx, |editor, window, cx| {
 7086        assert_text_with_selections(
 7087            editor,
 7088            indoc! {r#"
 7089                use mod1::mod2::{mod3, mod4};
 7090
 7091                fn fn_1(param1: bool, param2: &str) {
 7092                    let var1 = "hel«lo woˇ»rld";
 7093                }
 7094            "#},
 7095            cx,
 7096        );
 7097        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7098        assert_text_with_selections(
 7099            editor,
 7100            indoc! {r#"
 7101                use mod1::mod2::{mod3, mod4};
 7102
 7103                fn fn_1(param1: bool, param2: &str) {
 7104                    let var1 = "«ˇhello world»";
 7105                }
 7106            "#},
 7107            cx,
 7108        );
 7109    });
 7110
 7111    // Test 5: Expansion beyond string
 7112    editor.update_in(cx, |editor, window, cx| {
 7113        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7114        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7115        assert_text_with_selections(
 7116            editor,
 7117            indoc! {r#"
 7118                use mod1::mod2::{mod3, mod4};
 7119
 7120                fn fn_1(param1: bool, param2: &str) {
 7121                    «ˇlet var1 = "hello world";»
 7122                }
 7123            "#},
 7124            cx,
 7125        );
 7126    });
 7127}
 7128
 7129#[gpui::test]
 7130async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 7131    init_test(cx, |_| {});
 7132
 7133    let base_text = r#"
 7134        impl A {
 7135            // this is an uncommitted comment
 7136
 7137            fn b() {
 7138                c();
 7139            }
 7140
 7141            // this is another uncommitted comment
 7142
 7143            fn d() {
 7144                // e
 7145                // f
 7146            }
 7147        }
 7148
 7149        fn g() {
 7150            // h
 7151        }
 7152    "#
 7153    .unindent();
 7154
 7155    let text = r#"
 7156        ˇimpl A {
 7157
 7158            fn b() {
 7159                c();
 7160            }
 7161
 7162            fn d() {
 7163                // e
 7164                // f
 7165            }
 7166        }
 7167
 7168        fn g() {
 7169            // h
 7170        }
 7171    "#
 7172    .unindent();
 7173
 7174    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7175    cx.set_state(&text);
 7176    cx.set_head_text(&base_text);
 7177    cx.update_editor(|editor, window, cx| {
 7178        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7179    });
 7180
 7181    cx.assert_state_with_diff(
 7182        "
 7183        ˇimpl A {
 7184      -     // this is an uncommitted comment
 7185
 7186            fn b() {
 7187                c();
 7188            }
 7189
 7190      -     // this is another uncommitted comment
 7191      -
 7192            fn d() {
 7193                // e
 7194                // f
 7195            }
 7196        }
 7197
 7198        fn g() {
 7199            // h
 7200        }
 7201    "
 7202        .unindent(),
 7203    );
 7204
 7205    let expected_display_text = "
 7206        impl A {
 7207            // this is an uncommitted comment
 7208
 7209            fn b() {
 7210 7211            }
 7212
 7213            // this is another uncommitted comment
 7214
 7215            fn d() {
 7216 7217            }
 7218        }
 7219
 7220        fn g() {
 7221 7222        }
 7223        "
 7224    .unindent();
 7225
 7226    cx.update_editor(|editor, window, cx| {
 7227        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7228        assert_eq!(editor.display_text(cx), expected_display_text);
 7229    });
 7230}
 7231
 7232#[gpui::test]
 7233async fn test_autoindent(cx: &mut TestAppContext) {
 7234    init_test(cx, |_| {});
 7235
 7236    let language = Arc::new(
 7237        Language::new(
 7238            LanguageConfig {
 7239                brackets: BracketPairConfig {
 7240                    pairs: vec![
 7241                        BracketPair {
 7242                            start: "{".to_string(),
 7243                            end: "}".to_string(),
 7244                            close: false,
 7245                            surround: false,
 7246                            newline: true,
 7247                        },
 7248                        BracketPair {
 7249                            start: "(".to_string(),
 7250                            end: ")".to_string(),
 7251                            close: false,
 7252                            surround: false,
 7253                            newline: true,
 7254                        },
 7255                    ],
 7256                    ..Default::default()
 7257                },
 7258                ..Default::default()
 7259            },
 7260            Some(tree_sitter_rust::LANGUAGE.into()),
 7261        )
 7262        .with_indents_query(
 7263            r#"
 7264                (_ "(" ")" @end) @indent
 7265                (_ "{" "}" @end) @indent
 7266            "#,
 7267        )
 7268        .unwrap(),
 7269    );
 7270
 7271    let text = "fn a() {}";
 7272
 7273    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7274    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7275    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7276    editor
 7277        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7278        .await;
 7279
 7280    editor.update_in(cx, |editor, window, cx| {
 7281        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7282        editor.newline(&Newline, window, cx);
 7283        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7284        assert_eq!(
 7285            editor.selections.ranges(cx),
 7286            &[
 7287                Point::new(1, 4)..Point::new(1, 4),
 7288                Point::new(3, 4)..Point::new(3, 4),
 7289                Point::new(5, 0)..Point::new(5, 0)
 7290            ]
 7291        );
 7292    });
 7293}
 7294
 7295#[gpui::test]
 7296async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7297    init_test(cx, |_| {});
 7298
 7299    {
 7300        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7301        cx.set_state(indoc! {"
 7302            impl A {
 7303
 7304                fn b() {}
 7305
 7306            «fn c() {
 7307
 7308            }ˇ»
 7309            }
 7310        "});
 7311
 7312        cx.update_editor(|editor, window, cx| {
 7313            editor.autoindent(&Default::default(), window, cx);
 7314        });
 7315
 7316        cx.assert_editor_state(indoc! {"
 7317            impl A {
 7318
 7319                fn b() {}
 7320
 7321                «fn c() {
 7322
 7323                }ˇ»
 7324            }
 7325        "});
 7326    }
 7327
 7328    {
 7329        let mut cx = EditorTestContext::new_multibuffer(
 7330            cx,
 7331            [indoc! { "
 7332                impl A {
 7333                «
 7334                // a
 7335                fn b(){}
 7336                »
 7337                «
 7338                    }
 7339                    fn c(){}
 7340                »
 7341            "}],
 7342        );
 7343
 7344        let buffer = cx.update_editor(|editor, _, cx| {
 7345            let buffer = editor.buffer().update(cx, |buffer, _| {
 7346                buffer.all_buffers().iter().next().unwrap().clone()
 7347            });
 7348            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7349            buffer
 7350        });
 7351
 7352        cx.run_until_parked();
 7353        cx.update_editor(|editor, window, cx| {
 7354            editor.select_all(&Default::default(), window, cx);
 7355            editor.autoindent(&Default::default(), window, cx)
 7356        });
 7357        cx.run_until_parked();
 7358
 7359        cx.update(|_, cx| {
 7360            assert_eq!(
 7361                buffer.read(cx).text(),
 7362                indoc! { "
 7363                    impl A {
 7364
 7365                        // a
 7366                        fn b(){}
 7367
 7368
 7369                    }
 7370                    fn c(){}
 7371
 7372                " }
 7373            )
 7374        });
 7375    }
 7376}
 7377
 7378#[gpui::test]
 7379async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7380    init_test(cx, |_| {});
 7381
 7382    let mut cx = EditorTestContext::new(cx).await;
 7383
 7384    let language = Arc::new(Language::new(
 7385        LanguageConfig {
 7386            brackets: BracketPairConfig {
 7387                pairs: vec![
 7388                    BracketPair {
 7389                        start: "{".to_string(),
 7390                        end: "}".to_string(),
 7391                        close: true,
 7392                        surround: true,
 7393                        newline: true,
 7394                    },
 7395                    BracketPair {
 7396                        start: "(".to_string(),
 7397                        end: ")".to_string(),
 7398                        close: true,
 7399                        surround: true,
 7400                        newline: true,
 7401                    },
 7402                    BracketPair {
 7403                        start: "/*".to_string(),
 7404                        end: " */".to_string(),
 7405                        close: true,
 7406                        surround: true,
 7407                        newline: true,
 7408                    },
 7409                    BracketPair {
 7410                        start: "[".to_string(),
 7411                        end: "]".to_string(),
 7412                        close: false,
 7413                        surround: false,
 7414                        newline: true,
 7415                    },
 7416                    BracketPair {
 7417                        start: "\"".to_string(),
 7418                        end: "\"".to_string(),
 7419                        close: true,
 7420                        surround: true,
 7421                        newline: false,
 7422                    },
 7423                    BracketPair {
 7424                        start: "<".to_string(),
 7425                        end: ">".to_string(),
 7426                        close: false,
 7427                        surround: true,
 7428                        newline: true,
 7429                    },
 7430                ],
 7431                ..Default::default()
 7432            },
 7433            autoclose_before: "})]".to_string(),
 7434            ..Default::default()
 7435        },
 7436        Some(tree_sitter_rust::LANGUAGE.into()),
 7437    ));
 7438
 7439    cx.language_registry().add(language.clone());
 7440    cx.update_buffer(|buffer, cx| {
 7441        buffer.set_language(Some(language), cx);
 7442    });
 7443
 7444    cx.set_state(
 7445        &r#"
 7446            🏀ˇ
 7447            εˇ
 7448            ❤️ˇ
 7449        "#
 7450        .unindent(),
 7451    );
 7452
 7453    // autoclose multiple nested brackets at multiple cursors
 7454    cx.update_editor(|editor, window, cx| {
 7455        editor.handle_input("{", window, cx);
 7456        editor.handle_input("{", window, cx);
 7457        editor.handle_input("{", window, cx);
 7458    });
 7459    cx.assert_editor_state(
 7460        &"
 7461            🏀{{{ˇ}}}
 7462            ε{{{ˇ}}}
 7463            ❤️{{{ˇ}}}
 7464        "
 7465        .unindent(),
 7466    );
 7467
 7468    // insert a different closing bracket
 7469    cx.update_editor(|editor, window, cx| {
 7470        editor.handle_input(")", window, cx);
 7471    });
 7472    cx.assert_editor_state(
 7473        &"
 7474            🏀{{{)ˇ}}}
 7475            ε{{{)ˇ}}}
 7476            ❤️{{{)ˇ}}}
 7477        "
 7478        .unindent(),
 7479    );
 7480
 7481    // skip over the auto-closed brackets when typing a closing bracket
 7482    cx.update_editor(|editor, window, cx| {
 7483        editor.move_right(&MoveRight, window, cx);
 7484        editor.handle_input("}", window, cx);
 7485        editor.handle_input("}", window, cx);
 7486        editor.handle_input("}", window, cx);
 7487    });
 7488    cx.assert_editor_state(
 7489        &"
 7490            🏀{{{)}}}}ˇ
 7491            ε{{{)}}}}ˇ
 7492            ❤️{{{)}}}}ˇ
 7493        "
 7494        .unindent(),
 7495    );
 7496
 7497    // autoclose multi-character pairs
 7498    cx.set_state(
 7499        &"
 7500            ˇ
 7501            ˇ
 7502        "
 7503        .unindent(),
 7504    );
 7505    cx.update_editor(|editor, window, cx| {
 7506        editor.handle_input("/", window, cx);
 7507        editor.handle_input("*", window, cx);
 7508    });
 7509    cx.assert_editor_state(
 7510        &"
 7511            /*ˇ */
 7512            /*ˇ */
 7513        "
 7514        .unindent(),
 7515    );
 7516
 7517    // one cursor autocloses a multi-character pair, one cursor
 7518    // does not autoclose.
 7519    cx.set_state(
 7520        &"
 7521 7522            ˇ
 7523        "
 7524        .unindent(),
 7525    );
 7526    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7527    cx.assert_editor_state(
 7528        &"
 7529            /*ˇ */
 7530 7531        "
 7532        .unindent(),
 7533    );
 7534
 7535    // Don't autoclose if the next character isn't whitespace and isn't
 7536    // listed in the language's "autoclose_before" section.
 7537    cx.set_state("ˇa b");
 7538    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7539    cx.assert_editor_state("{ˇa b");
 7540
 7541    // Don't autoclose if `close` is false for the bracket pair
 7542    cx.set_state("ˇ");
 7543    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7544    cx.assert_editor_state("");
 7545
 7546    // Surround with brackets if text is selected
 7547    cx.set_state("«aˇ» b");
 7548    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7549    cx.assert_editor_state("{«aˇ»} b");
 7550
 7551    // Autoclose when not immediately after a word character
 7552    cx.set_state("a ˇ");
 7553    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7554    cx.assert_editor_state("a \"ˇ\"");
 7555
 7556    // Autoclose pair where the start and end characters are the same
 7557    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7558    cx.assert_editor_state("a \"\"ˇ");
 7559
 7560    // Don't autoclose when immediately after a word character
 7561    cx.set_state("");
 7562    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7563    cx.assert_editor_state("a\"ˇ");
 7564
 7565    // Do autoclose when after a non-word character
 7566    cx.set_state("");
 7567    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7568    cx.assert_editor_state("{\"ˇ\"");
 7569
 7570    // Non identical pairs autoclose regardless of preceding character
 7571    cx.set_state("");
 7572    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7573    cx.assert_editor_state("a{ˇ}");
 7574
 7575    // Don't autoclose pair if autoclose is disabled
 7576    cx.set_state("ˇ");
 7577    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7578    cx.assert_editor_state("");
 7579
 7580    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7581    cx.set_state("«aˇ» b");
 7582    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7583    cx.assert_editor_state("<«aˇ»> b");
 7584}
 7585
 7586#[gpui::test]
 7587async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7588    init_test(cx, |settings| {
 7589        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7590    });
 7591
 7592    let mut cx = EditorTestContext::new(cx).await;
 7593
 7594    let language = Arc::new(Language::new(
 7595        LanguageConfig {
 7596            brackets: BracketPairConfig {
 7597                pairs: vec![
 7598                    BracketPair {
 7599                        start: "{".to_string(),
 7600                        end: "}".to_string(),
 7601                        close: true,
 7602                        surround: true,
 7603                        newline: true,
 7604                    },
 7605                    BracketPair {
 7606                        start: "(".to_string(),
 7607                        end: ")".to_string(),
 7608                        close: true,
 7609                        surround: true,
 7610                        newline: true,
 7611                    },
 7612                    BracketPair {
 7613                        start: "[".to_string(),
 7614                        end: "]".to_string(),
 7615                        close: false,
 7616                        surround: false,
 7617                        newline: true,
 7618                    },
 7619                ],
 7620                ..Default::default()
 7621            },
 7622            autoclose_before: "})]".to_string(),
 7623            ..Default::default()
 7624        },
 7625        Some(tree_sitter_rust::LANGUAGE.into()),
 7626    ));
 7627
 7628    cx.language_registry().add(language.clone());
 7629    cx.update_buffer(|buffer, cx| {
 7630        buffer.set_language(Some(language), cx);
 7631    });
 7632
 7633    cx.set_state(
 7634        &"
 7635            ˇ
 7636            ˇ
 7637            ˇ
 7638        "
 7639        .unindent(),
 7640    );
 7641
 7642    // ensure only matching closing brackets are skipped over
 7643    cx.update_editor(|editor, window, cx| {
 7644        editor.handle_input("}", window, cx);
 7645        editor.move_left(&MoveLeft, window, cx);
 7646        editor.handle_input(")", window, cx);
 7647        editor.move_left(&MoveLeft, window, cx);
 7648    });
 7649    cx.assert_editor_state(
 7650        &"
 7651            ˇ)}
 7652            ˇ)}
 7653            ˇ)}
 7654        "
 7655        .unindent(),
 7656    );
 7657
 7658    // skip-over closing brackets at multiple cursors
 7659    cx.update_editor(|editor, window, cx| {
 7660        editor.handle_input(")", window, cx);
 7661        editor.handle_input("}", window, cx);
 7662    });
 7663    cx.assert_editor_state(
 7664        &"
 7665            )}ˇ
 7666            )}ˇ
 7667            )}ˇ
 7668        "
 7669        .unindent(),
 7670    );
 7671
 7672    // ignore non-close brackets
 7673    cx.update_editor(|editor, window, cx| {
 7674        editor.handle_input("]", window, cx);
 7675        editor.move_left(&MoveLeft, window, cx);
 7676        editor.handle_input("]", window, cx);
 7677    });
 7678    cx.assert_editor_state(
 7679        &"
 7680            )}]ˇ]
 7681            )}]ˇ]
 7682            )}]ˇ]
 7683        "
 7684        .unindent(),
 7685    );
 7686}
 7687
 7688#[gpui::test]
 7689async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7690    init_test(cx, |_| {});
 7691
 7692    let mut cx = EditorTestContext::new(cx).await;
 7693
 7694    let html_language = Arc::new(
 7695        Language::new(
 7696            LanguageConfig {
 7697                name: "HTML".into(),
 7698                brackets: BracketPairConfig {
 7699                    pairs: vec![
 7700                        BracketPair {
 7701                            start: "<".into(),
 7702                            end: ">".into(),
 7703                            close: true,
 7704                            ..Default::default()
 7705                        },
 7706                        BracketPair {
 7707                            start: "{".into(),
 7708                            end: "}".into(),
 7709                            close: true,
 7710                            ..Default::default()
 7711                        },
 7712                        BracketPair {
 7713                            start: "(".into(),
 7714                            end: ")".into(),
 7715                            close: true,
 7716                            ..Default::default()
 7717                        },
 7718                    ],
 7719                    ..Default::default()
 7720                },
 7721                autoclose_before: "})]>".into(),
 7722                ..Default::default()
 7723            },
 7724            Some(tree_sitter_html::LANGUAGE.into()),
 7725        )
 7726        .with_injection_query(
 7727            r#"
 7728            (script_element
 7729                (raw_text) @injection.content
 7730                (#set! injection.language "javascript"))
 7731            "#,
 7732        )
 7733        .unwrap(),
 7734    );
 7735
 7736    let javascript_language = Arc::new(Language::new(
 7737        LanguageConfig {
 7738            name: "JavaScript".into(),
 7739            brackets: BracketPairConfig {
 7740                pairs: vec![
 7741                    BracketPair {
 7742                        start: "/*".into(),
 7743                        end: " */".into(),
 7744                        close: true,
 7745                        ..Default::default()
 7746                    },
 7747                    BracketPair {
 7748                        start: "{".into(),
 7749                        end: "}".into(),
 7750                        close: true,
 7751                        ..Default::default()
 7752                    },
 7753                    BracketPair {
 7754                        start: "(".into(),
 7755                        end: ")".into(),
 7756                        close: true,
 7757                        ..Default::default()
 7758                    },
 7759                ],
 7760                ..Default::default()
 7761            },
 7762            autoclose_before: "})]>".into(),
 7763            ..Default::default()
 7764        },
 7765        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7766    ));
 7767
 7768    cx.language_registry().add(html_language.clone());
 7769    cx.language_registry().add(javascript_language.clone());
 7770
 7771    cx.update_buffer(|buffer, cx| {
 7772        buffer.set_language(Some(html_language), cx);
 7773    });
 7774
 7775    cx.set_state(
 7776        &r#"
 7777            <body>ˇ
 7778                <script>
 7779                    var x = 1;ˇ
 7780                </script>
 7781            </body>ˇ
 7782        "#
 7783        .unindent(),
 7784    );
 7785
 7786    // Precondition: different languages are active at different locations.
 7787    cx.update_editor(|editor, window, cx| {
 7788        let snapshot = editor.snapshot(window, cx);
 7789        let cursors = editor.selections.ranges::<usize>(cx);
 7790        let languages = cursors
 7791            .iter()
 7792            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7793            .collect::<Vec<_>>();
 7794        assert_eq!(
 7795            languages,
 7796            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7797        );
 7798    });
 7799
 7800    // Angle brackets autoclose in HTML, but not JavaScript.
 7801    cx.update_editor(|editor, window, cx| {
 7802        editor.handle_input("<", window, cx);
 7803        editor.handle_input("a", window, cx);
 7804    });
 7805    cx.assert_editor_state(
 7806        &r#"
 7807            <body><aˇ>
 7808                <script>
 7809                    var x = 1;<aˇ
 7810                </script>
 7811            </body><aˇ>
 7812        "#
 7813        .unindent(),
 7814    );
 7815
 7816    // Curly braces and parens autoclose in both HTML and JavaScript.
 7817    cx.update_editor(|editor, window, cx| {
 7818        editor.handle_input(" b=", window, cx);
 7819        editor.handle_input("{", window, cx);
 7820        editor.handle_input("c", window, cx);
 7821        editor.handle_input("(", window, cx);
 7822    });
 7823    cx.assert_editor_state(
 7824        &r#"
 7825            <body><a b={c(ˇ)}>
 7826                <script>
 7827                    var x = 1;<a b={c(ˇ)}
 7828                </script>
 7829            </body><a b={c(ˇ)}>
 7830        "#
 7831        .unindent(),
 7832    );
 7833
 7834    // Brackets that were already autoclosed are skipped.
 7835    cx.update_editor(|editor, window, cx| {
 7836        editor.handle_input(")", window, cx);
 7837        editor.handle_input("d", window, cx);
 7838        editor.handle_input("}", window, cx);
 7839    });
 7840    cx.assert_editor_state(
 7841        &r#"
 7842            <body><a b={c()d}ˇ>
 7843                <script>
 7844                    var x = 1;<a b={c()d}ˇ
 7845                </script>
 7846            </body><a b={c()d}ˇ>
 7847        "#
 7848        .unindent(),
 7849    );
 7850    cx.update_editor(|editor, window, cx| {
 7851        editor.handle_input(">", window, cx);
 7852    });
 7853    cx.assert_editor_state(
 7854        &r#"
 7855            <body><a b={c()d}>ˇ
 7856                <script>
 7857                    var x = 1;<a b={c()d}>ˇ
 7858                </script>
 7859            </body><a b={c()d}>ˇ
 7860        "#
 7861        .unindent(),
 7862    );
 7863
 7864    // Reset
 7865    cx.set_state(
 7866        &r#"
 7867            <body>ˇ
 7868                <script>
 7869                    var x = 1;ˇ
 7870                </script>
 7871            </body>ˇ
 7872        "#
 7873        .unindent(),
 7874    );
 7875
 7876    cx.update_editor(|editor, window, cx| {
 7877        editor.handle_input("<", window, cx);
 7878    });
 7879    cx.assert_editor_state(
 7880        &r#"
 7881            <body><ˇ>
 7882                <script>
 7883                    var x = 1;<ˇ
 7884                </script>
 7885            </body><ˇ>
 7886        "#
 7887        .unindent(),
 7888    );
 7889
 7890    // When backspacing, the closing angle brackets are removed.
 7891    cx.update_editor(|editor, window, cx| {
 7892        editor.backspace(&Backspace, window, cx);
 7893    });
 7894    cx.assert_editor_state(
 7895        &r#"
 7896            <body>ˇ
 7897                <script>
 7898                    var x = 1;ˇ
 7899                </script>
 7900            </body>ˇ
 7901        "#
 7902        .unindent(),
 7903    );
 7904
 7905    // Block comments autoclose in JavaScript, but not HTML.
 7906    cx.update_editor(|editor, window, cx| {
 7907        editor.handle_input("/", window, cx);
 7908        editor.handle_input("*", window, cx);
 7909    });
 7910    cx.assert_editor_state(
 7911        &r#"
 7912            <body>/*ˇ
 7913                <script>
 7914                    var x = 1;/*ˇ */
 7915                </script>
 7916            </body>/*ˇ
 7917        "#
 7918        .unindent(),
 7919    );
 7920}
 7921
 7922#[gpui::test]
 7923async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7924    init_test(cx, |_| {});
 7925
 7926    let mut cx = EditorTestContext::new(cx).await;
 7927
 7928    let rust_language = Arc::new(
 7929        Language::new(
 7930            LanguageConfig {
 7931                name: "Rust".into(),
 7932                brackets: serde_json::from_value(json!([
 7933                    { "start": "{", "end": "}", "close": true, "newline": true },
 7934                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7935                ]))
 7936                .unwrap(),
 7937                autoclose_before: "})]>".into(),
 7938                ..Default::default()
 7939            },
 7940            Some(tree_sitter_rust::LANGUAGE.into()),
 7941        )
 7942        .with_override_query("(string_literal) @string")
 7943        .unwrap(),
 7944    );
 7945
 7946    cx.language_registry().add(rust_language.clone());
 7947    cx.update_buffer(|buffer, cx| {
 7948        buffer.set_language(Some(rust_language), cx);
 7949    });
 7950
 7951    cx.set_state(
 7952        &r#"
 7953            let x = ˇ
 7954        "#
 7955        .unindent(),
 7956    );
 7957
 7958    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7959    cx.update_editor(|editor, window, cx| {
 7960        editor.handle_input("\"", window, cx);
 7961    });
 7962    cx.assert_editor_state(
 7963        &r#"
 7964            let x = "ˇ"
 7965        "#
 7966        .unindent(),
 7967    );
 7968
 7969    // Inserting another quotation mark. The cursor moves across the existing
 7970    // automatically-inserted quotation mark.
 7971    cx.update_editor(|editor, window, cx| {
 7972        editor.handle_input("\"", window, cx);
 7973    });
 7974    cx.assert_editor_state(
 7975        &r#"
 7976            let x = ""ˇ
 7977        "#
 7978        .unindent(),
 7979    );
 7980
 7981    // Reset
 7982    cx.set_state(
 7983        &r#"
 7984            let x = ˇ
 7985        "#
 7986        .unindent(),
 7987    );
 7988
 7989    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7990    cx.update_editor(|editor, window, cx| {
 7991        editor.handle_input("\"", window, cx);
 7992        editor.handle_input(" ", window, cx);
 7993        editor.move_left(&Default::default(), window, cx);
 7994        editor.handle_input("\\", window, cx);
 7995        editor.handle_input("\"", window, cx);
 7996    });
 7997    cx.assert_editor_state(
 7998        &r#"
 7999            let x = "\"ˇ "
 8000        "#
 8001        .unindent(),
 8002    );
 8003
 8004    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 8005    // mark. Nothing is inserted.
 8006    cx.update_editor(|editor, window, cx| {
 8007        editor.move_right(&Default::default(), window, cx);
 8008        editor.handle_input("\"", window, cx);
 8009    });
 8010    cx.assert_editor_state(
 8011        &r#"
 8012            let x = "\" "ˇ
 8013        "#
 8014        .unindent(),
 8015    );
 8016}
 8017
 8018#[gpui::test]
 8019async fn test_surround_with_pair(cx: &mut TestAppContext) {
 8020    init_test(cx, |_| {});
 8021
 8022    let language = Arc::new(Language::new(
 8023        LanguageConfig {
 8024            brackets: BracketPairConfig {
 8025                pairs: vec![
 8026                    BracketPair {
 8027                        start: "{".to_string(),
 8028                        end: "}".to_string(),
 8029                        close: true,
 8030                        surround: true,
 8031                        newline: true,
 8032                    },
 8033                    BracketPair {
 8034                        start: "/* ".to_string(),
 8035                        end: "*/".to_string(),
 8036                        close: true,
 8037                        surround: true,
 8038                        ..Default::default()
 8039                    },
 8040                ],
 8041                ..Default::default()
 8042            },
 8043            ..Default::default()
 8044        },
 8045        Some(tree_sitter_rust::LANGUAGE.into()),
 8046    ));
 8047
 8048    let text = r#"
 8049        a
 8050        b
 8051        c
 8052    "#
 8053    .unindent();
 8054
 8055    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8056    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8057    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8058    editor
 8059        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8060        .await;
 8061
 8062    editor.update_in(cx, |editor, window, cx| {
 8063        editor.change_selections(None, window, cx, |s| {
 8064            s.select_display_ranges([
 8065                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8066                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8067                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 8068            ])
 8069        });
 8070
 8071        editor.handle_input("{", window, cx);
 8072        editor.handle_input("{", window, cx);
 8073        editor.handle_input("{", window, cx);
 8074        assert_eq!(
 8075            editor.text(cx),
 8076            "
 8077                {{{a}}}
 8078                {{{b}}}
 8079                {{{c}}}
 8080            "
 8081            .unindent()
 8082        );
 8083        assert_eq!(
 8084            editor.selections.display_ranges(cx),
 8085            [
 8086                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 8087                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 8088                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 8089            ]
 8090        );
 8091
 8092        editor.undo(&Undo, window, cx);
 8093        editor.undo(&Undo, window, cx);
 8094        editor.undo(&Undo, window, cx);
 8095        assert_eq!(
 8096            editor.text(cx),
 8097            "
 8098                a
 8099                b
 8100                c
 8101            "
 8102            .unindent()
 8103        );
 8104        assert_eq!(
 8105            editor.selections.display_ranges(cx),
 8106            [
 8107                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8108                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8109                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8110            ]
 8111        );
 8112
 8113        // Ensure inserting the first character of a multi-byte bracket pair
 8114        // doesn't surround the selections with the bracket.
 8115        editor.handle_input("/", window, cx);
 8116        assert_eq!(
 8117            editor.text(cx),
 8118            "
 8119                /
 8120                /
 8121                /
 8122            "
 8123            .unindent()
 8124        );
 8125        assert_eq!(
 8126            editor.selections.display_ranges(cx),
 8127            [
 8128                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8129                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8130                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8131            ]
 8132        );
 8133
 8134        editor.undo(&Undo, window, cx);
 8135        assert_eq!(
 8136            editor.text(cx),
 8137            "
 8138                a
 8139                b
 8140                c
 8141            "
 8142            .unindent()
 8143        );
 8144        assert_eq!(
 8145            editor.selections.display_ranges(cx),
 8146            [
 8147                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8148                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8149                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8150            ]
 8151        );
 8152
 8153        // Ensure inserting the last character of a multi-byte bracket pair
 8154        // doesn't surround the selections with the bracket.
 8155        editor.handle_input("*", window, cx);
 8156        assert_eq!(
 8157            editor.text(cx),
 8158            "
 8159                *
 8160                *
 8161                *
 8162            "
 8163            .unindent()
 8164        );
 8165        assert_eq!(
 8166            editor.selections.display_ranges(cx),
 8167            [
 8168                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8169                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8170                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8171            ]
 8172        );
 8173    });
 8174}
 8175
 8176#[gpui::test]
 8177async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8178    init_test(cx, |_| {});
 8179
 8180    let language = Arc::new(Language::new(
 8181        LanguageConfig {
 8182            brackets: BracketPairConfig {
 8183                pairs: vec![BracketPair {
 8184                    start: "{".to_string(),
 8185                    end: "}".to_string(),
 8186                    close: true,
 8187                    surround: true,
 8188                    newline: true,
 8189                }],
 8190                ..Default::default()
 8191            },
 8192            autoclose_before: "}".to_string(),
 8193            ..Default::default()
 8194        },
 8195        Some(tree_sitter_rust::LANGUAGE.into()),
 8196    ));
 8197
 8198    let text = r#"
 8199        a
 8200        b
 8201        c
 8202    "#
 8203    .unindent();
 8204
 8205    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8206    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8207    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8208    editor
 8209        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8210        .await;
 8211
 8212    editor.update_in(cx, |editor, window, cx| {
 8213        editor.change_selections(None, window, cx, |s| {
 8214            s.select_ranges([
 8215                Point::new(0, 1)..Point::new(0, 1),
 8216                Point::new(1, 1)..Point::new(1, 1),
 8217                Point::new(2, 1)..Point::new(2, 1),
 8218            ])
 8219        });
 8220
 8221        editor.handle_input("{", window, cx);
 8222        editor.handle_input("{", window, cx);
 8223        editor.handle_input("_", window, cx);
 8224        assert_eq!(
 8225            editor.text(cx),
 8226            "
 8227                a{{_}}
 8228                b{{_}}
 8229                c{{_}}
 8230            "
 8231            .unindent()
 8232        );
 8233        assert_eq!(
 8234            editor.selections.ranges::<Point>(cx),
 8235            [
 8236                Point::new(0, 4)..Point::new(0, 4),
 8237                Point::new(1, 4)..Point::new(1, 4),
 8238                Point::new(2, 4)..Point::new(2, 4)
 8239            ]
 8240        );
 8241
 8242        editor.backspace(&Default::default(), window, cx);
 8243        editor.backspace(&Default::default(), window, cx);
 8244        assert_eq!(
 8245            editor.text(cx),
 8246            "
 8247                a{}
 8248                b{}
 8249                c{}
 8250            "
 8251            .unindent()
 8252        );
 8253        assert_eq!(
 8254            editor.selections.ranges::<Point>(cx),
 8255            [
 8256                Point::new(0, 2)..Point::new(0, 2),
 8257                Point::new(1, 2)..Point::new(1, 2),
 8258                Point::new(2, 2)..Point::new(2, 2)
 8259            ]
 8260        );
 8261
 8262        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8263        assert_eq!(
 8264            editor.text(cx),
 8265            "
 8266                a
 8267                b
 8268                c
 8269            "
 8270            .unindent()
 8271        );
 8272        assert_eq!(
 8273            editor.selections.ranges::<Point>(cx),
 8274            [
 8275                Point::new(0, 1)..Point::new(0, 1),
 8276                Point::new(1, 1)..Point::new(1, 1),
 8277                Point::new(2, 1)..Point::new(2, 1)
 8278            ]
 8279        );
 8280    });
 8281}
 8282
 8283#[gpui::test]
 8284async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8285    init_test(cx, |settings| {
 8286        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8287    });
 8288
 8289    let mut cx = EditorTestContext::new(cx).await;
 8290
 8291    let language = Arc::new(Language::new(
 8292        LanguageConfig {
 8293            brackets: BracketPairConfig {
 8294                pairs: vec![
 8295                    BracketPair {
 8296                        start: "{".to_string(),
 8297                        end: "}".to_string(),
 8298                        close: true,
 8299                        surround: true,
 8300                        newline: true,
 8301                    },
 8302                    BracketPair {
 8303                        start: "(".to_string(),
 8304                        end: ")".to_string(),
 8305                        close: true,
 8306                        surround: true,
 8307                        newline: true,
 8308                    },
 8309                    BracketPair {
 8310                        start: "[".to_string(),
 8311                        end: "]".to_string(),
 8312                        close: false,
 8313                        surround: true,
 8314                        newline: true,
 8315                    },
 8316                ],
 8317                ..Default::default()
 8318            },
 8319            autoclose_before: "})]".to_string(),
 8320            ..Default::default()
 8321        },
 8322        Some(tree_sitter_rust::LANGUAGE.into()),
 8323    ));
 8324
 8325    cx.language_registry().add(language.clone());
 8326    cx.update_buffer(|buffer, cx| {
 8327        buffer.set_language(Some(language), cx);
 8328    });
 8329
 8330    cx.set_state(
 8331        &"
 8332            {(ˇ)}
 8333            [[ˇ]]
 8334            {(ˇ)}
 8335        "
 8336        .unindent(),
 8337    );
 8338
 8339    cx.update_editor(|editor, window, cx| {
 8340        editor.backspace(&Default::default(), window, cx);
 8341        editor.backspace(&Default::default(), window, cx);
 8342    });
 8343
 8344    cx.assert_editor_state(
 8345        &"
 8346            ˇ
 8347            ˇ]]
 8348            ˇ
 8349        "
 8350        .unindent(),
 8351    );
 8352
 8353    cx.update_editor(|editor, window, cx| {
 8354        editor.handle_input("{", window, cx);
 8355        editor.handle_input("{", window, cx);
 8356        editor.move_right(&MoveRight, window, cx);
 8357        editor.move_right(&MoveRight, window, cx);
 8358        editor.move_left(&MoveLeft, window, cx);
 8359        editor.move_left(&MoveLeft, window, cx);
 8360        editor.backspace(&Default::default(), window, cx);
 8361    });
 8362
 8363    cx.assert_editor_state(
 8364        &"
 8365            {ˇ}
 8366            {ˇ}]]
 8367            {ˇ}
 8368        "
 8369        .unindent(),
 8370    );
 8371
 8372    cx.update_editor(|editor, window, cx| {
 8373        editor.backspace(&Default::default(), window, cx);
 8374    });
 8375
 8376    cx.assert_editor_state(
 8377        &"
 8378            ˇ
 8379            ˇ]]
 8380            ˇ
 8381        "
 8382        .unindent(),
 8383    );
 8384}
 8385
 8386#[gpui::test]
 8387async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8388    init_test(cx, |_| {});
 8389
 8390    let language = Arc::new(Language::new(
 8391        LanguageConfig::default(),
 8392        Some(tree_sitter_rust::LANGUAGE.into()),
 8393    ));
 8394
 8395    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8396    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8397    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8398    editor
 8399        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8400        .await;
 8401
 8402    editor.update_in(cx, |editor, window, cx| {
 8403        editor.set_auto_replace_emoji_shortcode(true);
 8404
 8405        editor.handle_input("Hello ", window, cx);
 8406        editor.handle_input(":wave", window, cx);
 8407        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8408
 8409        editor.handle_input(":", window, cx);
 8410        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8411
 8412        editor.handle_input(" :smile", window, cx);
 8413        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8414
 8415        editor.handle_input(":", window, cx);
 8416        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8417
 8418        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8419        editor.handle_input(":wave", window, cx);
 8420        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8421
 8422        editor.handle_input(":", window, cx);
 8423        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8424
 8425        editor.handle_input(":1", window, cx);
 8426        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8427
 8428        editor.handle_input(":", window, cx);
 8429        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8430
 8431        // Ensure shortcode does not get replaced when it is part of a word
 8432        editor.handle_input(" Test:wave", window, cx);
 8433        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8434
 8435        editor.handle_input(":", window, cx);
 8436        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8437
 8438        editor.set_auto_replace_emoji_shortcode(false);
 8439
 8440        // Ensure shortcode does not get replaced when auto replace is off
 8441        editor.handle_input(" :wave", window, cx);
 8442        assert_eq!(
 8443            editor.text(cx),
 8444            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8445        );
 8446
 8447        editor.handle_input(":", window, cx);
 8448        assert_eq!(
 8449            editor.text(cx),
 8450            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8451        );
 8452    });
 8453}
 8454
 8455#[gpui::test]
 8456async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8457    init_test(cx, |_| {});
 8458
 8459    let (text, insertion_ranges) = marked_text_ranges(
 8460        indoc! {"
 8461            ˇ
 8462        "},
 8463        false,
 8464    );
 8465
 8466    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8467    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8468
 8469    _ = editor.update_in(cx, |editor, window, cx| {
 8470        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8471
 8472        editor
 8473            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8474            .unwrap();
 8475
 8476        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8477            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8478            assert_eq!(editor.text(cx), expected_text);
 8479            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8480        }
 8481
 8482        assert(
 8483            editor,
 8484            cx,
 8485            indoc! {"
 8486            type «» =•
 8487            "},
 8488        );
 8489
 8490        assert!(editor.context_menu_visible(), "There should be a matches");
 8491    });
 8492}
 8493
 8494#[gpui::test]
 8495async fn test_snippets(cx: &mut TestAppContext) {
 8496    init_test(cx, |_| {});
 8497
 8498    let (text, insertion_ranges) = marked_text_ranges(
 8499        indoc! {"
 8500            a.ˇ b
 8501            a.ˇ b
 8502            a.ˇ b
 8503        "},
 8504        false,
 8505    );
 8506
 8507    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8508    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8509
 8510    editor.update_in(cx, |editor, window, cx| {
 8511        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8512
 8513        editor
 8514            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8515            .unwrap();
 8516
 8517        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8518            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8519            assert_eq!(editor.text(cx), expected_text);
 8520            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8521        }
 8522
 8523        assert(
 8524            editor,
 8525            cx,
 8526            indoc! {"
 8527                a.f(«one», two, «three») b
 8528                a.f(«one», two, «three») b
 8529                a.f(«one», two, «three») b
 8530            "},
 8531        );
 8532
 8533        // Can't move earlier than the first tab stop
 8534        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8535        assert(
 8536            editor,
 8537            cx,
 8538            indoc! {"
 8539                a.f(«one», two, «three») b
 8540                a.f(«one», two, «three») b
 8541                a.f(«one», two, «three») b
 8542            "},
 8543        );
 8544
 8545        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8546        assert(
 8547            editor,
 8548            cx,
 8549            indoc! {"
 8550                a.f(one, «two», three) b
 8551                a.f(one, «two», three) b
 8552                a.f(one, «two», three) b
 8553            "},
 8554        );
 8555
 8556        editor.move_to_prev_snippet_tabstop(window, cx);
 8557        assert(
 8558            editor,
 8559            cx,
 8560            indoc! {"
 8561                a.f(«one», two, «three») b
 8562                a.f(«one», two, «three») b
 8563                a.f(«one», two, «three») b
 8564            "},
 8565        );
 8566
 8567        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8568        assert(
 8569            editor,
 8570            cx,
 8571            indoc! {"
 8572                a.f(one, «two», three) b
 8573                a.f(one, «two», three) b
 8574                a.f(one, «two», three) b
 8575            "},
 8576        );
 8577        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8578        assert(
 8579            editor,
 8580            cx,
 8581            indoc! {"
 8582                a.f(one, two, three)ˇ b
 8583                a.f(one, two, three)ˇ b
 8584                a.f(one, two, three)ˇ b
 8585            "},
 8586        );
 8587
 8588        // As soon as the last tab stop is reached, snippet state is gone
 8589        editor.move_to_prev_snippet_tabstop(window, cx);
 8590        assert(
 8591            editor,
 8592            cx,
 8593            indoc! {"
 8594                a.f(one, two, three)ˇ b
 8595                a.f(one, two, three)ˇ b
 8596                a.f(one, two, three)ˇ b
 8597            "},
 8598        );
 8599    });
 8600}
 8601
 8602#[gpui::test]
 8603async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8604    init_test(cx, |_| {});
 8605
 8606    let fs = FakeFs::new(cx.executor());
 8607    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8608
 8609    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8610
 8611    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8612    language_registry.add(rust_lang());
 8613    let mut fake_servers = language_registry.register_fake_lsp(
 8614        "Rust",
 8615        FakeLspAdapter {
 8616            capabilities: lsp::ServerCapabilities {
 8617                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8618                ..Default::default()
 8619            },
 8620            ..Default::default()
 8621        },
 8622    );
 8623
 8624    let buffer = project
 8625        .update(cx, |project, cx| {
 8626            project.open_local_buffer(path!("/file.rs"), cx)
 8627        })
 8628        .await
 8629        .unwrap();
 8630
 8631    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8632    let (editor, cx) = cx.add_window_view(|window, cx| {
 8633        build_editor_with_project(project.clone(), buffer, window, cx)
 8634    });
 8635    editor.update_in(cx, |editor, window, cx| {
 8636        editor.set_text("one\ntwo\nthree\n", window, cx)
 8637    });
 8638    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8639
 8640    cx.executor().start_waiting();
 8641    let fake_server = fake_servers.next().await.unwrap();
 8642
 8643    {
 8644        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8645            move |params, _| async move {
 8646                assert_eq!(
 8647                    params.text_document.uri,
 8648                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8649                );
 8650                assert_eq!(params.options.tab_size, 4);
 8651                Ok(Some(vec![lsp::TextEdit::new(
 8652                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8653                    ", ".to_string(),
 8654                )]))
 8655            },
 8656        );
 8657        let save = editor
 8658            .update_in(cx, |editor, window, cx| {
 8659                editor.save(true, project.clone(), window, cx)
 8660            })
 8661            .unwrap();
 8662        cx.executor().start_waiting();
 8663        save.await;
 8664
 8665        assert_eq!(
 8666            editor.update(cx, |editor, cx| editor.text(cx)),
 8667            "one, two\nthree\n"
 8668        );
 8669        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8670    }
 8671
 8672    {
 8673        editor.update_in(cx, |editor, window, cx| {
 8674            editor.set_text("one\ntwo\nthree\n", window, cx)
 8675        });
 8676        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8677
 8678        // Ensure we can still save even if formatting hangs.
 8679        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8680            move |params, _| async move {
 8681                assert_eq!(
 8682                    params.text_document.uri,
 8683                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8684                );
 8685                futures::future::pending::<()>().await;
 8686                unreachable!()
 8687            },
 8688        );
 8689        let save = editor
 8690            .update_in(cx, |editor, window, cx| {
 8691                editor.save(true, project.clone(), window, cx)
 8692            })
 8693            .unwrap();
 8694        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8695        cx.executor().start_waiting();
 8696        save.await;
 8697        assert_eq!(
 8698            editor.update(cx, |editor, cx| editor.text(cx)),
 8699            "one\ntwo\nthree\n"
 8700        );
 8701    }
 8702
 8703    // For non-dirty buffer, no formatting request should be sent
 8704    {
 8705        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8706
 8707        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8708            panic!("Should not be invoked on non-dirty buffer");
 8709        });
 8710        let save = editor
 8711            .update_in(cx, |editor, window, cx| {
 8712                editor.save(true, project.clone(), window, cx)
 8713            })
 8714            .unwrap();
 8715        cx.executor().start_waiting();
 8716        save.await;
 8717    }
 8718
 8719    // Set rust language override and assert overridden tabsize is sent to language server
 8720    update_test_language_settings(cx, |settings| {
 8721        settings.languages.insert(
 8722            "Rust".into(),
 8723            LanguageSettingsContent {
 8724                tab_size: NonZeroU32::new(8),
 8725                ..Default::default()
 8726            },
 8727        );
 8728    });
 8729
 8730    {
 8731        editor.update_in(cx, |editor, window, cx| {
 8732            editor.set_text("somehting_new\n", window, cx)
 8733        });
 8734        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8735        let _formatting_request_signal = fake_server
 8736            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8737                assert_eq!(
 8738                    params.text_document.uri,
 8739                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8740                );
 8741                assert_eq!(params.options.tab_size, 8);
 8742                Ok(Some(vec![]))
 8743            });
 8744        let save = editor
 8745            .update_in(cx, |editor, window, cx| {
 8746                editor.save(true, project.clone(), window, cx)
 8747            })
 8748            .unwrap();
 8749        cx.executor().start_waiting();
 8750        save.await;
 8751    }
 8752}
 8753
 8754#[gpui::test]
 8755async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8756    init_test(cx, |_| {});
 8757
 8758    let cols = 4;
 8759    let rows = 10;
 8760    let sample_text_1 = sample_text(rows, cols, 'a');
 8761    assert_eq!(
 8762        sample_text_1,
 8763        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8764    );
 8765    let sample_text_2 = sample_text(rows, cols, 'l');
 8766    assert_eq!(
 8767        sample_text_2,
 8768        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8769    );
 8770    let sample_text_3 = sample_text(rows, cols, 'v');
 8771    assert_eq!(
 8772        sample_text_3,
 8773        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8774    );
 8775
 8776    let fs = FakeFs::new(cx.executor());
 8777    fs.insert_tree(
 8778        path!("/a"),
 8779        json!({
 8780            "main.rs": sample_text_1,
 8781            "other.rs": sample_text_2,
 8782            "lib.rs": sample_text_3,
 8783        }),
 8784    )
 8785    .await;
 8786
 8787    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8788    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8789    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8790
 8791    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8792    language_registry.add(rust_lang());
 8793    let mut fake_servers = language_registry.register_fake_lsp(
 8794        "Rust",
 8795        FakeLspAdapter {
 8796            capabilities: lsp::ServerCapabilities {
 8797                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8798                ..Default::default()
 8799            },
 8800            ..Default::default()
 8801        },
 8802    );
 8803
 8804    let worktree = project.update(cx, |project, cx| {
 8805        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8806        assert_eq!(worktrees.len(), 1);
 8807        worktrees.pop().unwrap()
 8808    });
 8809    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8810
 8811    let buffer_1 = project
 8812        .update(cx, |project, cx| {
 8813            project.open_buffer((worktree_id, "main.rs"), cx)
 8814        })
 8815        .await
 8816        .unwrap();
 8817    let buffer_2 = project
 8818        .update(cx, |project, cx| {
 8819            project.open_buffer((worktree_id, "other.rs"), cx)
 8820        })
 8821        .await
 8822        .unwrap();
 8823    let buffer_3 = project
 8824        .update(cx, |project, cx| {
 8825            project.open_buffer((worktree_id, "lib.rs"), cx)
 8826        })
 8827        .await
 8828        .unwrap();
 8829
 8830    let multi_buffer = cx.new(|cx| {
 8831        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8832        multi_buffer.push_excerpts(
 8833            buffer_1.clone(),
 8834            [
 8835                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8836                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8837                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8838            ],
 8839            cx,
 8840        );
 8841        multi_buffer.push_excerpts(
 8842            buffer_2.clone(),
 8843            [
 8844                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8845                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8846                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8847            ],
 8848            cx,
 8849        );
 8850        multi_buffer.push_excerpts(
 8851            buffer_3.clone(),
 8852            [
 8853                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8854                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8855                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8856            ],
 8857            cx,
 8858        );
 8859        multi_buffer
 8860    });
 8861    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8862        Editor::new(
 8863            EditorMode::full(),
 8864            multi_buffer,
 8865            Some(project.clone()),
 8866            window,
 8867            cx,
 8868        )
 8869    });
 8870
 8871    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8872        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8873            s.select_ranges(Some(1..2))
 8874        });
 8875        editor.insert("|one|two|three|", window, cx);
 8876    });
 8877    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8878    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8879        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8880            s.select_ranges(Some(60..70))
 8881        });
 8882        editor.insert("|four|five|six|", window, cx);
 8883    });
 8884    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8885
 8886    // First two buffers should be edited, but not the third one.
 8887    assert_eq!(
 8888        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8889        "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}",
 8890    );
 8891    buffer_1.update(cx, |buffer, _| {
 8892        assert!(buffer.is_dirty());
 8893        assert_eq!(
 8894            buffer.text(),
 8895            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8896        )
 8897    });
 8898    buffer_2.update(cx, |buffer, _| {
 8899        assert!(buffer.is_dirty());
 8900        assert_eq!(
 8901            buffer.text(),
 8902            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8903        )
 8904    });
 8905    buffer_3.update(cx, |buffer, _| {
 8906        assert!(!buffer.is_dirty());
 8907        assert_eq!(buffer.text(), sample_text_3,)
 8908    });
 8909    cx.executor().run_until_parked();
 8910
 8911    cx.executor().start_waiting();
 8912    let save = multi_buffer_editor
 8913        .update_in(cx, |editor, window, cx| {
 8914            editor.save(true, project.clone(), window, cx)
 8915        })
 8916        .unwrap();
 8917
 8918    let fake_server = fake_servers.next().await.unwrap();
 8919    fake_server
 8920        .server
 8921        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8922            Ok(Some(vec![lsp::TextEdit::new(
 8923                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8924                format!("[{} formatted]", params.text_document.uri),
 8925            )]))
 8926        })
 8927        .detach();
 8928    save.await;
 8929
 8930    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8931    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8932    assert_eq!(
 8933        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8934        uri!(
 8935            "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}"
 8936        ),
 8937    );
 8938    buffer_1.update(cx, |buffer, _| {
 8939        assert!(!buffer.is_dirty());
 8940        assert_eq!(
 8941            buffer.text(),
 8942            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8943        )
 8944    });
 8945    buffer_2.update(cx, |buffer, _| {
 8946        assert!(!buffer.is_dirty());
 8947        assert_eq!(
 8948            buffer.text(),
 8949            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8950        )
 8951    });
 8952    buffer_3.update(cx, |buffer, _| {
 8953        assert!(!buffer.is_dirty());
 8954        assert_eq!(buffer.text(), sample_text_3,)
 8955    });
 8956}
 8957
 8958#[gpui::test]
 8959async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8960    init_test(cx, |_| {});
 8961
 8962    let fs = FakeFs::new(cx.executor());
 8963    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8964
 8965    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8966
 8967    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8968    language_registry.add(rust_lang());
 8969    let mut fake_servers = language_registry.register_fake_lsp(
 8970        "Rust",
 8971        FakeLspAdapter {
 8972            capabilities: lsp::ServerCapabilities {
 8973                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8974                ..Default::default()
 8975            },
 8976            ..Default::default()
 8977        },
 8978    );
 8979
 8980    let buffer = project
 8981        .update(cx, |project, cx| {
 8982            project.open_local_buffer(path!("/file.rs"), cx)
 8983        })
 8984        .await
 8985        .unwrap();
 8986
 8987    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8988    let (editor, cx) = cx.add_window_view(|window, cx| {
 8989        build_editor_with_project(project.clone(), buffer, window, cx)
 8990    });
 8991    editor.update_in(cx, |editor, window, cx| {
 8992        editor.set_text("one\ntwo\nthree\n", window, cx)
 8993    });
 8994    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8995
 8996    cx.executor().start_waiting();
 8997    let fake_server = fake_servers.next().await.unwrap();
 8998
 8999    let save = editor
 9000        .update_in(cx, |editor, window, cx| {
 9001            editor.save(true, project.clone(), window, cx)
 9002        })
 9003        .unwrap();
 9004    fake_server
 9005        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9006            assert_eq!(
 9007                params.text_document.uri,
 9008                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9009            );
 9010            assert_eq!(params.options.tab_size, 4);
 9011            Ok(Some(vec![lsp::TextEdit::new(
 9012                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9013                ", ".to_string(),
 9014            )]))
 9015        })
 9016        .next()
 9017        .await;
 9018    cx.executor().start_waiting();
 9019    save.await;
 9020    assert_eq!(
 9021        editor.update(cx, |editor, cx| editor.text(cx)),
 9022        "one, two\nthree\n"
 9023    );
 9024    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9025
 9026    editor.update_in(cx, |editor, window, cx| {
 9027        editor.set_text("one\ntwo\nthree\n", window, cx)
 9028    });
 9029    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9030
 9031    // Ensure we can still save even if formatting hangs.
 9032    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 9033        move |params, _| async move {
 9034            assert_eq!(
 9035                params.text_document.uri,
 9036                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9037            );
 9038            futures::future::pending::<()>().await;
 9039            unreachable!()
 9040        },
 9041    );
 9042    let save = editor
 9043        .update_in(cx, |editor, window, cx| {
 9044            editor.save(true, project.clone(), window, cx)
 9045        })
 9046        .unwrap();
 9047    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9048    cx.executor().start_waiting();
 9049    save.await;
 9050    assert_eq!(
 9051        editor.update(cx, |editor, cx| editor.text(cx)),
 9052        "one\ntwo\nthree\n"
 9053    );
 9054    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9055
 9056    // For non-dirty buffer, no formatting request should be sent
 9057    let save = editor
 9058        .update_in(cx, |editor, window, cx| {
 9059            editor.save(true, project.clone(), window, cx)
 9060        })
 9061        .unwrap();
 9062    let _pending_format_request = fake_server
 9063        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 9064            panic!("Should not be invoked on non-dirty buffer");
 9065        })
 9066        .next();
 9067    cx.executor().start_waiting();
 9068    save.await;
 9069
 9070    // Set Rust language override and assert overridden tabsize is sent to language server
 9071    update_test_language_settings(cx, |settings| {
 9072        settings.languages.insert(
 9073            "Rust".into(),
 9074            LanguageSettingsContent {
 9075                tab_size: NonZeroU32::new(8),
 9076                ..Default::default()
 9077            },
 9078        );
 9079    });
 9080
 9081    editor.update_in(cx, |editor, window, cx| {
 9082        editor.set_text("somehting_new\n", window, cx)
 9083    });
 9084    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9085    let save = editor
 9086        .update_in(cx, |editor, window, cx| {
 9087            editor.save(true, project.clone(), window, cx)
 9088        })
 9089        .unwrap();
 9090    fake_server
 9091        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9092            assert_eq!(
 9093                params.text_document.uri,
 9094                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9095            );
 9096            assert_eq!(params.options.tab_size, 8);
 9097            Ok(Some(vec![]))
 9098        })
 9099        .next()
 9100        .await;
 9101    cx.executor().start_waiting();
 9102    save.await;
 9103}
 9104
 9105#[gpui::test]
 9106async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 9107    init_test(cx, |settings| {
 9108        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9109            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9110        ))
 9111    });
 9112
 9113    let fs = FakeFs::new(cx.executor());
 9114    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9115
 9116    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9117
 9118    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9119    language_registry.add(Arc::new(Language::new(
 9120        LanguageConfig {
 9121            name: "Rust".into(),
 9122            matcher: LanguageMatcher {
 9123                path_suffixes: vec!["rs".to_string()],
 9124                ..Default::default()
 9125            },
 9126            ..LanguageConfig::default()
 9127        },
 9128        Some(tree_sitter_rust::LANGUAGE.into()),
 9129    )));
 9130    update_test_language_settings(cx, |settings| {
 9131        // Enable Prettier formatting for the same buffer, and ensure
 9132        // LSP is called instead of Prettier.
 9133        settings.defaults.prettier = Some(PrettierSettings {
 9134            allowed: true,
 9135            ..PrettierSettings::default()
 9136        });
 9137    });
 9138    let mut fake_servers = language_registry.register_fake_lsp(
 9139        "Rust",
 9140        FakeLspAdapter {
 9141            capabilities: lsp::ServerCapabilities {
 9142                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9143                ..Default::default()
 9144            },
 9145            ..Default::default()
 9146        },
 9147    );
 9148
 9149    let buffer = project
 9150        .update(cx, |project, cx| {
 9151            project.open_local_buffer(path!("/file.rs"), cx)
 9152        })
 9153        .await
 9154        .unwrap();
 9155
 9156    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9157    let (editor, cx) = cx.add_window_view(|window, cx| {
 9158        build_editor_with_project(project.clone(), buffer, window, cx)
 9159    });
 9160    editor.update_in(cx, |editor, window, cx| {
 9161        editor.set_text("one\ntwo\nthree\n", window, cx)
 9162    });
 9163
 9164    cx.executor().start_waiting();
 9165    let fake_server = fake_servers.next().await.unwrap();
 9166
 9167    let format = editor
 9168        .update_in(cx, |editor, window, cx| {
 9169            editor.perform_format(
 9170                project.clone(),
 9171                FormatTrigger::Manual,
 9172                FormatTarget::Buffers,
 9173                window,
 9174                cx,
 9175            )
 9176        })
 9177        .unwrap();
 9178    fake_server
 9179        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9180            assert_eq!(
 9181                params.text_document.uri,
 9182                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9183            );
 9184            assert_eq!(params.options.tab_size, 4);
 9185            Ok(Some(vec![lsp::TextEdit::new(
 9186                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9187                ", ".to_string(),
 9188            )]))
 9189        })
 9190        .next()
 9191        .await;
 9192    cx.executor().start_waiting();
 9193    format.await;
 9194    assert_eq!(
 9195        editor.update(cx, |editor, cx| editor.text(cx)),
 9196        "one, two\nthree\n"
 9197    );
 9198
 9199    editor.update_in(cx, |editor, window, cx| {
 9200        editor.set_text("one\ntwo\nthree\n", window, cx)
 9201    });
 9202    // Ensure we don't lock if formatting hangs.
 9203    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9204        move |params, _| async move {
 9205            assert_eq!(
 9206                params.text_document.uri,
 9207                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9208            );
 9209            futures::future::pending::<()>().await;
 9210            unreachable!()
 9211        },
 9212    );
 9213    let format = editor
 9214        .update_in(cx, |editor, window, cx| {
 9215            editor.perform_format(
 9216                project,
 9217                FormatTrigger::Manual,
 9218                FormatTarget::Buffers,
 9219                window,
 9220                cx,
 9221            )
 9222        })
 9223        .unwrap();
 9224    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9225    cx.executor().start_waiting();
 9226    format.await;
 9227    assert_eq!(
 9228        editor.update(cx, |editor, cx| editor.text(cx)),
 9229        "one\ntwo\nthree\n"
 9230    );
 9231}
 9232
 9233#[gpui::test]
 9234async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9235    init_test(cx, |settings| {
 9236        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9237        settings.defaults.formatter =
 9238            Some(language_settings::SelectedFormatter::List(FormatterList(
 9239                vec![
 9240                    Formatter::LanguageServer { name: None },
 9241                    Formatter::CodeActions(
 9242                        [
 9243                            ("code-action-1".into(), true),
 9244                            ("code-action-2".into(), true),
 9245                        ]
 9246                        .into_iter()
 9247                        .collect(),
 9248                    ),
 9249                ]
 9250                .into(),
 9251            )))
 9252    });
 9253
 9254    let fs = FakeFs::new(cx.executor());
 9255    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9256        .await;
 9257
 9258    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9259    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9260    language_registry.add(rust_lang());
 9261
 9262    let mut fake_servers = language_registry.register_fake_lsp(
 9263        "Rust",
 9264        FakeLspAdapter {
 9265            capabilities: lsp::ServerCapabilities {
 9266                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9267                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9268                    commands: vec!["the-command-for-code-action-1".into()],
 9269                    ..Default::default()
 9270                }),
 9271                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9272                ..Default::default()
 9273            },
 9274            ..Default::default()
 9275        },
 9276    );
 9277
 9278    let buffer = project
 9279        .update(cx, |project, cx| {
 9280            project.open_local_buffer(path!("/file.rs"), cx)
 9281        })
 9282        .await
 9283        .unwrap();
 9284
 9285    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9286    let (editor, cx) = cx.add_window_view(|window, cx| {
 9287        build_editor_with_project(project.clone(), buffer, window, cx)
 9288    });
 9289
 9290    cx.executor().start_waiting();
 9291
 9292    let fake_server = fake_servers.next().await.unwrap();
 9293    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9294        move |_params, _| async move {
 9295            Ok(Some(vec![lsp::TextEdit::new(
 9296                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9297                "applied-formatting\n".to_string(),
 9298            )]))
 9299        },
 9300    );
 9301    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9302        move |params, _| async move {
 9303            assert_eq!(
 9304                params.context.only,
 9305                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9306            );
 9307            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9308            Ok(Some(vec![
 9309                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9310                    kind: Some("code-action-1".into()),
 9311                    edit: Some(lsp::WorkspaceEdit::new(
 9312                        [(
 9313                            uri.clone(),
 9314                            vec![lsp::TextEdit::new(
 9315                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9316                                "applied-code-action-1-edit\n".to_string(),
 9317                            )],
 9318                        )]
 9319                        .into_iter()
 9320                        .collect(),
 9321                    )),
 9322                    command: Some(lsp::Command {
 9323                        command: "the-command-for-code-action-1".into(),
 9324                        ..Default::default()
 9325                    }),
 9326                    ..Default::default()
 9327                }),
 9328                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9329                    kind: Some("code-action-2".into()),
 9330                    edit: Some(lsp::WorkspaceEdit::new(
 9331                        [(
 9332                            uri.clone(),
 9333                            vec![lsp::TextEdit::new(
 9334                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9335                                "applied-code-action-2-edit\n".to_string(),
 9336                            )],
 9337                        )]
 9338                        .into_iter()
 9339                        .collect(),
 9340                    )),
 9341                    ..Default::default()
 9342                }),
 9343            ]))
 9344        },
 9345    );
 9346
 9347    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9348        move |params, _| async move { Ok(params) }
 9349    });
 9350
 9351    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9352    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9353        let fake = fake_server.clone();
 9354        let lock = command_lock.clone();
 9355        move |params, _| {
 9356            assert_eq!(params.command, "the-command-for-code-action-1");
 9357            let fake = fake.clone();
 9358            let lock = lock.clone();
 9359            async move {
 9360                lock.lock().await;
 9361                fake.server
 9362                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9363                        label: None,
 9364                        edit: lsp::WorkspaceEdit {
 9365                            changes: Some(
 9366                                [(
 9367                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9368                                    vec![lsp::TextEdit {
 9369                                        range: lsp::Range::new(
 9370                                            lsp::Position::new(0, 0),
 9371                                            lsp::Position::new(0, 0),
 9372                                        ),
 9373                                        new_text: "applied-code-action-1-command\n".into(),
 9374                                    }],
 9375                                )]
 9376                                .into_iter()
 9377                                .collect(),
 9378                            ),
 9379                            ..Default::default()
 9380                        },
 9381                    })
 9382                    .await
 9383                    .into_response()
 9384                    .unwrap();
 9385                Ok(Some(json!(null)))
 9386            }
 9387        }
 9388    });
 9389
 9390    cx.executor().start_waiting();
 9391    editor
 9392        .update_in(cx, |editor, window, cx| {
 9393            editor.perform_format(
 9394                project.clone(),
 9395                FormatTrigger::Manual,
 9396                FormatTarget::Buffers,
 9397                window,
 9398                cx,
 9399            )
 9400        })
 9401        .unwrap()
 9402        .await;
 9403    editor.update(cx, |editor, cx| {
 9404        assert_eq!(
 9405            editor.text(cx),
 9406            r#"
 9407                applied-code-action-2-edit
 9408                applied-code-action-1-command
 9409                applied-code-action-1-edit
 9410                applied-formatting
 9411                one
 9412                two
 9413                three
 9414            "#
 9415            .unindent()
 9416        );
 9417    });
 9418
 9419    editor.update_in(cx, |editor, window, cx| {
 9420        editor.undo(&Default::default(), window, cx);
 9421        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9422    });
 9423
 9424    // Perform a manual edit while waiting for an LSP command
 9425    // that's being run as part of a formatting code action.
 9426    let lock_guard = command_lock.lock().await;
 9427    let format = editor
 9428        .update_in(cx, |editor, window, cx| {
 9429            editor.perform_format(
 9430                project.clone(),
 9431                FormatTrigger::Manual,
 9432                FormatTarget::Buffers,
 9433                window,
 9434                cx,
 9435            )
 9436        })
 9437        .unwrap();
 9438    cx.run_until_parked();
 9439    editor.update(cx, |editor, cx| {
 9440        assert_eq!(
 9441            editor.text(cx),
 9442            r#"
 9443                applied-code-action-1-edit
 9444                applied-formatting
 9445                one
 9446                two
 9447                three
 9448            "#
 9449            .unindent()
 9450        );
 9451
 9452        editor.buffer.update(cx, |buffer, cx| {
 9453            let ix = buffer.len(cx);
 9454            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9455        });
 9456    });
 9457
 9458    // Allow the LSP command to proceed. Because the buffer was edited,
 9459    // the second code action will not be run.
 9460    drop(lock_guard);
 9461    format.await;
 9462    editor.update_in(cx, |editor, window, cx| {
 9463        assert_eq!(
 9464            editor.text(cx),
 9465            r#"
 9466                applied-code-action-1-command
 9467                applied-code-action-1-edit
 9468                applied-formatting
 9469                one
 9470                two
 9471                three
 9472                edited
 9473            "#
 9474            .unindent()
 9475        );
 9476
 9477        // The manual edit is undone first, because it is the last thing the user did
 9478        // (even though the command completed afterwards).
 9479        editor.undo(&Default::default(), window, cx);
 9480        assert_eq!(
 9481            editor.text(cx),
 9482            r#"
 9483                applied-code-action-1-command
 9484                applied-code-action-1-edit
 9485                applied-formatting
 9486                one
 9487                two
 9488                three
 9489            "#
 9490            .unindent()
 9491        );
 9492
 9493        // All the formatting (including the command, which completed after the manual edit)
 9494        // is undone together.
 9495        editor.undo(&Default::default(), window, cx);
 9496        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9497    });
 9498}
 9499
 9500#[gpui::test]
 9501async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9502    init_test(cx, |settings| {
 9503        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9504            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9505        ))
 9506    });
 9507
 9508    let fs = FakeFs::new(cx.executor());
 9509    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9510
 9511    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9512
 9513    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9514    language_registry.add(Arc::new(Language::new(
 9515        LanguageConfig {
 9516            name: "TypeScript".into(),
 9517            matcher: LanguageMatcher {
 9518                path_suffixes: vec!["ts".to_string()],
 9519                ..Default::default()
 9520            },
 9521            ..LanguageConfig::default()
 9522        },
 9523        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9524    )));
 9525    update_test_language_settings(cx, |settings| {
 9526        settings.defaults.prettier = Some(PrettierSettings {
 9527            allowed: true,
 9528            ..PrettierSettings::default()
 9529        });
 9530    });
 9531    let mut fake_servers = language_registry.register_fake_lsp(
 9532        "TypeScript",
 9533        FakeLspAdapter {
 9534            capabilities: lsp::ServerCapabilities {
 9535                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9536                ..Default::default()
 9537            },
 9538            ..Default::default()
 9539        },
 9540    );
 9541
 9542    let buffer = project
 9543        .update(cx, |project, cx| {
 9544            project.open_local_buffer(path!("/file.ts"), cx)
 9545        })
 9546        .await
 9547        .unwrap();
 9548
 9549    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9550    let (editor, cx) = cx.add_window_view(|window, cx| {
 9551        build_editor_with_project(project.clone(), buffer, window, cx)
 9552    });
 9553    editor.update_in(cx, |editor, window, cx| {
 9554        editor.set_text(
 9555            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9556            window,
 9557            cx,
 9558        )
 9559    });
 9560
 9561    cx.executor().start_waiting();
 9562    let fake_server = fake_servers.next().await.unwrap();
 9563
 9564    let format = editor
 9565        .update_in(cx, |editor, window, cx| {
 9566            editor.perform_code_action_kind(
 9567                project.clone(),
 9568                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9569                window,
 9570                cx,
 9571            )
 9572        })
 9573        .unwrap();
 9574    fake_server
 9575        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9576            assert_eq!(
 9577                params.text_document.uri,
 9578                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9579            );
 9580            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9581                lsp::CodeAction {
 9582                    title: "Organize Imports".to_string(),
 9583                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9584                    edit: Some(lsp::WorkspaceEdit {
 9585                        changes: Some(
 9586                            [(
 9587                                params.text_document.uri.clone(),
 9588                                vec![lsp::TextEdit::new(
 9589                                    lsp::Range::new(
 9590                                        lsp::Position::new(1, 0),
 9591                                        lsp::Position::new(2, 0),
 9592                                    ),
 9593                                    "".to_string(),
 9594                                )],
 9595                            )]
 9596                            .into_iter()
 9597                            .collect(),
 9598                        ),
 9599                        ..Default::default()
 9600                    }),
 9601                    ..Default::default()
 9602                },
 9603            )]))
 9604        })
 9605        .next()
 9606        .await;
 9607    cx.executor().start_waiting();
 9608    format.await;
 9609    assert_eq!(
 9610        editor.update(cx, |editor, cx| editor.text(cx)),
 9611        "import { a } from 'module';\n\nconst x = a;\n"
 9612    );
 9613
 9614    editor.update_in(cx, |editor, window, cx| {
 9615        editor.set_text(
 9616            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9617            window,
 9618            cx,
 9619        )
 9620    });
 9621    // Ensure we don't lock if code action hangs.
 9622    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9623        move |params, _| async move {
 9624            assert_eq!(
 9625                params.text_document.uri,
 9626                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9627            );
 9628            futures::future::pending::<()>().await;
 9629            unreachable!()
 9630        },
 9631    );
 9632    let format = editor
 9633        .update_in(cx, |editor, window, cx| {
 9634            editor.perform_code_action_kind(
 9635                project,
 9636                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9637                window,
 9638                cx,
 9639            )
 9640        })
 9641        .unwrap();
 9642    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9643    cx.executor().start_waiting();
 9644    format.await;
 9645    assert_eq!(
 9646        editor.update(cx, |editor, cx| editor.text(cx)),
 9647        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9648    );
 9649}
 9650
 9651#[gpui::test]
 9652async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9653    init_test(cx, |_| {});
 9654
 9655    let mut cx = EditorLspTestContext::new_rust(
 9656        lsp::ServerCapabilities {
 9657            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9658            ..Default::default()
 9659        },
 9660        cx,
 9661    )
 9662    .await;
 9663
 9664    cx.set_state(indoc! {"
 9665        one.twoˇ
 9666    "});
 9667
 9668    // The format request takes a long time. When it completes, it inserts
 9669    // a newline and an indent before the `.`
 9670    cx.lsp
 9671        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9672            let executor = cx.background_executor().clone();
 9673            async move {
 9674                executor.timer(Duration::from_millis(100)).await;
 9675                Ok(Some(vec![lsp::TextEdit {
 9676                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9677                    new_text: "\n    ".into(),
 9678                }]))
 9679            }
 9680        });
 9681
 9682    // Submit a format request.
 9683    let format_1 = cx
 9684        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9685        .unwrap();
 9686    cx.executor().run_until_parked();
 9687
 9688    // Submit a second format request.
 9689    let format_2 = cx
 9690        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9691        .unwrap();
 9692    cx.executor().run_until_parked();
 9693
 9694    // Wait for both format requests to complete
 9695    cx.executor().advance_clock(Duration::from_millis(200));
 9696    cx.executor().start_waiting();
 9697    format_1.await.unwrap();
 9698    cx.executor().start_waiting();
 9699    format_2.await.unwrap();
 9700
 9701    // The formatting edits only happens once.
 9702    cx.assert_editor_state(indoc! {"
 9703        one
 9704            .twoˇ
 9705    "});
 9706}
 9707
 9708#[gpui::test]
 9709async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9710    init_test(cx, |settings| {
 9711        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9712    });
 9713
 9714    let mut cx = EditorLspTestContext::new_rust(
 9715        lsp::ServerCapabilities {
 9716            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9717            ..Default::default()
 9718        },
 9719        cx,
 9720    )
 9721    .await;
 9722
 9723    // Set up a buffer white some trailing whitespace and no trailing newline.
 9724    cx.set_state(
 9725        &[
 9726            "one ",   //
 9727            "twoˇ",   //
 9728            "three ", //
 9729            "four",   //
 9730        ]
 9731        .join("\n"),
 9732    );
 9733
 9734    // Submit a format request.
 9735    let format = cx
 9736        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9737        .unwrap();
 9738
 9739    // Record which buffer changes have been sent to the language server
 9740    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9741    cx.lsp
 9742        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9743            let buffer_changes = buffer_changes.clone();
 9744            move |params, _| {
 9745                buffer_changes.lock().extend(
 9746                    params
 9747                        .content_changes
 9748                        .into_iter()
 9749                        .map(|e| (e.range.unwrap(), e.text)),
 9750                );
 9751            }
 9752        });
 9753
 9754    // Handle formatting requests to the language server.
 9755    cx.lsp
 9756        .set_request_handler::<lsp::request::Formatting, _, _>({
 9757            let buffer_changes = buffer_changes.clone();
 9758            move |_, _| {
 9759                // When formatting is requested, trailing whitespace has already been stripped,
 9760                // and the trailing newline has already been added.
 9761                assert_eq!(
 9762                    &buffer_changes.lock()[1..],
 9763                    &[
 9764                        (
 9765                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9766                            "".into()
 9767                        ),
 9768                        (
 9769                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9770                            "".into()
 9771                        ),
 9772                        (
 9773                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9774                            "\n".into()
 9775                        ),
 9776                    ]
 9777                );
 9778
 9779                // Insert blank lines between each line of the buffer.
 9780                async move {
 9781                    Ok(Some(vec![
 9782                        lsp::TextEdit {
 9783                            range: lsp::Range::new(
 9784                                lsp::Position::new(1, 0),
 9785                                lsp::Position::new(1, 0),
 9786                            ),
 9787                            new_text: "\n".into(),
 9788                        },
 9789                        lsp::TextEdit {
 9790                            range: lsp::Range::new(
 9791                                lsp::Position::new(2, 0),
 9792                                lsp::Position::new(2, 0),
 9793                            ),
 9794                            new_text: "\n".into(),
 9795                        },
 9796                    ]))
 9797                }
 9798            }
 9799        });
 9800
 9801    // After formatting the buffer, the trailing whitespace is stripped,
 9802    // a newline is appended, and the edits provided by the language server
 9803    // have been applied.
 9804    format.await.unwrap();
 9805    cx.assert_editor_state(
 9806        &[
 9807            "one",   //
 9808            "",      //
 9809            "twoˇ",  //
 9810            "",      //
 9811            "three", //
 9812            "four",  //
 9813            "",      //
 9814        ]
 9815        .join("\n"),
 9816    );
 9817
 9818    // Undoing the formatting undoes the trailing whitespace removal, the
 9819    // trailing newline, and the LSP edits.
 9820    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9821    cx.assert_editor_state(
 9822        &[
 9823            "one ",   //
 9824            "twoˇ",   //
 9825            "three ", //
 9826            "four",   //
 9827        ]
 9828        .join("\n"),
 9829    );
 9830}
 9831
 9832#[gpui::test]
 9833async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9834    cx: &mut TestAppContext,
 9835) {
 9836    init_test(cx, |_| {});
 9837
 9838    cx.update(|cx| {
 9839        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9840            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9841                settings.auto_signature_help = Some(true);
 9842            });
 9843        });
 9844    });
 9845
 9846    let mut cx = EditorLspTestContext::new_rust(
 9847        lsp::ServerCapabilities {
 9848            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9849                ..Default::default()
 9850            }),
 9851            ..Default::default()
 9852        },
 9853        cx,
 9854    )
 9855    .await;
 9856
 9857    let language = Language::new(
 9858        LanguageConfig {
 9859            name: "Rust".into(),
 9860            brackets: BracketPairConfig {
 9861                pairs: vec![
 9862                    BracketPair {
 9863                        start: "{".to_string(),
 9864                        end: "}".to_string(),
 9865                        close: true,
 9866                        surround: true,
 9867                        newline: true,
 9868                    },
 9869                    BracketPair {
 9870                        start: "(".to_string(),
 9871                        end: ")".to_string(),
 9872                        close: true,
 9873                        surround: true,
 9874                        newline: true,
 9875                    },
 9876                    BracketPair {
 9877                        start: "/*".to_string(),
 9878                        end: " */".to_string(),
 9879                        close: true,
 9880                        surround: true,
 9881                        newline: true,
 9882                    },
 9883                    BracketPair {
 9884                        start: "[".to_string(),
 9885                        end: "]".to_string(),
 9886                        close: false,
 9887                        surround: false,
 9888                        newline: true,
 9889                    },
 9890                    BracketPair {
 9891                        start: "\"".to_string(),
 9892                        end: "\"".to_string(),
 9893                        close: true,
 9894                        surround: true,
 9895                        newline: false,
 9896                    },
 9897                    BracketPair {
 9898                        start: "<".to_string(),
 9899                        end: ">".to_string(),
 9900                        close: false,
 9901                        surround: true,
 9902                        newline: true,
 9903                    },
 9904                ],
 9905                ..Default::default()
 9906            },
 9907            autoclose_before: "})]".to_string(),
 9908            ..Default::default()
 9909        },
 9910        Some(tree_sitter_rust::LANGUAGE.into()),
 9911    );
 9912    let language = Arc::new(language);
 9913
 9914    cx.language_registry().add(language.clone());
 9915    cx.update_buffer(|buffer, cx| {
 9916        buffer.set_language(Some(language), cx);
 9917    });
 9918
 9919    cx.set_state(
 9920        &r#"
 9921            fn main() {
 9922                sampleˇ
 9923            }
 9924        "#
 9925        .unindent(),
 9926    );
 9927
 9928    cx.update_editor(|editor, window, cx| {
 9929        editor.handle_input("(", window, cx);
 9930    });
 9931    cx.assert_editor_state(
 9932        &"
 9933            fn main() {
 9934                sample(ˇ)
 9935            }
 9936        "
 9937        .unindent(),
 9938    );
 9939
 9940    let mocked_response = lsp::SignatureHelp {
 9941        signatures: vec![lsp::SignatureInformation {
 9942            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9943            documentation: None,
 9944            parameters: Some(vec![
 9945                lsp::ParameterInformation {
 9946                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9947                    documentation: None,
 9948                },
 9949                lsp::ParameterInformation {
 9950                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9951                    documentation: None,
 9952                },
 9953            ]),
 9954            active_parameter: None,
 9955        }],
 9956        active_signature: Some(0),
 9957        active_parameter: Some(0),
 9958    };
 9959    handle_signature_help_request(&mut cx, mocked_response).await;
 9960
 9961    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9962        .await;
 9963
 9964    cx.editor(|editor, _, _| {
 9965        let signature_help_state = editor.signature_help_state.popover().cloned();
 9966        assert_eq!(
 9967            signature_help_state.unwrap().label,
 9968            "param1: u8, param2: u8"
 9969        );
 9970    });
 9971}
 9972
 9973#[gpui::test]
 9974async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9975    init_test(cx, |_| {});
 9976
 9977    cx.update(|cx| {
 9978        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9979            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9980                settings.auto_signature_help = Some(false);
 9981                settings.show_signature_help_after_edits = Some(false);
 9982            });
 9983        });
 9984    });
 9985
 9986    let mut cx = EditorLspTestContext::new_rust(
 9987        lsp::ServerCapabilities {
 9988            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9989                ..Default::default()
 9990            }),
 9991            ..Default::default()
 9992        },
 9993        cx,
 9994    )
 9995    .await;
 9996
 9997    let language = Language::new(
 9998        LanguageConfig {
 9999            name: "Rust".into(),
10000            brackets: BracketPairConfig {
10001                pairs: vec![
10002                    BracketPair {
10003                        start: "{".to_string(),
10004                        end: "}".to_string(),
10005                        close: true,
10006                        surround: true,
10007                        newline: true,
10008                    },
10009                    BracketPair {
10010                        start: "(".to_string(),
10011                        end: ")".to_string(),
10012                        close: true,
10013                        surround: true,
10014                        newline: true,
10015                    },
10016                    BracketPair {
10017                        start: "/*".to_string(),
10018                        end: " */".to_string(),
10019                        close: true,
10020                        surround: true,
10021                        newline: true,
10022                    },
10023                    BracketPair {
10024                        start: "[".to_string(),
10025                        end: "]".to_string(),
10026                        close: false,
10027                        surround: false,
10028                        newline: true,
10029                    },
10030                    BracketPair {
10031                        start: "\"".to_string(),
10032                        end: "\"".to_string(),
10033                        close: true,
10034                        surround: true,
10035                        newline: false,
10036                    },
10037                    BracketPair {
10038                        start: "<".to_string(),
10039                        end: ">".to_string(),
10040                        close: false,
10041                        surround: true,
10042                        newline: true,
10043                    },
10044                ],
10045                ..Default::default()
10046            },
10047            autoclose_before: "})]".to_string(),
10048            ..Default::default()
10049        },
10050        Some(tree_sitter_rust::LANGUAGE.into()),
10051    );
10052    let language = Arc::new(language);
10053
10054    cx.language_registry().add(language.clone());
10055    cx.update_buffer(|buffer, cx| {
10056        buffer.set_language(Some(language), cx);
10057    });
10058
10059    // Ensure that signature_help is not called when no signature help is enabled.
10060    cx.set_state(
10061        &r#"
10062            fn main() {
10063                sampleˇ
10064            }
10065        "#
10066        .unindent(),
10067    );
10068    cx.update_editor(|editor, window, cx| {
10069        editor.handle_input("(", window, cx);
10070    });
10071    cx.assert_editor_state(
10072        &"
10073            fn main() {
10074                sample(ˇ)
10075            }
10076        "
10077        .unindent(),
10078    );
10079    cx.editor(|editor, _, _| {
10080        assert!(editor.signature_help_state.task().is_none());
10081    });
10082
10083    let mocked_response = lsp::SignatureHelp {
10084        signatures: vec![lsp::SignatureInformation {
10085            label: "fn sample(param1: u8, param2: u8)".to_string(),
10086            documentation: None,
10087            parameters: Some(vec![
10088                lsp::ParameterInformation {
10089                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10090                    documentation: None,
10091                },
10092                lsp::ParameterInformation {
10093                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10094                    documentation: None,
10095                },
10096            ]),
10097            active_parameter: None,
10098        }],
10099        active_signature: Some(0),
10100        active_parameter: Some(0),
10101    };
10102
10103    // Ensure that signature_help is called when enabled afte edits
10104    cx.update(|_, cx| {
10105        cx.update_global::<SettingsStore, _>(|settings, cx| {
10106            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10107                settings.auto_signature_help = Some(false);
10108                settings.show_signature_help_after_edits = Some(true);
10109            });
10110        });
10111    });
10112    cx.set_state(
10113        &r#"
10114            fn main() {
10115                sampleˇ
10116            }
10117        "#
10118        .unindent(),
10119    );
10120    cx.update_editor(|editor, window, cx| {
10121        editor.handle_input("(", window, cx);
10122    });
10123    cx.assert_editor_state(
10124        &"
10125            fn main() {
10126                sample(ˇ)
10127            }
10128        "
10129        .unindent(),
10130    );
10131    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10132    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10133        .await;
10134    cx.update_editor(|editor, _, _| {
10135        let signature_help_state = editor.signature_help_state.popover().cloned();
10136        assert!(signature_help_state.is_some());
10137        assert_eq!(
10138            signature_help_state.unwrap().label,
10139            "param1: u8, param2: u8"
10140        );
10141        editor.signature_help_state = SignatureHelpState::default();
10142    });
10143
10144    // Ensure that signature_help is called when auto signature help override is enabled
10145    cx.update(|_, cx| {
10146        cx.update_global::<SettingsStore, _>(|settings, cx| {
10147            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10148                settings.auto_signature_help = Some(true);
10149                settings.show_signature_help_after_edits = Some(false);
10150            });
10151        });
10152    });
10153    cx.set_state(
10154        &r#"
10155            fn main() {
10156                sampleˇ
10157            }
10158        "#
10159        .unindent(),
10160    );
10161    cx.update_editor(|editor, window, cx| {
10162        editor.handle_input("(", window, cx);
10163    });
10164    cx.assert_editor_state(
10165        &"
10166            fn main() {
10167                sample(ˇ)
10168            }
10169        "
10170        .unindent(),
10171    );
10172    handle_signature_help_request(&mut cx, mocked_response).await;
10173    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10174        .await;
10175    cx.editor(|editor, _, _| {
10176        let signature_help_state = editor.signature_help_state.popover().cloned();
10177        assert!(signature_help_state.is_some());
10178        assert_eq!(
10179            signature_help_state.unwrap().label,
10180            "param1: u8, param2: u8"
10181        );
10182    });
10183}
10184
10185#[gpui::test]
10186async fn test_signature_help(cx: &mut TestAppContext) {
10187    init_test(cx, |_| {});
10188    cx.update(|cx| {
10189        cx.update_global::<SettingsStore, _>(|settings, cx| {
10190            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10191                settings.auto_signature_help = Some(true);
10192            });
10193        });
10194    });
10195
10196    let mut cx = EditorLspTestContext::new_rust(
10197        lsp::ServerCapabilities {
10198            signature_help_provider: Some(lsp::SignatureHelpOptions {
10199                ..Default::default()
10200            }),
10201            ..Default::default()
10202        },
10203        cx,
10204    )
10205    .await;
10206
10207    // A test that directly calls `show_signature_help`
10208    cx.update_editor(|editor, window, cx| {
10209        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10210    });
10211
10212    let mocked_response = lsp::SignatureHelp {
10213        signatures: vec![lsp::SignatureInformation {
10214            label: "fn sample(param1: u8, param2: u8)".to_string(),
10215            documentation: None,
10216            parameters: Some(vec![
10217                lsp::ParameterInformation {
10218                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10219                    documentation: None,
10220                },
10221                lsp::ParameterInformation {
10222                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10223                    documentation: None,
10224                },
10225            ]),
10226            active_parameter: None,
10227        }],
10228        active_signature: Some(0),
10229        active_parameter: Some(0),
10230    };
10231    handle_signature_help_request(&mut cx, mocked_response).await;
10232
10233    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10234        .await;
10235
10236    cx.editor(|editor, _, _| {
10237        let signature_help_state = editor.signature_help_state.popover().cloned();
10238        assert!(signature_help_state.is_some());
10239        assert_eq!(
10240            signature_help_state.unwrap().label,
10241            "param1: u8, param2: u8"
10242        );
10243    });
10244
10245    // When exiting outside from inside the brackets, `signature_help` is closed.
10246    cx.set_state(indoc! {"
10247        fn main() {
10248            sample(ˇ);
10249        }
10250
10251        fn sample(param1: u8, param2: u8) {}
10252    "});
10253
10254    cx.update_editor(|editor, window, cx| {
10255        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10256    });
10257
10258    let mocked_response = lsp::SignatureHelp {
10259        signatures: Vec::new(),
10260        active_signature: None,
10261        active_parameter: None,
10262    };
10263    handle_signature_help_request(&mut cx, mocked_response).await;
10264
10265    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10266        .await;
10267
10268    cx.editor(|editor, _, _| {
10269        assert!(!editor.signature_help_state.is_shown());
10270    });
10271
10272    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10273    cx.set_state(indoc! {"
10274        fn main() {
10275            sample(ˇ);
10276        }
10277
10278        fn sample(param1: u8, param2: u8) {}
10279    "});
10280
10281    let mocked_response = lsp::SignatureHelp {
10282        signatures: vec![lsp::SignatureInformation {
10283            label: "fn sample(param1: u8, param2: u8)".to_string(),
10284            documentation: None,
10285            parameters: Some(vec![
10286                lsp::ParameterInformation {
10287                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10288                    documentation: None,
10289                },
10290                lsp::ParameterInformation {
10291                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10292                    documentation: None,
10293                },
10294            ]),
10295            active_parameter: None,
10296        }],
10297        active_signature: Some(0),
10298        active_parameter: Some(0),
10299    };
10300    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10301    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10302        .await;
10303    cx.editor(|editor, _, _| {
10304        assert!(editor.signature_help_state.is_shown());
10305    });
10306
10307    // Restore the popover with more parameter input
10308    cx.set_state(indoc! {"
10309        fn main() {
10310            sample(param1, param2ˇ);
10311        }
10312
10313        fn sample(param1: u8, param2: u8) {}
10314    "});
10315
10316    let mocked_response = lsp::SignatureHelp {
10317        signatures: vec![lsp::SignatureInformation {
10318            label: "fn sample(param1: u8, param2: u8)".to_string(),
10319            documentation: None,
10320            parameters: Some(vec![
10321                lsp::ParameterInformation {
10322                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10323                    documentation: None,
10324                },
10325                lsp::ParameterInformation {
10326                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10327                    documentation: None,
10328                },
10329            ]),
10330            active_parameter: None,
10331        }],
10332        active_signature: Some(0),
10333        active_parameter: Some(1),
10334    };
10335    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10336    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10337        .await;
10338
10339    // When selecting a range, the popover is gone.
10340    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10341    cx.update_editor(|editor, window, cx| {
10342        editor.change_selections(None, window, cx, |s| {
10343            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10344        })
10345    });
10346    cx.assert_editor_state(indoc! {"
10347        fn main() {
10348            sample(param1, «ˇparam2»);
10349        }
10350
10351        fn sample(param1: u8, param2: u8) {}
10352    "});
10353    cx.editor(|editor, _, _| {
10354        assert!(!editor.signature_help_state.is_shown());
10355    });
10356
10357    // When unselecting again, the popover is back if within the brackets.
10358    cx.update_editor(|editor, window, cx| {
10359        editor.change_selections(None, window, cx, |s| {
10360            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10361        })
10362    });
10363    cx.assert_editor_state(indoc! {"
10364        fn main() {
10365            sample(param1, ˇparam2);
10366        }
10367
10368        fn sample(param1: u8, param2: u8) {}
10369    "});
10370    handle_signature_help_request(&mut cx, mocked_response).await;
10371    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10372        .await;
10373    cx.editor(|editor, _, _| {
10374        assert!(editor.signature_help_state.is_shown());
10375    });
10376
10377    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10378    cx.update_editor(|editor, window, cx| {
10379        editor.change_selections(None, window, cx, |s| {
10380            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10381            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10382        })
10383    });
10384    cx.assert_editor_state(indoc! {"
10385        fn main() {
10386            sample(param1, ˇparam2);
10387        }
10388
10389        fn sample(param1: u8, param2: u8) {}
10390    "});
10391
10392    let mocked_response = lsp::SignatureHelp {
10393        signatures: vec![lsp::SignatureInformation {
10394            label: "fn sample(param1: u8, param2: u8)".to_string(),
10395            documentation: None,
10396            parameters: Some(vec![
10397                lsp::ParameterInformation {
10398                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10399                    documentation: None,
10400                },
10401                lsp::ParameterInformation {
10402                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10403                    documentation: None,
10404                },
10405            ]),
10406            active_parameter: None,
10407        }],
10408        active_signature: Some(0),
10409        active_parameter: Some(1),
10410    };
10411    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10412    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10413        .await;
10414    cx.update_editor(|editor, _, cx| {
10415        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10416    });
10417    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10418        .await;
10419    cx.update_editor(|editor, window, cx| {
10420        editor.change_selections(None, window, cx, |s| {
10421            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10422        })
10423    });
10424    cx.assert_editor_state(indoc! {"
10425        fn main() {
10426            sample(param1, «ˇparam2»);
10427        }
10428
10429        fn sample(param1: u8, param2: u8) {}
10430    "});
10431    cx.update_editor(|editor, window, cx| {
10432        editor.change_selections(None, window, cx, |s| {
10433            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10434        })
10435    });
10436    cx.assert_editor_state(indoc! {"
10437        fn main() {
10438            sample(param1, ˇparam2);
10439        }
10440
10441        fn sample(param1: u8, param2: u8) {}
10442    "});
10443    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10444        .await;
10445}
10446
10447#[gpui::test]
10448async fn test_completion_mode(cx: &mut TestAppContext) {
10449    init_test(cx, |_| {});
10450    let mut cx = EditorLspTestContext::new_rust(
10451        lsp::ServerCapabilities {
10452            completion_provider: Some(lsp::CompletionOptions {
10453                resolve_provider: Some(true),
10454                ..Default::default()
10455            }),
10456            ..Default::default()
10457        },
10458        cx,
10459    )
10460    .await;
10461
10462    struct Run {
10463        run_description: &'static str,
10464        initial_state: String,
10465        buffer_marked_text: String,
10466        completion_text: &'static str,
10467        expected_with_insert_mode: String,
10468        expected_with_replace_mode: String,
10469        expected_with_replace_subsequence_mode: String,
10470        expected_with_replace_suffix_mode: String,
10471    }
10472
10473    let runs = [
10474        Run {
10475            run_description: "Start of word matches completion text",
10476            initial_state: "before ediˇ after".into(),
10477            buffer_marked_text: "before <edi|> after".into(),
10478            completion_text: "editor",
10479            expected_with_insert_mode: "before editorˇ after".into(),
10480            expected_with_replace_mode: "before editorˇ after".into(),
10481            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10482            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10483        },
10484        Run {
10485            run_description: "Accept same text at the middle of the word",
10486            initial_state: "before ediˇtor after".into(),
10487            buffer_marked_text: "before <edi|tor> after".into(),
10488            completion_text: "editor",
10489            expected_with_insert_mode: "before editorˇtor after".into(),
10490            expected_with_replace_mode: "before editorˇ after".into(),
10491            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10492            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10493        },
10494        Run {
10495            run_description: "End of word matches completion text -- cursor at end",
10496            initial_state: "before torˇ after".into(),
10497            buffer_marked_text: "before <tor|> after".into(),
10498            completion_text: "editor",
10499            expected_with_insert_mode: "before editorˇ after".into(),
10500            expected_with_replace_mode: "before editorˇ after".into(),
10501            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10502            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10503        },
10504        Run {
10505            run_description: "End of word matches completion text -- cursor at start",
10506            initial_state: "before ˇtor after".into(),
10507            buffer_marked_text: "before <|tor> after".into(),
10508            completion_text: "editor",
10509            expected_with_insert_mode: "before editorˇtor after".into(),
10510            expected_with_replace_mode: "before editorˇ after".into(),
10511            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10512            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10513        },
10514        Run {
10515            run_description: "Prepend text containing whitespace",
10516            initial_state: "pˇfield: bool".into(),
10517            buffer_marked_text: "<p|field>: bool".into(),
10518            completion_text: "pub ",
10519            expected_with_insert_mode: "pub ˇfield: bool".into(),
10520            expected_with_replace_mode: "pub ˇ: bool".into(),
10521            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10522            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10523        },
10524        Run {
10525            run_description: "Add element to start of list",
10526            initial_state: "[element_ˇelement_2]".into(),
10527            buffer_marked_text: "[<element_|element_2>]".into(),
10528            completion_text: "element_1",
10529            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10530            expected_with_replace_mode: "[element_1ˇ]".into(),
10531            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10532            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10533        },
10534        Run {
10535            run_description: "Add element to start of list -- first and second elements are equal",
10536            initial_state: "[elˇelement]".into(),
10537            buffer_marked_text: "[<el|element>]".into(),
10538            completion_text: "element",
10539            expected_with_insert_mode: "[elementˇelement]".into(),
10540            expected_with_replace_mode: "[elementˇ]".into(),
10541            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10542            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10543        },
10544        Run {
10545            run_description: "Ends with matching suffix",
10546            initial_state: "SubˇError".into(),
10547            buffer_marked_text: "<Sub|Error>".into(),
10548            completion_text: "SubscriptionError",
10549            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10550            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10551            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10552            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10553        },
10554        Run {
10555            run_description: "Suffix is a subsequence -- contiguous",
10556            initial_state: "SubˇErr".into(),
10557            buffer_marked_text: "<Sub|Err>".into(),
10558            completion_text: "SubscriptionError",
10559            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10560            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10561            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10562            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10563        },
10564        Run {
10565            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10566            initial_state: "Suˇscrirr".into(),
10567            buffer_marked_text: "<Su|scrirr>".into(),
10568            completion_text: "SubscriptionError",
10569            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10570            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10571            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10572            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10573        },
10574        Run {
10575            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10576            initial_state: "foo(indˇix)".into(),
10577            buffer_marked_text: "foo(<ind|ix>)".into(),
10578            completion_text: "node_index",
10579            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10580            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10581            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10582            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10583        },
10584    ];
10585
10586    for run in runs {
10587        let run_variations = [
10588            (LspInsertMode::Insert, run.expected_with_insert_mode),
10589            (LspInsertMode::Replace, run.expected_with_replace_mode),
10590            (
10591                LspInsertMode::ReplaceSubsequence,
10592                run.expected_with_replace_subsequence_mode,
10593            ),
10594            (
10595                LspInsertMode::ReplaceSuffix,
10596                run.expected_with_replace_suffix_mode,
10597            ),
10598        ];
10599
10600        for (lsp_insert_mode, expected_text) in run_variations {
10601            eprintln!(
10602                "run = {:?}, mode = {lsp_insert_mode:.?}",
10603                run.run_description,
10604            );
10605
10606            update_test_language_settings(&mut cx, |settings| {
10607                settings.defaults.completions = Some(CompletionSettings {
10608                    lsp_insert_mode,
10609                    words: WordsCompletionMode::Disabled,
10610                    lsp: true,
10611                    lsp_fetch_timeout_ms: 0,
10612                });
10613            });
10614
10615            cx.set_state(&run.initial_state);
10616            cx.update_editor(|editor, window, cx| {
10617                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10618            });
10619
10620            let counter = Arc::new(AtomicUsize::new(0));
10621            handle_completion_request_with_insert_and_replace(
10622                &mut cx,
10623                &run.buffer_marked_text,
10624                vec![run.completion_text],
10625                counter.clone(),
10626            )
10627            .await;
10628            cx.condition(|editor, _| editor.context_menu_visible())
10629                .await;
10630            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10631
10632            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10633                editor
10634                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10635                    .unwrap()
10636            });
10637            cx.assert_editor_state(&expected_text);
10638            handle_resolve_completion_request(&mut cx, None).await;
10639            apply_additional_edits.await.unwrap();
10640        }
10641    }
10642}
10643
10644#[gpui::test]
10645async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10646    init_test(cx, |_| {});
10647    let mut cx = EditorLspTestContext::new_rust(
10648        lsp::ServerCapabilities {
10649            completion_provider: Some(lsp::CompletionOptions {
10650                resolve_provider: Some(true),
10651                ..Default::default()
10652            }),
10653            ..Default::default()
10654        },
10655        cx,
10656    )
10657    .await;
10658
10659    let initial_state = "SubˇError";
10660    let buffer_marked_text = "<Sub|Error>";
10661    let completion_text = "SubscriptionError";
10662    let expected_with_insert_mode = "SubscriptionErrorˇError";
10663    let expected_with_replace_mode = "SubscriptionErrorˇ";
10664
10665    update_test_language_settings(&mut cx, |settings| {
10666        settings.defaults.completions = Some(CompletionSettings {
10667            words: WordsCompletionMode::Disabled,
10668            // set the opposite here to ensure that the action is overriding the default behavior
10669            lsp_insert_mode: LspInsertMode::Insert,
10670            lsp: true,
10671            lsp_fetch_timeout_ms: 0,
10672        });
10673    });
10674
10675    cx.set_state(initial_state);
10676    cx.update_editor(|editor, window, cx| {
10677        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10678    });
10679
10680    let counter = Arc::new(AtomicUsize::new(0));
10681    handle_completion_request_with_insert_and_replace(
10682        &mut cx,
10683        &buffer_marked_text,
10684        vec![completion_text],
10685        counter.clone(),
10686    )
10687    .await;
10688    cx.condition(|editor, _| editor.context_menu_visible())
10689        .await;
10690    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10691
10692    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10693        editor
10694            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10695            .unwrap()
10696    });
10697    cx.assert_editor_state(&expected_with_replace_mode);
10698    handle_resolve_completion_request(&mut cx, None).await;
10699    apply_additional_edits.await.unwrap();
10700
10701    update_test_language_settings(&mut cx, |settings| {
10702        settings.defaults.completions = Some(CompletionSettings {
10703            words: WordsCompletionMode::Disabled,
10704            // set the opposite here to ensure that the action is overriding the default behavior
10705            lsp_insert_mode: LspInsertMode::Replace,
10706            lsp: true,
10707            lsp_fetch_timeout_ms: 0,
10708        });
10709    });
10710
10711    cx.set_state(initial_state);
10712    cx.update_editor(|editor, window, cx| {
10713        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10714    });
10715    handle_completion_request_with_insert_and_replace(
10716        &mut cx,
10717        &buffer_marked_text,
10718        vec![completion_text],
10719        counter.clone(),
10720    )
10721    .await;
10722    cx.condition(|editor, _| editor.context_menu_visible())
10723        .await;
10724    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10725
10726    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10727        editor
10728            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10729            .unwrap()
10730    });
10731    cx.assert_editor_state(&expected_with_insert_mode);
10732    handle_resolve_completion_request(&mut cx, None).await;
10733    apply_additional_edits.await.unwrap();
10734}
10735
10736#[gpui::test]
10737async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10738    init_test(cx, |_| {});
10739    let mut cx = EditorLspTestContext::new_rust(
10740        lsp::ServerCapabilities {
10741            completion_provider: Some(lsp::CompletionOptions {
10742                resolve_provider: Some(true),
10743                ..Default::default()
10744            }),
10745            ..Default::default()
10746        },
10747        cx,
10748    )
10749    .await;
10750
10751    // scenario: surrounding text matches completion text
10752    let completion_text = "to_offset";
10753    let initial_state = indoc! {"
10754        1. buf.to_offˇsuffix
10755        2. buf.to_offˇsuf
10756        3. buf.to_offˇfix
10757        4. buf.to_offˇ
10758        5. into_offˇensive
10759        6. ˇsuffix
10760        7. let ˇ //
10761        8. aaˇzz
10762        9. buf.to_off«zzzzzˇ»suffix
10763        10. buf.«ˇzzzzz»suffix
10764        11. to_off«ˇzzzzz»
10765
10766        buf.to_offˇsuffix  // newest cursor
10767    "};
10768    let completion_marked_buffer = indoc! {"
10769        1. buf.to_offsuffix
10770        2. buf.to_offsuf
10771        3. buf.to_offfix
10772        4. buf.to_off
10773        5. into_offensive
10774        6. suffix
10775        7. let  //
10776        8. aazz
10777        9. buf.to_offzzzzzsuffix
10778        10. buf.zzzzzsuffix
10779        11. to_offzzzzz
10780
10781        buf.<to_off|suffix>  // newest cursor
10782    "};
10783    let expected = indoc! {"
10784        1. buf.to_offsetˇ
10785        2. buf.to_offsetˇsuf
10786        3. buf.to_offsetˇfix
10787        4. buf.to_offsetˇ
10788        5. into_offsetˇensive
10789        6. to_offsetˇsuffix
10790        7. let to_offsetˇ //
10791        8. aato_offsetˇzz
10792        9. buf.to_offsetˇ
10793        10. buf.to_offsetˇsuffix
10794        11. to_offsetˇ
10795
10796        buf.to_offsetˇ  // newest cursor
10797    "};
10798    cx.set_state(initial_state);
10799    cx.update_editor(|editor, window, cx| {
10800        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10801    });
10802    handle_completion_request_with_insert_and_replace(
10803        &mut cx,
10804        completion_marked_buffer,
10805        vec![completion_text],
10806        Arc::new(AtomicUsize::new(0)),
10807    )
10808    .await;
10809    cx.condition(|editor, _| editor.context_menu_visible())
10810        .await;
10811    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10812        editor
10813            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10814            .unwrap()
10815    });
10816    cx.assert_editor_state(expected);
10817    handle_resolve_completion_request(&mut cx, None).await;
10818    apply_additional_edits.await.unwrap();
10819
10820    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10821    let completion_text = "foo_and_bar";
10822    let initial_state = indoc! {"
10823        1. ooanbˇ
10824        2. zooanbˇ
10825        3. ooanbˇz
10826        4. zooanbˇz
10827        5. ooanˇ
10828        6. oanbˇ
10829
10830        ooanbˇ
10831    "};
10832    let completion_marked_buffer = indoc! {"
10833        1. ooanb
10834        2. zooanb
10835        3. ooanbz
10836        4. zooanbz
10837        5. ooan
10838        6. oanb
10839
10840        <ooanb|>
10841    "};
10842    let expected = indoc! {"
10843        1. foo_and_barˇ
10844        2. zfoo_and_barˇ
10845        3. foo_and_barˇz
10846        4. zfoo_and_barˇz
10847        5. ooanfoo_and_barˇ
10848        6. oanbfoo_and_barˇ
10849
10850        foo_and_barˇ
10851    "};
10852    cx.set_state(initial_state);
10853    cx.update_editor(|editor, window, cx| {
10854        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10855    });
10856    handle_completion_request_with_insert_and_replace(
10857        &mut cx,
10858        completion_marked_buffer,
10859        vec![completion_text],
10860        Arc::new(AtomicUsize::new(0)),
10861    )
10862    .await;
10863    cx.condition(|editor, _| editor.context_menu_visible())
10864        .await;
10865    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10866        editor
10867            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10868            .unwrap()
10869    });
10870    cx.assert_editor_state(expected);
10871    handle_resolve_completion_request(&mut cx, None).await;
10872    apply_additional_edits.await.unwrap();
10873
10874    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10875    // (expects the same as if it was inserted at the end)
10876    let completion_text = "foo_and_bar";
10877    let initial_state = indoc! {"
10878        1. ooˇanb
10879        2. zooˇanb
10880        3. ooˇanbz
10881        4. zooˇanbz
10882
10883        ooˇanb
10884    "};
10885    let completion_marked_buffer = indoc! {"
10886        1. ooanb
10887        2. zooanb
10888        3. ooanbz
10889        4. zooanbz
10890
10891        <oo|anb>
10892    "};
10893    let expected = indoc! {"
10894        1. foo_and_barˇ
10895        2. zfoo_and_barˇ
10896        3. foo_and_barˇz
10897        4. zfoo_and_barˇz
10898
10899        foo_and_barˇ
10900    "};
10901    cx.set_state(initial_state);
10902    cx.update_editor(|editor, window, cx| {
10903        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10904    });
10905    handle_completion_request_with_insert_and_replace(
10906        &mut cx,
10907        completion_marked_buffer,
10908        vec![completion_text],
10909        Arc::new(AtomicUsize::new(0)),
10910    )
10911    .await;
10912    cx.condition(|editor, _| editor.context_menu_visible())
10913        .await;
10914    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10915        editor
10916            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10917            .unwrap()
10918    });
10919    cx.assert_editor_state(expected);
10920    handle_resolve_completion_request(&mut cx, None).await;
10921    apply_additional_edits.await.unwrap();
10922}
10923
10924// This used to crash
10925#[gpui::test]
10926async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10927    init_test(cx, |_| {});
10928
10929    let buffer_text = indoc! {"
10930        fn main() {
10931            10.satu;
10932
10933            //
10934            // separate cursors so they open in different excerpts (manually reproducible)
10935            //
10936
10937            10.satu20;
10938        }
10939    "};
10940    let multibuffer_text_with_selections = indoc! {"
10941        fn main() {
10942            10.satuˇ;
10943
10944            //
10945
10946            //
10947
10948            10.satuˇ20;
10949        }
10950    "};
10951    let expected_multibuffer = indoc! {"
10952        fn main() {
10953            10.saturating_sub()ˇ;
10954
10955            //
10956
10957            //
10958
10959            10.saturating_sub()ˇ;
10960        }
10961    "};
10962
10963    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10964    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10965
10966    let fs = FakeFs::new(cx.executor());
10967    fs.insert_tree(
10968        path!("/a"),
10969        json!({
10970            "main.rs": buffer_text,
10971        }),
10972    )
10973    .await;
10974
10975    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10976    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10977    language_registry.add(rust_lang());
10978    let mut fake_servers = language_registry.register_fake_lsp(
10979        "Rust",
10980        FakeLspAdapter {
10981            capabilities: lsp::ServerCapabilities {
10982                completion_provider: Some(lsp::CompletionOptions {
10983                    resolve_provider: None,
10984                    ..lsp::CompletionOptions::default()
10985                }),
10986                ..lsp::ServerCapabilities::default()
10987            },
10988            ..FakeLspAdapter::default()
10989        },
10990    );
10991    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10992    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10993    let buffer = project
10994        .update(cx, |project, cx| {
10995            project.open_local_buffer(path!("/a/main.rs"), cx)
10996        })
10997        .await
10998        .unwrap();
10999
11000    let multi_buffer = cx.new(|cx| {
11001        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
11002        multi_buffer.push_excerpts(
11003            buffer.clone(),
11004            [ExcerptRange::new(0..first_excerpt_end)],
11005            cx,
11006        );
11007        multi_buffer.push_excerpts(
11008            buffer.clone(),
11009            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
11010            cx,
11011        );
11012        multi_buffer
11013    });
11014
11015    let editor = workspace
11016        .update(cx, |_, window, cx| {
11017            cx.new(|cx| {
11018                Editor::new(
11019                    EditorMode::Full {
11020                        scale_ui_elements_with_buffer_font_size: false,
11021                        show_active_line_background: false,
11022                        sized_by_content: false,
11023                    },
11024                    multi_buffer.clone(),
11025                    Some(project.clone()),
11026                    window,
11027                    cx,
11028                )
11029            })
11030        })
11031        .unwrap();
11032
11033    let pane = workspace
11034        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11035        .unwrap();
11036    pane.update_in(cx, |pane, window, cx| {
11037        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
11038    });
11039
11040    let fake_server = fake_servers.next().await.unwrap();
11041
11042    editor.update_in(cx, |editor, window, cx| {
11043        editor.change_selections(None, window, cx, |s| {
11044            s.select_ranges([
11045                Point::new(1, 11)..Point::new(1, 11),
11046                Point::new(7, 11)..Point::new(7, 11),
11047            ])
11048        });
11049
11050        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
11051    });
11052
11053    editor.update_in(cx, |editor, window, cx| {
11054        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11055    });
11056
11057    fake_server
11058        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11059            let completion_item = lsp::CompletionItem {
11060                label: "saturating_sub()".into(),
11061                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11062                    lsp::InsertReplaceEdit {
11063                        new_text: "saturating_sub()".to_owned(),
11064                        insert: lsp::Range::new(
11065                            lsp::Position::new(7, 7),
11066                            lsp::Position::new(7, 11),
11067                        ),
11068                        replace: lsp::Range::new(
11069                            lsp::Position::new(7, 7),
11070                            lsp::Position::new(7, 13),
11071                        ),
11072                    },
11073                )),
11074                ..lsp::CompletionItem::default()
11075            };
11076
11077            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
11078        })
11079        .next()
11080        .await
11081        .unwrap();
11082
11083    cx.condition(&editor, |editor, _| editor.context_menu_visible())
11084        .await;
11085
11086    editor
11087        .update_in(cx, |editor, window, cx| {
11088            editor
11089                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11090                .unwrap()
11091        })
11092        .await
11093        .unwrap();
11094
11095    editor.update(cx, |editor, cx| {
11096        assert_text_with_selections(editor, expected_multibuffer, cx);
11097    })
11098}
11099
11100#[gpui::test]
11101async fn test_completion(cx: &mut TestAppContext) {
11102    init_test(cx, |_| {});
11103
11104    let mut cx = EditorLspTestContext::new_rust(
11105        lsp::ServerCapabilities {
11106            completion_provider: Some(lsp::CompletionOptions {
11107                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11108                resolve_provider: Some(true),
11109                ..Default::default()
11110            }),
11111            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11112            ..Default::default()
11113        },
11114        cx,
11115    )
11116    .await;
11117    let counter = Arc::new(AtomicUsize::new(0));
11118
11119    cx.set_state(indoc! {"
11120        oneˇ
11121        two
11122        three
11123    "});
11124    cx.simulate_keystroke(".");
11125    handle_completion_request(
11126        &mut cx,
11127        indoc! {"
11128            one.|<>
11129            two
11130            three
11131        "},
11132        vec!["first_completion", "second_completion"],
11133        counter.clone(),
11134    )
11135    .await;
11136    cx.condition(|editor, _| editor.context_menu_visible())
11137        .await;
11138    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11139
11140    let _handler = handle_signature_help_request(
11141        &mut cx,
11142        lsp::SignatureHelp {
11143            signatures: vec![lsp::SignatureInformation {
11144                label: "test signature".to_string(),
11145                documentation: None,
11146                parameters: Some(vec![lsp::ParameterInformation {
11147                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
11148                    documentation: None,
11149                }]),
11150                active_parameter: None,
11151            }],
11152            active_signature: None,
11153            active_parameter: None,
11154        },
11155    );
11156    cx.update_editor(|editor, window, cx| {
11157        assert!(
11158            !editor.signature_help_state.is_shown(),
11159            "No signature help was called for"
11160        );
11161        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11162    });
11163    cx.run_until_parked();
11164    cx.update_editor(|editor, _, _| {
11165        assert!(
11166            !editor.signature_help_state.is_shown(),
11167            "No signature help should be shown when completions menu is open"
11168        );
11169    });
11170
11171    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11172        editor.context_menu_next(&Default::default(), window, cx);
11173        editor
11174            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11175            .unwrap()
11176    });
11177    cx.assert_editor_state(indoc! {"
11178        one.second_completionˇ
11179        two
11180        three
11181    "});
11182
11183    handle_resolve_completion_request(
11184        &mut cx,
11185        Some(vec![
11186            (
11187                //This overlaps with the primary completion edit which is
11188                //misbehavior from the LSP spec, test that we filter it out
11189                indoc! {"
11190                    one.second_ˇcompletion
11191                    two
11192                    threeˇ
11193                "},
11194                "overlapping additional edit",
11195            ),
11196            (
11197                indoc! {"
11198                    one.second_completion
11199                    two
11200                    threeˇ
11201                "},
11202                "\nadditional edit",
11203            ),
11204        ]),
11205    )
11206    .await;
11207    apply_additional_edits.await.unwrap();
11208    cx.assert_editor_state(indoc! {"
11209        one.second_completionˇ
11210        two
11211        three
11212        additional edit
11213    "});
11214
11215    cx.set_state(indoc! {"
11216        one.second_completion
11217        twoˇ
11218        threeˇ
11219        additional edit
11220    "});
11221    cx.simulate_keystroke(" ");
11222    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11223    cx.simulate_keystroke("s");
11224    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11225
11226    cx.assert_editor_state(indoc! {"
11227        one.second_completion
11228        two sˇ
11229        three sˇ
11230        additional edit
11231    "});
11232    handle_completion_request(
11233        &mut cx,
11234        indoc! {"
11235            one.second_completion
11236            two s
11237            three <s|>
11238            additional edit
11239        "},
11240        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11241        counter.clone(),
11242    )
11243    .await;
11244    cx.condition(|editor, _| editor.context_menu_visible())
11245        .await;
11246    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11247
11248    cx.simulate_keystroke("i");
11249
11250    handle_completion_request(
11251        &mut cx,
11252        indoc! {"
11253            one.second_completion
11254            two si
11255            three <si|>
11256            additional edit
11257        "},
11258        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11259        counter.clone(),
11260    )
11261    .await;
11262    cx.condition(|editor, _| editor.context_menu_visible())
11263        .await;
11264    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11265
11266    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11267        editor
11268            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11269            .unwrap()
11270    });
11271    cx.assert_editor_state(indoc! {"
11272        one.second_completion
11273        two sixth_completionˇ
11274        three sixth_completionˇ
11275        additional edit
11276    "});
11277
11278    apply_additional_edits.await.unwrap();
11279
11280    update_test_language_settings(&mut cx, |settings| {
11281        settings.defaults.show_completions_on_input = Some(false);
11282    });
11283    cx.set_state("editorˇ");
11284    cx.simulate_keystroke(".");
11285    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11286    cx.simulate_keystrokes("c l o");
11287    cx.assert_editor_state("editor.cloˇ");
11288    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11289    cx.update_editor(|editor, window, cx| {
11290        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11291    });
11292    handle_completion_request(
11293        &mut cx,
11294        "editor.<clo|>",
11295        vec!["close", "clobber"],
11296        counter.clone(),
11297    )
11298    .await;
11299    cx.condition(|editor, _| editor.context_menu_visible())
11300        .await;
11301    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11302
11303    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11304        editor
11305            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11306            .unwrap()
11307    });
11308    cx.assert_editor_state("editor.closeˇ");
11309    handle_resolve_completion_request(&mut cx, None).await;
11310    apply_additional_edits.await.unwrap();
11311}
11312
11313#[gpui::test]
11314async fn test_word_completion(cx: &mut TestAppContext) {
11315    let lsp_fetch_timeout_ms = 10;
11316    init_test(cx, |language_settings| {
11317        language_settings.defaults.completions = Some(CompletionSettings {
11318            words: WordsCompletionMode::Fallback,
11319            lsp: true,
11320            lsp_fetch_timeout_ms: 10,
11321            lsp_insert_mode: LspInsertMode::Insert,
11322        });
11323    });
11324
11325    let mut cx = EditorLspTestContext::new_rust(
11326        lsp::ServerCapabilities {
11327            completion_provider: Some(lsp::CompletionOptions {
11328                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11329                ..lsp::CompletionOptions::default()
11330            }),
11331            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11332            ..lsp::ServerCapabilities::default()
11333        },
11334        cx,
11335    )
11336    .await;
11337
11338    let throttle_completions = Arc::new(AtomicBool::new(false));
11339
11340    let lsp_throttle_completions = throttle_completions.clone();
11341    let _completion_requests_handler =
11342        cx.lsp
11343            .server
11344            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11345                let lsp_throttle_completions = lsp_throttle_completions.clone();
11346                let cx = cx.clone();
11347                async move {
11348                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11349                        cx.background_executor()
11350                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11351                            .await;
11352                    }
11353                    Ok(Some(lsp::CompletionResponse::Array(vec![
11354                        lsp::CompletionItem {
11355                            label: "first".into(),
11356                            ..lsp::CompletionItem::default()
11357                        },
11358                        lsp::CompletionItem {
11359                            label: "last".into(),
11360                            ..lsp::CompletionItem::default()
11361                        },
11362                    ])))
11363                }
11364            });
11365
11366    cx.set_state(indoc! {"
11367        oneˇ
11368        two
11369        three
11370    "});
11371    cx.simulate_keystroke(".");
11372    cx.executor().run_until_parked();
11373    cx.condition(|editor, _| editor.context_menu_visible())
11374        .await;
11375    cx.update_editor(|editor, window, cx| {
11376        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11377        {
11378            assert_eq!(
11379                completion_menu_entries(&menu),
11380                &["first", "last"],
11381                "When LSP server is fast to reply, no fallback word completions are used"
11382            );
11383        } else {
11384            panic!("expected completion menu to be open");
11385        }
11386        editor.cancel(&Cancel, window, cx);
11387    });
11388    cx.executor().run_until_parked();
11389    cx.condition(|editor, _| !editor.context_menu_visible())
11390        .await;
11391
11392    throttle_completions.store(true, atomic::Ordering::Release);
11393    cx.simulate_keystroke(".");
11394    cx.executor()
11395        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11396    cx.executor().run_until_parked();
11397    cx.condition(|editor, _| editor.context_menu_visible())
11398        .await;
11399    cx.update_editor(|editor, _, _| {
11400        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11401        {
11402            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11403                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11404        } else {
11405            panic!("expected completion menu to be open");
11406        }
11407    });
11408}
11409
11410#[gpui::test]
11411async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11412    init_test(cx, |language_settings| {
11413        language_settings.defaults.completions = Some(CompletionSettings {
11414            words: WordsCompletionMode::Enabled,
11415            lsp: true,
11416            lsp_fetch_timeout_ms: 0,
11417            lsp_insert_mode: LspInsertMode::Insert,
11418        });
11419    });
11420
11421    let mut cx = EditorLspTestContext::new_rust(
11422        lsp::ServerCapabilities {
11423            completion_provider: Some(lsp::CompletionOptions {
11424                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11425                ..lsp::CompletionOptions::default()
11426            }),
11427            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11428            ..lsp::ServerCapabilities::default()
11429        },
11430        cx,
11431    )
11432    .await;
11433
11434    let _completion_requests_handler =
11435        cx.lsp
11436            .server
11437            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11438                Ok(Some(lsp::CompletionResponse::Array(vec![
11439                    lsp::CompletionItem {
11440                        label: "first".into(),
11441                        ..lsp::CompletionItem::default()
11442                    },
11443                    lsp::CompletionItem {
11444                        label: "last".into(),
11445                        ..lsp::CompletionItem::default()
11446                    },
11447                ])))
11448            });
11449
11450    cx.set_state(indoc! {"ˇ
11451        first
11452        last
11453        second
11454    "});
11455    cx.simulate_keystroke(".");
11456    cx.executor().run_until_parked();
11457    cx.condition(|editor, _| editor.context_menu_visible())
11458        .await;
11459    cx.update_editor(|editor, _, _| {
11460        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11461        {
11462            assert_eq!(
11463                completion_menu_entries(&menu),
11464                &["first", "last", "second"],
11465                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11466            );
11467        } else {
11468            panic!("expected completion menu to be open");
11469        }
11470    });
11471}
11472
11473#[gpui::test]
11474async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11475    init_test(cx, |language_settings| {
11476        language_settings.defaults.completions = Some(CompletionSettings {
11477            words: WordsCompletionMode::Disabled,
11478            lsp: true,
11479            lsp_fetch_timeout_ms: 0,
11480            lsp_insert_mode: LspInsertMode::Insert,
11481        });
11482    });
11483
11484    let mut cx = EditorLspTestContext::new_rust(
11485        lsp::ServerCapabilities {
11486            completion_provider: Some(lsp::CompletionOptions {
11487                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11488                ..lsp::CompletionOptions::default()
11489            }),
11490            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11491            ..lsp::ServerCapabilities::default()
11492        },
11493        cx,
11494    )
11495    .await;
11496
11497    let _completion_requests_handler =
11498        cx.lsp
11499            .server
11500            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11501                panic!("LSP completions should not be queried when dealing with word completions")
11502            });
11503
11504    cx.set_state(indoc! {"ˇ
11505        first
11506        last
11507        second
11508    "});
11509    cx.update_editor(|editor, window, cx| {
11510        editor.show_word_completions(&ShowWordCompletions, window, cx);
11511    });
11512    cx.executor().run_until_parked();
11513    cx.condition(|editor, _| editor.context_menu_visible())
11514        .await;
11515    cx.update_editor(|editor, _, _| {
11516        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11517        {
11518            assert_eq!(
11519                completion_menu_entries(&menu),
11520                &["first", "last", "second"],
11521                "`ShowWordCompletions` action should show word completions"
11522            );
11523        } else {
11524            panic!("expected completion menu to be open");
11525        }
11526    });
11527
11528    cx.simulate_keystroke("l");
11529    cx.executor().run_until_parked();
11530    cx.condition(|editor, _| editor.context_menu_visible())
11531        .await;
11532    cx.update_editor(|editor, _, _| {
11533        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11534        {
11535            assert_eq!(
11536                completion_menu_entries(&menu),
11537                &["last"],
11538                "After showing word completions, further editing should filter them and not query the LSP"
11539            );
11540        } else {
11541            panic!("expected completion menu to be open");
11542        }
11543    });
11544}
11545
11546#[gpui::test]
11547async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11548    init_test(cx, |language_settings| {
11549        language_settings.defaults.completions = Some(CompletionSettings {
11550            words: WordsCompletionMode::Fallback,
11551            lsp: false,
11552            lsp_fetch_timeout_ms: 0,
11553            lsp_insert_mode: LspInsertMode::Insert,
11554        });
11555    });
11556
11557    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11558
11559    cx.set_state(indoc! {"ˇ
11560        0_usize
11561        let
11562        33
11563        4.5f32
11564    "});
11565    cx.update_editor(|editor, window, cx| {
11566        editor.show_completions(&ShowCompletions::default(), window, cx);
11567    });
11568    cx.executor().run_until_parked();
11569    cx.condition(|editor, _| editor.context_menu_visible())
11570        .await;
11571    cx.update_editor(|editor, window, cx| {
11572        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11573        {
11574            assert_eq!(
11575                completion_menu_entries(&menu),
11576                &["let"],
11577                "With no digits in the completion query, no digits should be in the word completions"
11578            );
11579        } else {
11580            panic!("expected completion menu to be open");
11581        }
11582        editor.cancel(&Cancel, window, cx);
11583    });
11584
11585    cx.set_state(indoc! {"11586        0_usize
11587        let
11588        3
11589        33.35f32
11590    "});
11591    cx.update_editor(|editor, window, cx| {
11592        editor.show_completions(&ShowCompletions::default(), window, cx);
11593    });
11594    cx.executor().run_until_parked();
11595    cx.condition(|editor, _| editor.context_menu_visible())
11596        .await;
11597    cx.update_editor(|editor, _, _| {
11598        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11599        {
11600            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11601                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11602        } else {
11603            panic!("expected completion menu to be open");
11604        }
11605    });
11606}
11607
11608fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11609    let position = || lsp::Position {
11610        line: params.text_document_position.position.line,
11611        character: params.text_document_position.position.character,
11612    };
11613    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11614        range: lsp::Range {
11615            start: position(),
11616            end: position(),
11617        },
11618        new_text: text.to_string(),
11619    }))
11620}
11621
11622#[gpui::test]
11623async fn test_multiline_completion(cx: &mut TestAppContext) {
11624    init_test(cx, |_| {});
11625
11626    let fs = FakeFs::new(cx.executor());
11627    fs.insert_tree(
11628        path!("/a"),
11629        json!({
11630            "main.ts": "a",
11631        }),
11632    )
11633    .await;
11634
11635    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11636    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11637    let typescript_language = Arc::new(Language::new(
11638        LanguageConfig {
11639            name: "TypeScript".into(),
11640            matcher: LanguageMatcher {
11641                path_suffixes: vec!["ts".to_string()],
11642                ..LanguageMatcher::default()
11643            },
11644            line_comments: vec!["// ".into()],
11645            ..LanguageConfig::default()
11646        },
11647        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11648    ));
11649    language_registry.add(typescript_language.clone());
11650    let mut fake_servers = language_registry.register_fake_lsp(
11651        "TypeScript",
11652        FakeLspAdapter {
11653            capabilities: lsp::ServerCapabilities {
11654                completion_provider: Some(lsp::CompletionOptions {
11655                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11656                    ..lsp::CompletionOptions::default()
11657                }),
11658                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11659                ..lsp::ServerCapabilities::default()
11660            },
11661            // Emulate vtsls label generation
11662            label_for_completion: Some(Box::new(|item, _| {
11663                let text = if let Some(description) = item
11664                    .label_details
11665                    .as_ref()
11666                    .and_then(|label_details| label_details.description.as_ref())
11667                {
11668                    format!("{} {}", item.label, description)
11669                } else if let Some(detail) = &item.detail {
11670                    format!("{} {}", item.label, detail)
11671                } else {
11672                    item.label.clone()
11673                };
11674                let len = text.len();
11675                Some(language::CodeLabel {
11676                    text,
11677                    runs: Vec::new(),
11678                    filter_range: 0..len,
11679                })
11680            })),
11681            ..FakeLspAdapter::default()
11682        },
11683    );
11684    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11685    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11686    let worktree_id = workspace
11687        .update(cx, |workspace, _window, cx| {
11688            workspace.project().update(cx, |project, cx| {
11689                project.worktrees(cx).next().unwrap().read(cx).id()
11690            })
11691        })
11692        .unwrap();
11693    let _buffer = project
11694        .update(cx, |project, cx| {
11695            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11696        })
11697        .await
11698        .unwrap();
11699    let editor = workspace
11700        .update(cx, |workspace, window, cx| {
11701            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11702        })
11703        .unwrap()
11704        .await
11705        .unwrap()
11706        .downcast::<Editor>()
11707        .unwrap();
11708    let fake_server = fake_servers.next().await.unwrap();
11709
11710    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11711    let multiline_label_2 = "a\nb\nc\n";
11712    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11713    let multiline_description = "d\ne\nf\n";
11714    let multiline_detail_2 = "g\nh\ni\n";
11715
11716    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11717        move |params, _| async move {
11718            Ok(Some(lsp::CompletionResponse::Array(vec![
11719                lsp::CompletionItem {
11720                    label: multiline_label.to_string(),
11721                    text_edit: gen_text_edit(&params, "new_text_1"),
11722                    ..lsp::CompletionItem::default()
11723                },
11724                lsp::CompletionItem {
11725                    label: "single line label 1".to_string(),
11726                    detail: Some(multiline_detail.to_string()),
11727                    text_edit: gen_text_edit(&params, "new_text_2"),
11728                    ..lsp::CompletionItem::default()
11729                },
11730                lsp::CompletionItem {
11731                    label: "single line label 2".to_string(),
11732                    label_details: Some(lsp::CompletionItemLabelDetails {
11733                        description: Some(multiline_description.to_string()),
11734                        detail: None,
11735                    }),
11736                    text_edit: gen_text_edit(&params, "new_text_2"),
11737                    ..lsp::CompletionItem::default()
11738                },
11739                lsp::CompletionItem {
11740                    label: multiline_label_2.to_string(),
11741                    detail: Some(multiline_detail_2.to_string()),
11742                    text_edit: gen_text_edit(&params, "new_text_3"),
11743                    ..lsp::CompletionItem::default()
11744                },
11745                lsp::CompletionItem {
11746                    label: "Label with many     spaces and \t but without newlines".to_string(),
11747                    detail: Some(
11748                        "Details with many     spaces and \t but without newlines".to_string(),
11749                    ),
11750                    text_edit: gen_text_edit(&params, "new_text_4"),
11751                    ..lsp::CompletionItem::default()
11752                },
11753            ])))
11754        },
11755    );
11756
11757    editor.update_in(cx, |editor, window, cx| {
11758        cx.focus_self(window);
11759        editor.move_to_end(&MoveToEnd, window, cx);
11760        editor.handle_input(".", window, cx);
11761    });
11762    cx.run_until_parked();
11763    completion_handle.next().await.unwrap();
11764
11765    editor.update(cx, |editor, _| {
11766        assert!(editor.context_menu_visible());
11767        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11768        {
11769            let completion_labels = menu
11770                .completions
11771                .borrow()
11772                .iter()
11773                .map(|c| c.label.text.clone())
11774                .collect::<Vec<_>>();
11775            assert_eq!(
11776                completion_labels,
11777                &[
11778                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11779                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11780                    "single line label 2 d e f ",
11781                    "a b c g h i ",
11782                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11783                ],
11784                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11785            );
11786
11787            for completion in menu
11788                .completions
11789                .borrow()
11790                .iter() {
11791                    assert_eq!(
11792                        completion.label.filter_range,
11793                        0..completion.label.text.len(),
11794                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11795                    );
11796                }
11797        } else {
11798            panic!("expected completion menu to be open");
11799        }
11800    });
11801}
11802
11803#[gpui::test]
11804async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11805    init_test(cx, |_| {});
11806    let mut cx = EditorLspTestContext::new_rust(
11807        lsp::ServerCapabilities {
11808            completion_provider: Some(lsp::CompletionOptions {
11809                trigger_characters: Some(vec![".".to_string()]),
11810                ..Default::default()
11811            }),
11812            ..Default::default()
11813        },
11814        cx,
11815    )
11816    .await;
11817    cx.lsp
11818        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11819            Ok(Some(lsp::CompletionResponse::Array(vec![
11820                lsp::CompletionItem {
11821                    label: "first".into(),
11822                    ..Default::default()
11823                },
11824                lsp::CompletionItem {
11825                    label: "last".into(),
11826                    ..Default::default()
11827                },
11828            ])))
11829        });
11830    cx.set_state("variableˇ");
11831    cx.simulate_keystroke(".");
11832    cx.executor().run_until_parked();
11833
11834    cx.update_editor(|editor, _, _| {
11835        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11836        {
11837            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11838        } else {
11839            panic!("expected completion menu to be open");
11840        }
11841    });
11842
11843    cx.update_editor(|editor, window, cx| {
11844        editor.move_page_down(&MovePageDown::default(), window, cx);
11845        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11846        {
11847            assert!(
11848                menu.selected_item == 1,
11849                "expected PageDown to select the last item from the context menu"
11850            );
11851        } else {
11852            panic!("expected completion menu to stay open after PageDown");
11853        }
11854    });
11855
11856    cx.update_editor(|editor, window, cx| {
11857        editor.move_page_up(&MovePageUp::default(), window, cx);
11858        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11859        {
11860            assert!(
11861                menu.selected_item == 0,
11862                "expected PageUp to select the first item from the context menu"
11863            );
11864        } else {
11865            panic!("expected completion menu to stay open after PageUp");
11866        }
11867    });
11868}
11869
11870#[gpui::test]
11871async fn test_as_is_completions(cx: &mut TestAppContext) {
11872    init_test(cx, |_| {});
11873    let mut cx = EditorLspTestContext::new_rust(
11874        lsp::ServerCapabilities {
11875            completion_provider: Some(lsp::CompletionOptions {
11876                ..Default::default()
11877            }),
11878            ..Default::default()
11879        },
11880        cx,
11881    )
11882    .await;
11883    cx.lsp
11884        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11885            Ok(Some(lsp::CompletionResponse::Array(vec![
11886                lsp::CompletionItem {
11887                    label: "unsafe".into(),
11888                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11889                        range: lsp::Range {
11890                            start: lsp::Position {
11891                                line: 1,
11892                                character: 2,
11893                            },
11894                            end: lsp::Position {
11895                                line: 1,
11896                                character: 3,
11897                            },
11898                        },
11899                        new_text: "unsafe".to_string(),
11900                    })),
11901                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11902                    ..Default::default()
11903                },
11904            ])))
11905        });
11906    cx.set_state("fn a() {}\n");
11907    cx.executor().run_until_parked();
11908    cx.update_editor(|editor, window, cx| {
11909        editor.show_completions(
11910            &ShowCompletions {
11911                trigger: Some("\n".into()),
11912            },
11913            window,
11914            cx,
11915        );
11916    });
11917    cx.executor().run_until_parked();
11918
11919    cx.update_editor(|editor, window, cx| {
11920        editor.confirm_completion(&Default::default(), window, cx)
11921    });
11922    cx.executor().run_until_parked();
11923    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11924}
11925
11926#[gpui::test]
11927async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11928    init_test(cx, |_| {});
11929
11930    let mut cx = EditorLspTestContext::new_rust(
11931        lsp::ServerCapabilities {
11932            completion_provider: Some(lsp::CompletionOptions {
11933                trigger_characters: Some(vec![".".to_string()]),
11934                resolve_provider: Some(true),
11935                ..Default::default()
11936            }),
11937            ..Default::default()
11938        },
11939        cx,
11940    )
11941    .await;
11942
11943    cx.set_state("fn main() { let a = 2ˇ; }");
11944    cx.simulate_keystroke(".");
11945    let completion_item = lsp::CompletionItem {
11946        label: "Some".into(),
11947        kind: Some(lsp::CompletionItemKind::SNIPPET),
11948        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11949        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11950            kind: lsp::MarkupKind::Markdown,
11951            value: "```rust\nSome(2)\n```".to_string(),
11952        })),
11953        deprecated: Some(false),
11954        sort_text: Some("Some".to_string()),
11955        filter_text: Some("Some".to_string()),
11956        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11957        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11958            range: lsp::Range {
11959                start: lsp::Position {
11960                    line: 0,
11961                    character: 22,
11962                },
11963                end: lsp::Position {
11964                    line: 0,
11965                    character: 22,
11966                },
11967            },
11968            new_text: "Some(2)".to_string(),
11969        })),
11970        additional_text_edits: Some(vec![lsp::TextEdit {
11971            range: lsp::Range {
11972                start: lsp::Position {
11973                    line: 0,
11974                    character: 20,
11975                },
11976                end: lsp::Position {
11977                    line: 0,
11978                    character: 22,
11979                },
11980            },
11981            new_text: "".to_string(),
11982        }]),
11983        ..Default::default()
11984    };
11985
11986    let closure_completion_item = completion_item.clone();
11987    let counter = Arc::new(AtomicUsize::new(0));
11988    let counter_clone = counter.clone();
11989    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11990        let task_completion_item = closure_completion_item.clone();
11991        counter_clone.fetch_add(1, atomic::Ordering::Release);
11992        async move {
11993            Ok(Some(lsp::CompletionResponse::Array(vec![
11994                task_completion_item,
11995            ])))
11996        }
11997    });
11998
11999    cx.condition(|editor, _| editor.context_menu_visible())
12000        .await;
12001    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
12002    assert!(request.next().await.is_some());
12003    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
12004
12005    cx.simulate_keystrokes("S o m");
12006    cx.condition(|editor, _| editor.context_menu_visible())
12007        .await;
12008    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
12009    assert!(request.next().await.is_some());
12010    assert!(request.next().await.is_some());
12011    assert!(request.next().await.is_some());
12012    request.close();
12013    assert!(request.next().await.is_none());
12014    assert_eq!(
12015        counter.load(atomic::Ordering::Acquire),
12016        4,
12017        "With the completions menu open, only one LSP request should happen per input"
12018    );
12019}
12020
12021#[gpui::test]
12022async fn test_toggle_comment(cx: &mut TestAppContext) {
12023    init_test(cx, |_| {});
12024    let mut cx = EditorTestContext::new(cx).await;
12025    let language = Arc::new(Language::new(
12026        LanguageConfig {
12027            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12028            ..Default::default()
12029        },
12030        Some(tree_sitter_rust::LANGUAGE.into()),
12031    ));
12032    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12033
12034    // If multiple selections intersect a line, the line is only toggled once.
12035    cx.set_state(indoc! {"
12036        fn a() {
12037            «//b();
12038            ˇ»// «c();
12039            //ˇ»  d();
12040        }
12041    "});
12042
12043    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12044
12045    cx.assert_editor_state(indoc! {"
12046        fn a() {
12047            «b();
12048            c();
12049            ˇ» d();
12050        }
12051    "});
12052
12053    // The comment prefix is inserted at the same column for every line in a
12054    // selection.
12055    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12056
12057    cx.assert_editor_state(indoc! {"
12058        fn a() {
12059            // «b();
12060            // c();
12061            ˇ»//  d();
12062        }
12063    "});
12064
12065    // If a selection ends at the beginning of a line, that line is not toggled.
12066    cx.set_selections_state(indoc! {"
12067        fn a() {
12068            // b();
12069            «// c();
12070        ˇ»    //  d();
12071        }
12072    "});
12073
12074    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12075
12076    cx.assert_editor_state(indoc! {"
12077        fn a() {
12078            // b();
12079            «c();
12080        ˇ»    //  d();
12081        }
12082    "});
12083
12084    // If a selection span a single line and is empty, the line is toggled.
12085    cx.set_state(indoc! {"
12086        fn a() {
12087            a();
12088            b();
12089        ˇ
12090        }
12091    "});
12092
12093    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12094
12095    cx.assert_editor_state(indoc! {"
12096        fn a() {
12097            a();
12098            b();
12099        //•ˇ
12100        }
12101    "});
12102
12103    // If a selection span multiple lines, empty lines are not toggled.
12104    cx.set_state(indoc! {"
12105        fn a() {
12106            «a();
12107
12108            c();ˇ»
12109        }
12110    "});
12111
12112    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12113
12114    cx.assert_editor_state(indoc! {"
12115        fn a() {
12116            // «a();
12117
12118            // c();ˇ»
12119        }
12120    "});
12121
12122    // If a selection includes multiple comment prefixes, all lines are uncommented.
12123    cx.set_state(indoc! {"
12124        fn a() {
12125            «// a();
12126            /// b();
12127            //! c();ˇ»
12128        }
12129    "});
12130
12131    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12132
12133    cx.assert_editor_state(indoc! {"
12134        fn a() {
12135            «a();
12136            b();
12137            c();ˇ»
12138        }
12139    "});
12140}
12141
12142#[gpui::test]
12143async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
12144    init_test(cx, |_| {});
12145    let mut cx = EditorTestContext::new(cx).await;
12146    let language = Arc::new(Language::new(
12147        LanguageConfig {
12148            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12149            ..Default::default()
12150        },
12151        Some(tree_sitter_rust::LANGUAGE.into()),
12152    ));
12153    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12154
12155    let toggle_comments = &ToggleComments {
12156        advance_downwards: false,
12157        ignore_indent: true,
12158    };
12159
12160    // If multiple selections intersect a line, the line is only toggled once.
12161    cx.set_state(indoc! {"
12162        fn a() {
12163        //    «b();
12164        //    c();
12165        //    ˇ» d();
12166        }
12167    "});
12168
12169    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12170
12171    cx.assert_editor_state(indoc! {"
12172        fn a() {
12173            «b();
12174            c();
12175            ˇ» d();
12176        }
12177    "});
12178
12179    // The comment prefix is inserted at the beginning of each line
12180    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12181
12182    cx.assert_editor_state(indoc! {"
12183        fn a() {
12184        //    «b();
12185        //    c();
12186        //    ˇ» d();
12187        }
12188    "});
12189
12190    // If a selection ends at the beginning of a line, that line is not toggled.
12191    cx.set_selections_state(indoc! {"
12192        fn a() {
12193        //    b();
12194        //    «c();
12195        ˇ»//     d();
12196        }
12197    "});
12198
12199    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12200
12201    cx.assert_editor_state(indoc! {"
12202        fn a() {
12203        //    b();
12204            «c();
12205        ˇ»//     d();
12206        }
12207    "});
12208
12209    // If a selection span a single line and is empty, the line is toggled.
12210    cx.set_state(indoc! {"
12211        fn a() {
12212            a();
12213            b();
12214        ˇ
12215        }
12216    "});
12217
12218    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12219
12220    cx.assert_editor_state(indoc! {"
12221        fn a() {
12222            a();
12223            b();
12224        //ˇ
12225        }
12226    "});
12227
12228    // If a selection span multiple lines, empty lines are not toggled.
12229    cx.set_state(indoc! {"
12230        fn a() {
12231            «a();
12232
12233            c();ˇ»
12234        }
12235    "});
12236
12237    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12238
12239    cx.assert_editor_state(indoc! {"
12240        fn a() {
12241        //    «a();
12242
12243        //    c();ˇ»
12244        }
12245    "});
12246
12247    // If a selection includes multiple comment prefixes, all lines are uncommented.
12248    cx.set_state(indoc! {"
12249        fn a() {
12250        //    «a();
12251        ///    b();
12252        //!    c();ˇ»
12253        }
12254    "});
12255
12256    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12257
12258    cx.assert_editor_state(indoc! {"
12259        fn a() {
12260            «a();
12261            b();
12262            c();ˇ»
12263        }
12264    "});
12265}
12266
12267#[gpui::test]
12268async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12269    init_test(cx, |_| {});
12270
12271    let language = Arc::new(Language::new(
12272        LanguageConfig {
12273            line_comments: vec!["// ".into()],
12274            ..Default::default()
12275        },
12276        Some(tree_sitter_rust::LANGUAGE.into()),
12277    ));
12278
12279    let mut cx = EditorTestContext::new(cx).await;
12280
12281    cx.language_registry().add(language.clone());
12282    cx.update_buffer(|buffer, cx| {
12283        buffer.set_language(Some(language), cx);
12284    });
12285
12286    let toggle_comments = &ToggleComments {
12287        advance_downwards: true,
12288        ignore_indent: false,
12289    };
12290
12291    // Single cursor on one line -> advance
12292    // Cursor moves horizontally 3 characters as well on non-blank line
12293    cx.set_state(indoc!(
12294        "fn a() {
12295             ˇdog();
12296             cat();
12297        }"
12298    ));
12299    cx.update_editor(|editor, window, cx| {
12300        editor.toggle_comments(toggle_comments, window, cx);
12301    });
12302    cx.assert_editor_state(indoc!(
12303        "fn a() {
12304             // dog();
12305             catˇ();
12306        }"
12307    ));
12308
12309    // Single selection on one line -> don't advance
12310    cx.set_state(indoc!(
12311        "fn a() {
12312             «dog()ˇ»;
12313             cat();
12314        }"
12315    ));
12316    cx.update_editor(|editor, window, cx| {
12317        editor.toggle_comments(toggle_comments, window, cx);
12318    });
12319    cx.assert_editor_state(indoc!(
12320        "fn a() {
12321             // «dog()ˇ»;
12322             cat();
12323        }"
12324    ));
12325
12326    // Multiple cursors on one line -> advance
12327    cx.set_state(indoc!(
12328        "fn a() {
12329             ˇdˇog();
12330             cat();
12331        }"
12332    ));
12333    cx.update_editor(|editor, window, cx| {
12334        editor.toggle_comments(toggle_comments, window, cx);
12335    });
12336    cx.assert_editor_state(indoc!(
12337        "fn a() {
12338             // dog();
12339             catˇ(ˇ);
12340        }"
12341    ));
12342
12343    // Multiple cursors on one line, with selection -> don't advance
12344    cx.set_state(indoc!(
12345        "fn a() {
12346             ˇdˇog«()ˇ»;
12347             cat();
12348        }"
12349    ));
12350    cx.update_editor(|editor, window, cx| {
12351        editor.toggle_comments(toggle_comments, window, cx);
12352    });
12353    cx.assert_editor_state(indoc!(
12354        "fn a() {
12355             // ˇdˇog«()ˇ»;
12356             cat();
12357        }"
12358    ));
12359
12360    // Single cursor on one line -> advance
12361    // Cursor moves to column 0 on blank line
12362    cx.set_state(indoc!(
12363        "fn a() {
12364             ˇdog();
12365
12366             cat();
12367        }"
12368    ));
12369    cx.update_editor(|editor, window, cx| {
12370        editor.toggle_comments(toggle_comments, window, cx);
12371    });
12372    cx.assert_editor_state(indoc!(
12373        "fn a() {
12374             // dog();
12375        ˇ
12376             cat();
12377        }"
12378    ));
12379
12380    // Single cursor on one line -> advance
12381    // Cursor starts and ends at column 0
12382    cx.set_state(indoc!(
12383        "fn a() {
12384         ˇ    dog();
12385             cat();
12386        }"
12387    ));
12388    cx.update_editor(|editor, window, cx| {
12389        editor.toggle_comments(toggle_comments, window, cx);
12390    });
12391    cx.assert_editor_state(indoc!(
12392        "fn a() {
12393             // dog();
12394         ˇ    cat();
12395        }"
12396    ));
12397}
12398
12399#[gpui::test]
12400async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12401    init_test(cx, |_| {});
12402
12403    let mut cx = EditorTestContext::new(cx).await;
12404
12405    let html_language = Arc::new(
12406        Language::new(
12407            LanguageConfig {
12408                name: "HTML".into(),
12409                block_comment: Some(("<!-- ".into(), " -->".into())),
12410                ..Default::default()
12411            },
12412            Some(tree_sitter_html::LANGUAGE.into()),
12413        )
12414        .with_injection_query(
12415            r#"
12416            (script_element
12417                (raw_text) @injection.content
12418                (#set! injection.language "javascript"))
12419            "#,
12420        )
12421        .unwrap(),
12422    );
12423
12424    let javascript_language = Arc::new(Language::new(
12425        LanguageConfig {
12426            name: "JavaScript".into(),
12427            line_comments: vec!["// ".into()],
12428            ..Default::default()
12429        },
12430        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12431    ));
12432
12433    cx.language_registry().add(html_language.clone());
12434    cx.language_registry().add(javascript_language.clone());
12435    cx.update_buffer(|buffer, cx| {
12436        buffer.set_language(Some(html_language), cx);
12437    });
12438
12439    // Toggle comments for empty selections
12440    cx.set_state(
12441        &r#"
12442            <p>A</p>ˇ
12443            <p>B</p>ˇ
12444            <p>C</p>ˇ
12445        "#
12446        .unindent(),
12447    );
12448    cx.update_editor(|editor, window, cx| {
12449        editor.toggle_comments(&ToggleComments::default(), window, cx)
12450    });
12451    cx.assert_editor_state(
12452        &r#"
12453            <!-- <p>A</p>ˇ -->
12454            <!-- <p>B</p>ˇ -->
12455            <!-- <p>C</p>ˇ -->
12456        "#
12457        .unindent(),
12458    );
12459    cx.update_editor(|editor, window, cx| {
12460        editor.toggle_comments(&ToggleComments::default(), window, cx)
12461    });
12462    cx.assert_editor_state(
12463        &r#"
12464            <p>A</p>ˇ
12465            <p>B</p>ˇ
12466            <p>C</p>ˇ
12467        "#
12468        .unindent(),
12469    );
12470
12471    // Toggle comments for mixture of empty and non-empty selections, where
12472    // multiple selections occupy a given line.
12473    cx.set_state(
12474        &r#"
12475            <p>A«</p>
12476            <p>ˇ»B</p>ˇ
12477            <p>C«</p>
12478            <p>ˇ»D</p>ˇ
12479        "#
12480        .unindent(),
12481    );
12482
12483    cx.update_editor(|editor, window, cx| {
12484        editor.toggle_comments(&ToggleComments::default(), window, cx)
12485    });
12486    cx.assert_editor_state(
12487        &r#"
12488            <!-- <p>A«</p>
12489            <p>ˇ»B</p>ˇ -->
12490            <!-- <p>C«</p>
12491            <p>ˇ»D</p>ˇ -->
12492        "#
12493        .unindent(),
12494    );
12495    cx.update_editor(|editor, window, cx| {
12496        editor.toggle_comments(&ToggleComments::default(), window, cx)
12497    });
12498    cx.assert_editor_state(
12499        &r#"
12500            <p>A«</p>
12501            <p>ˇ»B</p>ˇ
12502            <p>C«</p>
12503            <p>ˇ»D</p>ˇ
12504        "#
12505        .unindent(),
12506    );
12507
12508    // Toggle comments when different languages are active for different
12509    // selections.
12510    cx.set_state(
12511        &r#"
12512            ˇ<script>
12513                ˇvar x = new Y();
12514            ˇ</script>
12515        "#
12516        .unindent(),
12517    );
12518    cx.executor().run_until_parked();
12519    cx.update_editor(|editor, window, cx| {
12520        editor.toggle_comments(&ToggleComments::default(), window, cx)
12521    });
12522    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12523    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12524    cx.assert_editor_state(
12525        &r#"
12526            <!-- ˇ<script> -->
12527                // ˇvar x = new Y();
12528            <!-- ˇ</script> -->
12529        "#
12530        .unindent(),
12531    );
12532}
12533
12534#[gpui::test]
12535fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12536    init_test(cx, |_| {});
12537
12538    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12539    let multibuffer = cx.new(|cx| {
12540        let mut multibuffer = MultiBuffer::new(ReadWrite);
12541        multibuffer.push_excerpts(
12542            buffer.clone(),
12543            [
12544                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12545                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12546            ],
12547            cx,
12548        );
12549        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12550        multibuffer
12551    });
12552
12553    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12554    editor.update_in(cx, |editor, window, cx| {
12555        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12556        editor.change_selections(None, window, cx, |s| {
12557            s.select_ranges([
12558                Point::new(0, 0)..Point::new(0, 0),
12559                Point::new(1, 0)..Point::new(1, 0),
12560            ])
12561        });
12562
12563        editor.handle_input("X", window, cx);
12564        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12565        assert_eq!(
12566            editor.selections.ranges(cx),
12567            [
12568                Point::new(0, 1)..Point::new(0, 1),
12569                Point::new(1, 1)..Point::new(1, 1),
12570            ]
12571        );
12572
12573        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12574        editor.change_selections(None, window, cx, |s| {
12575            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12576        });
12577        editor.backspace(&Default::default(), window, cx);
12578        assert_eq!(editor.text(cx), "Xa\nbbb");
12579        assert_eq!(
12580            editor.selections.ranges(cx),
12581            [Point::new(1, 0)..Point::new(1, 0)]
12582        );
12583
12584        editor.change_selections(None, window, cx, |s| {
12585            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12586        });
12587        editor.backspace(&Default::default(), window, cx);
12588        assert_eq!(editor.text(cx), "X\nbb");
12589        assert_eq!(
12590            editor.selections.ranges(cx),
12591            [Point::new(0, 1)..Point::new(0, 1)]
12592        );
12593    });
12594}
12595
12596#[gpui::test]
12597fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12598    init_test(cx, |_| {});
12599
12600    let markers = vec![('[', ']').into(), ('(', ')').into()];
12601    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12602        indoc! {"
12603            [aaaa
12604            (bbbb]
12605            cccc)",
12606        },
12607        markers.clone(),
12608    );
12609    let excerpt_ranges = markers.into_iter().map(|marker| {
12610        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12611        ExcerptRange::new(context.clone())
12612    });
12613    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12614    let multibuffer = cx.new(|cx| {
12615        let mut multibuffer = MultiBuffer::new(ReadWrite);
12616        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12617        multibuffer
12618    });
12619
12620    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12621    editor.update_in(cx, |editor, window, cx| {
12622        let (expected_text, selection_ranges) = marked_text_ranges(
12623            indoc! {"
12624                aaaa
12625                bˇbbb
12626                bˇbbˇb
12627                cccc"
12628            },
12629            true,
12630        );
12631        assert_eq!(editor.text(cx), expected_text);
12632        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12633
12634        editor.handle_input("X", window, cx);
12635
12636        let (expected_text, expected_selections) = marked_text_ranges(
12637            indoc! {"
12638                aaaa
12639                bXˇbbXb
12640                bXˇbbXˇb
12641                cccc"
12642            },
12643            false,
12644        );
12645        assert_eq!(editor.text(cx), expected_text);
12646        assert_eq!(editor.selections.ranges(cx), expected_selections);
12647
12648        editor.newline(&Newline, window, cx);
12649        let (expected_text, expected_selections) = marked_text_ranges(
12650            indoc! {"
12651                aaaa
12652                bX
12653                ˇbbX
12654                b
12655                bX
12656                ˇbbX
12657                ˇb
12658                cccc"
12659            },
12660            false,
12661        );
12662        assert_eq!(editor.text(cx), expected_text);
12663        assert_eq!(editor.selections.ranges(cx), expected_selections);
12664    });
12665}
12666
12667#[gpui::test]
12668fn test_refresh_selections(cx: &mut TestAppContext) {
12669    init_test(cx, |_| {});
12670
12671    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12672    let mut excerpt1_id = None;
12673    let multibuffer = cx.new(|cx| {
12674        let mut multibuffer = MultiBuffer::new(ReadWrite);
12675        excerpt1_id = multibuffer
12676            .push_excerpts(
12677                buffer.clone(),
12678                [
12679                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12680                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12681                ],
12682                cx,
12683            )
12684            .into_iter()
12685            .next();
12686        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12687        multibuffer
12688    });
12689
12690    let editor = cx.add_window(|window, cx| {
12691        let mut editor = build_editor(multibuffer.clone(), window, cx);
12692        let snapshot = editor.snapshot(window, cx);
12693        editor.change_selections(None, window, cx, |s| {
12694            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12695        });
12696        editor.begin_selection(
12697            Point::new(2, 1).to_display_point(&snapshot),
12698            true,
12699            1,
12700            window,
12701            cx,
12702        );
12703        assert_eq!(
12704            editor.selections.ranges(cx),
12705            [
12706                Point::new(1, 3)..Point::new(1, 3),
12707                Point::new(2, 1)..Point::new(2, 1),
12708            ]
12709        );
12710        editor
12711    });
12712
12713    // Refreshing selections is a no-op when excerpts haven't changed.
12714    _ = editor.update(cx, |editor, window, cx| {
12715        editor.change_selections(None, window, cx, |s| s.refresh());
12716        assert_eq!(
12717            editor.selections.ranges(cx),
12718            [
12719                Point::new(1, 3)..Point::new(1, 3),
12720                Point::new(2, 1)..Point::new(2, 1),
12721            ]
12722        );
12723    });
12724
12725    multibuffer.update(cx, |multibuffer, cx| {
12726        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12727    });
12728    _ = editor.update(cx, |editor, window, cx| {
12729        // Removing an excerpt causes the first selection to become degenerate.
12730        assert_eq!(
12731            editor.selections.ranges(cx),
12732            [
12733                Point::new(0, 0)..Point::new(0, 0),
12734                Point::new(0, 1)..Point::new(0, 1)
12735            ]
12736        );
12737
12738        // Refreshing selections will relocate the first selection to the original buffer
12739        // location.
12740        editor.change_selections(None, window, cx, |s| s.refresh());
12741        assert_eq!(
12742            editor.selections.ranges(cx),
12743            [
12744                Point::new(0, 1)..Point::new(0, 1),
12745                Point::new(0, 3)..Point::new(0, 3)
12746            ]
12747        );
12748        assert!(editor.selections.pending_anchor().is_some());
12749    });
12750}
12751
12752#[gpui::test]
12753fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12754    init_test(cx, |_| {});
12755
12756    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12757    let mut excerpt1_id = None;
12758    let multibuffer = cx.new(|cx| {
12759        let mut multibuffer = MultiBuffer::new(ReadWrite);
12760        excerpt1_id = multibuffer
12761            .push_excerpts(
12762                buffer.clone(),
12763                [
12764                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12765                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12766                ],
12767                cx,
12768            )
12769            .into_iter()
12770            .next();
12771        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12772        multibuffer
12773    });
12774
12775    let editor = cx.add_window(|window, cx| {
12776        let mut editor = build_editor(multibuffer.clone(), window, cx);
12777        let snapshot = editor.snapshot(window, cx);
12778        editor.begin_selection(
12779            Point::new(1, 3).to_display_point(&snapshot),
12780            false,
12781            1,
12782            window,
12783            cx,
12784        );
12785        assert_eq!(
12786            editor.selections.ranges(cx),
12787            [Point::new(1, 3)..Point::new(1, 3)]
12788        );
12789        editor
12790    });
12791
12792    multibuffer.update(cx, |multibuffer, cx| {
12793        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12794    });
12795    _ = editor.update(cx, |editor, window, cx| {
12796        assert_eq!(
12797            editor.selections.ranges(cx),
12798            [Point::new(0, 0)..Point::new(0, 0)]
12799        );
12800
12801        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12802        editor.change_selections(None, window, cx, |s| s.refresh());
12803        assert_eq!(
12804            editor.selections.ranges(cx),
12805            [Point::new(0, 3)..Point::new(0, 3)]
12806        );
12807        assert!(editor.selections.pending_anchor().is_some());
12808    });
12809}
12810
12811#[gpui::test]
12812async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12813    init_test(cx, |_| {});
12814
12815    let language = Arc::new(
12816        Language::new(
12817            LanguageConfig {
12818                brackets: BracketPairConfig {
12819                    pairs: vec![
12820                        BracketPair {
12821                            start: "{".to_string(),
12822                            end: "}".to_string(),
12823                            close: true,
12824                            surround: true,
12825                            newline: true,
12826                        },
12827                        BracketPair {
12828                            start: "/* ".to_string(),
12829                            end: " */".to_string(),
12830                            close: true,
12831                            surround: true,
12832                            newline: true,
12833                        },
12834                    ],
12835                    ..Default::default()
12836                },
12837                ..Default::default()
12838            },
12839            Some(tree_sitter_rust::LANGUAGE.into()),
12840        )
12841        .with_indents_query("")
12842        .unwrap(),
12843    );
12844
12845    let text = concat!(
12846        "{   }\n",     //
12847        "  x\n",       //
12848        "  /*   */\n", //
12849        "x\n",         //
12850        "{{} }\n",     //
12851    );
12852
12853    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12854    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12855    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12856    editor
12857        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12858        .await;
12859
12860    editor.update_in(cx, |editor, window, cx| {
12861        editor.change_selections(None, window, cx, |s| {
12862            s.select_display_ranges([
12863                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12864                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12865                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12866            ])
12867        });
12868        editor.newline(&Newline, window, cx);
12869
12870        assert_eq!(
12871            editor.buffer().read(cx).read(cx).text(),
12872            concat!(
12873                "{ \n",    // Suppress rustfmt
12874                "\n",      //
12875                "}\n",     //
12876                "  x\n",   //
12877                "  /* \n", //
12878                "  \n",    //
12879                "  */\n",  //
12880                "x\n",     //
12881                "{{} \n",  //
12882                "}\n",     //
12883            )
12884        );
12885    });
12886}
12887
12888#[gpui::test]
12889fn test_highlighted_ranges(cx: &mut TestAppContext) {
12890    init_test(cx, |_| {});
12891
12892    let editor = cx.add_window(|window, cx| {
12893        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12894        build_editor(buffer.clone(), window, cx)
12895    });
12896
12897    _ = editor.update(cx, |editor, window, cx| {
12898        struct Type1;
12899        struct Type2;
12900
12901        let buffer = editor.buffer.read(cx).snapshot(cx);
12902
12903        let anchor_range =
12904            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12905
12906        editor.highlight_background::<Type1>(
12907            &[
12908                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12909                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12910                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12911                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12912            ],
12913            |_| Hsla::red(),
12914            cx,
12915        );
12916        editor.highlight_background::<Type2>(
12917            &[
12918                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12919                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12920                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12921                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12922            ],
12923            |_| Hsla::green(),
12924            cx,
12925        );
12926
12927        let snapshot = editor.snapshot(window, cx);
12928        let mut highlighted_ranges = editor.background_highlights_in_range(
12929            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12930            &snapshot,
12931            cx.theme().colors(),
12932        );
12933        // Enforce a consistent ordering based on color without relying on the ordering of the
12934        // highlight's `TypeId` which is non-executor.
12935        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12936        assert_eq!(
12937            highlighted_ranges,
12938            &[
12939                (
12940                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12941                    Hsla::red(),
12942                ),
12943                (
12944                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12945                    Hsla::red(),
12946                ),
12947                (
12948                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12949                    Hsla::green(),
12950                ),
12951                (
12952                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12953                    Hsla::green(),
12954                ),
12955            ]
12956        );
12957        assert_eq!(
12958            editor.background_highlights_in_range(
12959                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12960                &snapshot,
12961                cx.theme().colors(),
12962            ),
12963            &[(
12964                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12965                Hsla::red(),
12966            )]
12967        );
12968    });
12969}
12970
12971#[gpui::test]
12972async fn test_following(cx: &mut TestAppContext) {
12973    init_test(cx, |_| {});
12974
12975    let fs = FakeFs::new(cx.executor());
12976    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12977
12978    let buffer = project.update(cx, |project, cx| {
12979        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12980        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12981    });
12982    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12983    let follower = cx.update(|cx| {
12984        cx.open_window(
12985            WindowOptions {
12986                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12987                    gpui::Point::new(px(0.), px(0.)),
12988                    gpui::Point::new(px(10.), px(80.)),
12989                ))),
12990                ..Default::default()
12991            },
12992            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12993        )
12994        .unwrap()
12995    });
12996
12997    let is_still_following = Rc::new(RefCell::new(true));
12998    let follower_edit_event_count = Rc::new(RefCell::new(0));
12999    let pending_update = Rc::new(RefCell::new(None));
13000    let leader_entity = leader.root(cx).unwrap();
13001    let follower_entity = follower.root(cx).unwrap();
13002    _ = follower.update(cx, {
13003        let update = pending_update.clone();
13004        let is_still_following = is_still_following.clone();
13005        let follower_edit_event_count = follower_edit_event_count.clone();
13006        |_, window, cx| {
13007            cx.subscribe_in(
13008                &leader_entity,
13009                window,
13010                move |_, leader, event, window, cx| {
13011                    leader.read(cx).add_event_to_update_proto(
13012                        event,
13013                        &mut update.borrow_mut(),
13014                        window,
13015                        cx,
13016                    );
13017                },
13018            )
13019            .detach();
13020
13021            cx.subscribe_in(
13022                &follower_entity,
13023                window,
13024                move |_, _, event: &EditorEvent, _window, _cx| {
13025                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
13026                        *is_still_following.borrow_mut() = false;
13027                    }
13028
13029                    if let EditorEvent::BufferEdited = event {
13030                        *follower_edit_event_count.borrow_mut() += 1;
13031                    }
13032                },
13033            )
13034            .detach();
13035        }
13036    });
13037
13038    // Update the selections only
13039    _ = leader.update(cx, |leader, window, cx| {
13040        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13041    });
13042    follower
13043        .update(cx, |follower, window, cx| {
13044            follower.apply_update_proto(
13045                &project,
13046                pending_update.borrow_mut().take().unwrap(),
13047                window,
13048                cx,
13049            )
13050        })
13051        .unwrap()
13052        .await
13053        .unwrap();
13054    _ = follower.update(cx, |follower, _, cx| {
13055        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
13056    });
13057    assert!(*is_still_following.borrow());
13058    assert_eq!(*follower_edit_event_count.borrow(), 0);
13059
13060    // Update the scroll position only
13061    _ = leader.update(cx, |leader, window, cx| {
13062        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13063    });
13064    follower
13065        .update(cx, |follower, window, cx| {
13066            follower.apply_update_proto(
13067                &project,
13068                pending_update.borrow_mut().take().unwrap(),
13069                window,
13070                cx,
13071            )
13072        })
13073        .unwrap()
13074        .await
13075        .unwrap();
13076    assert_eq!(
13077        follower
13078            .update(cx, |follower, _, cx| follower.scroll_position(cx))
13079            .unwrap(),
13080        gpui::Point::new(1.5, 3.5)
13081    );
13082    assert!(*is_still_following.borrow());
13083    assert_eq!(*follower_edit_event_count.borrow(), 0);
13084
13085    // Update the selections and scroll position. The follower's scroll position is updated
13086    // via autoscroll, not via the leader's exact scroll position.
13087    _ = leader.update(cx, |leader, window, cx| {
13088        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
13089        leader.request_autoscroll(Autoscroll::newest(), cx);
13090        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13091    });
13092    follower
13093        .update(cx, |follower, window, cx| {
13094            follower.apply_update_proto(
13095                &project,
13096                pending_update.borrow_mut().take().unwrap(),
13097                window,
13098                cx,
13099            )
13100        })
13101        .unwrap()
13102        .await
13103        .unwrap();
13104    _ = follower.update(cx, |follower, _, cx| {
13105        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
13106        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
13107    });
13108    assert!(*is_still_following.borrow());
13109
13110    // Creating a pending selection that precedes another selection
13111    _ = leader.update(cx, |leader, window, cx| {
13112        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13113        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
13114    });
13115    follower
13116        .update(cx, |follower, window, cx| {
13117            follower.apply_update_proto(
13118                &project,
13119                pending_update.borrow_mut().take().unwrap(),
13120                window,
13121                cx,
13122            )
13123        })
13124        .unwrap()
13125        .await
13126        .unwrap();
13127    _ = follower.update(cx, |follower, _, cx| {
13128        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
13129    });
13130    assert!(*is_still_following.borrow());
13131
13132    // Extend the pending selection so that it surrounds another selection
13133    _ = leader.update(cx, |leader, window, cx| {
13134        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
13135    });
13136    follower
13137        .update(cx, |follower, window, cx| {
13138            follower.apply_update_proto(
13139                &project,
13140                pending_update.borrow_mut().take().unwrap(),
13141                window,
13142                cx,
13143            )
13144        })
13145        .unwrap()
13146        .await
13147        .unwrap();
13148    _ = follower.update(cx, |follower, _, cx| {
13149        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
13150    });
13151
13152    // Scrolling locally breaks the follow
13153    _ = follower.update(cx, |follower, window, cx| {
13154        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13155        follower.set_scroll_anchor(
13156            ScrollAnchor {
13157                anchor: top_anchor,
13158                offset: gpui::Point::new(0.0, 0.5),
13159            },
13160            window,
13161            cx,
13162        );
13163    });
13164    assert!(!(*is_still_following.borrow()));
13165}
13166
13167#[gpui::test]
13168async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13169    init_test(cx, |_| {});
13170
13171    let fs = FakeFs::new(cx.executor());
13172    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13173    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13174    let pane = workspace
13175        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13176        .unwrap();
13177
13178    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13179
13180    let leader = pane.update_in(cx, |_, window, cx| {
13181        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13182        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13183    });
13184
13185    // Start following the editor when it has no excerpts.
13186    let mut state_message =
13187        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13188    let workspace_entity = workspace.root(cx).unwrap();
13189    let follower_1 = cx
13190        .update_window(*workspace.deref(), |_, window, cx| {
13191            Editor::from_state_proto(
13192                workspace_entity,
13193                ViewId {
13194                    creator: CollaboratorId::PeerId(PeerId::default()),
13195                    id: 0,
13196                },
13197                &mut state_message,
13198                window,
13199                cx,
13200            )
13201        })
13202        .unwrap()
13203        .unwrap()
13204        .await
13205        .unwrap();
13206
13207    let update_message = Rc::new(RefCell::new(None));
13208    follower_1.update_in(cx, {
13209        let update = update_message.clone();
13210        |_, window, cx| {
13211            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13212                leader.read(cx).add_event_to_update_proto(
13213                    event,
13214                    &mut update.borrow_mut(),
13215                    window,
13216                    cx,
13217                );
13218            })
13219            .detach();
13220        }
13221    });
13222
13223    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13224        (
13225            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13226            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13227        )
13228    });
13229
13230    // Insert some excerpts.
13231    leader.update(cx, |leader, cx| {
13232        leader.buffer.update(cx, |multibuffer, cx| {
13233            multibuffer.set_excerpts_for_path(
13234                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13235                buffer_1.clone(),
13236                vec![
13237                    Point::row_range(0..3),
13238                    Point::row_range(1..6),
13239                    Point::row_range(12..15),
13240                ],
13241                0,
13242                cx,
13243            );
13244            multibuffer.set_excerpts_for_path(
13245                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13246                buffer_2.clone(),
13247                vec![Point::row_range(0..6), Point::row_range(8..12)],
13248                0,
13249                cx,
13250            );
13251        });
13252    });
13253
13254    // Apply the update of adding the excerpts.
13255    follower_1
13256        .update_in(cx, |follower, window, cx| {
13257            follower.apply_update_proto(
13258                &project,
13259                update_message.borrow().clone().unwrap(),
13260                window,
13261                cx,
13262            )
13263        })
13264        .await
13265        .unwrap();
13266    assert_eq!(
13267        follower_1.update(cx, |editor, cx| editor.text(cx)),
13268        leader.update(cx, |editor, cx| editor.text(cx))
13269    );
13270    update_message.borrow_mut().take();
13271
13272    // Start following separately after it already has excerpts.
13273    let mut state_message =
13274        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13275    let workspace_entity = workspace.root(cx).unwrap();
13276    let follower_2 = cx
13277        .update_window(*workspace.deref(), |_, window, cx| {
13278            Editor::from_state_proto(
13279                workspace_entity,
13280                ViewId {
13281                    creator: CollaboratorId::PeerId(PeerId::default()),
13282                    id: 0,
13283                },
13284                &mut state_message,
13285                window,
13286                cx,
13287            )
13288        })
13289        .unwrap()
13290        .unwrap()
13291        .await
13292        .unwrap();
13293    assert_eq!(
13294        follower_2.update(cx, |editor, cx| editor.text(cx)),
13295        leader.update(cx, |editor, cx| editor.text(cx))
13296    );
13297
13298    // Remove some excerpts.
13299    leader.update(cx, |leader, cx| {
13300        leader.buffer.update(cx, |multibuffer, cx| {
13301            let excerpt_ids = multibuffer.excerpt_ids();
13302            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13303            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13304        });
13305    });
13306
13307    // Apply the update of removing the excerpts.
13308    follower_1
13309        .update_in(cx, |follower, window, cx| {
13310            follower.apply_update_proto(
13311                &project,
13312                update_message.borrow().clone().unwrap(),
13313                window,
13314                cx,
13315            )
13316        })
13317        .await
13318        .unwrap();
13319    follower_2
13320        .update_in(cx, |follower, window, cx| {
13321            follower.apply_update_proto(
13322                &project,
13323                update_message.borrow().clone().unwrap(),
13324                window,
13325                cx,
13326            )
13327        })
13328        .await
13329        .unwrap();
13330    update_message.borrow_mut().take();
13331    assert_eq!(
13332        follower_1.update(cx, |editor, cx| editor.text(cx)),
13333        leader.update(cx, |editor, cx| editor.text(cx))
13334    );
13335}
13336
13337#[gpui::test]
13338async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13339    init_test(cx, |_| {});
13340
13341    let mut cx = EditorTestContext::new(cx).await;
13342    let lsp_store =
13343        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13344
13345    cx.set_state(indoc! {"
13346        ˇfn func(abc def: i32) -> u32 {
13347        }
13348    "});
13349
13350    cx.update(|_, cx| {
13351        lsp_store.update(cx, |lsp_store, cx| {
13352            lsp_store
13353                .update_diagnostics(
13354                    LanguageServerId(0),
13355                    lsp::PublishDiagnosticsParams {
13356                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13357                        version: None,
13358                        diagnostics: vec![
13359                            lsp::Diagnostic {
13360                                range: lsp::Range::new(
13361                                    lsp::Position::new(0, 11),
13362                                    lsp::Position::new(0, 12),
13363                                ),
13364                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13365                                ..Default::default()
13366                            },
13367                            lsp::Diagnostic {
13368                                range: lsp::Range::new(
13369                                    lsp::Position::new(0, 12),
13370                                    lsp::Position::new(0, 15),
13371                                ),
13372                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13373                                ..Default::default()
13374                            },
13375                            lsp::Diagnostic {
13376                                range: lsp::Range::new(
13377                                    lsp::Position::new(0, 25),
13378                                    lsp::Position::new(0, 28),
13379                                ),
13380                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13381                                ..Default::default()
13382                            },
13383                        ],
13384                    },
13385                    &[],
13386                    cx,
13387                )
13388                .unwrap()
13389        });
13390    });
13391
13392    executor.run_until_parked();
13393
13394    cx.update_editor(|editor, window, cx| {
13395        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13396    });
13397
13398    cx.assert_editor_state(indoc! {"
13399        fn func(abc def: i32) -> ˇu32 {
13400        }
13401    "});
13402
13403    cx.update_editor(|editor, window, cx| {
13404        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13405    });
13406
13407    cx.assert_editor_state(indoc! {"
13408        fn func(abc ˇdef: i32) -> u32 {
13409        }
13410    "});
13411
13412    cx.update_editor(|editor, window, cx| {
13413        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13414    });
13415
13416    cx.assert_editor_state(indoc! {"
13417        fn func(abcˇ def: i32) -> u32 {
13418        }
13419    "});
13420
13421    cx.update_editor(|editor, window, cx| {
13422        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13423    });
13424
13425    cx.assert_editor_state(indoc! {"
13426        fn func(abc def: i32) -> ˇu32 {
13427        }
13428    "});
13429}
13430
13431#[gpui::test]
13432async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13433    init_test(cx, |_| {});
13434
13435    let mut cx = EditorTestContext::new(cx).await;
13436
13437    let diff_base = r#"
13438        use some::mod;
13439
13440        const A: u32 = 42;
13441
13442        fn main() {
13443            println!("hello");
13444
13445            println!("world");
13446        }
13447        "#
13448    .unindent();
13449
13450    // Edits are modified, removed, modified, added
13451    cx.set_state(
13452        &r#"
13453        use some::modified;
13454
13455        ˇ
13456        fn main() {
13457            println!("hello there");
13458
13459            println!("around the");
13460            println!("world");
13461        }
13462        "#
13463        .unindent(),
13464    );
13465
13466    cx.set_head_text(&diff_base);
13467    executor.run_until_parked();
13468
13469    cx.update_editor(|editor, window, cx| {
13470        //Wrap around the bottom of the buffer
13471        for _ in 0..3 {
13472            editor.go_to_next_hunk(&GoToHunk, window, cx);
13473        }
13474    });
13475
13476    cx.assert_editor_state(
13477        &r#"
13478        ˇuse some::modified;
13479
13480
13481        fn main() {
13482            println!("hello there");
13483
13484            println!("around the");
13485            println!("world");
13486        }
13487        "#
13488        .unindent(),
13489    );
13490
13491    cx.update_editor(|editor, window, cx| {
13492        //Wrap around the top of the buffer
13493        for _ in 0..2 {
13494            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13495        }
13496    });
13497
13498    cx.assert_editor_state(
13499        &r#"
13500        use some::modified;
13501
13502
13503        fn main() {
13504        ˇ    println!("hello there");
13505
13506            println!("around the");
13507            println!("world");
13508        }
13509        "#
13510        .unindent(),
13511    );
13512
13513    cx.update_editor(|editor, window, cx| {
13514        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13515    });
13516
13517    cx.assert_editor_state(
13518        &r#"
13519        use some::modified;
13520
13521        ˇ
13522        fn main() {
13523            println!("hello there");
13524
13525            println!("around the");
13526            println!("world");
13527        }
13528        "#
13529        .unindent(),
13530    );
13531
13532    cx.update_editor(|editor, window, cx| {
13533        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13534    });
13535
13536    cx.assert_editor_state(
13537        &r#"
13538        ˇuse some::modified;
13539
13540
13541        fn main() {
13542            println!("hello there");
13543
13544            println!("around the");
13545            println!("world");
13546        }
13547        "#
13548        .unindent(),
13549    );
13550
13551    cx.update_editor(|editor, window, cx| {
13552        for _ in 0..2 {
13553            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13554        }
13555    });
13556
13557    cx.assert_editor_state(
13558        &r#"
13559        use some::modified;
13560
13561
13562        fn main() {
13563        ˇ    println!("hello there");
13564
13565            println!("around the");
13566            println!("world");
13567        }
13568        "#
13569        .unindent(),
13570    );
13571
13572    cx.update_editor(|editor, window, cx| {
13573        editor.fold(&Fold, window, cx);
13574    });
13575
13576    cx.update_editor(|editor, window, cx| {
13577        editor.go_to_next_hunk(&GoToHunk, window, cx);
13578    });
13579
13580    cx.assert_editor_state(
13581        &r#"
13582        ˇuse some::modified;
13583
13584
13585        fn main() {
13586            println!("hello there");
13587
13588            println!("around the");
13589            println!("world");
13590        }
13591        "#
13592        .unindent(),
13593    );
13594}
13595
13596#[test]
13597fn test_split_words() {
13598    fn split(text: &str) -> Vec<&str> {
13599        split_words(text).collect()
13600    }
13601
13602    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13603    assert_eq!(split("hello_world"), &["hello_", "world"]);
13604    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13605    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13606    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13607    assert_eq!(split("helloworld"), &["helloworld"]);
13608
13609    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13610}
13611
13612#[gpui::test]
13613async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13614    init_test(cx, |_| {});
13615
13616    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13617    let mut assert = |before, after| {
13618        let _state_context = cx.set_state(before);
13619        cx.run_until_parked();
13620        cx.update_editor(|editor, window, cx| {
13621            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13622        });
13623        cx.run_until_parked();
13624        cx.assert_editor_state(after);
13625    };
13626
13627    // Outside bracket jumps to outside of matching bracket
13628    assert("console.logˇ(var);", "console.log(var)ˇ;");
13629    assert("console.log(var)ˇ;", "console.logˇ(var);");
13630
13631    // Inside bracket jumps to inside of matching bracket
13632    assert("console.log(ˇvar);", "console.log(varˇ);");
13633    assert("console.log(varˇ);", "console.log(ˇvar);");
13634
13635    // When outside a bracket and inside, favor jumping to the inside bracket
13636    assert(
13637        "console.log('foo', [1, 2, 3]ˇ);",
13638        "console.log(ˇ'foo', [1, 2, 3]);",
13639    );
13640    assert(
13641        "console.log(ˇ'foo', [1, 2, 3]);",
13642        "console.log('foo', [1, 2, 3]ˇ);",
13643    );
13644
13645    // Bias forward if two options are equally likely
13646    assert(
13647        "let result = curried_fun()ˇ();",
13648        "let result = curried_fun()()ˇ;",
13649    );
13650
13651    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13652    assert(
13653        indoc! {"
13654            function test() {
13655                console.log('test')ˇ
13656            }"},
13657        indoc! {"
13658            function test() {
13659                console.logˇ('test')
13660            }"},
13661    );
13662}
13663
13664#[gpui::test]
13665async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13666    init_test(cx, |_| {});
13667
13668    let fs = FakeFs::new(cx.executor());
13669    fs.insert_tree(
13670        path!("/a"),
13671        json!({
13672            "main.rs": "fn main() { let a = 5; }",
13673            "other.rs": "// Test file",
13674        }),
13675    )
13676    .await;
13677    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13678
13679    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13680    language_registry.add(Arc::new(Language::new(
13681        LanguageConfig {
13682            name: "Rust".into(),
13683            matcher: LanguageMatcher {
13684                path_suffixes: vec!["rs".to_string()],
13685                ..Default::default()
13686            },
13687            brackets: BracketPairConfig {
13688                pairs: vec![BracketPair {
13689                    start: "{".to_string(),
13690                    end: "}".to_string(),
13691                    close: true,
13692                    surround: true,
13693                    newline: true,
13694                }],
13695                disabled_scopes_by_bracket_ix: Vec::new(),
13696            },
13697            ..Default::default()
13698        },
13699        Some(tree_sitter_rust::LANGUAGE.into()),
13700    )));
13701    let mut fake_servers = language_registry.register_fake_lsp(
13702        "Rust",
13703        FakeLspAdapter {
13704            capabilities: lsp::ServerCapabilities {
13705                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13706                    first_trigger_character: "{".to_string(),
13707                    more_trigger_character: None,
13708                }),
13709                ..Default::default()
13710            },
13711            ..Default::default()
13712        },
13713    );
13714
13715    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13716
13717    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13718
13719    let worktree_id = workspace
13720        .update(cx, |workspace, _, cx| {
13721            workspace.project().update(cx, |project, cx| {
13722                project.worktrees(cx).next().unwrap().read(cx).id()
13723            })
13724        })
13725        .unwrap();
13726
13727    let buffer = project
13728        .update(cx, |project, cx| {
13729            project.open_local_buffer(path!("/a/main.rs"), cx)
13730        })
13731        .await
13732        .unwrap();
13733    let editor_handle = workspace
13734        .update(cx, |workspace, window, cx| {
13735            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13736        })
13737        .unwrap()
13738        .await
13739        .unwrap()
13740        .downcast::<Editor>()
13741        .unwrap();
13742
13743    cx.executor().start_waiting();
13744    let fake_server = fake_servers.next().await.unwrap();
13745
13746    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13747        |params, _| async move {
13748            assert_eq!(
13749                params.text_document_position.text_document.uri,
13750                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13751            );
13752            assert_eq!(
13753                params.text_document_position.position,
13754                lsp::Position::new(0, 21),
13755            );
13756
13757            Ok(Some(vec![lsp::TextEdit {
13758                new_text: "]".to_string(),
13759                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13760            }]))
13761        },
13762    );
13763
13764    editor_handle.update_in(cx, |editor, window, cx| {
13765        window.focus(&editor.focus_handle(cx));
13766        editor.change_selections(None, window, cx, |s| {
13767            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13768        });
13769        editor.handle_input("{", window, cx);
13770    });
13771
13772    cx.executor().run_until_parked();
13773
13774    buffer.update(cx, |buffer, _| {
13775        assert_eq!(
13776            buffer.text(),
13777            "fn main() { let a = {5}; }",
13778            "No extra braces from on type formatting should appear in the buffer"
13779        )
13780    });
13781}
13782
13783#[gpui::test]
13784async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13785    init_test(cx, |_| {});
13786
13787    let fs = FakeFs::new(cx.executor());
13788    fs.insert_tree(
13789        path!("/a"),
13790        json!({
13791            "main.rs": "fn main() { let a = 5; }",
13792            "other.rs": "// Test file",
13793        }),
13794    )
13795    .await;
13796
13797    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13798
13799    let server_restarts = Arc::new(AtomicUsize::new(0));
13800    let closure_restarts = Arc::clone(&server_restarts);
13801    let language_server_name = "test language server";
13802    let language_name: LanguageName = "Rust".into();
13803
13804    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13805    language_registry.add(Arc::new(Language::new(
13806        LanguageConfig {
13807            name: language_name.clone(),
13808            matcher: LanguageMatcher {
13809                path_suffixes: vec!["rs".to_string()],
13810                ..Default::default()
13811            },
13812            ..Default::default()
13813        },
13814        Some(tree_sitter_rust::LANGUAGE.into()),
13815    )));
13816    let mut fake_servers = language_registry.register_fake_lsp(
13817        "Rust",
13818        FakeLspAdapter {
13819            name: language_server_name,
13820            initialization_options: Some(json!({
13821                "testOptionValue": true
13822            })),
13823            initializer: Some(Box::new(move |fake_server| {
13824                let task_restarts = Arc::clone(&closure_restarts);
13825                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13826                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13827                    futures::future::ready(Ok(()))
13828                });
13829            })),
13830            ..Default::default()
13831        },
13832    );
13833
13834    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13835    let _buffer = project
13836        .update(cx, |project, cx| {
13837            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13838        })
13839        .await
13840        .unwrap();
13841    let _fake_server = fake_servers.next().await.unwrap();
13842    update_test_language_settings(cx, |language_settings| {
13843        language_settings.languages.insert(
13844            language_name.clone(),
13845            LanguageSettingsContent {
13846                tab_size: NonZeroU32::new(8),
13847                ..Default::default()
13848            },
13849        );
13850    });
13851    cx.executor().run_until_parked();
13852    assert_eq!(
13853        server_restarts.load(atomic::Ordering::Acquire),
13854        0,
13855        "Should not restart LSP server on an unrelated change"
13856    );
13857
13858    update_test_project_settings(cx, |project_settings| {
13859        project_settings.lsp.insert(
13860            "Some other server name".into(),
13861            LspSettings {
13862                binary: None,
13863                settings: None,
13864                initialization_options: Some(json!({
13865                    "some other init value": false
13866                })),
13867                enable_lsp_tasks: false,
13868            },
13869        );
13870    });
13871    cx.executor().run_until_parked();
13872    assert_eq!(
13873        server_restarts.load(atomic::Ordering::Acquire),
13874        0,
13875        "Should not restart LSP server on an unrelated LSP settings change"
13876    );
13877
13878    update_test_project_settings(cx, |project_settings| {
13879        project_settings.lsp.insert(
13880            language_server_name.into(),
13881            LspSettings {
13882                binary: None,
13883                settings: None,
13884                initialization_options: Some(json!({
13885                    "anotherInitValue": false
13886                })),
13887                enable_lsp_tasks: false,
13888            },
13889        );
13890    });
13891    cx.executor().run_until_parked();
13892    assert_eq!(
13893        server_restarts.load(atomic::Ordering::Acquire),
13894        1,
13895        "Should restart LSP server on a related LSP settings change"
13896    );
13897
13898    update_test_project_settings(cx, |project_settings| {
13899        project_settings.lsp.insert(
13900            language_server_name.into(),
13901            LspSettings {
13902                binary: None,
13903                settings: None,
13904                initialization_options: Some(json!({
13905                    "anotherInitValue": false
13906                })),
13907                enable_lsp_tasks: false,
13908            },
13909        );
13910    });
13911    cx.executor().run_until_parked();
13912    assert_eq!(
13913        server_restarts.load(atomic::Ordering::Acquire),
13914        1,
13915        "Should not restart LSP server on a related LSP settings change that is the same"
13916    );
13917
13918    update_test_project_settings(cx, |project_settings| {
13919        project_settings.lsp.insert(
13920            language_server_name.into(),
13921            LspSettings {
13922                binary: None,
13923                settings: None,
13924                initialization_options: None,
13925                enable_lsp_tasks: false,
13926            },
13927        );
13928    });
13929    cx.executor().run_until_parked();
13930    assert_eq!(
13931        server_restarts.load(atomic::Ordering::Acquire),
13932        2,
13933        "Should restart LSP server on another related LSP settings change"
13934    );
13935}
13936
13937#[gpui::test]
13938async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13939    init_test(cx, |_| {});
13940
13941    let mut cx = EditorLspTestContext::new_rust(
13942        lsp::ServerCapabilities {
13943            completion_provider: Some(lsp::CompletionOptions {
13944                trigger_characters: Some(vec![".".to_string()]),
13945                resolve_provider: Some(true),
13946                ..Default::default()
13947            }),
13948            ..Default::default()
13949        },
13950        cx,
13951    )
13952    .await;
13953
13954    cx.set_state("fn main() { let a = 2ˇ; }");
13955    cx.simulate_keystroke(".");
13956    let completion_item = lsp::CompletionItem {
13957        label: "some".into(),
13958        kind: Some(lsp::CompletionItemKind::SNIPPET),
13959        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13960        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13961            kind: lsp::MarkupKind::Markdown,
13962            value: "```rust\nSome(2)\n```".to_string(),
13963        })),
13964        deprecated: Some(false),
13965        sort_text: Some("fffffff2".to_string()),
13966        filter_text: Some("some".to_string()),
13967        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13968        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13969            range: lsp::Range {
13970                start: lsp::Position {
13971                    line: 0,
13972                    character: 22,
13973                },
13974                end: lsp::Position {
13975                    line: 0,
13976                    character: 22,
13977                },
13978            },
13979            new_text: "Some(2)".to_string(),
13980        })),
13981        additional_text_edits: Some(vec![lsp::TextEdit {
13982            range: lsp::Range {
13983                start: lsp::Position {
13984                    line: 0,
13985                    character: 20,
13986                },
13987                end: lsp::Position {
13988                    line: 0,
13989                    character: 22,
13990                },
13991            },
13992            new_text: "".to_string(),
13993        }]),
13994        ..Default::default()
13995    };
13996
13997    let closure_completion_item = completion_item.clone();
13998    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13999        let task_completion_item = closure_completion_item.clone();
14000        async move {
14001            Ok(Some(lsp::CompletionResponse::Array(vec![
14002                task_completion_item,
14003            ])))
14004        }
14005    });
14006
14007    request.next().await;
14008
14009    cx.condition(|editor, _| editor.context_menu_visible())
14010        .await;
14011    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
14012        editor
14013            .confirm_completion(&ConfirmCompletion::default(), window, cx)
14014            .unwrap()
14015    });
14016    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
14017
14018    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14019        let task_completion_item = completion_item.clone();
14020        async move { Ok(task_completion_item) }
14021    })
14022    .next()
14023    .await
14024    .unwrap();
14025    apply_additional_edits.await.unwrap();
14026    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
14027}
14028
14029#[gpui::test]
14030async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
14031    init_test(cx, |_| {});
14032
14033    let mut cx = EditorLspTestContext::new_rust(
14034        lsp::ServerCapabilities {
14035            completion_provider: Some(lsp::CompletionOptions {
14036                trigger_characters: Some(vec![".".to_string()]),
14037                resolve_provider: Some(true),
14038                ..Default::default()
14039            }),
14040            ..Default::default()
14041        },
14042        cx,
14043    )
14044    .await;
14045
14046    cx.set_state("fn main() { let a = 2ˇ; }");
14047    cx.simulate_keystroke(".");
14048
14049    let item1 = lsp::CompletionItem {
14050        label: "method id()".to_string(),
14051        filter_text: Some("id".to_string()),
14052        detail: None,
14053        documentation: None,
14054        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14055            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14056            new_text: ".id".to_string(),
14057        })),
14058        ..lsp::CompletionItem::default()
14059    };
14060
14061    let item2 = lsp::CompletionItem {
14062        label: "other".to_string(),
14063        filter_text: Some("other".to_string()),
14064        detail: None,
14065        documentation: None,
14066        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14067            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14068            new_text: ".other".to_string(),
14069        })),
14070        ..lsp::CompletionItem::default()
14071    };
14072
14073    let item1 = item1.clone();
14074    cx.set_request_handler::<lsp::request::Completion, _, _>({
14075        let item1 = item1.clone();
14076        move |_, _, _| {
14077            let item1 = item1.clone();
14078            let item2 = item2.clone();
14079            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
14080        }
14081    })
14082    .next()
14083    .await;
14084
14085    cx.condition(|editor, _| editor.context_menu_visible())
14086        .await;
14087    cx.update_editor(|editor, _, _| {
14088        let context_menu = editor.context_menu.borrow_mut();
14089        let context_menu = context_menu
14090            .as_ref()
14091            .expect("Should have the context menu deployed");
14092        match context_menu {
14093            CodeContextMenu::Completions(completions_menu) => {
14094                let completions = completions_menu.completions.borrow_mut();
14095                assert_eq!(
14096                    completions
14097                        .iter()
14098                        .map(|completion| &completion.label.text)
14099                        .collect::<Vec<_>>(),
14100                    vec!["method id()", "other"]
14101                )
14102            }
14103            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14104        }
14105    });
14106
14107    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
14108        let item1 = item1.clone();
14109        move |_, item_to_resolve, _| {
14110            let item1 = item1.clone();
14111            async move {
14112                if item1 == item_to_resolve {
14113                    Ok(lsp::CompletionItem {
14114                        label: "method id()".to_string(),
14115                        filter_text: Some("id".to_string()),
14116                        detail: Some("Now resolved!".to_string()),
14117                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
14118                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14119                            range: lsp::Range::new(
14120                                lsp::Position::new(0, 22),
14121                                lsp::Position::new(0, 22),
14122                            ),
14123                            new_text: ".id".to_string(),
14124                        })),
14125                        ..lsp::CompletionItem::default()
14126                    })
14127                } else {
14128                    Ok(item_to_resolve)
14129                }
14130            }
14131        }
14132    })
14133    .next()
14134    .await
14135    .unwrap();
14136    cx.run_until_parked();
14137
14138    cx.update_editor(|editor, window, cx| {
14139        editor.context_menu_next(&Default::default(), window, cx);
14140    });
14141
14142    cx.update_editor(|editor, _, _| {
14143        let context_menu = editor.context_menu.borrow_mut();
14144        let context_menu = context_menu
14145            .as_ref()
14146            .expect("Should have the context menu deployed");
14147        match context_menu {
14148            CodeContextMenu::Completions(completions_menu) => {
14149                let completions = completions_menu.completions.borrow_mut();
14150                assert_eq!(
14151                    completions
14152                        .iter()
14153                        .map(|completion| &completion.label.text)
14154                        .collect::<Vec<_>>(),
14155                    vec!["method id() Now resolved!", "other"],
14156                    "Should update first completion label, but not second as the filter text did not match."
14157                );
14158            }
14159            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14160        }
14161    });
14162}
14163
14164#[gpui::test]
14165async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14166    init_test(cx, |_| {});
14167    let mut cx = EditorLspTestContext::new_rust(
14168        lsp::ServerCapabilities {
14169            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14170            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14171            completion_provider: Some(lsp::CompletionOptions {
14172                resolve_provider: Some(true),
14173                ..Default::default()
14174            }),
14175            ..Default::default()
14176        },
14177        cx,
14178    )
14179    .await;
14180    cx.set_state(indoc! {"
14181        struct TestStruct {
14182            field: i32
14183        }
14184
14185        fn mainˇ() {
14186            let unused_var = 42;
14187            let test_struct = TestStruct { field: 42 };
14188        }
14189    "});
14190    let symbol_range = cx.lsp_range(indoc! {"
14191        struct TestStruct {
14192            field: i32
14193        }
14194
14195        «fn main»() {
14196            let unused_var = 42;
14197            let test_struct = TestStruct { field: 42 };
14198        }
14199    "});
14200    let mut hover_requests =
14201        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14202            Ok(Some(lsp::Hover {
14203                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14204                    kind: lsp::MarkupKind::Markdown,
14205                    value: "Function documentation".to_string(),
14206                }),
14207                range: Some(symbol_range),
14208            }))
14209        });
14210
14211    // Case 1: Test that code action menu hide hover popover
14212    cx.dispatch_action(Hover);
14213    hover_requests.next().await;
14214    cx.condition(|editor, _| editor.hover_state.visible()).await;
14215    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14216        move |_, _, _| async move {
14217            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14218                lsp::CodeAction {
14219                    title: "Remove unused variable".to_string(),
14220                    kind: Some(CodeActionKind::QUICKFIX),
14221                    edit: Some(lsp::WorkspaceEdit {
14222                        changes: Some(
14223                            [(
14224                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14225                                vec![lsp::TextEdit {
14226                                    range: lsp::Range::new(
14227                                        lsp::Position::new(5, 4),
14228                                        lsp::Position::new(5, 27),
14229                                    ),
14230                                    new_text: "".to_string(),
14231                                }],
14232                            )]
14233                            .into_iter()
14234                            .collect(),
14235                        ),
14236                        ..Default::default()
14237                    }),
14238                    ..Default::default()
14239                },
14240            )]))
14241        },
14242    );
14243    cx.update_editor(|editor, window, cx| {
14244        editor.toggle_code_actions(
14245            &ToggleCodeActions {
14246                deployed_from_indicator: None,
14247                quick_launch: false,
14248            },
14249            window,
14250            cx,
14251        );
14252    });
14253    code_action_requests.next().await;
14254    cx.run_until_parked();
14255    cx.condition(|editor, _| editor.context_menu_visible())
14256        .await;
14257    cx.update_editor(|editor, _, _| {
14258        assert!(
14259            !editor.hover_state.visible(),
14260            "Hover popover should be hidden when code action menu is shown"
14261        );
14262        // Hide code actions
14263        editor.context_menu.take();
14264    });
14265
14266    // Case 2: Test that code completions hide hover popover
14267    cx.dispatch_action(Hover);
14268    hover_requests.next().await;
14269    cx.condition(|editor, _| editor.hover_state.visible()).await;
14270    let counter = Arc::new(AtomicUsize::new(0));
14271    let mut completion_requests =
14272        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14273            let counter = counter.clone();
14274            async move {
14275                counter.fetch_add(1, atomic::Ordering::Release);
14276                Ok(Some(lsp::CompletionResponse::Array(vec![
14277                    lsp::CompletionItem {
14278                        label: "main".into(),
14279                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14280                        detail: Some("() -> ()".to_string()),
14281                        ..Default::default()
14282                    },
14283                    lsp::CompletionItem {
14284                        label: "TestStruct".into(),
14285                        kind: Some(lsp::CompletionItemKind::STRUCT),
14286                        detail: Some("struct TestStruct".to_string()),
14287                        ..Default::default()
14288                    },
14289                ])))
14290            }
14291        });
14292    cx.update_editor(|editor, window, cx| {
14293        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14294    });
14295    completion_requests.next().await;
14296    cx.condition(|editor, _| editor.context_menu_visible())
14297        .await;
14298    cx.update_editor(|editor, _, _| {
14299        assert!(
14300            !editor.hover_state.visible(),
14301            "Hover popover should be hidden when completion menu is shown"
14302        );
14303    });
14304}
14305
14306#[gpui::test]
14307async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14308    init_test(cx, |_| {});
14309
14310    let mut cx = EditorLspTestContext::new_rust(
14311        lsp::ServerCapabilities {
14312            completion_provider: Some(lsp::CompletionOptions {
14313                trigger_characters: Some(vec![".".to_string()]),
14314                resolve_provider: Some(true),
14315                ..Default::default()
14316            }),
14317            ..Default::default()
14318        },
14319        cx,
14320    )
14321    .await;
14322
14323    cx.set_state("fn main() { let a = 2ˇ; }");
14324    cx.simulate_keystroke(".");
14325
14326    let unresolved_item_1 = lsp::CompletionItem {
14327        label: "id".to_string(),
14328        filter_text: Some("id".to_string()),
14329        detail: None,
14330        documentation: None,
14331        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14332            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14333            new_text: ".id".to_string(),
14334        })),
14335        ..lsp::CompletionItem::default()
14336    };
14337    let resolved_item_1 = lsp::CompletionItem {
14338        additional_text_edits: Some(vec![lsp::TextEdit {
14339            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14340            new_text: "!!".to_string(),
14341        }]),
14342        ..unresolved_item_1.clone()
14343    };
14344    let unresolved_item_2 = lsp::CompletionItem {
14345        label: "other".to_string(),
14346        filter_text: Some("other".to_string()),
14347        detail: None,
14348        documentation: None,
14349        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14350            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14351            new_text: ".other".to_string(),
14352        })),
14353        ..lsp::CompletionItem::default()
14354    };
14355    let resolved_item_2 = lsp::CompletionItem {
14356        additional_text_edits: Some(vec![lsp::TextEdit {
14357            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14358            new_text: "??".to_string(),
14359        }]),
14360        ..unresolved_item_2.clone()
14361    };
14362
14363    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14364    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14365    cx.lsp
14366        .server
14367        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14368            let unresolved_item_1 = unresolved_item_1.clone();
14369            let resolved_item_1 = resolved_item_1.clone();
14370            let unresolved_item_2 = unresolved_item_2.clone();
14371            let resolved_item_2 = resolved_item_2.clone();
14372            let resolve_requests_1 = resolve_requests_1.clone();
14373            let resolve_requests_2 = resolve_requests_2.clone();
14374            move |unresolved_request, _| {
14375                let unresolved_item_1 = unresolved_item_1.clone();
14376                let resolved_item_1 = resolved_item_1.clone();
14377                let unresolved_item_2 = unresolved_item_2.clone();
14378                let resolved_item_2 = resolved_item_2.clone();
14379                let resolve_requests_1 = resolve_requests_1.clone();
14380                let resolve_requests_2 = resolve_requests_2.clone();
14381                async move {
14382                    if unresolved_request == unresolved_item_1 {
14383                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14384                        Ok(resolved_item_1.clone())
14385                    } else if unresolved_request == unresolved_item_2 {
14386                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14387                        Ok(resolved_item_2.clone())
14388                    } else {
14389                        panic!("Unexpected completion item {unresolved_request:?}")
14390                    }
14391                }
14392            }
14393        })
14394        .detach();
14395
14396    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14397        let unresolved_item_1 = unresolved_item_1.clone();
14398        let unresolved_item_2 = unresolved_item_2.clone();
14399        async move {
14400            Ok(Some(lsp::CompletionResponse::Array(vec![
14401                unresolved_item_1,
14402                unresolved_item_2,
14403            ])))
14404        }
14405    })
14406    .next()
14407    .await;
14408
14409    cx.condition(|editor, _| editor.context_menu_visible())
14410        .await;
14411    cx.update_editor(|editor, _, _| {
14412        let context_menu = editor.context_menu.borrow_mut();
14413        let context_menu = context_menu
14414            .as_ref()
14415            .expect("Should have the context menu deployed");
14416        match context_menu {
14417            CodeContextMenu::Completions(completions_menu) => {
14418                let completions = completions_menu.completions.borrow_mut();
14419                assert_eq!(
14420                    completions
14421                        .iter()
14422                        .map(|completion| &completion.label.text)
14423                        .collect::<Vec<_>>(),
14424                    vec!["id", "other"]
14425                )
14426            }
14427            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14428        }
14429    });
14430    cx.run_until_parked();
14431
14432    cx.update_editor(|editor, window, cx| {
14433        editor.context_menu_next(&ContextMenuNext, window, cx);
14434    });
14435    cx.run_until_parked();
14436    cx.update_editor(|editor, window, cx| {
14437        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14438    });
14439    cx.run_until_parked();
14440    cx.update_editor(|editor, window, cx| {
14441        editor.context_menu_next(&ContextMenuNext, window, cx);
14442    });
14443    cx.run_until_parked();
14444    cx.update_editor(|editor, window, cx| {
14445        editor
14446            .compose_completion(&ComposeCompletion::default(), window, cx)
14447            .expect("No task returned")
14448    })
14449    .await
14450    .expect("Completion failed");
14451    cx.run_until_parked();
14452
14453    cx.update_editor(|editor, _, cx| {
14454        assert_eq!(
14455            resolve_requests_1.load(atomic::Ordering::Acquire),
14456            1,
14457            "Should always resolve once despite multiple selections"
14458        );
14459        assert_eq!(
14460            resolve_requests_2.load(atomic::Ordering::Acquire),
14461            1,
14462            "Should always resolve once after multiple selections and applying the completion"
14463        );
14464        assert_eq!(
14465            editor.text(cx),
14466            "fn main() { let a = ??.other; }",
14467            "Should use resolved data when applying the completion"
14468        );
14469    });
14470}
14471
14472#[gpui::test]
14473async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14474    init_test(cx, |_| {});
14475
14476    let item_0 = lsp::CompletionItem {
14477        label: "abs".into(),
14478        insert_text: Some("abs".into()),
14479        data: Some(json!({ "very": "special"})),
14480        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14481        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14482            lsp::InsertReplaceEdit {
14483                new_text: "abs".to_string(),
14484                insert: lsp::Range::default(),
14485                replace: lsp::Range::default(),
14486            },
14487        )),
14488        ..lsp::CompletionItem::default()
14489    };
14490    let items = iter::once(item_0.clone())
14491        .chain((11..51).map(|i| lsp::CompletionItem {
14492            label: format!("item_{}", i),
14493            insert_text: Some(format!("item_{}", i)),
14494            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14495            ..lsp::CompletionItem::default()
14496        }))
14497        .collect::<Vec<_>>();
14498
14499    let default_commit_characters = vec!["?".to_string()];
14500    let default_data = json!({ "default": "data"});
14501    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14502    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14503    let default_edit_range = lsp::Range {
14504        start: lsp::Position {
14505            line: 0,
14506            character: 5,
14507        },
14508        end: lsp::Position {
14509            line: 0,
14510            character: 5,
14511        },
14512    };
14513
14514    let mut cx = EditorLspTestContext::new_rust(
14515        lsp::ServerCapabilities {
14516            completion_provider: Some(lsp::CompletionOptions {
14517                trigger_characters: Some(vec![".".to_string()]),
14518                resolve_provider: Some(true),
14519                ..Default::default()
14520            }),
14521            ..Default::default()
14522        },
14523        cx,
14524    )
14525    .await;
14526
14527    cx.set_state("fn main() { let a = 2ˇ; }");
14528    cx.simulate_keystroke(".");
14529
14530    let completion_data = default_data.clone();
14531    let completion_characters = default_commit_characters.clone();
14532    let completion_items = items.clone();
14533    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14534        let default_data = completion_data.clone();
14535        let default_commit_characters = completion_characters.clone();
14536        let items = completion_items.clone();
14537        async move {
14538            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14539                items,
14540                item_defaults: Some(lsp::CompletionListItemDefaults {
14541                    data: Some(default_data.clone()),
14542                    commit_characters: Some(default_commit_characters.clone()),
14543                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14544                        default_edit_range,
14545                    )),
14546                    insert_text_format: Some(default_insert_text_format),
14547                    insert_text_mode: Some(default_insert_text_mode),
14548                }),
14549                ..lsp::CompletionList::default()
14550            })))
14551        }
14552    })
14553    .next()
14554    .await;
14555
14556    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14557    cx.lsp
14558        .server
14559        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14560            let closure_resolved_items = resolved_items.clone();
14561            move |item_to_resolve, _| {
14562                let closure_resolved_items = closure_resolved_items.clone();
14563                async move {
14564                    closure_resolved_items.lock().push(item_to_resolve.clone());
14565                    Ok(item_to_resolve)
14566                }
14567            }
14568        })
14569        .detach();
14570
14571    cx.condition(|editor, _| editor.context_menu_visible())
14572        .await;
14573    cx.run_until_parked();
14574    cx.update_editor(|editor, _, _| {
14575        let menu = editor.context_menu.borrow_mut();
14576        match menu.as_ref().expect("should have the completions menu") {
14577            CodeContextMenu::Completions(completions_menu) => {
14578                assert_eq!(
14579                    completions_menu
14580                        .entries
14581                        .borrow()
14582                        .iter()
14583                        .map(|mat| mat.string.clone())
14584                        .collect::<Vec<String>>(),
14585                    items
14586                        .iter()
14587                        .map(|completion| completion.label.clone())
14588                        .collect::<Vec<String>>()
14589                );
14590            }
14591            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14592        }
14593    });
14594    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14595    // with 4 from the end.
14596    assert_eq!(
14597        *resolved_items.lock(),
14598        [&items[0..16], &items[items.len() - 4..items.len()]]
14599            .concat()
14600            .iter()
14601            .cloned()
14602            .map(|mut item| {
14603                if item.data.is_none() {
14604                    item.data = Some(default_data.clone());
14605                }
14606                item
14607            })
14608            .collect::<Vec<lsp::CompletionItem>>(),
14609        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14610    );
14611    resolved_items.lock().clear();
14612
14613    cx.update_editor(|editor, window, cx| {
14614        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14615    });
14616    cx.run_until_parked();
14617    // Completions that have already been resolved are skipped.
14618    assert_eq!(
14619        *resolved_items.lock(),
14620        items[items.len() - 16..items.len() - 4]
14621            .iter()
14622            .cloned()
14623            .map(|mut item| {
14624                if item.data.is_none() {
14625                    item.data = Some(default_data.clone());
14626                }
14627                item
14628            })
14629            .collect::<Vec<lsp::CompletionItem>>()
14630    );
14631    resolved_items.lock().clear();
14632}
14633
14634#[gpui::test]
14635async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14636    init_test(cx, |_| {});
14637
14638    let mut cx = EditorLspTestContext::new(
14639        Language::new(
14640            LanguageConfig {
14641                matcher: LanguageMatcher {
14642                    path_suffixes: vec!["jsx".into()],
14643                    ..Default::default()
14644                },
14645                overrides: [(
14646                    "element".into(),
14647                    LanguageConfigOverride {
14648                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14649                        ..Default::default()
14650                    },
14651                )]
14652                .into_iter()
14653                .collect(),
14654                ..Default::default()
14655            },
14656            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14657        )
14658        .with_override_query("(jsx_self_closing_element) @element")
14659        .unwrap(),
14660        lsp::ServerCapabilities {
14661            completion_provider: Some(lsp::CompletionOptions {
14662                trigger_characters: Some(vec![":".to_string()]),
14663                ..Default::default()
14664            }),
14665            ..Default::default()
14666        },
14667        cx,
14668    )
14669    .await;
14670
14671    cx.lsp
14672        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14673            Ok(Some(lsp::CompletionResponse::Array(vec![
14674                lsp::CompletionItem {
14675                    label: "bg-blue".into(),
14676                    ..Default::default()
14677                },
14678                lsp::CompletionItem {
14679                    label: "bg-red".into(),
14680                    ..Default::default()
14681                },
14682                lsp::CompletionItem {
14683                    label: "bg-yellow".into(),
14684                    ..Default::default()
14685                },
14686            ])))
14687        });
14688
14689    cx.set_state(r#"<p class="bgˇ" />"#);
14690
14691    // Trigger completion when typing a dash, because the dash is an extra
14692    // word character in the 'element' scope, which contains the cursor.
14693    cx.simulate_keystroke("-");
14694    cx.executor().run_until_parked();
14695    cx.update_editor(|editor, _, _| {
14696        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14697        {
14698            assert_eq!(
14699                completion_menu_entries(&menu),
14700                &["bg-red", "bg-blue", "bg-yellow"]
14701            );
14702        } else {
14703            panic!("expected completion menu to be open");
14704        }
14705    });
14706
14707    cx.simulate_keystroke("l");
14708    cx.executor().run_until_parked();
14709    cx.update_editor(|editor, _, _| {
14710        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14711        {
14712            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14713        } else {
14714            panic!("expected completion menu to be open");
14715        }
14716    });
14717
14718    // When filtering completions, consider the character after the '-' to
14719    // be the start of a subword.
14720    cx.set_state(r#"<p class="yelˇ" />"#);
14721    cx.simulate_keystroke("l");
14722    cx.executor().run_until_parked();
14723    cx.update_editor(|editor, _, _| {
14724        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14725        {
14726            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14727        } else {
14728            panic!("expected completion menu to be open");
14729        }
14730    });
14731}
14732
14733fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14734    let entries = menu.entries.borrow();
14735    entries.iter().map(|mat| mat.string.clone()).collect()
14736}
14737
14738#[gpui::test]
14739async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14740    init_test(cx, |settings| {
14741        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14742            FormatterList(vec![Formatter::Prettier].into()),
14743        ))
14744    });
14745
14746    let fs = FakeFs::new(cx.executor());
14747    fs.insert_file(path!("/file.ts"), Default::default()).await;
14748
14749    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14750    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14751
14752    language_registry.add(Arc::new(Language::new(
14753        LanguageConfig {
14754            name: "TypeScript".into(),
14755            matcher: LanguageMatcher {
14756                path_suffixes: vec!["ts".to_string()],
14757                ..Default::default()
14758            },
14759            ..Default::default()
14760        },
14761        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14762    )));
14763    update_test_language_settings(cx, |settings| {
14764        settings.defaults.prettier = Some(PrettierSettings {
14765            allowed: true,
14766            ..PrettierSettings::default()
14767        });
14768    });
14769
14770    let test_plugin = "test_plugin";
14771    let _ = language_registry.register_fake_lsp(
14772        "TypeScript",
14773        FakeLspAdapter {
14774            prettier_plugins: vec![test_plugin],
14775            ..Default::default()
14776        },
14777    );
14778
14779    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14780    let buffer = project
14781        .update(cx, |project, cx| {
14782            project.open_local_buffer(path!("/file.ts"), cx)
14783        })
14784        .await
14785        .unwrap();
14786
14787    let buffer_text = "one\ntwo\nthree\n";
14788    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14789    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14790    editor.update_in(cx, |editor, window, cx| {
14791        editor.set_text(buffer_text, window, cx)
14792    });
14793
14794    editor
14795        .update_in(cx, |editor, window, cx| {
14796            editor.perform_format(
14797                project.clone(),
14798                FormatTrigger::Manual,
14799                FormatTarget::Buffers,
14800                window,
14801                cx,
14802            )
14803        })
14804        .unwrap()
14805        .await;
14806    assert_eq!(
14807        editor.update(cx, |editor, cx| editor.text(cx)),
14808        buffer_text.to_string() + prettier_format_suffix,
14809        "Test prettier formatting was not applied to the original buffer text",
14810    );
14811
14812    update_test_language_settings(cx, |settings| {
14813        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14814    });
14815    let format = editor.update_in(cx, |editor, window, cx| {
14816        editor.perform_format(
14817            project.clone(),
14818            FormatTrigger::Manual,
14819            FormatTarget::Buffers,
14820            window,
14821            cx,
14822        )
14823    });
14824    format.await.unwrap();
14825    assert_eq!(
14826        editor.update(cx, |editor, cx| editor.text(cx)),
14827        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14828        "Autoformatting (via test prettier) was not applied to the original buffer text",
14829    );
14830}
14831
14832#[gpui::test]
14833async fn test_addition_reverts(cx: &mut TestAppContext) {
14834    init_test(cx, |_| {});
14835    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14836    let base_text = indoc! {r#"
14837        struct Row;
14838        struct Row1;
14839        struct Row2;
14840
14841        struct Row4;
14842        struct Row5;
14843        struct Row6;
14844
14845        struct Row8;
14846        struct Row9;
14847        struct Row10;"#};
14848
14849    // When addition hunks are not adjacent to carets, no hunk revert is performed
14850    assert_hunk_revert(
14851        indoc! {r#"struct Row;
14852                   struct Row1;
14853                   struct Row1.1;
14854                   struct Row1.2;
14855                   struct Row2;ˇ
14856
14857                   struct Row4;
14858                   struct Row5;
14859                   struct Row6;
14860
14861                   struct Row8;
14862                   ˇstruct Row9;
14863                   struct Row9.1;
14864                   struct Row9.2;
14865                   struct Row9.3;
14866                   struct Row10;"#},
14867        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14868        indoc! {r#"struct Row;
14869                   struct Row1;
14870                   struct Row1.1;
14871                   struct Row1.2;
14872                   struct Row2;ˇ
14873
14874                   struct Row4;
14875                   struct Row5;
14876                   struct Row6;
14877
14878                   struct Row8;
14879                   ˇstruct Row9;
14880                   struct Row9.1;
14881                   struct Row9.2;
14882                   struct Row9.3;
14883                   struct Row10;"#},
14884        base_text,
14885        &mut cx,
14886    );
14887    // Same for selections
14888    assert_hunk_revert(
14889        indoc! {r#"struct Row;
14890                   struct Row1;
14891                   struct Row2;
14892                   struct Row2.1;
14893                   struct Row2.2;
14894                   «ˇ
14895                   struct Row4;
14896                   struct» Row5;
14897                   «struct Row6;
14898                   ˇ»
14899                   struct Row9.1;
14900                   struct Row9.2;
14901                   struct Row9.3;
14902                   struct Row8;
14903                   struct Row9;
14904                   struct Row10;"#},
14905        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14906        indoc! {r#"struct Row;
14907                   struct Row1;
14908                   struct Row2;
14909                   struct Row2.1;
14910                   struct Row2.2;
14911                   «ˇ
14912                   struct Row4;
14913                   struct» Row5;
14914                   «struct Row6;
14915                   ˇ»
14916                   struct Row9.1;
14917                   struct Row9.2;
14918                   struct Row9.3;
14919                   struct Row8;
14920                   struct Row9;
14921                   struct Row10;"#},
14922        base_text,
14923        &mut cx,
14924    );
14925
14926    // When carets and selections intersect the addition hunks, those are reverted.
14927    // Adjacent carets got merged.
14928    assert_hunk_revert(
14929        indoc! {r#"struct Row;
14930                   ˇ// something on the top
14931                   struct Row1;
14932                   struct Row2;
14933                   struct Roˇw3.1;
14934                   struct Row2.2;
14935                   struct Row2.3;ˇ
14936
14937                   struct Row4;
14938                   struct ˇRow5.1;
14939                   struct Row5.2;
14940                   struct «Rowˇ»5.3;
14941                   struct Row5;
14942                   struct Row6;
14943                   ˇ
14944                   struct Row9.1;
14945                   struct «Rowˇ»9.2;
14946                   struct «ˇRow»9.3;
14947                   struct Row8;
14948                   struct Row9;
14949                   «ˇ// something on bottom»
14950                   struct Row10;"#},
14951        vec![
14952            DiffHunkStatusKind::Added,
14953            DiffHunkStatusKind::Added,
14954            DiffHunkStatusKind::Added,
14955            DiffHunkStatusKind::Added,
14956            DiffHunkStatusKind::Added,
14957        ],
14958        indoc! {r#"struct Row;
14959                   ˇstruct Row1;
14960                   struct Row2;
14961                   ˇ
14962                   struct Row4;
14963                   ˇstruct Row5;
14964                   struct Row6;
14965                   ˇ
14966                   ˇstruct Row8;
14967                   struct Row9;
14968                   ˇstruct Row10;"#},
14969        base_text,
14970        &mut cx,
14971    );
14972}
14973
14974#[gpui::test]
14975async fn test_modification_reverts(cx: &mut TestAppContext) {
14976    init_test(cx, |_| {});
14977    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14978    let base_text = indoc! {r#"
14979        struct Row;
14980        struct Row1;
14981        struct Row2;
14982
14983        struct Row4;
14984        struct Row5;
14985        struct Row6;
14986
14987        struct Row8;
14988        struct Row9;
14989        struct Row10;"#};
14990
14991    // Modification hunks behave the same as the addition ones.
14992    assert_hunk_revert(
14993        indoc! {r#"struct Row;
14994                   struct Row1;
14995                   struct Row33;
14996                   ˇ
14997                   struct Row4;
14998                   struct Row5;
14999                   struct Row6;
15000                   ˇ
15001                   struct Row99;
15002                   struct Row9;
15003                   struct Row10;"#},
15004        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15005        indoc! {r#"struct Row;
15006                   struct Row1;
15007                   struct Row33;
15008                   ˇ
15009                   struct Row4;
15010                   struct Row5;
15011                   struct Row6;
15012                   ˇ
15013                   struct Row99;
15014                   struct Row9;
15015                   struct Row10;"#},
15016        base_text,
15017        &mut cx,
15018    );
15019    assert_hunk_revert(
15020        indoc! {r#"struct Row;
15021                   struct Row1;
15022                   struct Row33;
15023                   «ˇ
15024                   struct Row4;
15025                   struct» Row5;
15026                   «struct Row6;
15027                   ˇ»
15028                   struct Row99;
15029                   struct Row9;
15030                   struct Row10;"#},
15031        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15032        indoc! {r#"struct Row;
15033                   struct Row1;
15034                   struct Row33;
15035                   «ˇ
15036                   struct Row4;
15037                   struct» Row5;
15038                   «struct Row6;
15039                   ˇ»
15040                   struct Row99;
15041                   struct Row9;
15042                   struct Row10;"#},
15043        base_text,
15044        &mut cx,
15045    );
15046
15047    assert_hunk_revert(
15048        indoc! {r#"ˇstruct Row1.1;
15049                   struct Row1;
15050                   «ˇstr»uct Row22;
15051
15052                   struct ˇRow44;
15053                   struct Row5;
15054                   struct «Rˇ»ow66;ˇ
15055
15056                   «struˇ»ct Row88;
15057                   struct Row9;
15058                   struct Row1011;ˇ"#},
15059        vec![
15060            DiffHunkStatusKind::Modified,
15061            DiffHunkStatusKind::Modified,
15062            DiffHunkStatusKind::Modified,
15063            DiffHunkStatusKind::Modified,
15064            DiffHunkStatusKind::Modified,
15065            DiffHunkStatusKind::Modified,
15066        ],
15067        indoc! {r#"struct Row;
15068                   ˇstruct Row1;
15069                   struct Row2;
15070                   ˇ
15071                   struct Row4;
15072                   ˇstruct Row5;
15073                   struct Row6;
15074                   ˇ
15075                   struct Row8;
15076                   ˇstruct Row9;
15077                   struct Row10;ˇ"#},
15078        base_text,
15079        &mut cx,
15080    );
15081}
15082
15083#[gpui::test]
15084async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
15085    init_test(cx, |_| {});
15086    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15087    let base_text = indoc! {r#"
15088        one
15089
15090        two
15091        three
15092        "#};
15093
15094    cx.set_head_text(base_text);
15095    cx.set_state("\nˇ\n");
15096    cx.executor().run_until_parked();
15097    cx.update_editor(|editor, _window, cx| {
15098        editor.expand_selected_diff_hunks(cx);
15099    });
15100    cx.executor().run_until_parked();
15101    cx.update_editor(|editor, window, cx| {
15102        editor.backspace(&Default::default(), window, cx);
15103    });
15104    cx.run_until_parked();
15105    cx.assert_state_with_diff(
15106        indoc! {r#"
15107
15108        - two
15109        - threeˇ
15110        +
15111        "#}
15112        .to_string(),
15113    );
15114}
15115
15116#[gpui::test]
15117async fn test_deletion_reverts(cx: &mut TestAppContext) {
15118    init_test(cx, |_| {});
15119    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15120    let base_text = indoc! {r#"struct Row;
15121struct Row1;
15122struct Row2;
15123
15124struct Row4;
15125struct Row5;
15126struct Row6;
15127
15128struct Row8;
15129struct Row9;
15130struct Row10;"#};
15131
15132    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
15133    assert_hunk_revert(
15134        indoc! {r#"struct Row;
15135                   struct Row2;
15136
15137                   ˇstruct Row4;
15138                   struct Row5;
15139                   struct Row6;
15140                   ˇ
15141                   struct Row8;
15142                   struct Row10;"#},
15143        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15144        indoc! {r#"struct Row;
15145                   struct Row2;
15146
15147                   ˇstruct Row4;
15148                   struct Row5;
15149                   struct Row6;
15150                   ˇ
15151                   struct Row8;
15152                   struct Row10;"#},
15153        base_text,
15154        &mut cx,
15155    );
15156    assert_hunk_revert(
15157        indoc! {r#"struct Row;
15158                   struct Row2;
15159
15160                   «ˇstruct Row4;
15161                   struct» Row5;
15162                   «struct Row6;
15163                   ˇ»
15164                   struct Row8;
15165                   struct Row10;"#},
15166        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15167        indoc! {r#"struct Row;
15168                   struct Row2;
15169
15170                   «ˇstruct Row4;
15171                   struct» Row5;
15172                   «struct Row6;
15173                   ˇ»
15174                   struct Row8;
15175                   struct Row10;"#},
15176        base_text,
15177        &mut cx,
15178    );
15179
15180    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15181    assert_hunk_revert(
15182        indoc! {r#"struct Row;
15183                   ˇstruct Row2;
15184
15185                   struct Row4;
15186                   struct Row5;
15187                   struct Row6;
15188
15189                   struct Row8;ˇ
15190                   struct Row10;"#},
15191        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15192        indoc! {r#"struct Row;
15193                   struct Row1;
15194                   ˇstruct Row2;
15195
15196                   struct Row4;
15197                   struct Row5;
15198                   struct Row6;
15199
15200                   struct Row8;ˇ
15201                   struct Row9;
15202                   struct Row10;"#},
15203        base_text,
15204        &mut cx,
15205    );
15206    assert_hunk_revert(
15207        indoc! {r#"struct Row;
15208                   struct Row2«ˇ;
15209                   struct Row4;
15210                   struct» Row5;
15211                   «struct Row6;
15212
15213                   struct Row8;ˇ»
15214                   struct Row10;"#},
15215        vec![
15216            DiffHunkStatusKind::Deleted,
15217            DiffHunkStatusKind::Deleted,
15218            DiffHunkStatusKind::Deleted,
15219        ],
15220        indoc! {r#"struct Row;
15221                   struct Row1;
15222                   struct Row2«ˇ;
15223
15224                   struct Row4;
15225                   struct» Row5;
15226                   «struct Row6;
15227
15228                   struct Row8;ˇ»
15229                   struct Row9;
15230                   struct Row10;"#},
15231        base_text,
15232        &mut cx,
15233    );
15234}
15235
15236#[gpui::test]
15237async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15238    init_test(cx, |_| {});
15239
15240    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15241    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15242    let base_text_3 =
15243        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15244
15245    let text_1 = edit_first_char_of_every_line(base_text_1);
15246    let text_2 = edit_first_char_of_every_line(base_text_2);
15247    let text_3 = edit_first_char_of_every_line(base_text_3);
15248
15249    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15250    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15251    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15252
15253    let multibuffer = cx.new(|cx| {
15254        let mut multibuffer = MultiBuffer::new(ReadWrite);
15255        multibuffer.push_excerpts(
15256            buffer_1.clone(),
15257            [
15258                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15259                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15260                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15261            ],
15262            cx,
15263        );
15264        multibuffer.push_excerpts(
15265            buffer_2.clone(),
15266            [
15267                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15268                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15269                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15270            ],
15271            cx,
15272        );
15273        multibuffer.push_excerpts(
15274            buffer_3.clone(),
15275            [
15276                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15277                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15278                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15279            ],
15280            cx,
15281        );
15282        multibuffer
15283    });
15284
15285    let fs = FakeFs::new(cx.executor());
15286    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15287    let (editor, cx) = cx
15288        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15289    editor.update_in(cx, |editor, _window, cx| {
15290        for (buffer, diff_base) in [
15291            (buffer_1.clone(), base_text_1),
15292            (buffer_2.clone(), base_text_2),
15293            (buffer_3.clone(), base_text_3),
15294        ] {
15295            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15296            editor
15297                .buffer
15298                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15299        }
15300    });
15301    cx.executor().run_until_parked();
15302
15303    editor.update_in(cx, |editor, window, cx| {
15304        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}");
15305        editor.select_all(&SelectAll, window, cx);
15306        editor.git_restore(&Default::default(), window, cx);
15307    });
15308    cx.executor().run_until_parked();
15309
15310    // When all ranges are selected, all buffer hunks are reverted.
15311    editor.update(cx, |editor, cx| {
15312        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");
15313    });
15314    buffer_1.update(cx, |buffer, _| {
15315        assert_eq!(buffer.text(), base_text_1);
15316    });
15317    buffer_2.update(cx, |buffer, _| {
15318        assert_eq!(buffer.text(), base_text_2);
15319    });
15320    buffer_3.update(cx, |buffer, _| {
15321        assert_eq!(buffer.text(), base_text_3);
15322    });
15323
15324    editor.update_in(cx, |editor, window, cx| {
15325        editor.undo(&Default::default(), window, cx);
15326    });
15327
15328    editor.update_in(cx, |editor, window, cx| {
15329        editor.change_selections(None, window, cx, |s| {
15330            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15331        });
15332        editor.git_restore(&Default::default(), window, cx);
15333    });
15334
15335    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15336    // but not affect buffer_2 and its related excerpts.
15337    editor.update(cx, |editor, cx| {
15338        assert_eq!(
15339            editor.text(cx),
15340            "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}"
15341        );
15342    });
15343    buffer_1.update(cx, |buffer, _| {
15344        assert_eq!(buffer.text(), base_text_1);
15345    });
15346    buffer_2.update(cx, |buffer, _| {
15347        assert_eq!(
15348            buffer.text(),
15349            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15350        );
15351    });
15352    buffer_3.update(cx, |buffer, _| {
15353        assert_eq!(
15354            buffer.text(),
15355            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15356        );
15357    });
15358
15359    fn edit_first_char_of_every_line(text: &str) -> String {
15360        text.split('\n')
15361            .map(|line| format!("X{}", &line[1..]))
15362            .collect::<Vec<_>>()
15363            .join("\n")
15364    }
15365}
15366
15367#[gpui::test]
15368async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15369    init_test(cx, |_| {});
15370
15371    let cols = 4;
15372    let rows = 10;
15373    let sample_text_1 = sample_text(rows, cols, 'a');
15374    assert_eq!(
15375        sample_text_1,
15376        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15377    );
15378    let sample_text_2 = sample_text(rows, cols, 'l');
15379    assert_eq!(
15380        sample_text_2,
15381        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15382    );
15383    let sample_text_3 = sample_text(rows, cols, 'v');
15384    assert_eq!(
15385        sample_text_3,
15386        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15387    );
15388
15389    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15390    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15391    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15392
15393    let multi_buffer = cx.new(|cx| {
15394        let mut multibuffer = MultiBuffer::new(ReadWrite);
15395        multibuffer.push_excerpts(
15396            buffer_1.clone(),
15397            [
15398                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15399                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15400                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15401            ],
15402            cx,
15403        );
15404        multibuffer.push_excerpts(
15405            buffer_2.clone(),
15406            [
15407                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15408                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15409                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15410            ],
15411            cx,
15412        );
15413        multibuffer.push_excerpts(
15414            buffer_3.clone(),
15415            [
15416                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15417                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15418                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15419            ],
15420            cx,
15421        );
15422        multibuffer
15423    });
15424
15425    let fs = FakeFs::new(cx.executor());
15426    fs.insert_tree(
15427        "/a",
15428        json!({
15429            "main.rs": sample_text_1,
15430            "other.rs": sample_text_2,
15431            "lib.rs": sample_text_3,
15432        }),
15433    )
15434    .await;
15435    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15436    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15437    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15438    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15439        Editor::new(
15440            EditorMode::full(),
15441            multi_buffer,
15442            Some(project.clone()),
15443            window,
15444            cx,
15445        )
15446    });
15447    let multibuffer_item_id = workspace
15448        .update(cx, |workspace, window, cx| {
15449            assert!(
15450                workspace.active_item(cx).is_none(),
15451                "active item should be None before the first item is added"
15452            );
15453            workspace.add_item_to_active_pane(
15454                Box::new(multi_buffer_editor.clone()),
15455                None,
15456                true,
15457                window,
15458                cx,
15459            );
15460            let active_item = workspace
15461                .active_item(cx)
15462                .expect("should have an active item after adding the multi buffer");
15463            assert!(
15464                !active_item.is_singleton(cx),
15465                "A multi buffer was expected to active after adding"
15466            );
15467            active_item.item_id()
15468        })
15469        .unwrap();
15470    cx.executor().run_until_parked();
15471
15472    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15473        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15474            s.select_ranges(Some(1..2))
15475        });
15476        editor.open_excerpts(&OpenExcerpts, window, cx);
15477    });
15478    cx.executor().run_until_parked();
15479    let first_item_id = workspace
15480        .update(cx, |workspace, window, cx| {
15481            let active_item = workspace
15482                .active_item(cx)
15483                .expect("should have an active item after navigating into the 1st buffer");
15484            let first_item_id = active_item.item_id();
15485            assert_ne!(
15486                first_item_id, multibuffer_item_id,
15487                "Should navigate into the 1st buffer and activate it"
15488            );
15489            assert!(
15490                active_item.is_singleton(cx),
15491                "New active item should be a singleton buffer"
15492            );
15493            assert_eq!(
15494                active_item
15495                    .act_as::<Editor>(cx)
15496                    .expect("should have navigated into an editor for the 1st buffer")
15497                    .read(cx)
15498                    .text(cx),
15499                sample_text_1
15500            );
15501
15502            workspace
15503                .go_back(workspace.active_pane().downgrade(), window, cx)
15504                .detach_and_log_err(cx);
15505
15506            first_item_id
15507        })
15508        .unwrap();
15509    cx.executor().run_until_parked();
15510    workspace
15511        .update(cx, |workspace, _, cx| {
15512            let active_item = workspace
15513                .active_item(cx)
15514                .expect("should have an active item after navigating back");
15515            assert_eq!(
15516                active_item.item_id(),
15517                multibuffer_item_id,
15518                "Should navigate back to the multi buffer"
15519            );
15520            assert!(!active_item.is_singleton(cx));
15521        })
15522        .unwrap();
15523
15524    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15525        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15526            s.select_ranges(Some(39..40))
15527        });
15528        editor.open_excerpts(&OpenExcerpts, window, cx);
15529    });
15530    cx.executor().run_until_parked();
15531    let second_item_id = workspace
15532        .update(cx, |workspace, window, cx| {
15533            let active_item = workspace
15534                .active_item(cx)
15535                .expect("should have an active item after navigating into the 2nd buffer");
15536            let second_item_id = active_item.item_id();
15537            assert_ne!(
15538                second_item_id, multibuffer_item_id,
15539                "Should navigate away from the multibuffer"
15540            );
15541            assert_ne!(
15542                second_item_id, first_item_id,
15543                "Should navigate into the 2nd buffer and activate it"
15544            );
15545            assert!(
15546                active_item.is_singleton(cx),
15547                "New active item should be a singleton buffer"
15548            );
15549            assert_eq!(
15550                active_item
15551                    .act_as::<Editor>(cx)
15552                    .expect("should have navigated into an editor")
15553                    .read(cx)
15554                    .text(cx),
15555                sample_text_2
15556            );
15557
15558            workspace
15559                .go_back(workspace.active_pane().downgrade(), window, cx)
15560                .detach_and_log_err(cx);
15561
15562            second_item_id
15563        })
15564        .unwrap();
15565    cx.executor().run_until_parked();
15566    workspace
15567        .update(cx, |workspace, _, cx| {
15568            let active_item = workspace
15569                .active_item(cx)
15570                .expect("should have an active item after navigating back from the 2nd buffer");
15571            assert_eq!(
15572                active_item.item_id(),
15573                multibuffer_item_id,
15574                "Should navigate back from the 2nd buffer to the multi buffer"
15575            );
15576            assert!(!active_item.is_singleton(cx));
15577        })
15578        .unwrap();
15579
15580    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15581        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15582            s.select_ranges(Some(70..70))
15583        });
15584        editor.open_excerpts(&OpenExcerpts, window, cx);
15585    });
15586    cx.executor().run_until_parked();
15587    workspace
15588        .update(cx, |workspace, window, cx| {
15589            let active_item = workspace
15590                .active_item(cx)
15591                .expect("should have an active item after navigating into the 3rd buffer");
15592            let third_item_id = active_item.item_id();
15593            assert_ne!(
15594                third_item_id, multibuffer_item_id,
15595                "Should navigate into the 3rd buffer and activate it"
15596            );
15597            assert_ne!(third_item_id, first_item_id);
15598            assert_ne!(third_item_id, second_item_id);
15599            assert!(
15600                active_item.is_singleton(cx),
15601                "New active item should be a singleton buffer"
15602            );
15603            assert_eq!(
15604                active_item
15605                    .act_as::<Editor>(cx)
15606                    .expect("should have navigated into an editor")
15607                    .read(cx)
15608                    .text(cx),
15609                sample_text_3
15610            );
15611
15612            workspace
15613                .go_back(workspace.active_pane().downgrade(), window, cx)
15614                .detach_and_log_err(cx);
15615        })
15616        .unwrap();
15617    cx.executor().run_until_parked();
15618    workspace
15619        .update(cx, |workspace, _, cx| {
15620            let active_item = workspace
15621                .active_item(cx)
15622                .expect("should have an active item after navigating back from the 3rd buffer");
15623            assert_eq!(
15624                active_item.item_id(),
15625                multibuffer_item_id,
15626                "Should navigate back from the 3rd buffer to the multi buffer"
15627            );
15628            assert!(!active_item.is_singleton(cx));
15629        })
15630        .unwrap();
15631}
15632
15633#[gpui::test]
15634async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15635    init_test(cx, |_| {});
15636
15637    let mut cx = EditorTestContext::new(cx).await;
15638
15639    let diff_base = r#"
15640        use some::mod;
15641
15642        const A: u32 = 42;
15643
15644        fn main() {
15645            println!("hello");
15646
15647            println!("world");
15648        }
15649        "#
15650    .unindent();
15651
15652    cx.set_state(
15653        &r#"
15654        use some::modified;
15655
15656        ˇ
15657        fn main() {
15658            println!("hello there");
15659
15660            println!("around the");
15661            println!("world");
15662        }
15663        "#
15664        .unindent(),
15665    );
15666
15667    cx.set_head_text(&diff_base);
15668    executor.run_until_parked();
15669
15670    cx.update_editor(|editor, window, cx| {
15671        editor.go_to_next_hunk(&GoToHunk, window, cx);
15672        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15673    });
15674    executor.run_until_parked();
15675    cx.assert_state_with_diff(
15676        r#"
15677          use some::modified;
15678
15679
15680          fn main() {
15681        -     println!("hello");
15682        + ˇ    println!("hello there");
15683
15684              println!("around the");
15685              println!("world");
15686          }
15687        "#
15688        .unindent(),
15689    );
15690
15691    cx.update_editor(|editor, window, cx| {
15692        for _ in 0..2 {
15693            editor.go_to_next_hunk(&GoToHunk, window, cx);
15694            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15695        }
15696    });
15697    executor.run_until_parked();
15698    cx.assert_state_with_diff(
15699        r#"
15700        - use some::mod;
15701        + ˇuse some::modified;
15702
15703
15704          fn main() {
15705        -     println!("hello");
15706        +     println!("hello there");
15707
15708        +     println!("around the");
15709              println!("world");
15710          }
15711        "#
15712        .unindent(),
15713    );
15714
15715    cx.update_editor(|editor, window, cx| {
15716        editor.go_to_next_hunk(&GoToHunk, window, cx);
15717        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15718    });
15719    executor.run_until_parked();
15720    cx.assert_state_with_diff(
15721        r#"
15722        - use some::mod;
15723        + use some::modified;
15724
15725        - const A: u32 = 42;
15726          ˇ
15727          fn main() {
15728        -     println!("hello");
15729        +     println!("hello there");
15730
15731        +     println!("around the");
15732              println!("world");
15733          }
15734        "#
15735        .unindent(),
15736    );
15737
15738    cx.update_editor(|editor, window, cx| {
15739        editor.cancel(&Cancel, window, cx);
15740    });
15741
15742    cx.assert_state_with_diff(
15743        r#"
15744          use some::modified;
15745
15746          ˇ
15747          fn main() {
15748              println!("hello there");
15749
15750              println!("around the");
15751              println!("world");
15752          }
15753        "#
15754        .unindent(),
15755    );
15756}
15757
15758#[gpui::test]
15759async fn test_diff_base_change_with_expanded_diff_hunks(
15760    executor: BackgroundExecutor,
15761    cx: &mut TestAppContext,
15762) {
15763    init_test(cx, |_| {});
15764
15765    let mut cx = EditorTestContext::new(cx).await;
15766
15767    let diff_base = r#"
15768        use some::mod1;
15769        use some::mod2;
15770
15771        const A: u32 = 42;
15772        const B: u32 = 42;
15773        const C: u32 = 42;
15774
15775        fn main() {
15776            println!("hello");
15777
15778            println!("world");
15779        }
15780        "#
15781    .unindent();
15782
15783    cx.set_state(
15784        &r#"
15785        use some::mod2;
15786
15787        const A: u32 = 42;
15788        const C: u32 = 42;
15789
15790        fn main(ˇ) {
15791            //println!("hello");
15792
15793            println!("world");
15794            //
15795            //
15796        }
15797        "#
15798        .unindent(),
15799    );
15800
15801    cx.set_head_text(&diff_base);
15802    executor.run_until_parked();
15803
15804    cx.update_editor(|editor, window, cx| {
15805        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15806    });
15807    executor.run_until_parked();
15808    cx.assert_state_with_diff(
15809        r#"
15810        - use some::mod1;
15811          use some::mod2;
15812
15813          const A: u32 = 42;
15814        - const B: u32 = 42;
15815          const C: u32 = 42;
15816
15817          fn main(ˇ) {
15818        -     println!("hello");
15819        +     //println!("hello");
15820
15821              println!("world");
15822        +     //
15823        +     //
15824          }
15825        "#
15826        .unindent(),
15827    );
15828
15829    cx.set_head_text("new diff base!");
15830    executor.run_until_parked();
15831    cx.assert_state_with_diff(
15832        r#"
15833        - new diff base!
15834        + use some::mod2;
15835        +
15836        + const A: u32 = 42;
15837        + const C: u32 = 42;
15838        +
15839        + fn main(ˇ) {
15840        +     //println!("hello");
15841        +
15842        +     println!("world");
15843        +     //
15844        +     //
15845        + }
15846        "#
15847        .unindent(),
15848    );
15849}
15850
15851#[gpui::test]
15852async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15853    init_test(cx, |_| {});
15854
15855    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15856    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15857    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15858    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15859    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15860    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15861
15862    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15863    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15864    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15865
15866    let multi_buffer = cx.new(|cx| {
15867        let mut multibuffer = MultiBuffer::new(ReadWrite);
15868        multibuffer.push_excerpts(
15869            buffer_1.clone(),
15870            [
15871                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15872                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15873                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15874            ],
15875            cx,
15876        );
15877        multibuffer.push_excerpts(
15878            buffer_2.clone(),
15879            [
15880                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15881                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15882                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15883            ],
15884            cx,
15885        );
15886        multibuffer.push_excerpts(
15887            buffer_3.clone(),
15888            [
15889                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15890                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15891                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15892            ],
15893            cx,
15894        );
15895        multibuffer
15896    });
15897
15898    let editor =
15899        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15900    editor
15901        .update(cx, |editor, _window, cx| {
15902            for (buffer, diff_base) in [
15903                (buffer_1.clone(), file_1_old),
15904                (buffer_2.clone(), file_2_old),
15905                (buffer_3.clone(), file_3_old),
15906            ] {
15907                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15908                editor
15909                    .buffer
15910                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15911            }
15912        })
15913        .unwrap();
15914
15915    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15916    cx.run_until_parked();
15917
15918    cx.assert_editor_state(
15919        &"
15920            ˇaaa
15921            ccc
15922            ddd
15923
15924            ggg
15925            hhh
15926
15927
15928            lll
15929            mmm
15930            NNN
15931
15932            qqq
15933            rrr
15934
15935            uuu
15936            111
15937            222
15938            333
15939
15940            666
15941            777
15942
15943            000
15944            !!!"
15945        .unindent(),
15946    );
15947
15948    cx.update_editor(|editor, window, cx| {
15949        editor.select_all(&SelectAll, window, cx);
15950        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15951    });
15952    cx.executor().run_until_parked();
15953
15954    cx.assert_state_with_diff(
15955        "
15956            «aaa
15957          - bbb
15958            ccc
15959            ddd
15960
15961            ggg
15962            hhh
15963
15964
15965            lll
15966            mmm
15967          - nnn
15968          + NNN
15969
15970            qqq
15971            rrr
15972
15973            uuu
15974            111
15975            222
15976            333
15977
15978          + 666
15979            777
15980
15981            000
15982            !!!ˇ»"
15983            .unindent(),
15984    );
15985}
15986
15987#[gpui::test]
15988async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15989    init_test(cx, |_| {});
15990
15991    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15992    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15993
15994    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15995    let multi_buffer = cx.new(|cx| {
15996        let mut multibuffer = MultiBuffer::new(ReadWrite);
15997        multibuffer.push_excerpts(
15998            buffer.clone(),
15999            [
16000                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
16001                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
16002                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
16003            ],
16004            cx,
16005        );
16006        multibuffer
16007    });
16008
16009    let editor =
16010        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
16011    editor
16012        .update(cx, |editor, _window, cx| {
16013            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
16014            editor
16015                .buffer
16016                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
16017        })
16018        .unwrap();
16019
16020    let mut cx = EditorTestContext::for_editor(editor, cx).await;
16021    cx.run_until_parked();
16022
16023    cx.update_editor(|editor, window, cx| {
16024        editor.expand_all_diff_hunks(&Default::default(), window, cx)
16025    });
16026    cx.executor().run_until_parked();
16027
16028    // When the start of a hunk coincides with the start of its excerpt,
16029    // the hunk is expanded. When the start of a a hunk is earlier than
16030    // the start of its excerpt, the hunk is not expanded.
16031    cx.assert_state_with_diff(
16032        "
16033            ˇaaa
16034          - bbb
16035          + BBB
16036
16037          - ddd
16038          - eee
16039          + DDD
16040          + EEE
16041            fff
16042
16043            iii
16044        "
16045        .unindent(),
16046    );
16047}
16048
16049#[gpui::test]
16050async fn test_edits_around_expanded_insertion_hunks(
16051    executor: BackgroundExecutor,
16052    cx: &mut TestAppContext,
16053) {
16054    init_test(cx, |_| {});
16055
16056    let mut cx = EditorTestContext::new(cx).await;
16057
16058    let diff_base = r#"
16059        use some::mod1;
16060        use some::mod2;
16061
16062        const A: u32 = 42;
16063
16064        fn main() {
16065            println!("hello");
16066
16067            println!("world");
16068        }
16069        "#
16070    .unindent();
16071    executor.run_until_parked();
16072    cx.set_state(
16073        &r#"
16074        use some::mod1;
16075        use some::mod2;
16076
16077        const A: u32 = 42;
16078        const B: u32 = 42;
16079        const C: u32 = 42;
16080        ˇ
16081
16082        fn main() {
16083            println!("hello");
16084
16085            println!("world");
16086        }
16087        "#
16088        .unindent(),
16089    );
16090
16091    cx.set_head_text(&diff_base);
16092    executor.run_until_parked();
16093
16094    cx.update_editor(|editor, window, cx| {
16095        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16096    });
16097    executor.run_until_parked();
16098
16099    cx.assert_state_with_diff(
16100        r#"
16101        use some::mod1;
16102        use some::mod2;
16103
16104        const A: u32 = 42;
16105      + const B: u32 = 42;
16106      + const C: u32 = 42;
16107      + ˇ
16108
16109        fn main() {
16110            println!("hello");
16111
16112            println!("world");
16113        }
16114      "#
16115        .unindent(),
16116    );
16117
16118    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
16119    executor.run_until_parked();
16120
16121    cx.assert_state_with_diff(
16122        r#"
16123        use some::mod1;
16124        use some::mod2;
16125
16126        const A: u32 = 42;
16127      + const B: u32 = 42;
16128      + const C: u32 = 42;
16129      + const D: u32 = 42;
16130      + ˇ
16131
16132        fn main() {
16133            println!("hello");
16134
16135            println!("world");
16136        }
16137      "#
16138        .unindent(),
16139    );
16140
16141    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
16142    executor.run_until_parked();
16143
16144    cx.assert_state_with_diff(
16145        r#"
16146        use some::mod1;
16147        use some::mod2;
16148
16149        const A: u32 = 42;
16150      + const B: u32 = 42;
16151      + const C: u32 = 42;
16152      + const D: u32 = 42;
16153      + const E: u32 = 42;
16154      + ˇ
16155
16156        fn main() {
16157            println!("hello");
16158
16159            println!("world");
16160        }
16161      "#
16162        .unindent(),
16163    );
16164
16165    cx.update_editor(|editor, window, cx| {
16166        editor.delete_line(&DeleteLine, window, cx);
16167    });
16168    executor.run_until_parked();
16169
16170    cx.assert_state_with_diff(
16171        r#"
16172        use some::mod1;
16173        use some::mod2;
16174
16175        const A: u32 = 42;
16176      + const B: u32 = 42;
16177      + const C: u32 = 42;
16178      + const D: u32 = 42;
16179      + const E: u32 = 42;
16180        ˇ
16181        fn main() {
16182            println!("hello");
16183
16184            println!("world");
16185        }
16186      "#
16187        .unindent(),
16188    );
16189
16190    cx.update_editor(|editor, window, cx| {
16191        editor.move_up(&MoveUp, window, cx);
16192        editor.delete_line(&DeleteLine, window, cx);
16193        editor.move_up(&MoveUp, window, cx);
16194        editor.delete_line(&DeleteLine, window, cx);
16195        editor.move_up(&MoveUp, window, cx);
16196        editor.delete_line(&DeleteLine, window, cx);
16197    });
16198    executor.run_until_parked();
16199    cx.assert_state_with_diff(
16200        r#"
16201        use some::mod1;
16202        use some::mod2;
16203
16204        const A: u32 = 42;
16205      + const B: u32 = 42;
16206        ˇ
16207        fn main() {
16208            println!("hello");
16209
16210            println!("world");
16211        }
16212      "#
16213        .unindent(),
16214    );
16215
16216    cx.update_editor(|editor, window, cx| {
16217        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16218        editor.delete_line(&DeleteLine, window, cx);
16219    });
16220    executor.run_until_parked();
16221    cx.assert_state_with_diff(
16222        r#"
16223        ˇ
16224        fn main() {
16225            println!("hello");
16226
16227            println!("world");
16228        }
16229      "#
16230        .unindent(),
16231    );
16232}
16233
16234#[gpui::test]
16235async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16236    init_test(cx, |_| {});
16237
16238    let mut cx = EditorTestContext::new(cx).await;
16239    cx.set_head_text(indoc! { "
16240        one
16241        two
16242        three
16243        four
16244        five
16245        "
16246    });
16247    cx.set_state(indoc! { "
16248        one
16249        ˇthree
16250        five
16251    "});
16252    cx.run_until_parked();
16253    cx.update_editor(|editor, window, cx| {
16254        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16255    });
16256    cx.assert_state_with_diff(
16257        indoc! { "
16258        one
16259      - two
16260        ˇthree
16261      - four
16262        five
16263    "}
16264        .to_string(),
16265    );
16266    cx.update_editor(|editor, window, cx| {
16267        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16268    });
16269
16270    cx.assert_state_with_diff(
16271        indoc! { "
16272        one
16273        ˇthree
16274        five
16275    "}
16276        .to_string(),
16277    );
16278
16279    cx.set_state(indoc! { "
16280        one
16281        ˇTWO
16282        three
16283        four
16284        five
16285    "});
16286    cx.run_until_parked();
16287    cx.update_editor(|editor, window, cx| {
16288        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16289    });
16290
16291    cx.assert_state_with_diff(
16292        indoc! { "
16293            one
16294          - two
16295          + ˇTWO
16296            three
16297            four
16298            five
16299        "}
16300        .to_string(),
16301    );
16302    cx.update_editor(|editor, window, cx| {
16303        editor.move_up(&Default::default(), window, cx);
16304        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16305    });
16306    cx.assert_state_with_diff(
16307        indoc! { "
16308            one
16309            ˇTWO
16310            three
16311            four
16312            five
16313        "}
16314        .to_string(),
16315    );
16316}
16317
16318#[gpui::test]
16319async fn test_edits_around_expanded_deletion_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
16336        fn main() {
16337            println!("hello");
16338
16339            println!("world");
16340        }
16341    "#
16342    .unindent();
16343    executor.run_until_parked();
16344    cx.set_state(
16345        &r#"
16346        use some::mod1;
16347        use some::mod2;
16348
16349        ˇconst B: u32 = 42;
16350        const C: u32 = 42;
16351
16352
16353        fn main() {
16354            println!("hello");
16355
16356            println!("world");
16357        }
16358        "#
16359        .unindent(),
16360    );
16361
16362    cx.set_head_text(&diff_base);
16363    executor.run_until_parked();
16364
16365    cx.update_editor(|editor, window, cx| {
16366        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16367    });
16368    executor.run_until_parked();
16369
16370    cx.assert_state_with_diff(
16371        r#"
16372        use some::mod1;
16373        use some::mod2;
16374
16375      - const A: u32 = 42;
16376        ˇconst B: u32 = 42;
16377        const C: u32 = 42;
16378
16379
16380        fn main() {
16381            println!("hello");
16382
16383            println!("world");
16384        }
16385      "#
16386        .unindent(),
16387    );
16388
16389    cx.update_editor(|editor, window, cx| {
16390        editor.delete_line(&DeleteLine, window, cx);
16391    });
16392    executor.run_until_parked();
16393    cx.assert_state_with_diff(
16394        r#"
16395        use some::mod1;
16396        use some::mod2;
16397
16398      - const A: u32 = 42;
16399      - const B: u32 = 42;
16400        ˇconst C: u32 = 42;
16401
16402
16403        fn main() {
16404            println!("hello");
16405
16406            println!("world");
16407        }
16408      "#
16409        .unindent(),
16410    );
16411
16412    cx.update_editor(|editor, window, cx| {
16413        editor.delete_line(&DeleteLine, window, cx);
16414    });
16415    executor.run_until_parked();
16416    cx.assert_state_with_diff(
16417        r#"
16418        use some::mod1;
16419        use some::mod2;
16420
16421      - const A: u32 = 42;
16422      - const B: u32 = 42;
16423      - const C: u32 = 42;
16424        ˇ
16425
16426        fn main() {
16427            println!("hello");
16428
16429            println!("world");
16430        }
16431      "#
16432        .unindent(),
16433    );
16434
16435    cx.update_editor(|editor, window, cx| {
16436        editor.handle_input("replacement", window, cx);
16437    });
16438    executor.run_until_parked();
16439    cx.assert_state_with_diff(
16440        r#"
16441        use some::mod1;
16442        use some::mod2;
16443
16444      - const A: u32 = 42;
16445      - const B: u32 = 42;
16446      - const C: u32 = 42;
16447      -
16448      + replacementˇ
16449
16450        fn main() {
16451            println!("hello");
16452
16453            println!("world");
16454        }
16455      "#
16456        .unindent(),
16457    );
16458}
16459
16460#[gpui::test]
16461async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16462    init_test(cx, |_| {});
16463
16464    let mut cx = EditorTestContext::new(cx).await;
16465
16466    let base_text = r#"
16467        one
16468        two
16469        three
16470        four
16471        five
16472    "#
16473    .unindent();
16474    executor.run_until_parked();
16475    cx.set_state(
16476        &r#"
16477        one
16478        two
16479        fˇour
16480        five
16481        "#
16482        .unindent(),
16483    );
16484
16485    cx.set_head_text(&base_text);
16486    executor.run_until_parked();
16487
16488    cx.update_editor(|editor, window, cx| {
16489        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16490    });
16491    executor.run_until_parked();
16492
16493    cx.assert_state_with_diff(
16494        r#"
16495          one
16496          two
16497        - three
16498          fˇour
16499          five
16500        "#
16501        .unindent(),
16502    );
16503
16504    cx.update_editor(|editor, window, cx| {
16505        editor.backspace(&Backspace, window, cx);
16506        editor.backspace(&Backspace, window, cx);
16507    });
16508    executor.run_until_parked();
16509    cx.assert_state_with_diff(
16510        r#"
16511          one
16512          two
16513        - threeˇ
16514        - four
16515        + our
16516          five
16517        "#
16518        .unindent(),
16519    );
16520}
16521
16522#[gpui::test]
16523async fn test_edit_after_expanded_modification_hunk(
16524    executor: BackgroundExecutor,
16525    cx: &mut TestAppContext,
16526) {
16527    init_test(cx, |_| {});
16528
16529    let mut cx = EditorTestContext::new(cx).await;
16530
16531    let diff_base = r#"
16532        use some::mod1;
16533        use some::mod2;
16534
16535        const A: u32 = 42;
16536        const B: u32 = 42;
16537        const C: u32 = 42;
16538        const D: u32 = 42;
16539
16540
16541        fn main() {
16542            println!("hello");
16543
16544            println!("world");
16545        }"#
16546    .unindent();
16547
16548    cx.set_state(
16549        &r#"
16550        use some::mod1;
16551        use some::mod2;
16552
16553        const A: u32 = 42;
16554        const B: u32 = 42;
16555        const C: u32 = 43ˇ
16556        const D: u32 = 42;
16557
16558
16559        fn main() {
16560            println!("hello");
16561
16562            println!("world");
16563        }"#
16564        .unindent(),
16565    );
16566
16567    cx.set_head_text(&diff_base);
16568    executor.run_until_parked();
16569    cx.update_editor(|editor, window, cx| {
16570        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16571    });
16572    executor.run_until_parked();
16573
16574    cx.assert_state_with_diff(
16575        r#"
16576        use some::mod1;
16577        use some::mod2;
16578
16579        const A: u32 = 42;
16580        const B: u32 = 42;
16581      - const C: u32 = 42;
16582      + const C: u32 = 43ˇ
16583        const D: u32 = 42;
16584
16585
16586        fn main() {
16587            println!("hello");
16588
16589            println!("world");
16590        }"#
16591        .unindent(),
16592    );
16593
16594    cx.update_editor(|editor, window, cx| {
16595        editor.handle_input("\nnew_line\n", window, cx);
16596    });
16597    executor.run_until_parked();
16598
16599    cx.assert_state_with_diff(
16600        r#"
16601        use some::mod1;
16602        use some::mod2;
16603
16604        const A: u32 = 42;
16605        const B: u32 = 42;
16606      - const C: u32 = 42;
16607      + const C: u32 = 43
16608      + new_line
16609      + ˇ
16610        const D: u32 = 42;
16611
16612
16613        fn main() {
16614            println!("hello");
16615
16616            println!("world");
16617        }"#
16618        .unindent(),
16619    );
16620}
16621
16622#[gpui::test]
16623async fn test_stage_and_unstage_added_file_hunk(
16624    executor: BackgroundExecutor,
16625    cx: &mut TestAppContext,
16626) {
16627    init_test(cx, |_| {});
16628
16629    let mut cx = EditorTestContext::new(cx).await;
16630    cx.update_editor(|editor, _, cx| {
16631        editor.set_expand_all_diff_hunks(cx);
16632    });
16633
16634    let working_copy = r#"
16635            ˇfn main() {
16636                println!("hello, world!");
16637            }
16638        "#
16639    .unindent();
16640
16641    cx.set_state(&working_copy);
16642    executor.run_until_parked();
16643
16644    cx.assert_state_with_diff(
16645        r#"
16646            + ˇfn main() {
16647            +     println!("hello, world!");
16648            + }
16649        "#
16650        .unindent(),
16651    );
16652    cx.assert_index_text(None);
16653
16654    cx.update_editor(|editor, window, cx| {
16655        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16656    });
16657    executor.run_until_parked();
16658    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16659    cx.assert_state_with_diff(
16660        r#"
16661            + ˇfn main() {
16662            +     println!("hello, world!");
16663            + }
16664        "#
16665        .unindent(),
16666    );
16667
16668    cx.update_editor(|editor, window, cx| {
16669        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16670    });
16671    executor.run_until_parked();
16672    cx.assert_index_text(None);
16673}
16674
16675async fn setup_indent_guides_editor(
16676    text: &str,
16677    cx: &mut TestAppContext,
16678) -> (BufferId, EditorTestContext) {
16679    init_test(cx, |_| {});
16680
16681    let mut cx = EditorTestContext::new(cx).await;
16682
16683    let buffer_id = cx.update_editor(|editor, window, cx| {
16684        editor.set_text(text, window, cx);
16685        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16686
16687        buffer_ids[0]
16688    });
16689
16690    (buffer_id, cx)
16691}
16692
16693fn assert_indent_guides(
16694    range: Range<u32>,
16695    expected: Vec<IndentGuide>,
16696    active_indices: Option<Vec<usize>>,
16697    cx: &mut EditorTestContext,
16698) {
16699    let indent_guides = cx.update_editor(|editor, window, cx| {
16700        let snapshot = editor.snapshot(window, cx).display_snapshot;
16701        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16702            editor,
16703            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16704            true,
16705            &snapshot,
16706            cx,
16707        );
16708
16709        indent_guides.sort_by(|a, b| {
16710            a.depth.cmp(&b.depth).then(
16711                a.start_row
16712                    .cmp(&b.start_row)
16713                    .then(a.end_row.cmp(&b.end_row)),
16714            )
16715        });
16716        indent_guides
16717    });
16718
16719    if let Some(expected) = active_indices {
16720        let active_indices = cx.update_editor(|editor, window, cx| {
16721            let snapshot = editor.snapshot(window, cx).display_snapshot;
16722            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16723        });
16724
16725        assert_eq!(
16726            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16727            expected,
16728            "Active indent guide indices do not match"
16729        );
16730    }
16731
16732    assert_eq!(indent_guides, expected, "Indent guides do not match");
16733}
16734
16735fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16736    IndentGuide {
16737        buffer_id,
16738        start_row: MultiBufferRow(start_row),
16739        end_row: MultiBufferRow(end_row),
16740        depth,
16741        tab_size: 4,
16742        settings: IndentGuideSettings {
16743            enabled: true,
16744            line_width: 1,
16745            active_line_width: 1,
16746            ..Default::default()
16747        },
16748    }
16749}
16750
16751#[gpui::test]
16752async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16753    let (buffer_id, mut cx) = setup_indent_guides_editor(
16754        &"
16755    fn main() {
16756        let a = 1;
16757    }"
16758        .unindent(),
16759        cx,
16760    )
16761    .await;
16762
16763    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16764}
16765
16766#[gpui::test]
16767async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16768    let (buffer_id, mut cx) = setup_indent_guides_editor(
16769        &"
16770    fn main() {
16771        let a = 1;
16772        let b = 2;
16773    }"
16774        .unindent(),
16775        cx,
16776    )
16777    .await;
16778
16779    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16780}
16781
16782#[gpui::test]
16783async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16784    let (buffer_id, mut cx) = setup_indent_guides_editor(
16785        &"
16786    fn main() {
16787        let a = 1;
16788        if a == 3 {
16789            let b = 2;
16790        } else {
16791            let c = 3;
16792        }
16793    }"
16794        .unindent(),
16795        cx,
16796    )
16797    .await;
16798
16799    assert_indent_guides(
16800        0..8,
16801        vec![
16802            indent_guide(buffer_id, 1, 6, 0),
16803            indent_guide(buffer_id, 3, 3, 1),
16804            indent_guide(buffer_id, 5, 5, 1),
16805        ],
16806        None,
16807        &mut cx,
16808    );
16809}
16810
16811#[gpui::test]
16812async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16813    let (buffer_id, mut cx) = setup_indent_guides_editor(
16814        &"
16815    fn main() {
16816        let a = 1;
16817            let b = 2;
16818        let c = 3;
16819    }"
16820        .unindent(),
16821        cx,
16822    )
16823    .await;
16824
16825    assert_indent_guides(
16826        0..5,
16827        vec![
16828            indent_guide(buffer_id, 1, 3, 0),
16829            indent_guide(buffer_id, 2, 2, 1),
16830        ],
16831        None,
16832        &mut cx,
16833    );
16834}
16835
16836#[gpui::test]
16837async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16838    let (buffer_id, mut cx) = setup_indent_guides_editor(
16839        &"
16840        fn main() {
16841            let a = 1;
16842
16843            let c = 3;
16844        }"
16845        .unindent(),
16846        cx,
16847    )
16848    .await;
16849
16850    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16851}
16852
16853#[gpui::test]
16854async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16855    let (buffer_id, mut cx) = setup_indent_guides_editor(
16856        &"
16857        fn main() {
16858            let a = 1;
16859
16860            let c = 3;
16861
16862            if a == 3 {
16863                let b = 2;
16864            } else {
16865                let c = 3;
16866            }
16867        }"
16868        .unindent(),
16869        cx,
16870    )
16871    .await;
16872
16873    assert_indent_guides(
16874        0..11,
16875        vec![
16876            indent_guide(buffer_id, 1, 9, 0),
16877            indent_guide(buffer_id, 6, 6, 1),
16878            indent_guide(buffer_id, 8, 8, 1),
16879        ],
16880        None,
16881        &mut cx,
16882    );
16883}
16884
16885#[gpui::test]
16886async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16887    let (buffer_id, mut cx) = setup_indent_guides_editor(
16888        &"
16889        fn main() {
16890            let a = 1;
16891
16892            let c = 3;
16893
16894            if a == 3 {
16895                let b = 2;
16896            } else {
16897                let c = 3;
16898            }
16899        }"
16900        .unindent(),
16901        cx,
16902    )
16903    .await;
16904
16905    assert_indent_guides(
16906        1..11,
16907        vec![
16908            indent_guide(buffer_id, 1, 9, 0),
16909            indent_guide(buffer_id, 6, 6, 1),
16910            indent_guide(buffer_id, 8, 8, 1),
16911        ],
16912        None,
16913        &mut cx,
16914    );
16915}
16916
16917#[gpui::test]
16918async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16919    let (buffer_id, mut cx) = setup_indent_guides_editor(
16920        &"
16921        fn main() {
16922            let a = 1;
16923
16924            let c = 3;
16925
16926            if a == 3 {
16927                let b = 2;
16928            } else {
16929                let c = 3;
16930            }
16931        }"
16932        .unindent(),
16933        cx,
16934    )
16935    .await;
16936
16937    assert_indent_guides(
16938        1..10,
16939        vec![
16940            indent_guide(buffer_id, 1, 9, 0),
16941            indent_guide(buffer_id, 6, 6, 1),
16942            indent_guide(buffer_id, 8, 8, 1),
16943        ],
16944        None,
16945        &mut cx,
16946    );
16947}
16948
16949#[gpui::test]
16950async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16951    let (buffer_id, mut cx) = setup_indent_guides_editor(
16952        &"
16953        block1
16954            block2
16955                block3
16956                    block4
16957            block2
16958        block1
16959        block1"
16960            .unindent(),
16961        cx,
16962    )
16963    .await;
16964
16965    assert_indent_guides(
16966        1..10,
16967        vec![
16968            indent_guide(buffer_id, 1, 4, 0),
16969            indent_guide(buffer_id, 2, 3, 1),
16970            indent_guide(buffer_id, 3, 3, 2),
16971        ],
16972        None,
16973        &mut cx,
16974    );
16975}
16976
16977#[gpui::test]
16978async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16979    let (buffer_id, mut cx) = setup_indent_guides_editor(
16980        &"
16981        block1
16982            block2
16983                block3
16984
16985        block1
16986        block1"
16987            .unindent(),
16988        cx,
16989    )
16990    .await;
16991
16992    assert_indent_guides(
16993        0..6,
16994        vec![
16995            indent_guide(buffer_id, 1, 2, 0),
16996            indent_guide(buffer_id, 2, 2, 1),
16997        ],
16998        None,
16999        &mut cx,
17000    );
17001}
17002
17003#[gpui::test]
17004async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
17005    let (buffer_id, mut cx) = setup_indent_guides_editor(
17006        &"
17007        block1
17008
17009
17010
17011            block2
17012        "
17013        .unindent(),
17014        cx,
17015    )
17016    .await;
17017
17018    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
17019}
17020
17021#[gpui::test]
17022async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
17023    let (buffer_id, mut cx) = setup_indent_guides_editor(
17024        &"
17025        def a:
17026        \tb = 3
17027        \tif True:
17028        \t\tc = 4
17029        \t\td = 5
17030        \tprint(b)
17031        "
17032        .unindent(),
17033        cx,
17034    )
17035    .await;
17036
17037    assert_indent_guides(
17038        0..6,
17039        vec![
17040            indent_guide(buffer_id, 1, 5, 0),
17041            indent_guide(buffer_id, 3, 4, 1),
17042        ],
17043        None,
17044        &mut cx,
17045    );
17046}
17047
17048#[gpui::test]
17049async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
17050    let (buffer_id, mut cx) = setup_indent_guides_editor(
17051        &"
17052    fn main() {
17053        let a = 1;
17054    }"
17055        .unindent(),
17056        cx,
17057    )
17058    .await;
17059
17060    cx.update_editor(|editor, window, cx| {
17061        editor.change_selections(None, window, cx, |s| {
17062            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17063        });
17064    });
17065
17066    assert_indent_guides(
17067        0..3,
17068        vec![indent_guide(buffer_id, 1, 1, 0)],
17069        Some(vec![0]),
17070        &mut cx,
17071    );
17072}
17073
17074#[gpui::test]
17075async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
17076    let (buffer_id, mut cx) = setup_indent_guides_editor(
17077        &"
17078    fn main() {
17079        if 1 == 2 {
17080            let a = 1;
17081        }
17082    }"
17083        .unindent(),
17084        cx,
17085    )
17086    .await;
17087
17088    cx.update_editor(|editor, window, cx| {
17089        editor.change_selections(None, window, cx, |s| {
17090            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17091        });
17092    });
17093
17094    assert_indent_guides(
17095        0..4,
17096        vec![
17097            indent_guide(buffer_id, 1, 3, 0),
17098            indent_guide(buffer_id, 2, 2, 1),
17099        ],
17100        Some(vec![1]),
17101        &mut cx,
17102    );
17103
17104    cx.update_editor(|editor, window, cx| {
17105        editor.change_selections(None, window, cx, |s| {
17106            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17107        });
17108    });
17109
17110    assert_indent_guides(
17111        0..4,
17112        vec![
17113            indent_guide(buffer_id, 1, 3, 0),
17114            indent_guide(buffer_id, 2, 2, 1),
17115        ],
17116        Some(vec![1]),
17117        &mut cx,
17118    );
17119
17120    cx.update_editor(|editor, window, cx| {
17121        editor.change_selections(None, window, cx, |s| {
17122            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
17123        });
17124    });
17125
17126    assert_indent_guides(
17127        0..4,
17128        vec![
17129            indent_guide(buffer_id, 1, 3, 0),
17130            indent_guide(buffer_id, 2, 2, 1),
17131        ],
17132        Some(vec![0]),
17133        &mut cx,
17134    );
17135}
17136
17137#[gpui::test]
17138async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
17139    let (buffer_id, mut cx) = setup_indent_guides_editor(
17140        &"
17141    fn main() {
17142        let a = 1;
17143
17144        let b = 2;
17145    }"
17146        .unindent(),
17147        cx,
17148    )
17149    .await;
17150
17151    cx.update_editor(|editor, window, cx| {
17152        editor.change_selections(None, window, cx, |s| {
17153            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17154        });
17155    });
17156
17157    assert_indent_guides(
17158        0..5,
17159        vec![indent_guide(buffer_id, 1, 3, 0)],
17160        Some(vec![0]),
17161        &mut cx,
17162    );
17163}
17164
17165#[gpui::test]
17166async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17167    let (buffer_id, mut cx) = setup_indent_guides_editor(
17168        &"
17169    def m:
17170        a = 1
17171        pass"
17172            .unindent(),
17173        cx,
17174    )
17175    .await;
17176
17177    cx.update_editor(|editor, window, cx| {
17178        editor.change_selections(None, window, cx, |s| {
17179            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17180        });
17181    });
17182
17183    assert_indent_guides(
17184        0..3,
17185        vec![indent_guide(buffer_id, 1, 2, 0)],
17186        Some(vec![0]),
17187        &mut cx,
17188    );
17189}
17190
17191#[gpui::test]
17192async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17193    init_test(cx, |_| {});
17194    let mut cx = EditorTestContext::new(cx).await;
17195    let text = indoc! {
17196        "
17197        impl A {
17198            fn b() {
17199                0;
17200                3;
17201                5;
17202                6;
17203                7;
17204            }
17205        }
17206        "
17207    };
17208    let base_text = indoc! {
17209        "
17210        impl A {
17211            fn b() {
17212                0;
17213                1;
17214                2;
17215                3;
17216                4;
17217            }
17218            fn c() {
17219                5;
17220                6;
17221                7;
17222            }
17223        }
17224        "
17225    };
17226
17227    cx.update_editor(|editor, window, cx| {
17228        editor.set_text(text, window, cx);
17229
17230        editor.buffer().update(cx, |multibuffer, cx| {
17231            let buffer = multibuffer.as_singleton().unwrap();
17232            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17233
17234            multibuffer.set_all_diff_hunks_expanded(cx);
17235            multibuffer.add_diff(diff, cx);
17236
17237            buffer.read(cx).remote_id()
17238        })
17239    });
17240    cx.run_until_parked();
17241
17242    cx.assert_state_with_diff(
17243        indoc! { "
17244          impl A {
17245              fn b() {
17246                  0;
17247        -         1;
17248        -         2;
17249                  3;
17250        -         4;
17251        -     }
17252        -     fn c() {
17253                  5;
17254                  6;
17255                  7;
17256              }
17257          }
17258          ˇ"
17259        }
17260        .to_string(),
17261    );
17262
17263    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17264        editor
17265            .snapshot(window, cx)
17266            .buffer_snapshot
17267            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17268            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17269            .collect::<Vec<_>>()
17270    });
17271    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17272    assert_eq!(
17273        actual_guides,
17274        vec![
17275            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17276            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17277            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17278        ]
17279    );
17280}
17281
17282#[gpui::test]
17283async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17284    init_test(cx, |_| {});
17285    let mut cx = EditorTestContext::new(cx).await;
17286
17287    let diff_base = r#"
17288        a
17289        b
17290        c
17291        "#
17292    .unindent();
17293
17294    cx.set_state(
17295        &r#"
17296        ˇA
17297        b
17298        C
17299        "#
17300        .unindent(),
17301    );
17302    cx.set_head_text(&diff_base);
17303    cx.update_editor(|editor, window, cx| {
17304        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17305    });
17306    executor.run_until_parked();
17307
17308    let both_hunks_expanded = r#"
17309        - a
17310        + ˇA
17311          b
17312        - c
17313        + C
17314        "#
17315    .unindent();
17316
17317    cx.assert_state_with_diff(both_hunks_expanded.clone());
17318
17319    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17320        let snapshot = editor.snapshot(window, cx);
17321        let hunks = editor
17322            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17323            .collect::<Vec<_>>();
17324        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17325        let buffer_id = hunks[0].buffer_id;
17326        hunks
17327            .into_iter()
17328            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17329            .collect::<Vec<_>>()
17330    });
17331    assert_eq!(hunk_ranges.len(), 2);
17332
17333    cx.update_editor(|editor, _, cx| {
17334        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17335    });
17336    executor.run_until_parked();
17337
17338    let second_hunk_expanded = r#"
17339          ˇA
17340          b
17341        - c
17342        + C
17343        "#
17344    .unindent();
17345
17346    cx.assert_state_with_diff(second_hunk_expanded);
17347
17348    cx.update_editor(|editor, _, cx| {
17349        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17350    });
17351    executor.run_until_parked();
17352
17353    cx.assert_state_with_diff(both_hunks_expanded.clone());
17354
17355    cx.update_editor(|editor, _, cx| {
17356        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17357    });
17358    executor.run_until_parked();
17359
17360    let first_hunk_expanded = r#"
17361        - a
17362        + ˇA
17363          b
17364          C
17365        "#
17366    .unindent();
17367
17368    cx.assert_state_with_diff(first_hunk_expanded);
17369
17370    cx.update_editor(|editor, _, cx| {
17371        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17372    });
17373    executor.run_until_parked();
17374
17375    cx.assert_state_with_diff(both_hunks_expanded);
17376
17377    cx.set_state(
17378        &r#"
17379        ˇA
17380        b
17381        "#
17382        .unindent(),
17383    );
17384    cx.run_until_parked();
17385
17386    // TODO this cursor position seems bad
17387    cx.assert_state_with_diff(
17388        r#"
17389        - ˇa
17390        + A
17391          b
17392        "#
17393        .unindent(),
17394    );
17395
17396    cx.update_editor(|editor, window, cx| {
17397        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17398    });
17399
17400    cx.assert_state_with_diff(
17401        r#"
17402            - ˇa
17403            + A
17404              b
17405            - c
17406            "#
17407        .unindent(),
17408    );
17409
17410    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17411        let snapshot = editor.snapshot(window, cx);
17412        let hunks = editor
17413            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17414            .collect::<Vec<_>>();
17415        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17416        let buffer_id = hunks[0].buffer_id;
17417        hunks
17418            .into_iter()
17419            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17420            .collect::<Vec<_>>()
17421    });
17422    assert_eq!(hunk_ranges.len(), 2);
17423
17424    cx.update_editor(|editor, _, cx| {
17425        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17426    });
17427    executor.run_until_parked();
17428
17429    cx.assert_state_with_diff(
17430        r#"
17431        - ˇa
17432        + A
17433          b
17434        "#
17435        .unindent(),
17436    );
17437}
17438
17439#[gpui::test]
17440async fn test_toggle_deletion_hunk_at_start_of_file(
17441    executor: BackgroundExecutor,
17442    cx: &mut TestAppContext,
17443) {
17444    init_test(cx, |_| {});
17445    let mut cx = EditorTestContext::new(cx).await;
17446
17447    let diff_base = r#"
17448        a
17449        b
17450        c
17451        "#
17452    .unindent();
17453
17454    cx.set_state(
17455        &r#"
17456        ˇb
17457        c
17458        "#
17459        .unindent(),
17460    );
17461    cx.set_head_text(&diff_base);
17462    cx.update_editor(|editor, window, cx| {
17463        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17464    });
17465    executor.run_until_parked();
17466
17467    let hunk_expanded = r#"
17468        - a
17469          ˇb
17470          c
17471        "#
17472    .unindent();
17473
17474    cx.assert_state_with_diff(hunk_expanded.clone());
17475
17476    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17477        let snapshot = editor.snapshot(window, cx);
17478        let hunks = editor
17479            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17480            .collect::<Vec<_>>();
17481        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17482        let buffer_id = hunks[0].buffer_id;
17483        hunks
17484            .into_iter()
17485            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17486            .collect::<Vec<_>>()
17487    });
17488    assert_eq!(hunk_ranges.len(), 1);
17489
17490    cx.update_editor(|editor, _, cx| {
17491        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17492    });
17493    executor.run_until_parked();
17494
17495    let hunk_collapsed = r#"
17496          ˇb
17497          c
17498        "#
17499    .unindent();
17500
17501    cx.assert_state_with_diff(hunk_collapsed);
17502
17503    cx.update_editor(|editor, _, cx| {
17504        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17505    });
17506    executor.run_until_parked();
17507
17508    cx.assert_state_with_diff(hunk_expanded.clone());
17509}
17510
17511#[gpui::test]
17512async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17513    init_test(cx, |_| {});
17514
17515    let fs = FakeFs::new(cx.executor());
17516    fs.insert_tree(
17517        path!("/test"),
17518        json!({
17519            ".git": {},
17520            "file-1": "ONE\n",
17521            "file-2": "TWO\n",
17522            "file-3": "THREE\n",
17523        }),
17524    )
17525    .await;
17526
17527    fs.set_head_for_repo(
17528        path!("/test/.git").as_ref(),
17529        &[
17530            ("file-1".into(), "one\n".into()),
17531            ("file-2".into(), "two\n".into()),
17532            ("file-3".into(), "three\n".into()),
17533        ],
17534    );
17535
17536    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17537    let mut buffers = vec![];
17538    for i in 1..=3 {
17539        let buffer = project
17540            .update(cx, |project, cx| {
17541                let path = format!(path!("/test/file-{}"), i);
17542                project.open_local_buffer(path, cx)
17543            })
17544            .await
17545            .unwrap();
17546        buffers.push(buffer);
17547    }
17548
17549    let multibuffer = cx.new(|cx| {
17550        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17551        multibuffer.set_all_diff_hunks_expanded(cx);
17552        for buffer in &buffers {
17553            let snapshot = buffer.read(cx).snapshot();
17554            multibuffer.set_excerpts_for_path(
17555                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17556                buffer.clone(),
17557                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17558                DEFAULT_MULTIBUFFER_CONTEXT,
17559                cx,
17560            );
17561        }
17562        multibuffer
17563    });
17564
17565    let editor = cx.add_window(|window, cx| {
17566        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17567    });
17568    cx.run_until_parked();
17569
17570    let snapshot = editor
17571        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17572        .unwrap();
17573    let hunks = snapshot
17574        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17575        .map(|hunk| match hunk {
17576            DisplayDiffHunk::Unfolded {
17577                display_row_range, ..
17578            } => display_row_range,
17579            DisplayDiffHunk::Folded { .. } => unreachable!(),
17580        })
17581        .collect::<Vec<_>>();
17582    assert_eq!(
17583        hunks,
17584        [
17585            DisplayRow(2)..DisplayRow(4),
17586            DisplayRow(7)..DisplayRow(9),
17587            DisplayRow(12)..DisplayRow(14),
17588        ]
17589    );
17590}
17591
17592#[gpui::test]
17593async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17594    init_test(cx, |_| {});
17595
17596    let mut cx = EditorTestContext::new(cx).await;
17597    cx.set_head_text(indoc! { "
17598        one
17599        two
17600        three
17601        four
17602        five
17603        "
17604    });
17605    cx.set_index_text(indoc! { "
17606        one
17607        two
17608        three
17609        four
17610        five
17611        "
17612    });
17613    cx.set_state(indoc! {"
17614        one
17615        TWO
17616        ˇTHREE
17617        FOUR
17618        five
17619    "});
17620    cx.run_until_parked();
17621    cx.update_editor(|editor, window, cx| {
17622        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17623    });
17624    cx.run_until_parked();
17625    cx.assert_index_text(Some(indoc! {"
17626        one
17627        TWO
17628        THREE
17629        FOUR
17630        five
17631    "}));
17632    cx.set_state(indoc! { "
17633        one
17634        TWO
17635        ˇTHREE-HUNDRED
17636        FOUR
17637        five
17638    "});
17639    cx.run_until_parked();
17640    cx.update_editor(|editor, window, cx| {
17641        let snapshot = editor.snapshot(window, cx);
17642        let hunks = editor
17643            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17644            .collect::<Vec<_>>();
17645        assert_eq!(hunks.len(), 1);
17646        assert_eq!(
17647            hunks[0].status(),
17648            DiffHunkStatus {
17649                kind: DiffHunkStatusKind::Modified,
17650                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17651            }
17652        );
17653
17654        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17655    });
17656    cx.run_until_parked();
17657    cx.assert_index_text(Some(indoc! {"
17658        one
17659        TWO
17660        THREE-HUNDRED
17661        FOUR
17662        five
17663    "}));
17664}
17665
17666#[gpui::test]
17667fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17668    init_test(cx, |_| {});
17669
17670    let editor = cx.add_window(|window, cx| {
17671        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17672        build_editor(buffer, window, cx)
17673    });
17674
17675    let render_args = Arc::new(Mutex::new(None));
17676    let snapshot = editor
17677        .update(cx, |editor, window, cx| {
17678            let snapshot = editor.buffer().read(cx).snapshot(cx);
17679            let range =
17680                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17681
17682            struct RenderArgs {
17683                row: MultiBufferRow,
17684                folded: bool,
17685                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17686            }
17687
17688            let crease = Crease::inline(
17689                range,
17690                FoldPlaceholder::test(),
17691                {
17692                    let toggle_callback = render_args.clone();
17693                    move |row, folded, callback, _window, _cx| {
17694                        *toggle_callback.lock() = Some(RenderArgs {
17695                            row,
17696                            folded,
17697                            callback,
17698                        });
17699                        div()
17700                    }
17701                },
17702                |_row, _folded, _window, _cx| div(),
17703            );
17704
17705            editor.insert_creases(Some(crease), cx);
17706            let snapshot = editor.snapshot(window, cx);
17707            let _div = snapshot.render_crease_toggle(
17708                MultiBufferRow(1),
17709                false,
17710                cx.entity().clone(),
17711                window,
17712                cx,
17713            );
17714            snapshot
17715        })
17716        .unwrap();
17717
17718    let render_args = render_args.lock().take().unwrap();
17719    assert_eq!(render_args.row, MultiBufferRow(1));
17720    assert!(!render_args.folded);
17721    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17722
17723    cx.update_window(*editor, |_, window, cx| {
17724        (render_args.callback)(true, window, cx)
17725    })
17726    .unwrap();
17727    let snapshot = editor
17728        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17729        .unwrap();
17730    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17731
17732    cx.update_window(*editor, |_, window, cx| {
17733        (render_args.callback)(false, window, cx)
17734    })
17735    .unwrap();
17736    let snapshot = editor
17737        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17738        .unwrap();
17739    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17740}
17741
17742#[gpui::test]
17743async fn test_input_text(cx: &mut TestAppContext) {
17744    init_test(cx, |_| {});
17745    let mut cx = EditorTestContext::new(cx).await;
17746
17747    cx.set_state(
17748        &r#"ˇone
17749        two
17750
17751        three
17752        fourˇ
17753        five
17754
17755        siˇx"#
17756            .unindent(),
17757    );
17758
17759    cx.dispatch_action(HandleInput(String::new()));
17760    cx.assert_editor_state(
17761        &r#"ˇone
17762        two
17763
17764        three
17765        fourˇ
17766        five
17767
17768        siˇx"#
17769            .unindent(),
17770    );
17771
17772    cx.dispatch_action(HandleInput("AAAA".to_string()));
17773    cx.assert_editor_state(
17774        &r#"AAAAˇone
17775        two
17776
17777        three
17778        fourAAAAˇ
17779        five
17780
17781        siAAAAˇx"#
17782            .unindent(),
17783    );
17784}
17785
17786#[gpui::test]
17787async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17788    init_test(cx, |_| {});
17789
17790    let mut cx = EditorTestContext::new(cx).await;
17791    cx.set_state(
17792        r#"let foo = 1;
17793let foo = 2;
17794let foo = 3;
17795let fooˇ = 4;
17796let foo = 5;
17797let foo = 6;
17798let foo = 7;
17799let foo = 8;
17800let foo = 9;
17801let foo = 10;
17802let foo = 11;
17803let foo = 12;
17804let foo = 13;
17805let foo = 14;
17806let foo = 15;"#,
17807    );
17808
17809    cx.update_editor(|e, window, cx| {
17810        assert_eq!(
17811            e.next_scroll_position,
17812            NextScrollCursorCenterTopBottom::Center,
17813            "Default next scroll direction is center",
17814        );
17815
17816        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17817        assert_eq!(
17818            e.next_scroll_position,
17819            NextScrollCursorCenterTopBottom::Top,
17820            "After center, next scroll direction should be top",
17821        );
17822
17823        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17824        assert_eq!(
17825            e.next_scroll_position,
17826            NextScrollCursorCenterTopBottom::Bottom,
17827            "After top, next scroll direction should be bottom",
17828        );
17829
17830        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17831        assert_eq!(
17832            e.next_scroll_position,
17833            NextScrollCursorCenterTopBottom::Center,
17834            "After bottom, scrolling should start over",
17835        );
17836
17837        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17838        assert_eq!(
17839            e.next_scroll_position,
17840            NextScrollCursorCenterTopBottom::Top,
17841            "Scrolling continues if retriggered fast enough"
17842        );
17843    });
17844
17845    cx.executor()
17846        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17847    cx.executor().run_until_parked();
17848    cx.update_editor(|e, _, _| {
17849        assert_eq!(
17850            e.next_scroll_position,
17851            NextScrollCursorCenterTopBottom::Center,
17852            "If scrolling is not triggered fast enough, it should reset"
17853        );
17854    });
17855}
17856
17857#[gpui::test]
17858async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17859    init_test(cx, |_| {});
17860    let mut cx = EditorLspTestContext::new_rust(
17861        lsp::ServerCapabilities {
17862            definition_provider: Some(lsp::OneOf::Left(true)),
17863            references_provider: Some(lsp::OneOf::Left(true)),
17864            ..lsp::ServerCapabilities::default()
17865        },
17866        cx,
17867    )
17868    .await;
17869
17870    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17871        let go_to_definition = cx
17872            .lsp
17873            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17874                move |params, _| async move {
17875                    if empty_go_to_definition {
17876                        Ok(None)
17877                    } else {
17878                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17879                            uri: params.text_document_position_params.text_document.uri,
17880                            range: lsp::Range::new(
17881                                lsp::Position::new(4, 3),
17882                                lsp::Position::new(4, 6),
17883                            ),
17884                        })))
17885                    }
17886                },
17887            );
17888        let references = cx
17889            .lsp
17890            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17891                Ok(Some(vec![lsp::Location {
17892                    uri: params.text_document_position.text_document.uri,
17893                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17894                }]))
17895            });
17896        (go_to_definition, references)
17897    };
17898
17899    cx.set_state(
17900        &r#"fn one() {
17901            let mut a = ˇtwo();
17902        }
17903
17904        fn two() {}"#
17905            .unindent(),
17906    );
17907    set_up_lsp_handlers(false, &mut cx);
17908    let navigated = cx
17909        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17910        .await
17911        .expect("Failed to navigate to definition");
17912    assert_eq!(
17913        navigated,
17914        Navigated::Yes,
17915        "Should have navigated to definition from the GetDefinition response"
17916    );
17917    cx.assert_editor_state(
17918        &r#"fn one() {
17919            let mut a = two();
17920        }
17921
17922        fn «twoˇ»() {}"#
17923            .unindent(),
17924    );
17925
17926    let editors = cx.update_workspace(|workspace, _, cx| {
17927        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17928    });
17929    cx.update_editor(|_, _, test_editor_cx| {
17930        assert_eq!(
17931            editors.len(),
17932            1,
17933            "Initially, only one, test, editor should be open in the workspace"
17934        );
17935        assert_eq!(
17936            test_editor_cx.entity(),
17937            editors.last().expect("Asserted len is 1").clone()
17938        );
17939    });
17940
17941    set_up_lsp_handlers(true, &mut cx);
17942    let navigated = cx
17943        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17944        .await
17945        .expect("Failed to navigate to lookup references");
17946    assert_eq!(
17947        navigated,
17948        Navigated::Yes,
17949        "Should have navigated to references as a fallback after empty GoToDefinition response"
17950    );
17951    // We should not change the selections in the existing file,
17952    // if opening another milti buffer with the references
17953    cx.assert_editor_state(
17954        &r#"fn one() {
17955            let mut a = two();
17956        }
17957
17958        fn «twoˇ»() {}"#
17959            .unindent(),
17960    );
17961    let editors = cx.update_workspace(|workspace, _, cx| {
17962        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17963    });
17964    cx.update_editor(|_, _, test_editor_cx| {
17965        assert_eq!(
17966            editors.len(),
17967            2,
17968            "After falling back to references search, we open a new editor with the results"
17969        );
17970        let references_fallback_text = editors
17971            .into_iter()
17972            .find(|new_editor| *new_editor != test_editor_cx.entity())
17973            .expect("Should have one non-test editor now")
17974            .read(test_editor_cx)
17975            .text(test_editor_cx);
17976        assert_eq!(
17977            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17978            "Should use the range from the references response and not the GoToDefinition one"
17979        );
17980    });
17981}
17982
17983#[gpui::test]
17984async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17985    init_test(cx, |_| {});
17986    cx.update(|cx| {
17987        let mut editor_settings = EditorSettings::get_global(cx).clone();
17988        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17989        EditorSettings::override_global(editor_settings, cx);
17990    });
17991    let mut cx = EditorLspTestContext::new_rust(
17992        lsp::ServerCapabilities {
17993            definition_provider: Some(lsp::OneOf::Left(true)),
17994            references_provider: Some(lsp::OneOf::Left(true)),
17995            ..lsp::ServerCapabilities::default()
17996        },
17997        cx,
17998    )
17999    .await;
18000    let original_state = r#"fn one() {
18001        let mut a = ˇtwo();
18002    }
18003
18004    fn two() {}"#
18005        .unindent();
18006    cx.set_state(&original_state);
18007
18008    let mut go_to_definition = cx
18009        .lsp
18010        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
18011            move |_, _| async move { Ok(None) },
18012        );
18013    let _references = cx
18014        .lsp
18015        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
18016            panic!("Should not call for references with no go to definition fallback")
18017        });
18018
18019    let navigated = cx
18020        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18021        .await
18022        .expect("Failed to navigate to lookup references");
18023    go_to_definition
18024        .next()
18025        .await
18026        .expect("Should have called the go_to_definition handler");
18027
18028    assert_eq!(
18029        navigated,
18030        Navigated::No,
18031        "Should have navigated to references as a fallback after empty GoToDefinition response"
18032    );
18033    cx.assert_editor_state(&original_state);
18034    let editors = cx.update_workspace(|workspace, _, cx| {
18035        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18036    });
18037    cx.update_editor(|_, _, _| {
18038        assert_eq!(
18039            editors.len(),
18040            1,
18041            "After unsuccessful fallback, no other editor should have been opened"
18042        );
18043    });
18044}
18045
18046#[gpui::test]
18047async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
18048    init_test(cx, |_| {});
18049
18050    let language = Arc::new(Language::new(
18051        LanguageConfig::default(),
18052        Some(tree_sitter_rust::LANGUAGE.into()),
18053    ));
18054
18055    let text = r#"
18056        #[cfg(test)]
18057        mod tests() {
18058            #[test]
18059            fn runnable_1() {
18060                let a = 1;
18061            }
18062
18063            #[test]
18064            fn runnable_2() {
18065                let a = 1;
18066                let b = 2;
18067            }
18068        }
18069    "#
18070    .unindent();
18071
18072    let fs = FakeFs::new(cx.executor());
18073    fs.insert_file("/file.rs", Default::default()).await;
18074
18075    let project = Project::test(fs, ["/a".as_ref()], cx).await;
18076    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18077    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18078    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
18079    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
18080
18081    let editor = cx.new_window_entity(|window, cx| {
18082        Editor::new(
18083            EditorMode::full(),
18084            multi_buffer,
18085            Some(project.clone()),
18086            window,
18087            cx,
18088        )
18089    });
18090
18091    editor.update_in(cx, |editor, window, cx| {
18092        let snapshot = editor.buffer().read(cx).snapshot(cx);
18093        editor.tasks.insert(
18094            (buffer.read(cx).remote_id(), 3),
18095            RunnableTasks {
18096                templates: vec![],
18097                offset: snapshot.anchor_before(43),
18098                column: 0,
18099                extra_variables: HashMap::default(),
18100                context_range: BufferOffset(43)..BufferOffset(85),
18101            },
18102        );
18103        editor.tasks.insert(
18104            (buffer.read(cx).remote_id(), 8),
18105            RunnableTasks {
18106                templates: vec![],
18107                offset: snapshot.anchor_before(86),
18108                column: 0,
18109                extra_variables: HashMap::default(),
18110                context_range: BufferOffset(86)..BufferOffset(191),
18111            },
18112        );
18113
18114        // Test finding task when cursor is inside function body
18115        editor.change_selections(None, window, cx, |s| {
18116            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
18117        });
18118        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18119        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
18120
18121        // Test finding task when cursor is on function name
18122        editor.change_selections(None, window, cx, |s| {
18123            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
18124        });
18125        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18126        assert_eq!(row, 8, "Should find task when cursor is on function name");
18127    });
18128}
18129
18130#[gpui::test]
18131async fn test_folding_buffers(cx: &mut TestAppContext) {
18132    init_test(cx, |_| {});
18133
18134    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18135    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
18136    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
18137
18138    let fs = FakeFs::new(cx.executor());
18139    fs.insert_tree(
18140        path!("/a"),
18141        json!({
18142            "first.rs": sample_text_1,
18143            "second.rs": sample_text_2,
18144            "third.rs": sample_text_3,
18145        }),
18146    )
18147    .await;
18148    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18149    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18150    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18151    let worktree = project.update(cx, |project, cx| {
18152        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18153        assert_eq!(worktrees.len(), 1);
18154        worktrees.pop().unwrap()
18155    });
18156    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18157
18158    let buffer_1 = project
18159        .update(cx, |project, cx| {
18160            project.open_buffer((worktree_id, "first.rs"), cx)
18161        })
18162        .await
18163        .unwrap();
18164    let buffer_2 = project
18165        .update(cx, |project, cx| {
18166            project.open_buffer((worktree_id, "second.rs"), cx)
18167        })
18168        .await
18169        .unwrap();
18170    let buffer_3 = project
18171        .update(cx, |project, cx| {
18172            project.open_buffer((worktree_id, "third.rs"), cx)
18173        })
18174        .await
18175        .unwrap();
18176
18177    let multi_buffer = cx.new(|cx| {
18178        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18179        multi_buffer.push_excerpts(
18180            buffer_1.clone(),
18181            [
18182                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18183                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18184                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18185            ],
18186            cx,
18187        );
18188        multi_buffer.push_excerpts(
18189            buffer_2.clone(),
18190            [
18191                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18192                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18193                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18194            ],
18195            cx,
18196        );
18197        multi_buffer.push_excerpts(
18198            buffer_3.clone(),
18199            [
18200                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18201                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18202                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18203            ],
18204            cx,
18205        );
18206        multi_buffer
18207    });
18208    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18209        Editor::new(
18210            EditorMode::full(),
18211            multi_buffer.clone(),
18212            Some(project.clone()),
18213            window,
18214            cx,
18215        )
18216    });
18217
18218    assert_eq!(
18219        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18220        "\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",
18221    );
18222
18223    multi_buffer_editor.update(cx, |editor, cx| {
18224        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18225    });
18226    assert_eq!(
18227        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18228        "\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",
18229        "After folding the first buffer, its text should not be displayed"
18230    );
18231
18232    multi_buffer_editor.update(cx, |editor, cx| {
18233        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18234    });
18235    assert_eq!(
18236        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18237        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18238        "After folding the second buffer, its text should not be displayed"
18239    );
18240
18241    multi_buffer_editor.update(cx, |editor, cx| {
18242        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18243    });
18244    assert_eq!(
18245        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18246        "\n\n\n\n\n",
18247        "After folding the third buffer, its text should not be displayed"
18248    );
18249
18250    // Emulate selection inside the fold logic, that should work
18251    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18252        editor
18253            .snapshot(window, cx)
18254            .next_line_boundary(Point::new(0, 4));
18255    });
18256
18257    multi_buffer_editor.update(cx, |editor, cx| {
18258        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18259    });
18260    assert_eq!(
18261        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18262        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18263        "After unfolding the second buffer, its text should be displayed"
18264    );
18265
18266    // Typing inside of buffer 1 causes that buffer to be unfolded.
18267    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18268        assert_eq!(
18269            multi_buffer
18270                .read(cx)
18271                .snapshot(cx)
18272                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18273                .collect::<String>(),
18274            "bbbb"
18275        );
18276        editor.change_selections(None, window, cx, |selections| {
18277            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18278        });
18279        editor.handle_input("B", window, cx);
18280    });
18281
18282    assert_eq!(
18283        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18284        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18285        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18286    );
18287
18288    multi_buffer_editor.update(cx, |editor, cx| {
18289        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18290    });
18291    assert_eq!(
18292        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18293        "\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",
18294        "After unfolding the all buffers, all original text should be displayed"
18295    );
18296}
18297
18298#[gpui::test]
18299async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18300    init_test(cx, |_| {});
18301
18302    let sample_text_1 = "1111\n2222\n3333".to_string();
18303    let sample_text_2 = "4444\n5555\n6666".to_string();
18304    let sample_text_3 = "7777\n8888\n9999".to_string();
18305
18306    let fs = FakeFs::new(cx.executor());
18307    fs.insert_tree(
18308        path!("/a"),
18309        json!({
18310            "first.rs": sample_text_1,
18311            "second.rs": sample_text_2,
18312            "third.rs": sample_text_3,
18313        }),
18314    )
18315    .await;
18316    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18317    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18318    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18319    let worktree = project.update(cx, |project, cx| {
18320        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18321        assert_eq!(worktrees.len(), 1);
18322        worktrees.pop().unwrap()
18323    });
18324    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18325
18326    let buffer_1 = project
18327        .update(cx, |project, cx| {
18328            project.open_buffer((worktree_id, "first.rs"), cx)
18329        })
18330        .await
18331        .unwrap();
18332    let buffer_2 = project
18333        .update(cx, |project, cx| {
18334            project.open_buffer((worktree_id, "second.rs"), cx)
18335        })
18336        .await
18337        .unwrap();
18338    let buffer_3 = project
18339        .update(cx, |project, cx| {
18340            project.open_buffer((worktree_id, "third.rs"), cx)
18341        })
18342        .await
18343        .unwrap();
18344
18345    let multi_buffer = cx.new(|cx| {
18346        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18347        multi_buffer.push_excerpts(
18348            buffer_1.clone(),
18349            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18350            cx,
18351        );
18352        multi_buffer.push_excerpts(
18353            buffer_2.clone(),
18354            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18355            cx,
18356        );
18357        multi_buffer.push_excerpts(
18358            buffer_3.clone(),
18359            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18360            cx,
18361        );
18362        multi_buffer
18363    });
18364
18365    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18366        Editor::new(
18367            EditorMode::full(),
18368            multi_buffer,
18369            Some(project.clone()),
18370            window,
18371            cx,
18372        )
18373    });
18374
18375    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18376    assert_eq!(
18377        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18378        full_text,
18379    );
18380
18381    multi_buffer_editor.update(cx, |editor, cx| {
18382        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18383    });
18384    assert_eq!(
18385        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18386        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18387        "After folding the first buffer, its text should not be displayed"
18388    );
18389
18390    multi_buffer_editor.update(cx, |editor, cx| {
18391        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18392    });
18393
18394    assert_eq!(
18395        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18396        "\n\n\n\n\n\n7777\n8888\n9999",
18397        "After folding the second buffer, its text should not be displayed"
18398    );
18399
18400    multi_buffer_editor.update(cx, |editor, cx| {
18401        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18402    });
18403    assert_eq!(
18404        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18405        "\n\n\n\n\n",
18406        "After folding the third buffer, its text should not be displayed"
18407    );
18408
18409    multi_buffer_editor.update(cx, |editor, cx| {
18410        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18411    });
18412    assert_eq!(
18413        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18414        "\n\n\n\n4444\n5555\n6666\n\n",
18415        "After unfolding the second buffer, its text should be displayed"
18416    );
18417
18418    multi_buffer_editor.update(cx, |editor, cx| {
18419        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18420    });
18421    assert_eq!(
18422        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18423        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18424        "After unfolding the first buffer, its text should be displayed"
18425    );
18426
18427    multi_buffer_editor.update(cx, |editor, cx| {
18428        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18429    });
18430    assert_eq!(
18431        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18432        full_text,
18433        "After unfolding all buffers, all original text should be displayed"
18434    );
18435}
18436
18437#[gpui::test]
18438async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18439    init_test(cx, |_| {});
18440
18441    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18442
18443    let fs = FakeFs::new(cx.executor());
18444    fs.insert_tree(
18445        path!("/a"),
18446        json!({
18447            "main.rs": sample_text,
18448        }),
18449    )
18450    .await;
18451    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18452    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18453    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18454    let worktree = project.update(cx, |project, cx| {
18455        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18456        assert_eq!(worktrees.len(), 1);
18457        worktrees.pop().unwrap()
18458    });
18459    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18460
18461    let buffer_1 = project
18462        .update(cx, |project, cx| {
18463            project.open_buffer((worktree_id, "main.rs"), cx)
18464        })
18465        .await
18466        .unwrap();
18467
18468    let multi_buffer = cx.new(|cx| {
18469        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18470        multi_buffer.push_excerpts(
18471            buffer_1.clone(),
18472            [ExcerptRange::new(
18473                Point::new(0, 0)
18474                    ..Point::new(
18475                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18476                        0,
18477                    ),
18478            )],
18479            cx,
18480        );
18481        multi_buffer
18482    });
18483    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18484        Editor::new(
18485            EditorMode::full(),
18486            multi_buffer,
18487            Some(project.clone()),
18488            window,
18489            cx,
18490        )
18491    });
18492
18493    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18494    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18495        enum TestHighlight {}
18496        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18497        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18498        editor.highlight_text::<TestHighlight>(
18499            vec![highlight_range.clone()],
18500            HighlightStyle::color(Hsla::green()),
18501            cx,
18502        );
18503        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18504    });
18505
18506    let full_text = format!("\n\n{sample_text}");
18507    assert_eq!(
18508        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18509        full_text,
18510    );
18511}
18512
18513#[gpui::test]
18514async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18515    init_test(cx, |_| {});
18516    cx.update(|cx| {
18517        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18518            "keymaps/default-linux.json",
18519            cx,
18520        )
18521        .unwrap();
18522        cx.bind_keys(default_key_bindings);
18523    });
18524
18525    let (editor, cx) = cx.add_window_view(|window, cx| {
18526        let multi_buffer = MultiBuffer::build_multi(
18527            [
18528                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18529                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18530                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18531                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18532            ],
18533            cx,
18534        );
18535        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18536
18537        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18538        // fold all but the second buffer, so that we test navigating between two
18539        // adjacent folded buffers, as well as folded buffers at the start and
18540        // end the multibuffer
18541        editor.fold_buffer(buffer_ids[0], cx);
18542        editor.fold_buffer(buffer_ids[2], cx);
18543        editor.fold_buffer(buffer_ids[3], cx);
18544
18545        editor
18546    });
18547    cx.simulate_resize(size(px(1000.), px(1000.)));
18548
18549    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18550    cx.assert_excerpts_with_selections(indoc! {"
18551        [EXCERPT]
18552        ˇ[FOLDED]
18553        [EXCERPT]
18554        a1
18555        b1
18556        [EXCERPT]
18557        [FOLDED]
18558        [EXCERPT]
18559        [FOLDED]
18560        "
18561    });
18562    cx.simulate_keystroke("down");
18563    cx.assert_excerpts_with_selections(indoc! {"
18564        [EXCERPT]
18565        [FOLDED]
18566        [EXCERPT]
18567        ˇa1
18568        b1
18569        [EXCERPT]
18570        [FOLDED]
18571        [EXCERPT]
18572        [FOLDED]
18573        "
18574    });
18575    cx.simulate_keystroke("down");
18576    cx.assert_excerpts_with_selections(indoc! {"
18577        [EXCERPT]
18578        [FOLDED]
18579        [EXCERPT]
18580        a1
18581        ˇb1
18582        [EXCERPT]
18583        [FOLDED]
18584        [EXCERPT]
18585        [FOLDED]
18586        "
18587    });
18588    cx.simulate_keystroke("down");
18589    cx.assert_excerpts_with_selections(indoc! {"
18590        [EXCERPT]
18591        [FOLDED]
18592        [EXCERPT]
18593        a1
18594        b1
18595        ˇ[EXCERPT]
18596        [FOLDED]
18597        [EXCERPT]
18598        [FOLDED]
18599        "
18600    });
18601    cx.simulate_keystroke("down");
18602    cx.assert_excerpts_with_selections(indoc! {"
18603        [EXCERPT]
18604        [FOLDED]
18605        [EXCERPT]
18606        a1
18607        b1
18608        [EXCERPT]
18609        ˇ[FOLDED]
18610        [EXCERPT]
18611        [FOLDED]
18612        "
18613    });
18614    for _ in 0..5 {
18615        cx.simulate_keystroke("down");
18616        cx.assert_excerpts_with_selections(indoc! {"
18617            [EXCERPT]
18618            [FOLDED]
18619            [EXCERPT]
18620            a1
18621            b1
18622            [EXCERPT]
18623            [FOLDED]
18624            [EXCERPT]
18625            ˇ[FOLDED]
18626            "
18627        });
18628    }
18629
18630    cx.simulate_keystroke("up");
18631    cx.assert_excerpts_with_selections(indoc! {"
18632        [EXCERPT]
18633        [FOLDED]
18634        [EXCERPT]
18635        a1
18636        b1
18637        [EXCERPT]
18638        ˇ[FOLDED]
18639        [EXCERPT]
18640        [FOLDED]
18641        "
18642    });
18643    cx.simulate_keystroke("up");
18644    cx.assert_excerpts_with_selections(indoc! {"
18645        [EXCERPT]
18646        [FOLDED]
18647        [EXCERPT]
18648        a1
18649        b1
18650        ˇ[EXCERPT]
18651        [FOLDED]
18652        [EXCERPT]
18653        [FOLDED]
18654        "
18655    });
18656    cx.simulate_keystroke("up");
18657    cx.assert_excerpts_with_selections(indoc! {"
18658        [EXCERPT]
18659        [FOLDED]
18660        [EXCERPT]
18661        a1
18662        ˇb1
18663        [EXCERPT]
18664        [FOLDED]
18665        [EXCERPT]
18666        [FOLDED]
18667        "
18668    });
18669    cx.simulate_keystroke("up");
18670    cx.assert_excerpts_with_selections(indoc! {"
18671        [EXCERPT]
18672        [FOLDED]
18673        [EXCERPT]
18674        ˇa1
18675        b1
18676        [EXCERPT]
18677        [FOLDED]
18678        [EXCERPT]
18679        [FOLDED]
18680        "
18681    });
18682    for _ in 0..5 {
18683        cx.simulate_keystroke("up");
18684        cx.assert_excerpts_with_selections(indoc! {"
18685            [EXCERPT]
18686            ˇ[FOLDED]
18687            [EXCERPT]
18688            a1
18689            b1
18690            [EXCERPT]
18691            [FOLDED]
18692            [EXCERPT]
18693            [FOLDED]
18694            "
18695        });
18696    }
18697}
18698
18699#[gpui::test]
18700async fn test_inline_completion_text(cx: &mut TestAppContext) {
18701    init_test(cx, |_| {});
18702
18703    // Simple insertion
18704    assert_highlighted_edits(
18705        "Hello, world!",
18706        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18707        true,
18708        cx,
18709        |highlighted_edits, cx| {
18710            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18711            assert_eq!(highlighted_edits.highlights.len(), 1);
18712            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18713            assert_eq!(
18714                highlighted_edits.highlights[0].1.background_color,
18715                Some(cx.theme().status().created_background)
18716            );
18717        },
18718    )
18719    .await;
18720
18721    // Replacement
18722    assert_highlighted_edits(
18723        "This is a test.",
18724        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18725        false,
18726        cx,
18727        |highlighted_edits, cx| {
18728            assert_eq!(highlighted_edits.text, "That is a test.");
18729            assert_eq!(highlighted_edits.highlights.len(), 1);
18730            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18731            assert_eq!(
18732                highlighted_edits.highlights[0].1.background_color,
18733                Some(cx.theme().status().created_background)
18734            );
18735        },
18736    )
18737    .await;
18738
18739    // Multiple edits
18740    assert_highlighted_edits(
18741        "Hello, world!",
18742        vec![
18743            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18744            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18745        ],
18746        false,
18747        cx,
18748        |highlighted_edits, cx| {
18749            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18750            assert_eq!(highlighted_edits.highlights.len(), 2);
18751            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18752            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18753            assert_eq!(
18754                highlighted_edits.highlights[0].1.background_color,
18755                Some(cx.theme().status().created_background)
18756            );
18757            assert_eq!(
18758                highlighted_edits.highlights[1].1.background_color,
18759                Some(cx.theme().status().created_background)
18760            );
18761        },
18762    )
18763    .await;
18764
18765    // Multiple lines with edits
18766    assert_highlighted_edits(
18767        "First line\nSecond line\nThird line\nFourth line",
18768        vec![
18769            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18770            (
18771                Point::new(2, 0)..Point::new(2, 10),
18772                "New third line".to_string(),
18773            ),
18774            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18775        ],
18776        false,
18777        cx,
18778        |highlighted_edits, cx| {
18779            assert_eq!(
18780                highlighted_edits.text,
18781                "Second modified\nNew third line\nFourth updated line"
18782            );
18783            assert_eq!(highlighted_edits.highlights.len(), 3);
18784            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18785            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18786            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18787            for highlight in &highlighted_edits.highlights {
18788                assert_eq!(
18789                    highlight.1.background_color,
18790                    Some(cx.theme().status().created_background)
18791                );
18792            }
18793        },
18794    )
18795    .await;
18796}
18797
18798#[gpui::test]
18799async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18800    init_test(cx, |_| {});
18801
18802    // Deletion
18803    assert_highlighted_edits(
18804        "Hello, world!",
18805        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18806        true,
18807        cx,
18808        |highlighted_edits, cx| {
18809            assert_eq!(highlighted_edits.text, "Hello, world!");
18810            assert_eq!(highlighted_edits.highlights.len(), 1);
18811            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18812            assert_eq!(
18813                highlighted_edits.highlights[0].1.background_color,
18814                Some(cx.theme().status().deleted_background)
18815            );
18816        },
18817    )
18818    .await;
18819
18820    // Insertion
18821    assert_highlighted_edits(
18822        "Hello, world!",
18823        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18824        true,
18825        cx,
18826        |highlighted_edits, cx| {
18827            assert_eq!(highlighted_edits.highlights.len(), 1);
18828            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18829            assert_eq!(
18830                highlighted_edits.highlights[0].1.background_color,
18831                Some(cx.theme().status().created_background)
18832            );
18833        },
18834    )
18835    .await;
18836}
18837
18838async fn assert_highlighted_edits(
18839    text: &str,
18840    edits: Vec<(Range<Point>, String)>,
18841    include_deletions: bool,
18842    cx: &mut TestAppContext,
18843    assertion_fn: impl Fn(HighlightedText, &App),
18844) {
18845    let window = cx.add_window(|window, cx| {
18846        let buffer = MultiBuffer::build_simple(text, cx);
18847        Editor::new(EditorMode::full(), buffer, None, window, cx)
18848    });
18849    let cx = &mut VisualTestContext::from_window(*window, cx);
18850
18851    let (buffer, snapshot) = window
18852        .update(cx, |editor, _window, cx| {
18853            (
18854                editor.buffer().clone(),
18855                editor.buffer().read(cx).snapshot(cx),
18856            )
18857        })
18858        .unwrap();
18859
18860    let edits = edits
18861        .into_iter()
18862        .map(|(range, edit)| {
18863            (
18864                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18865                edit,
18866            )
18867        })
18868        .collect::<Vec<_>>();
18869
18870    let text_anchor_edits = edits
18871        .clone()
18872        .into_iter()
18873        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18874        .collect::<Vec<_>>();
18875
18876    let edit_preview = window
18877        .update(cx, |_, _window, cx| {
18878            buffer
18879                .read(cx)
18880                .as_singleton()
18881                .unwrap()
18882                .read(cx)
18883                .preview_edits(text_anchor_edits.into(), cx)
18884        })
18885        .unwrap()
18886        .await;
18887
18888    cx.update(|_window, cx| {
18889        let highlighted_edits = inline_completion_edit_text(
18890            &snapshot.as_singleton().unwrap().2,
18891            &edits,
18892            &edit_preview,
18893            include_deletions,
18894            cx,
18895        );
18896        assertion_fn(highlighted_edits, cx)
18897    });
18898}
18899
18900#[track_caller]
18901fn assert_breakpoint(
18902    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18903    path: &Arc<Path>,
18904    expected: Vec<(u32, Breakpoint)>,
18905) {
18906    if expected.len() == 0usize {
18907        assert!(!breakpoints.contains_key(path), "{}", path.display());
18908    } else {
18909        let mut breakpoint = breakpoints
18910            .get(path)
18911            .unwrap()
18912            .into_iter()
18913            .map(|breakpoint| {
18914                (
18915                    breakpoint.row,
18916                    Breakpoint {
18917                        message: breakpoint.message.clone(),
18918                        state: breakpoint.state,
18919                        condition: breakpoint.condition.clone(),
18920                        hit_condition: breakpoint.hit_condition.clone(),
18921                    },
18922                )
18923            })
18924            .collect::<Vec<_>>();
18925
18926        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18927
18928        assert_eq!(expected, breakpoint);
18929    }
18930}
18931
18932fn add_log_breakpoint_at_cursor(
18933    editor: &mut Editor,
18934    log_message: &str,
18935    window: &mut Window,
18936    cx: &mut Context<Editor>,
18937) {
18938    let (anchor, bp) = editor
18939        .breakpoints_at_cursors(window, cx)
18940        .first()
18941        .and_then(|(anchor, bp)| {
18942            if let Some(bp) = bp {
18943                Some((*anchor, bp.clone()))
18944            } else {
18945                None
18946            }
18947        })
18948        .unwrap_or_else(|| {
18949            let cursor_position: Point = editor.selections.newest(cx).head();
18950
18951            let breakpoint_position = editor
18952                .snapshot(window, cx)
18953                .display_snapshot
18954                .buffer_snapshot
18955                .anchor_before(Point::new(cursor_position.row, 0));
18956
18957            (breakpoint_position, Breakpoint::new_log(&log_message))
18958        });
18959
18960    editor.edit_breakpoint_at_anchor(
18961        anchor,
18962        bp,
18963        BreakpointEditAction::EditLogMessage(log_message.into()),
18964        cx,
18965    );
18966}
18967
18968#[gpui::test]
18969async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18970    init_test(cx, |_| {});
18971
18972    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18973    let fs = FakeFs::new(cx.executor());
18974    fs.insert_tree(
18975        path!("/a"),
18976        json!({
18977            "main.rs": sample_text,
18978        }),
18979    )
18980    .await;
18981    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18982    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18983    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18984
18985    let fs = FakeFs::new(cx.executor());
18986    fs.insert_tree(
18987        path!("/a"),
18988        json!({
18989            "main.rs": sample_text,
18990        }),
18991    )
18992    .await;
18993    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18994    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18995    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18996    let worktree_id = workspace
18997        .update(cx, |workspace, _window, cx| {
18998            workspace.project().update(cx, |project, cx| {
18999                project.worktrees(cx).next().unwrap().read(cx).id()
19000            })
19001        })
19002        .unwrap();
19003
19004    let buffer = project
19005        .update(cx, |project, cx| {
19006            project.open_buffer((worktree_id, "main.rs"), cx)
19007        })
19008        .await
19009        .unwrap();
19010
19011    let (editor, cx) = cx.add_window_view(|window, cx| {
19012        Editor::new(
19013            EditorMode::full(),
19014            MultiBuffer::build_from_buffer(buffer, cx),
19015            Some(project.clone()),
19016            window,
19017            cx,
19018        )
19019    });
19020
19021    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19022    let abs_path = project.read_with(cx, |project, cx| {
19023        project
19024            .absolute_path(&project_path, cx)
19025            .map(|path_buf| Arc::from(path_buf.to_owned()))
19026            .unwrap()
19027    });
19028
19029    // assert we can add breakpoint on the first line
19030    editor.update_in(cx, |editor, window, cx| {
19031        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19032        editor.move_to_end(&MoveToEnd, window, cx);
19033        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19034    });
19035
19036    let breakpoints = editor.update(cx, |editor, cx| {
19037        editor
19038            .breakpoint_store()
19039            .as_ref()
19040            .unwrap()
19041            .read(cx)
19042            .all_source_breakpoints(cx)
19043            .clone()
19044    });
19045
19046    assert_eq!(1, breakpoints.len());
19047    assert_breakpoint(
19048        &breakpoints,
19049        &abs_path,
19050        vec![
19051            (0, Breakpoint::new_standard()),
19052            (3, Breakpoint::new_standard()),
19053        ],
19054    );
19055
19056    editor.update_in(cx, |editor, window, cx| {
19057        editor.move_to_beginning(&MoveToBeginning, window, cx);
19058        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19059    });
19060
19061    let breakpoints = editor.update(cx, |editor, cx| {
19062        editor
19063            .breakpoint_store()
19064            .as_ref()
19065            .unwrap()
19066            .read(cx)
19067            .all_source_breakpoints(cx)
19068            .clone()
19069    });
19070
19071    assert_eq!(1, breakpoints.len());
19072    assert_breakpoint(
19073        &breakpoints,
19074        &abs_path,
19075        vec![(3, Breakpoint::new_standard())],
19076    );
19077
19078    editor.update_in(cx, |editor, window, cx| {
19079        editor.move_to_end(&MoveToEnd, window, cx);
19080        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19081    });
19082
19083    let breakpoints = editor.update(cx, |editor, cx| {
19084        editor
19085            .breakpoint_store()
19086            .as_ref()
19087            .unwrap()
19088            .read(cx)
19089            .all_source_breakpoints(cx)
19090            .clone()
19091    });
19092
19093    assert_eq!(0, breakpoints.len());
19094    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19095}
19096
19097#[gpui::test]
19098async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
19099    init_test(cx, |_| {});
19100
19101    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19102
19103    let fs = FakeFs::new(cx.executor());
19104    fs.insert_tree(
19105        path!("/a"),
19106        json!({
19107            "main.rs": sample_text,
19108        }),
19109    )
19110    .await;
19111    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19112    let (workspace, cx) =
19113        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19114
19115    let worktree_id = workspace.update(cx, |workspace, cx| {
19116        workspace.project().update(cx, |project, cx| {
19117            project.worktrees(cx).next().unwrap().read(cx).id()
19118        })
19119    });
19120
19121    let buffer = project
19122        .update(cx, |project, cx| {
19123            project.open_buffer((worktree_id, "main.rs"), cx)
19124        })
19125        .await
19126        .unwrap();
19127
19128    let (editor, cx) = cx.add_window_view(|window, cx| {
19129        Editor::new(
19130            EditorMode::full(),
19131            MultiBuffer::build_from_buffer(buffer, cx),
19132            Some(project.clone()),
19133            window,
19134            cx,
19135        )
19136    });
19137
19138    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19139    let abs_path = project.read_with(cx, |project, cx| {
19140        project
19141            .absolute_path(&project_path, cx)
19142            .map(|path_buf| Arc::from(path_buf.to_owned()))
19143            .unwrap()
19144    });
19145
19146    editor.update_in(cx, |editor, window, cx| {
19147        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19148    });
19149
19150    let breakpoints = editor.update(cx, |editor, cx| {
19151        editor
19152            .breakpoint_store()
19153            .as_ref()
19154            .unwrap()
19155            .read(cx)
19156            .all_source_breakpoints(cx)
19157            .clone()
19158    });
19159
19160    assert_breakpoint(
19161        &breakpoints,
19162        &abs_path,
19163        vec![(0, Breakpoint::new_log("hello world"))],
19164    );
19165
19166    // Removing a log message from a log breakpoint should remove it
19167    editor.update_in(cx, |editor, window, cx| {
19168        add_log_breakpoint_at_cursor(editor, "", window, cx);
19169    });
19170
19171    let breakpoints = editor.update(cx, |editor, cx| {
19172        editor
19173            .breakpoint_store()
19174            .as_ref()
19175            .unwrap()
19176            .read(cx)
19177            .all_source_breakpoints(cx)
19178            .clone()
19179    });
19180
19181    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19182
19183    editor.update_in(cx, |editor, window, cx| {
19184        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19185        editor.move_to_end(&MoveToEnd, window, cx);
19186        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19187        // Not adding a log message to a standard breakpoint shouldn't remove it
19188        add_log_breakpoint_at_cursor(editor, "", window, cx);
19189    });
19190
19191    let breakpoints = editor.update(cx, |editor, cx| {
19192        editor
19193            .breakpoint_store()
19194            .as_ref()
19195            .unwrap()
19196            .read(cx)
19197            .all_source_breakpoints(cx)
19198            .clone()
19199    });
19200
19201    assert_breakpoint(
19202        &breakpoints,
19203        &abs_path,
19204        vec![
19205            (0, Breakpoint::new_standard()),
19206            (3, Breakpoint::new_standard()),
19207        ],
19208    );
19209
19210    editor.update_in(cx, |editor, window, cx| {
19211        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19212    });
19213
19214    let breakpoints = editor.update(cx, |editor, cx| {
19215        editor
19216            .breakpoint_store()
19217            .as_ref()
19218            .unwrap()
19219            .read(cx)
19220            .all_source_breakpoints(cx)
19221            .clone()
19222    });
19223
19224    assert_breakpoint(
19225        &breakpoints,
19226        &abs_path,
19227        vec![
19228            (0, Breakpoint::new_standard()),
19229            (3, Breakpoint::new_log("hello world")),
19230        ],
19231    );
19232
19233    editor.update_in(cx, |editor, window, cx| {
19234        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19235    });
19236
19237    let breakpoints = editor.update(cx, |editor, cx| {
19238        editor
19239            .breakpoint_store()
19240            .as_ref()
19241            .unwrap()
19242            .read(cx)
19243            .all_source_breakpoints(cx)
19244            .clone()
19245    });
19246
19247    assert_breakpoint(
19248        &breakpoints,
19249        &abs_path,
19250        vec![
19251            (0, Breakpoint::new_standard()),
19252            (3, Breakpoint::new_log("hello Earth!!")),
19253        ],
19254    );
19255}
19256
19257/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19258/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19259/// or when breakpoints were placed out of order. This tests for a regression too
19260#[gpui::test]
19261async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19262    init_test(cx, |_| {});
19263
19264    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19265    let fs = FakeFs::new(cx.executor());
19266    fs.insert_tree(
19267        path!("/a"),
19268        json!({
19269            "main.rs": sample_text,
19270        }),
19271    )
19272    .await;
19273    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19274    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19275    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19276
19277    let fs = FakeFs::new(cx.executor());
19278    fs.insert_tree(
19279        path!("/a"),
19280        json!({
19281            "main.rs": sample_text,
19282        }),
19283    )
19284    .await;
19285    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19286    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19287    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19288    let worktree_id = workspace
19289        .update(cx, |workspace, _window, cx| {
19290            workspace.project().update(cx, |project, cx| {
19291                project.worktrees(cx).next().unwrap().read(cx).id()
19292            })
19293        })
19294        .unwrap();
19295
19296    let buffer = project
19297        .update(cx, |project, cx| {
19298            project.open_buffer((worktree_id, "main.rs"), cx)
19299        })
19300        .await
19301        .unwrap();
19302
19303    let (editor, cx) = cx.add_window_view(|window, cx| {
19304        Editor::new(
19305            EditorMode::full(),
19306            MultiBuffer::build_from_buffer(buffer, cx),
19307            Some(project.clone()),
19308            window,
19309            cx,
19310        )
19311    });
19312
19313    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19314    let abs_path = project.read_with(cx, |project, cx| {
19315        project
19316            .absolute_path(&project_path, cx)
19317            .map(|path_buf| Arc::from(path_buf.to_owned()))
19318            .unwrap()
19319    });
19320
19321    // assert we can add breakpoint on the first line
19322    editor.update_in(cx, |editor, window, cx| {
19323        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19324        editor.move_to_end(&MoveToEnd, window, cx);
19325        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19326        editor.move_up(&MoveUp, window, cx);
19327        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19328    });
19329
19330    let breakpoints = editor.update(cx, |editor, cx| {
19331        editor
19332            .breakpoint_store()
19333            .as_ref()
19334            .unwrap()
19335            .read(cx)
19336            .all_source_breakpoints(cx)
19337            .clone()
19338    });
19339
19340    assert_eq!(1, breakpoints.len());
19341    assert_breakpoint(
19342        &breakpoints,
19343        &abs_path,
19344        vec![
19345            (0, Breakpoint::new_standard()),
19346            (2, Breakpoint::new_standard()),
19347            (3, Breakpoint::new_standard()),
19348        ],
19349    );
19350
19351    editor.update_in(cx, |editor, window, cx| {
19352        editor.move_to_beginning(&MoveToBeginning, window, cx);
19353        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19354        editor.move_to_end(&MoveToEnd, window, cx);
19355        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19356        // Disabling a breakpoint that doesn't exist should do nothing
19357        editor.move_up(&MoveUp, window, cx);
19358        editor.move_up(&MoveUp, window, cx);
19359        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19360    });
19361
19362    let breakpoints = editor.update(cx, |editor, cx| {
19363        editor
19364            .breakpoint_store()
19365            .as_ref()
19366            .unwrap()
19367            .read(cx)
19368            .all_source_breakpoints(cx)
19369            .clone()
19370    });
19371
19372    let disable_breakpoint = {
19373        let mut bp = Breakpoint::new_standard();
19374        bp.state = BreakpointState::Disabled;
19375        bp
19376    };
19377
19378    assert_eq!(1, breakpoints.len());
19379    assert_breakpoint(
19380        &breakpoints,
19381        &abs_path,
19382        vec![
19383            (0, disable_breakpoint.clone()),
19384            (2, Breakpoint::new_standard()),
19385            (3, disable_breakpoint.clone()),
19386        ],
19387    );
19388
19389    editor.update_in(cx, |editor, window, cx| {
19390        editor.move_to_beginning(&MoveToBeginning, window, cx);
19391        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19392        editor.move_to_end(&MoveToEnd, window, cx);
19393        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19394        editor.move_up(&MoveUp, window, cx);
19395        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19396    });
19397
19398    let breakpoints = editor.update(cx, |editor, cx| {
19399        editor
19400            .breakpoint_store()
19401            .as_ref()
19402            .unwrap()
19403            .read(cx)
19404            .all_source_breakpoints(cx)
19405            .clone()
19406    });
19407
19408    assert_eq!(1, breakpoints.len());
19409    assert_breakpoint(
19410        &breakpoints,
19411        &abs_path,
19412        vec![
19413            (0, Breakpoint::new_standard()),
19414            (2, disable_breakpoint),
19415            (3, Breakpoint::new_standard()),
19416        ],
19417    );
19418}
19419
19420#[gpui::test]
19421async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19422    init_test(cx, |_| {});
19423    let capabilities = lsp::ServerCapabilities {
19424        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19425            prepare_provider: Some(true),
19426            work_done_progress_options: Default::default(),
19427        })),
19428        ..Default::default()
19429    };
19430    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19431
19432    cx.set_state(indoc! {"
19433        struct Fˇoo {}
19434    "});
19435
19436    cx.update_editor(|editor, _, cx| {
19437        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19438        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19439        editor.highlight_background::<DocumentHighlightRead>(
19440            &[highlight_range],
19441            |c| c.editor_document_highlight_read_background,
19442            cx,
19443        );
19444    });
19445
19446    let mut prepare_rename_handler = cx
19447        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19448            move |_, _, _| async move {
19449                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19450                    start: lsp::Position {
19451                        line: 0,
19452                        character: 7,
19453                    },
19454                    end: lsp::Position {
19455                        line: 0,
19456                        character: 10,
19457                    },
19458                })))
19459            },
19460        );
19461    let prepare_rename_task = cx
19462        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19463        .expect("Prepare rename was not started");
19464    prepare_rename_handler.next().await.unwrap();
19465    prepare_rename_task.await.expect("Prepare rename failed");
19466
19467    let mut rename_handler =
19468        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19469            let edit = lsp::TextEdit {
19470                range: lsp::Range {
19471                    start: lsp::Position {
19472                        line: 0,
19473                        character: 7,
19474                    },
19475                    end: lsp::Position {
19476                        line: 0,
19477                        character: 10,
19478                    },
19479                },
19480                new_text: "FooRenamed".to_string(),
19481            };
19482            Ok(Some(lsp::WorkspaceEdit::new(
19483                // Specify the same edit twice
19484                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19485            )))
19486        });
19487    let rename_task = cx
19488        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19489        .expect("Confirm rename was not started");
19490    rename_handler.next().await.unwrap();
19491    rename_task.await.expect("Confirm rename failed");
19492    cx.run_until_parked();
19493
19494    // Despite two edits, only one is actually applied as those are identical
19495    cx.assert_editor_state(indoc! {"
19496        struct FooRenamedˇ {}
19497    "});
19498}
19499
19500#[gpui::test]
19501async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19502    init_test(cx, |_| {});
19503    // These capabilities indicate that the server does not support prepare rename.
19504    let capabilities = lsp::ServerCapabilities {
19505        rename_provider: Some(lsp::OneOf::Left(true)),
19506        ..Default::default()
19507    };
19508    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19509
19510    cx.set_state(indoc! {"
19511        struct Fˇoo {}
19512    "});
19513
19514    cx.update_editor(|editor, _window, cx| {
19515        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19516        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19517        editor.highlight_background::<DocumentHighlightRead>(
19518            &[highlight_range],
19519            |c| c.editor_document_highlight_read_background,
19520            cx,
19521        );
19522    });
19523
19524    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19525        .expect("Prepare rename was not started")
19526        .await
19527        .expect("Prepare rename failed");
19528
19529    let mut rename_handler =
19530        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19531            let edit = lsp::TextEdit {
19532                range: lsp::Range {
19533                    start: lsp::Position {
19534                        line: 0,
19535                        character: 7,
19536                    },
19537                    end: lsp::Position {
19538                        line: 0,
19539                        character: 10,
19540                    },
19541                },
19542                new_text: "FooRenamed".to_string(),
19543            };
19544            Ok(Some(lsp::WorkspaceEdit::new(
19545                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19546            )))
19547        });
19548    let rename_task = cx
19549        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19550        .expect("Confirm rename was not started");
19551    rename_handler.next().await.unwrap();
19552    rename_task.await.expect("Confirm rename failed");
19553    cx.run_until_parked();
19554
19555    // Correct range is renamed, as `surrounding_word` is used to find it.
19556    cx.assert_editor_state(indoc! {"
19557        struct FooRenamedˇ {}
19558    "});
19559}
19560
19561#[gpui::test]
19562async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19563    init_test(cx, |_| {});
19564    let mut cx = EditorTestContext::new(cx).await;
19565
19566    let language = Arc::new(
19567        Language::new(
19568            LanguageConfig::default(),
19569            Some(tree_sitter_html::LANGUAGE.into()),
19570        )
19571        .with_brackets_query(
19572            r#"
19573            ("<" @open "/>" @close)
19574            ("</" @open ">" @close)
19575            ("<" @open ">" @close)
19576            ("\"" @open "\"" @close)
19577            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19578        "#,
19579        )
19580        .unwrap(),
19581    );
19582    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19583
19584    cx.set_state(indoc! {"
19585        <span>ˇ</span>
19586    "});
19587    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19588    cx.assert_editor_state(indoc! {"
19589        <span>
19590        ˇ
19591        </span>
19592    "});
19593
19594    cx.set_state(indoc! {"
19595        <span><span></span>ˇ</span>
19596    "});
19597    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19598    cx.assert_editor_state(indoc! {"
19599        <span><span></span>
19600        ˇ</span>
19601    "});
19602
19603    cx.set_state(indoc! {"
19604        <span>ˇ
19605        </span>
19606    "});
19607    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19608    cx.assert_editor_state(indoc! {"
19609        <span>
19610        ˇ
19611        </span>
19612    "});
19613}
19614
19615#[gpui::test(iterations = 10)]
19616async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19617    init_test(cx, |_| {});
19618
19619    let fs = FakeFs::new(cx.executor());
19620    fs.insert_tree(
19621        path!("/dir"),
19622        json!({
19623            "a.ts": "a",
19624        }),
19625    )
19626    .await;
19627
19628    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19629    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19630    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19631
19632    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19633    language_registry.add(Arc::new(Language::new(
19634        LanguageConfig {
19635            name: "TypeScript".into(),
19636            matcher: LanguageMatcher {
19637                path_suffixes: vec!["ts".to_string()],
19638                ..Default::default()
19639            },
19640            ..Default::default()
19641        },
19642        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19643    )));
19644    let mut fake_language_servers = language_registry.register_fake_lsp(
19645        "TypeScript",
19646        FakeLspAdapter {
19647            capabilities: lsp::ServerCapabilities {
19648                code_lens_provider: Some(lsp::CodeLensOptions {
19649                    resolve_provider: Some(true),
19650                }),
19651                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19652                    commands: vec!["_the/command".to_string()],
19653                    ..lsp::ExecuteCommandOptions::default()
19654                }),
19655                ..lsp::ServerCapabilities::default()
19656            },
19657            ..FakeLspAdapter::default()
19658        },
19659    );
19660
19661    let (buffer, _handle) = project
19662        .update(cx, |p, cx| {
19663            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19664        })
19665        .await
19666        .unwrap();
19667    cx.executor().run_until_parked();
19668
19669    let fake_server = fake_language_servers.next().await.unwrap();
19670
19671    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19672    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19673    drop(buffer_snapshot);
19674    let actions = cx
19675        .update_window(*workspace, |_, window, cx| {
19676            project.code_actions(&buffer, anchor..anchor, window, cx)
19677        })
19678        .unwrap();
19679
19680    fake_server
19681        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19682            Ok(Some(vec![
19683                lsp::CodeLens {
19684                    range: lsp::Range::default(),
19685                    command: Some(lsp::Command {
19686                        title: "Code lens command".to_owned(),
19687                        command: "_the/command".to_owned(),
19688                        arguments: None,
19689                    }),
19690                    data: None,
19691                },
19692                lsp::CodeLens {
19693                    range: lsp::Range::default(),
19694                    command: Some(lsp::Command {
19695                        title: "Command not in capabilities".to_owned(),
19696                        command: "not in capabilities".to_owned(),
19697                        arguments: None,
19698                    }),
19699                    data: None,
19700                },
19701                lsp::CodeLens {
19702                    range: lsp::Range {
19703                        start: lsp::Position {
19704                            line: 1,
19705                            character: 1,
19706                        },
19707                        end: lsp::Position {
19708                            line: 1,
19709                            character: 1,
19710                        },
19711                    },
19712                    command: Some(lsp::Command {
19713                        title: "Command not in range".to_owned(),
19714                        command: "_the/command".to_owned(),
19715                        arguments: None,
19716                    }),
19717                    data: None,
19718                },
19719            ]))
19720        })
19721        .next()
19722        .await;
19723
19724    let actions = actions.await.unwrap();
19725    assert_eq!(
19726        actions.len(),
19727        1,
19728        "Should have only one valid action for the 0..0 range"
19729    );
19730    let action = actions[0].clone();
19731    let apply = project.update(cx, |project, cx| {
19732        project.apply_code_action(buffer.clone(), action, true, cx)
19733    });
19734
19735    // Resolving the code action does not populate its edits. In absence of
19736    // edits, we must execute the given command.
19737    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19738        |mut lens, _| async move {
19739            let lens_command = lens.command.as_mut().expect("should have a command");
19740            assert_eq!(lens_command.title, "Code lens command");
19741            lens_command.arguments = Some(vec![json!("the-argument")]);
19742            Ok(lens)
19743        },
19744    );
19745
19746    // While executing the command, the language server sends the editor
19747    // a `workspaceEdit` request.
19748    fake_server
19749        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19750            let fake = fake_server.clone();
19751            move |params, _| {
19752                assert_eq!(params.command, "_the/command");
19753                let fake = fake.clone();
19754                async move {
19755                    fake.server
19756                        .request::<lsp::request::ApplyWorkspaceEdit>(
19757                            lsp::ApplyWorkspaceEditParams {
19758                                label: None,
19759                                edit: lsp::WorkspaceEdit {
19760                                    changes: Some(
19761                                        [(
19762                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19763                                            vec![lsp::TextEdit {
19764                                                range: lsp::Range::new(
19765                                                    lsp::Position::new(0, 0),
19766                                                    lsp::Position::new(0, 0),
19767                                                ),
19768                                                new_text: "X".into(),
19769                                            }],
19770                                        )]
19771                                        .into_iter()
19772                                        .collect(),
19773                                    ),
19774                                    ..Default::default()
19775                                },
19776                            },
19777                        )
19778                        .await
19779                        .into_response()
19780                        .unwrap();
19781                    Ok(Some(json!(null)))
19782                }
19783            }
19784        })
19785        .next()
19786        .await;
19787
19788    // Applying the code lens command returns a project transaction containing the edits
19789    // sent by the language server in its `workspaceEdit` request.
19790    let transaction = apply.await.unwrap();
19791    assert!(transaction.0.contains_key(&buffer));
19792    buffer.update(cx, |buffer, cx| {
19793        assert_eq!(buffer.text(), "Xa");
19794        buffer.undo(cx);
19795        assert_eq!(buffer.text(), "a");
19796    });
19797}
19798
19799#[gpui::test]
19800async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19801    init_test(cx, |_| {});
19802
19803    let fs = FakeFs::new(cx.executor());
19804    let main_text = r#"fn main() {
19805println!("1");
19806println!("2");
19807println!("3");
19808println!("4");
19809println!("5");
19810}"#;
19811    let lib_text = "mod foo {}";
19812    fs.insert_tree(
19813        path!("/a"),
19814        json!({
19815            "lib.rs": lib_text,
19816            "main.rs": main_text,
19817        }),
19818    )
19819    .await;
19820
19821    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19822    let (workspace, cx) =
19823        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19824    let worktree_id = workspace.update(cx, |workspace, cx| {
19825        workspace.project().update(cx, |project, cx| {
19826            project.worktrees(cx).next().unwrap().read(cx).id()
19827        })
19828    });
19829
19830    let expected_ranges = vec![
19831        Point::new(0, 0)..Point::new(0, 0),
19832        Point::new(1, 0)..Point::new(1, 1),
19833        Point::new(2, 0)..Point::new(2, 2),
19834        Point::new(3, 0)..Point::new(3, 3),
19835    ];
19836
19837    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19838    let editor_1 = workspace
19839        .update_in(cx, |workspace, window, cx| {
19840            workspace.open_path(
19841                (worktree_id, "main.rs"),
19842                Some(pane_1.downgrade()),
19843                true,
19844                window,
19845                cx,
19846            )
19847        })
19848        .unwrap()
19849        .await
19850        .downcast::<Editor>()
19851        .unwrap();
19852    pane_1.update(cx, |pane, cx| {
19853        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19854        open_editor.update(cx, |editor, cx| {
19855            assert_eq!(
19856                editor.display_text(cx),
19857                main_text,
19858                "Original main.rs text on initial open",
19859            );
19860            assert_eq!(
19861                editor
19862                    .selections
19863                    .all::<Point>(cx)
19864                    .into_iter()
19865                    .map(|s| s.range())
19866                    .collect::<Vec<_>>(),
19867                vec![Point::zero()..Point::zero()],
19868                "Default selections on initial open",
19869            );
19870        })
19871    });
19872    editor_1.update_in(cx, |editor, window, cx| {
19873        editor.change_selections(None, window, cx, |s| {
19874            s.select_ranges(expected_ranges.clone());
19875        });
19876    });
19877
19878    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19879        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19880    });
19881    let editor_2 = workspace
19882        .update_in(cx, |workspace, window, cx| {
19883            workspace.open_path(
19884                (worktree_id, "main.rs"),
19885                Some(pane_2.downgrade()),
19886                true,
19887                window,
19888                cx,
19889            )
19890        })
19891        .unwrap()
19892        .await
19893        .downcast::<Editor>()
19894        .unwrap();
19895    pane_2.update(cx, |pane, cx| {
19896        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19897        open_editor.update(cx, |editor, cx| {
19898            assert_eq!(
19899                editor.display_text(cx),
19900                main_text,
19901                "Original main.rs text on initial open in another panel",
19902            );
19903            assert_eq!(
19904                editor
19905                    .selections
19906                    .all::<Point>(cx)
19907                    .into_iter()
19908                    .map(|s| s.range())
19909                    .collect::<Vec<_>>(),
19910                vec![Point::zero()..Point::zero()],
19911                "Default selections on initial open in another panel",
19912            );
19913        })
19914    });
19915
19916    editor_2.update_in(cx, |editor, window, cx| {
19917        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19918    });
19919
19920    let _other_editor_1 = workspace
19921        .update_in(cx, |workspace, window, cx| {
19922            workspace.open_path(
19923                (worktree_id, "lib.rs"),
19924                Some(pane_1.downgrade()),
19925                true,
19926                window,
19927                cx,
19928            )
19929        })
19930        .unwrap()
19931        .await
19932        .downcast::<Editor>()
19933        .unwrap();
19934    pane_1
19935        .update_in(cx, |pane, window, cx| {
19936            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19937                .unwrap()
19938        })
19939        .await
19940        .unwrap();
19941    drop(editor_1);
19942    pane_1.update(cx, |pane, cx| {
19943        pane.active_item()
19944            .unwrap()
19945            .downcast::<Editor>()
19946            .unwrap()
19947            .update(cx, |editor, cx| {
19948                assert_eq!(
19949                    editor.display_text(cx),
19950                    lib_text,
19951                    "Other file should be open and active",
19952                );
19953            });
19954        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19955    });
19956
19957    let _other_editor_2 = workspace
19958        .update_in(cx, |workspace, window, cx| {
19959            workspace.open_path(
19960                (worktree_id, "lib.rs"),
19961                Some(pane_2.downgrade()),
19962                true,
19963                window,
19964                cx,
19965            )
19966        })
19967        .unwrap()
19968        .await
19969        .downcast::<Editor>()
19970        .unwrap();
19971    pane_2
19972        .update_in(cx, |pane, window, cx| {
19973            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19974                .unwrap()
19975        })
19976        .await
19977        .unwrap();
19978    drop(editor_2);
19979    pane_2.update(cx, |pane, cx| {
19980        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19981        open_editor.update(cx, |editor, cx| {
19982            assert_eq!(
19983                editor.display_text(cx),
19984                lib_text,
19985                "Other file should be open and active in another panel too",
19986            );
19987        });
19988        assert_eq!(
19989            pane.items().count(),
19990            1,
19991            "No other editors should be open in another pane",
19992        );
19993    });
19994
19995    let _editor_1_reopened = workspace
19996        .update_in(cx, |workspace, window, cx| {
19997            workspace.open_path(
19998                (worktree_id, "main.rs"),
19999                Some(pane_1.downgrade()),
20000                true,
20001                window,
20002                cx,
20003            )
20004        })
20005        .unwrap()
20006        .await
20007        .downcast::<Editor>()
20008        .unwrap();
20009    let _editor_2_reopened = workspace
20010        .update_in(cx, |workspace, window, cx| {
20011            workspace.open_path(
20012                (worktree_id, "main.rs"),
20013                Some(pane_2.downgrade()),
20014                true,
20015                window,
20016                cx,
20017            )
20018        })
20019        .unwrap()
20020        .await
20021        .downcast::<Editor>()
20022        .unwrap();
20023    pane_1.update(cx, |pane, cx| {
20024        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20025        open_editor.update(cx, |editor, cx| {
20026            assert_eq!(
20027                editor.display_text(cx),
20028                main_text,
20029                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
20030            );
20031            assert_eq!(
20032                editor
20033                    .selections
20034                    .all::<Point>(cx)
20035                    .into_iter()
20036                    .map(|s| s.range())
20037                    .collect::<Vec<_>>(),
20038                expected_ranges,
20039                "Previous editor in the 1st panel had selections and should get them restored on reopen",
20040            );
20041        })
20042    });
20043    pane_2.update(cx, |pane, cx| {
20044        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20045        open_editor.update(cx, |editor, cx| {
20046            assert_eq!(
20047                editor.display_text(cx),
20048                r#"fn main() {
20049⋯rintln!("1");
20050⋯intln!("2");
20051⋯ntln!("3");
20052println!("4");
20053println!("5");
20054}"#,
20055                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
20056            );
20057            assert_eq!(
20058                editor
20059                    .selections
20060                    .all::<Point>(cx)
20061                    .into_iter()
20062                    .map(|s| s.range())
20063                    .collect::<Vec<_>>(),
20064                vec![Point::zero()..Point::zero()],
20065                "Previous editor in the 2nd pane had no selections changed hence should restore none",
20066            );
20067        })
20068    });
20069}
20070
20071#[gpui::test]
20072async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
20073    init_test(cx, |_| {});
20074
20075    let fs = FakeFs::new(cx.executor());
20076    let main_text = r#"fn main() {
20077println!("1");
20078println!("2");
20079println!("3");
20080println!("4");
20081println!("5");
20082}"#;
20083    let lib_text = "mod foo {}";
20084    fs.insert_tree(
20085        path!("/a"),
20086        json!({
20087            "lib.rs": lib_text,
20088            "main.rs": main_text,
20089        }),
20090    )
20091    .await;
20092
20093    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20094    let (workspace, cx) =
20095        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20096    let worktree_id = workspace.update(cx, |workspace, cx| {
20097        workspace.project().update(cx, |project, cx| {
20098            project.worktrees(cx).next().unwrap().read(cx).id()
20099        })
20100    });
20101
20102    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20103    let editor = workspace
20104        .update_in(cx, |workspace, window, cx| {
20105            workspace.open_path(
20106                (worktree_id, "main.rs"),
20107                Some(pane.downgrade()),
20108                true,
20109                window,
20110                cx,
20111            )
20112        })
20113        .unwrap()
20114        .await
20115        .downcast::<Editor>()
20116        .unwrap();
20117    pane.update(cx, |pane, cx| {
20118        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20119        open_editor.update(cx, |editor, cx| {
20120            assert_eq!(
20121                editor.display_text(cx),
20122                main_text,
20123                "Original main.rs text on initial open",
20124            );
20125        })
20126    });
20127    editor.update_in(cx, |editor, window, cx| {
20128        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
20129    });
20130
20131    cx.update_global(|store: &mut SettingsStore, cx| {
20132        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20133            s.restore_on_file_reopen = Some(false);
20134        });
20135    });
20136    editor.update_in(cx, |editor, window, cx| {
20137        editor.fold_ranges(
20138            vec![
20139                Point::new(1, 0)..Point::new(1, 1),
20140                Point::new(2, 0)..Point::new(2, 2),
20141                Point::new(3, 0)..Point::new(3, 3),
20142            ],
20143            false,
20144            window,
20145            cx,
20146        );
20147    });
20148    pane.update_in(cx, |pane, window, cx| {
20149        pane.close_all_items(&CloseAllItems::default(), window, cx)
20150            .unwrap()
20151    })
20152    .await
20153    .unwrap();
20154    pane.update(cx, |pane, _| {
20155        assert!(pane.active_item().is_none());
20156    });
20157    cx.update_global(|store: &mut SettingsStore, cx| {
20158        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20159            s.restore_on_file_reopen = Some(true);
20160        });
20161    });
20162
20163    let _editor_reopened = workspace
20164        .update_in(cx, |workspace, window, cx| {
20165            workspace.open_path(
20166                (worktree_id, "main.rs"),
20167                Some(pane.downgrade()),
20168                true,
20169                window,
20170                cx,
20171            )
20172        })
20173        .unwrap()
20174        .await
20175        .downcast::<Editor>()
20176        .unwrap();
20177    pane.update(cx, |pane, cx| {
20178        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20179        open_editor.update(cx, |editor, cx| {
20180            assert_eq!(
20181                editor.display_text(cx),
20182                main_text,
20183                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20184            );
20185        })
20186    });
20187}
20188
20189#[gpui::test]
20190async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20191    struct EmptyModalView {
20192        focus_handle: gpui::FocusHandle,
20193    }
20194    impl EventEmitter<DismissEvent> for EmptyModalView {}
20195    impl Render for EmptyModalView {
20196        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20197            div()
20198        }
20199    }
20200    impl Focusable for EmptyModalView {
20201        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20202            self.focus_handle.clone()
20203        }
20204    }
20205    impl workspace::ModalView for EmptyModalView {}
20206    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20207        EmptyModalView {
20208            focus_handle: cx.focus_handle(),
20209        }
20210    }
20211
20212    init_test(cx, |_| {});
20213
20214    let fs = FakeFs::new(cx.executor());
20215    let project = Project::test(fs, [], cx).await;
20216    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20217    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20218    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20219    let editor = cx.new_window_entity(|window, cx| {
20220        Editor::new(
20221            EditorMode::full(),
20222            buffer,
20223            Some(project.clone()),
20224            window,
20225            cx,
20226        )
20227    });
20228    workspace
20229        .update(cx, |workspace, window, cx| {
20230            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20231        })
20232        .unwrap();
20233    editor.update_in(cx, |editor, window, cx| {
20234        editor.open_context_menu(&OpenContextMenu, window, cx);
20235        assert!(editor.mouse_context_menu.is_some());
20236    });
20237    workspace
20238        .update(cx, |workspace, window, cx| {
20239            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20240        })
20241        .unwrap();
20242    cx.read(|cx| {
20243        assert!(editor.read(cx).mouse_context_menu.is_none());
20244    });
20245}
20246
20247#[gpui::test]
20248async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20249    init_test(cx, |_| {});
20250
20251    let fs = FakeFs::new(cx.executor());
20252    fs.insert_file(path!("/file.html"), Default::default())
20253        .await;
20254
20255    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20256
20257    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20258    let html_language = Arc::new(Language::new(
20259        LanguageConfig {
20260            name: "HTML".into(),
20261            matcher: LanguageMatcher {
20262                path_suffixes: vec!["html".to_string()],
20263                ..LanguageMatcher::default()
20264            },
20265            brackets: BracketPairConfig {
20266                pairs: vec![BracketPair {
20267                    start: "<".into(),
20268                    end: ">".into(),
20269                    close: true,
20270                    ..Default::default()
20271                }],
20272                ..Default::default()
20273            },
20274            ..Default::default()
20275        },
20276        Some(tree_sitter_html::LANGUAGE.into()),
20277    ));
20278    language_registry.add(html_language);
20279    let mut fake_servers = language_registry.register_fake_lsp(
20280        "HTML",
20281        FakeLspAdapter {
20282            capabilities: lsp::ServerCapabilities {
20283                completion_provider: Some(lsp::CompletionOptions {
20284                    resolve_provider: Some(true),
20285                    ..Default::default()
20286                }),
20287                ..Default::default()
20288            },
20289            ..Default::default()
20290        },
20291    );
20292
20293    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20294    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20295
20296    let worktree_id = workspace
20297        .update(cx, |workspace, _window, cx| {
20298            workspace.project().update(cx, |project, cx| {
20299                project.worktrees(cx).next().unwrap().read(cx).id()
20300            })
20301        })
20302        .unwrap();
20303    project
20304        .update(cx, |project, cx| {
20305            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20306        })
20307        .await
20308        .unwrap();
20309    let editor = workspace
20310        .update(cx, |workspace, window, cx| {
20311            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20312        })
20313        .unwrap()
20314        .await
20315        .unwrap()
20316        .downcast::<Editor>()
20317        .unwrap();
20318
20319    let fake_server = fake_servers.next().await.unwrap();
20320    editor.update_in(cx, |editor, window, cx| {
20321        editor.set_text("<ad></ad>", window, cx);
20322        editor.change_selections(None, window, cx, |selections| {
20323            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20324        });
20325        let Some((buffer, _)) = editor
20326            .buffer
20327            .read(cx)
20328            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20329        else {
20330            panic!("Failed to get buffer for selection position");
20331        };
20332        let buffer = buffer.read(cx);
20333        let buffer_id = buffer.remote_id();
20334        let opening_range =
20335            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20336        let closing_range =
20337            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20338        let mut linked_ranges = HashMap::default();
20339        linked_ranges.insert(
20340            buffer_id,
20341            vec![(opening_range.clone(), vec![closing_range.clone()])],
20342        );
20343        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20344    });
20345    let mut completion_handle =
20346        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20347            Ok(Some(lsp::CompletionResponse::Array(vec![
20348                lsp::CompletionItem {
20349                    label: "head".to_string(),
20350                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20351                        lsp::InsertReplaceEdit {
20352                            new_text: "head".to_string(),
20353                            insert: lsp::Range::new(
20354                                lsp::Position::new(0, 1),
20355                                lsp::Position::new(0, 3),
20356                            ),
20357                            replace: lsp::Range::new(
20358                                lsp::Position::new(0, 1),
20359                                lsp::Position::new(0, 3),
20360                            ),
20361                        },
20362                    )),
20363                    ..Default::default()
20364                },
20365            ])))
20366        });
20367    editor.update_in(cx, |editor, window, cx| {
20368        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20369    });
20370    cx.run_until_parked();
20371    completion_handle.next().await.unwrap();
20372    editor.update(cx, |editor, _| {
20373        assert!(
20374            editor.context_menu_visible(),
20375            "Completion menu should be visible"
20376        );
20377    });
20378    editor.update_in(cx, |editor, window, cx| {
20379        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20380    });
20381    cx.executor().run_until_parked();
20382    editor.update(cx, |editor, cx| {
20383        assert_eq!(editor.text(cx), "<head></head>");
20384    });
20385}
20386
20387#[gpui::test]
20388async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20389    init_test(cx, |_| {});
20390
20391    let fs = FakeFs::new(cx.executor());
20392    fs.insert_tree(
20393        path!("/root"),
20394        json!({
20395            "a": {
20396                "main.rs": "fn main() {}",
20397            },
20398            "foo": {
20399                "bar": {
20400                    "external_file.rs": "pub mod external {}",
20401                }
20402            }
20403        }),
20404    )
20405    .await;
20406
20407    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20408    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20409    language_registry.add(rust_lang());
20410    let _fake_servers = language_registry.register_fake_lsp(
20411        "Rust",
20412        FakeLspAdapter {
20413            ..FakeLspAdapter::default()
20414        },
20415    );
20416    let (workspace, cx) =
20417        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20418    let worktree_id = workspace.update(cx, |workspace, cx| {
20419        workspace.project().update(cx, |project, cx| {
20420            project.worktrees(cx).next().unwrap().read(cx).id()
20421        })
20422    });
20423
20424    let assert_language_servers_count =
20425        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20426            project.update(cx, |project, cx| {
20427                let current = project
20428                    .lsp_store()
20429                    .read(cx)
20430                    .as_local()
20431                    .unwrap()
20432                    .language_servers
20433                    .len();
20434                assert_eq!(expected, current, "{context}");
20435            });
20436        };
20437
20438    assert_language_servers_count(
20439        0,
20440        "No servers should be running before any file is open",
20441        cx,
20442    );
20443    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20444    let main_editor = workspace
20445        .update_in(cx, |workspace, window, cx| {
20446            workspace.open_path(
20447                (worktree_id, "main.rs"),
20448                Some(pane.downgrade()),
20449                true,
20450                window,
20451                cx,
20452            )
20453        })
20454        .unwrap()
20455        .await
20456        .downcast::<Editor>()
20457        .unwrap();
20458    pane.update(cx, |pane, cx| {
20459        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20460        open_editor.update(cx, |editor, cx| {
20461            assert_eq!(
20462                editor.display_text(cx),
20463                "fn main() {}",
20464                "Original main.rs text on initial open",
20465            );
20466        });
20467        assert_eq!(open_editor, main_editor);
20468    });
20469    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20470
20471    let external_editor = workspace
20472        .update_in(cx, |workspace, window, cx| {
20473            workspace.open_abs_path(
20474                PathBuf::from("/root/foo/bar/external_file.rs"),
20475                OpenOptions::default(),
20476                window,
20477                cx,
20478            )
20479        })
20480        .await
20481        .expect("opening external file")
20482        .downcast::<Editor>()
20483        .expect("downcasted external file's open element to editor");
20484    pane.update(cx, |pane, cx| {
20485        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20486        open_editor.update(cx, |editor, cx| {
20487            assert_eq!(
20488                editor.display_text(cx),
20489                "pub mod external {}",
20490                "External file is open now",
20491            );
20492        });
20493        assert_eq!(open_editor, external_editor);
20494    });
20495    assert_language_servers_count(
20496        1,
20497        "Second, external, *.rs file should join the existing server",
20498        cx,
20499    );
20500
20501    pane.update_in(cx, |pane, window, cx| {
20502        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20503    })
20504    .unwrap()
20505    .await
20506    .unwrap();
20507    pane.update_in(cx, |pane, window, cx| {
20508        pane.navigate_backward(window, cx);
20509    });
20510    cx.run_until_parked();
20511    pane.update(cx, |pane, cx| {
20512        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20513        open_editor.update(cx, |editor, cx| {
20514            assert_eq!(
20515                editor.display_text(cx),
20516                "pub mod external {}",
20517                "External file is open now",
20518            );
20519        });
20520    });
20521    assert_language_servers_count(
20522        1,
20523        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20524        cx,
20525    );
20526
20527    cx.update(|_, cx| {
20528        workspace::reload(&workspace::Reload::default(), cx);
20529    });
20530    assert_language_servers_count(
20531        1,
20532        "After reloading the worktree with local and external files opened, only one project should be started",
20533        cx,
20534    );
20535}
20536
20537#[gpui::test]
20538async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20539    init_test(cx, |_| {});
20540
20541    let mut cx = EditorTestContext::new(cx).await;
20542    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20543    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20544
20545    // test cursor move to start of each line on tab
20546    // for `if`, `elif`, `else`, `while`, `with` and `for`
20547    cx.set_state(indoc! {"
20548        def main():
20549        ˇ    for item in items:
20550        ˇ        while item.active:
20551        ˇ            if item.value > 10:
20552        ˇ                continue
20553        ˇ            elif item.value < 0:
20554        ˇ                break
20555        ˇ            else:
20556        ˇ                with item.context() as ctx:
20557        ˇ                    yield count
20558        ˇ        else:
20559        ˇ            log('while else')
20560        ˇ    else:
20561        ˇ        log('for else')
20562    "});
20563    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20564    cx.assert_editor_state(indoc! {"
20565        def main():
20566            ˇfor item in items:
20567                ˇwhile item.active:
20568                    ˇif item.value > 10:
20569                        ˇcontinue
20570                    ˇelif item.value < 0:
20571                        ˇbreak
20572                    ˇelse:
20573                        ˇwith item.context() as ctx:
20574                            ˇyield count
20575                ˇelse:
20576                    ˇlog('while else')
20577            ˇelse:
20578                ˇlog('for else')
20579    "});
20580    // test relative indent is preserved when tab
20581    // for `if`, `elif`, `else`, `while`, `with` and `for`
20582    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20583    cx.assert_editor_state(indoc! {"
20584        def main():
20585                ˇfor item in items:
20586                    ˇwhile item.active:
20587                        ˇif item.value > 10:
20588                            ˇcontinue
20589                        ˇelif item.value < 0:
20590                            ˇbreak
20591                        ˇelse:
20592                            ˇwith item.context() as ctx:
20593                                ˇyield count
20594                    ˇelse:
20595                        ˇlog('while else')
20596                ˇelse:
20597                    ˇlog('for else')
20598    "});
20599
20600    // test cursor move to start of each line on tab
20601    // for `try`, `except`, `else`, `finally`, `match` and `def`
20602    cx.set_state(indoc! {"
20603        def main():
20604        ˇ    try:
20605        ˇ       fetch()
20606        ˇ    except ValueError:
20607        ˇ       handle_error()
20608        ˇ    else:
20609        ˇ        match value:
20610        ˇ            case _:
20611        ˇ    finally:
20612        ˇ        def status():
20613        ˇ            return 0
20614    "});
20615    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20616    cx.assert_editor_state(indoc! {"
20617        def main():
20618            ˇtry:
20619                ˇfetch()
20620            ˇexcept ValueError:
20621                ˇhandle_error()
20622            ˇelse:
20623                ˇmatch value:
20624                    ˇcase _:
20625            ˇfinally:
20626                ˇdef status():
20627                    ˇreturn 0
20628    "});
20629    // test relative indent is preserved when tab
20630    // for `try`, `except`, `else`, `finally`, `match` and `def`
20631    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20632    cx.assert_editor_state(indoc! {"
20633        def main():
20634                ˇtry:
20635                    ˇfetch()
20636                ˇexcept ValueError:
20637                    ˇhandle_error()
20638                ˇelse:
20639                    ˇmatch value:
20640                        ˇcase _:
20641                ˇfinally:
20642                    ˇdef status():
20643                        ˇreturn 0
20644    "});
20645}
20646
20647#[gpui::test]
20648async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20649    init_test(cx, |_| {});
20650
20651    let mut cx = EditorTestContext::new(cx).await;
20652    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20653    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20654
20655    // test `else` auto outdents when typed inside `if` block
20656    cx.set_state(indoc! {"
20657        def main():
20658            if i == 2:
20659                return
20660                ˇ
20661    "});
20662    cx.update_editor(|editor, window, cx| {
20663        editor.handle_input("else:", window, cx);
20664    });
20665    cx.assert_editor_state(indoc! {"
20666        def main():
20667            if i == 2:
20668                return
20669            else:ˇ
20670    "});
20671
20672    // test `except` auto outdents when typed inside `try` block
20673    cx.set_state(indoc! {"
20674        def main():
20675            try:
20676                i = 2
20677                ˇ
20678    "});
20679    cx.update_editor(|editor, window, cx| {
20680        editor.handle_input("except:", window, cx);
20681    });
20682    cx.assert_editor_state(indoc! {"
20683        def main():
20684            try:
20685                i = 2
20686            except:ˇ
20687    "});
20688
20689    // test `else` auto outdents when typed inside `except` block
20690    cx.set_state(indoc! {"
20691        def main():
20692            try:
20693                i = 2
20694            except:
20695                j = 2
20696                ˇ
20697    "});
20698    cx.update_editor(|editor, window, cx| {
20699        editor.handle_input("else:", window, cx);
20700    });
20701    cx.assert_editor_state(indoc! {"
20702        def main():
20703            try:
20704                i = 2
20705            except:
20706                j = 2
20707            else:ˇ
20708    "});
20709
20710    // test `finally` auto outdents when typed inside `else` block
20711    cx.set_state(indoc! {"
20712        def main():
20713            try:
20714                i = 2
20715            except:
20716                j = 2
20717            else:
20718                k = 2
20719                ˇ
20720    "});
20721    cx.update_editor(|editor, window, cx| {
20722        editor.handle_input("finally:", window, cx);
20723    });
20724    cx.assert_editor_state(indoc! {"
20725        def main():
20726            try:
20727                i = 2
20728            except:
20729                j = 2
20730            else:
20731                k = 2
20732            finally:ˇ
20733    "});
20734
20735    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20736    // cx.set_state(indoc! {"
20737    //     def main():
20738    //         try:
20739    //             for i in range(n):
20740    //                 pass
20741    //             ˇ
20742    // "});
20743    // cx.update_editor(|editor, window, cx| {
20744    //     editor.handle_input("except:", window, cx);
20745    // });
20746    // cx.assert_editor_state(indoc! {"
20747    //     def main():
20748    //         try:
20749    //             for i in range(n):
20750    //                 pass
20751    //         except:ˇ
20752    // "});
20753
20754    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20755    // cx.set_state(indoc! {"
20756    //     def main():
20757    //         try:
20758    //             i = 2
20759    //         except:
20760    //             for i in range(n):
20761    //                 pass
20762    //             ˇ
20763    // "});
20764    // cx.update_editor(|editor, window, cx| {
20765    //     editor.handle_input("else:", window, cx);
20766    // });
20767    // cx.assert_editor_state(indoc! {"
20768    //     def main():
20769    //         try:
20770    //             i = 2
20771    //         except:
20772    //             for i in range(n):
20773    //                 pass
20774    //         else:ˇ
20775    // "});
20776
20777    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20778    // cx.set_state(indoc! {"
20779    //     def main():
20780    //         try:
20781    //             i = 2
20782    //         except:
20783    //             j = 2
20784    //         else:
20785    //             for i in range(n):
20786    //                 pass
20787    //             ˇ
20788    // "});
20789    // cx.update_editor(|editor, window, cx| {
20790    //     editor.handle_input("finally:", window, cx);
20791    // });
20792    // cx.assert_editor_state(indoc! {"
20793    //     def main():
20794    //         try:
20795    //             i = 2
20796    //         except:
20797    //             j = 2
20798    //         else:
20799    //             for i in range(n):
20800    //                 pass
20801    //         finally:ˇ
20802    // "});
20803
20804    // test `else` stays at correct indent when typed after `for` block
20805    cx.set_state(indoc! {"
20806        def main():
20807            for i in range(10):
20808                if i == 3:
20809                    break
20810            ˇ
20811    "});
20812    cx.update_editor(|editor, window, cx| {
20813        editor.handle_input("else:", window, cx);
20814    });
20815    cx.assert_editor_state(indoc! {"
20816        def main():
20817            for i in range(10):
20818                if i == 3:
20819                    break
20820            else:ˇ
20821    "});
20822}
20823
20824#[gpui::test]
20825async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
20826    init_test(cx, |_| {});
20827    update_test_language_settings(cx, |settings| {
20828        settings.defaults.extend_comment_on_newline = Some(false);
20829    });
20830    let mut cx = EditorTestContext::new(cx).await;
20831    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20832    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20833
20834    // test correct indent after newline on comment
20835    cx.set_state(indoc! {"
20836        # COMMENT:ˇ
20837    "});
20838    cx.update_editor(|editor, window, cx| {
20839        editor.newline(&Newline, window, cx);
20840    });
20841    cx.assert_editor_state(indoc! {"
20842        # COMMENT:
20843        ˇ
20844    "});
20845
20846    // test correct indent after newline in curly brackets
20847    cx.set_state(indoc! {"
20848        {ˇ}
20849    "});
20850    cx.update_editor(|editor, window, cx| {
20851        editor.newline(&Newline, window, cx);
20852    });
20853    cx.run_until_parked();
20854    cx.assert_editor_state(indoc! {"
20855        {
20856            ˇ
20857        }
20858    "});
20859}
20860
20861fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20862    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20863    point..point
20864}
20865
20866fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20867    let (text, ranges) = marked_text_ranges(marked_text, true);
20868    assert_eq!(editor.text(cx), text);
20869    assert_eq!(
20870        editor.selections.ranges(cx),
20871        ranges,
20872        "Assert selections are {}",
20873        marked_text
20874    );
20875}
20876
20877pub fn handle_signature_help_request(
20878    cx: &mut EditorLspTestContext,
20879    mocked_response: lsp::SignatureHelp,
20880) -> impl Future<Output = ()> + use<> {
20881    let mut request =
20882        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20883            let mocked_response = mocked_response.clone();
20884            async move { Ok(Some(mocked_response)) }
20885        });
20886
20887    async move {
20888        request.next().await;
20889    }
20890}
20891
20892/// Handle completion request passing a marked string specifying where the completion
20893/// should be triggered from using '|' character, what range should be replaced, and what completions
20894/// should be returned using '<' and '>' to delimit the range.
20895///
20896/// Also see `handle_completion_request_with_insert_and_replace`.
20897#[track_caller]
20898pub fn handle_completion_request(
20899    cx: &mut EditorLspTestContext,
20900    marked_string: &str,
20901    completions: Vec<&'static str>,
20902    counter: Arc<AtomicUsize>,
20903) -> impl Future<Output = ()> {
20904    let complete_from_marker: TextRangeMarker = '|'.into();
20905    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20906    let (_, mut marked_ranges) = marked_text_ranges_by(
20907        marked_string,
20908        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20909    );
20910
20911    let complete_from_position =
20912        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20913    let replace_range =
20914        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20915
20916    let mut request =
20917        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20918            let completions = completions.clone();
20919            counter.fetch_add(1, atomic::Ordering::Release);
20920            async move {
20921                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20922                assert_eq!(
20923                    params.text_document_position.position,
20924                    complete_from_position
20925                );
20926                Ok(Some(lsp::CompletionResponse::Array(
20927                    completions
20928                        .iter()
20929                        .map(|completion_text| lsp::CompletionItem {
20930                            label: completion_text.to_string(),
20931                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20932                                range: replace_range,
20933                                new_text: completion_text.to_string(),
20934                            })),
20935                            ..Default::default()
20936                        })
20937                        .collect(),
20938                )))
20939            }
20940        });
20941
20942    async move {
20943        request.next().await;
20944    }
20945}
20946
20947/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20948/// given instead, which also contains an `insert` range.
20949///
20950/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20951/// that is, `replace_range.start..cursor_pos`.
20952pub fn handle_completion_request_with_insert_and_replace(
20953    cx: &mut EditorLspTestContext,
20954    marked_string: &str,
20955    completions: Vec<&'static str>,
20956    counter: Arc<AtomicUsize>,
20957) -> impl Future<Output = ()> {
20958    let complete_from_marker: TextRangeMarker = '|'.into();
20959    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20960    let (_, mut marked_ranges) = marked_text_ranges_by(
20961        marked_string,
20962        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20963    );
20964
20965    let complete_from_position =
20966        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20967    let replace_range =
20968        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20969
20970    let mut request =
20971        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20972            let completions = completions.clone();
20973            counter.fetch_add(1, atomic::Ordering::Release);
20974            async move {
20975                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20976                assert_eq!(
20977                    params.text_document_position.position, complete_from_position,
20978                    "marker `|` position doesn't match",
20979                );
20980                Ok(Some(lsp::CompletionResponse::Array(
20981                    completions
20982                        .iter()
20983                        .map(|completion_text| lsp::CompletionItem {
20984                            label: completion_text.to_string(),
20985                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20986                                lsp::InsertReplaceEdit {
20987                                    insert: lsp::Range {
20988                                        start: replace_range.start,
20989                                        end: complete_from_position,
20990                                    },
20991                                    replace: replace_range,
20992                                    new_text: completion_text.to_string(),
20993                                },
20994                            )),
20995                            ..Default::default()
20996                        })
20997                        .collect(),
20998                )))
20999            }
21000        });
21001
21002    async move {
21003        request.next().await;
21004    }
21005}
21006
21007fn handle_resolve_completion_request(
21008    cx: &mut EditorLspTestContext,
21009    edits: Option<Vec<(&'static str, &'static str)>>,
21010) -> impl Future<Output = ()> {
21011    let edits = edits.map(|edits| {
21012        edits
21013            .iter()
21014            .map(|(marked_string, new_text)| {
21015                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
21016                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
21017                lsp::TextEdit::new(replace_range, new_text.to_string())
21018            })
21019            .collect::<Vec<_>>()
21020    });
21021
21022    let mut request =
21023        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
21024            let edits = edits.clone();
21025            async move {
21026                Ok(lsp::CompletionItem {
21027                    additional_text_edits: edits,
21028                    ..Default::default()
21029                })
21030            }
21031        });
21032
21033    async move {
21034        request.next().await;
21035    }
21036}
21037
21038pub(crate) fn update_test_language_settings(
21039    cx: &mut TestAppContext,
21040    f: impl Fn(&mut AllLanguageSettingsContent),
21041) {
21042    cx.update(|cx| {
21043        SettingsStore::update_global(cx, |store, cx| {
21044            store.update_user_settings::<AllLanguageSettings>(cx, f);
21045        });
21046    });
21047}
21048
21049pub(crate) fn update_test_project_settings(
21050    cx: &mut TestAppContext,
21051    f: impl Fn(&mut ProjectSettings),
21052) {
21053    cx.update(|cx| {
21054        SettingsStore::update_global(cx, |store, cx| {
21055            store.update_user_settings::<ProjectSettings>(cx, f);
21056        });
21057    });
21058}
21059
21060pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
21061    cx.update(|cx| {
21062        assets::Assets.load_test_fonts(cx);
21063        let store = SettingsStore::test(cx);
21064        cx.set_global(store);
21065        theme::init(theme::LoadThemes::JustBase, cx);
21066        release_channel::init(SemanticVersion::default(), cx);
21067        client::init_settings(cx);
21068        language::init(cx);
21069        Project::init_settings(cx);
21070        workspace::init_settings(cx);
21071        crate::init(cx);
21072    });
21073
21074    update_test_language_settings(cx, f);
21075}
21076
21077#[track_caller]
21078fn assert_hunk_revert(
21079    not_reverted_text_with_selections: &str,
21080    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
21081    expected_reverted_text_with_selections: &str,
21082    base_text: &str,
21083    cx: &mut EditorLspTestContext,
21084) {
21085    cx.set_state(not_reverted_text_with_selections);
21086    cx.set_head_text(base_text);
21087    cx.executor().run_until_parked();
21088
21089    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
21090        let snapshot = editor.snapshot(window, cx);
21091        let reverted_hunk_statuses = snapshot
21092            .buffer_snapshot
21093            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
21094            .map(|hunk| hunk.status().kind)
21095            .collect::<Vec<_>>();
21096
21097        editor.git_restore(&Default::default(), window, cx);
21098        reverted_hunk_statuses
21099    });
21100    cx.executor().run_until_parked();
21101    cx.assert_editor_state(expected_reverted_text_with_selections);
21102    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
21103}