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(
 2867        Language::new(
 2868            LanguageConfig {
 2869                documentation: Some(language::DocumentationConfig {
 2870                    start: "/**".into(),
 2871                    end: "*/".into(),
 2872                    prefix: "* ".into(),
 2873                    tab_size: NonZeroU32::new(1).unwrap(),
 2874                }),
 2875
 2876                ..LanguageConfig::default()
 2877            },
 2878            Some(tree_sitter_rust::LANGUAGE.into()),
 2879        )
 2880        .with_override_query("[(line_comment)(block_comment)] @comment.inclusive")
 2881        .unwrap(),
 2882    );
 2883
 2884    {
 2885        let mut cx = EditorTestContext::new(cx).await;
 2886        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2887        cx.set_state(indoc! {"
 2888        /**ˇ
 2889    "});
 2890
 2891        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2892        cx.assert_editor_state(indoc! {"
 2893        /**
 2894         * ˇ
 2895    "});
 2896        // Ensure that if cursor is before the comment start,
 2897        // we do not actually insert a comment prefix.
 2898        cx.set_state(indoc! {"
 2899        ˇ/**
 2900    "});
 2901        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2902        cx.assert_editor_state(indoc! {"
 2903
 2904        ˇ/**
 2905    "});
 2906        // Ensure that if cursor is between it doesn't add comment prefix.
 2907        cx.set_state(indoc! {"
 2908        /*ˇ*
 2909    "});
 2910        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2911        cx.assert_editor_state(indoc! {"
 2912        /*
 2913        ˇ*
 2914    "});
 2915        // Ensure that if suffix exists on same line after cursor it adds new line.
 2916        cx.set_state(indoc! {"
 2917        /**ˇ*/
 2918    "});
 2919        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2920        cx.assert_editor_state(indoc! {"
 2921        /**
 2922         * ˇ
 2923         */
 2924    "});
 2925        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2926        cx.set_state(indoc! {"
 2927        /**ˇ */
 2928    "});
 2929        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2930        cx.assert_editor_state(indoc! {"
 2931        /**
 2932         * ˇ
 2933         */
 2934    "});
 2935        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2936        cx.set_state(indoc! {"
 2937        /** ˇ*/
 2938    "});
 2939        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2940        cx.assert_editor_state(
 2941            indoc! {"
 2942        /**s
 2943         * ˇ
 2944         */
 2945    "}
 2946            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2947            .as_str(),
 2948        );
 2949        // Ensure that delimiter space is preserved when newline on already
 2950        // spaced delimiter.
 2951        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2952        cx.assert_editor_state(
 2953            indoc! {"
 2954        /**s
 2955         *s
 2956         * ˇ
 2957         */
 2958    "}
 2959            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2960            .as_str(),
 2961        );
 2962        // Ensure that delimiter space is preserved when space is not
 2963        // on existing delimiter.
 2964        cx.set_state(indoc! {"
 2965        /**
 2966 2967         */
 2968    "});
 2969        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2970        cx.assert_editor_state(indoc! {"
 2971        /**
 2972         *
 2973         * ˇ
 2974         */
 2975    "});
 2976        // Ensure that if suffix exists on same line after cursor it
 2977        // doesn't add extra new line if prefix is not on same line.
 2978        cx.set_state(indoc! {"
 2979        /**
 2980        ˇ*/
 2981    "});
 2982        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2983        cx.assert_editor_state(indoc! {"
 2984        /**
 2985
 2986        ˇ*/
 2987    "});
 2988        // Ensure that it detects suffix after existing prefix.
 2989        cx.set_state(indoc! {"
 2990        /**ˇ/
 2991    "});
 2992        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2993        cx.assert_editor_state(indoc! {"
 2994        /**
 2995        ˇ/
 2996    "});
 2997        // Ensure that if suffix exists on same line before
 2998        // cursor it does not add comment prefix.
 2999        cx.set_state(indoc! {"
 3000        /** */ˇ
 3001    "});
 3002        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3003        cx.assert_editor_state(indoc! {"
 3004        /** */
 3005        ˇ
 3006    "});
 3007        // Ensure that if suffix exists on same line before
 3008        // cursor it does not add comment prefix.
 3009        cx.set_state(indoc! {"
 3010        /**
 3011         *
 3012         */ˇ
 3013    "});
 3014        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3015        cx.assert_editor_state(indoc! {"
 3016        /**
 3017         *
 3018         */
 3019         ˇ
 3020    "});
 3021
 3022        // Ensure that inline comment followed by code
 3023        // doesn't add comment prefix on newline
 3024        cx.set_state(indoc! {"
 3025        /** */ textˇ
 3026    "});
 3027        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3028        cx.assert_editor_state(indoc! {"
 3029        /** */ text
 3030        ˇ
 3031    "});
 3032
 3033        // Ensure that text after comment end tag
 3034        // doesn't add comment prefix on newline
 3035        cx.set_state(indoc! {"
 3036        /**
 3037         *
 3038         */ˇtext
 3039    "});
 3040        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3041        cx.assert_editor_state(indoc! {"
 3042        /**
 3043         *
 3044         */
 3045         ˇtext
 3046    "});
 3047
 3048        // Ensure if not comment block it doesn't
 3049        // add comment prefix on newline
 3050        cx.set_state(indoc! {"
 3051        * textˇ
 3052    "});
 3053        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3054        cx.assert_editor_state(indoc! {"
 3055        * text
 3056        ˇ
 3057    "});
 3058    }
 3059    // Ensure that comment continuations can be disabled.
 3060    update_test_language_settings(cx, |settings| {
 3061        settings.defaults.extend_comment_on_newline = Some(false);
 3062    });
 3063    let mut cx = EditorTestContext::new(cx).await;
 3064    cx.set_state(indoc! {"
 3065        /**ˇ
 3066    "});
 3067    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3068    cx.assert_editor_state(indoc! {"
 3069        /**
 3070        ˇ
 3071    "});
 3072}
 3073
 3074#[gpui::test]
 3075fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 3076    init_test(cx, |_| {});
 3077
 3078    let editor = cx.add_window(|window, cx| {
 3079        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3080        let mut editor = build_editor(buffer.clone(), window, cx);
 3081        editor.change_selections(None, window, cx, |s| {
 3082            s.select_ranges([3..4, 11..12, 19..20])
 3083        });
 3084        editor
 3085    });
 3086
 3087    _ = editor.update(cx, |editor, window, cx| {
 3088        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3089        editor.buffer.update(cx, |buffer, cx| {
 3090            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3091            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3092        });
 3093        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3094
 3095        editor.insert("Z", window, cx);
 3096        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3097
 3098        // The selections are moved after the inserted characters
 3099        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3100    });
 3101}
 3102
 3103#[gpui::test]
 3104async fn test_tab(cx: &mut TestAppContext) {
 3105    init_test(cx, |settings| {
 3106        settings.defaults.tab_size = NonZeroU32::new(3)
 3107    });
 3108
 3109    let mut cx = EditorTestContext::new(cx).await;
 3110    cx.set_state(indoc! {"
 3111        ˇabˇc
 3112        ˇ🏀ˇ🏀ˇefg
 3113 3114    "});
 3115    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3116    cx.assert_editor_state(indoc! {"
 3117           ˇab ˇc
 3118           ˇ🏀  ˇ🏀  ˇefg
 3119        d  ˇ
 3120    "});
 3121
 3122    cx.set_state(indoc! {"
 3123        a
 3124        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3125    "});
 3126    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3127    cx.assert_editor_state(indoc! {"
 3128        a
 3129           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3130    "});
 3131}
 3132
 3133#[gpui::test]
 3134async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3135    init_test(cx, |_| {});
 3136
 3137    let mut cx = EditorTestContext::new(cx).await;
 3138    let language = Arc::new(
 3139        Language::new(
 3140            LanguageConfig::default(),
 3141            Some(tree_sitter_rust::LANGUAGE.into()),
 3142        )
 3143        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3144        .unwrap(),
 3145    );
 3146    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3147
 3148    // test when all cursors are not at suggested indent
 3149    // then simply move to their suggested indent location
 3150    cx.set_state(indoc! {"
 3151        const a: B = (
 3152            c(
 3153        ˇ
 3154        ˇ    )
 3155        );
 3156    "});
 3157    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3158    cx.assert_editor_state(indoc! {"
 3159        const a: B = (
 3160            c(
 3161                ˇ
 3162            ˇ)
 3163        );
 3164    "});
 3165
 3166    // test cursor already at suggested indent not moving when
 3167    // other cursors are yet to reach their suggested indents
 3168    cx.set_state(indoc! {"
 3169        ˇ
 3170        const a: B = (
 3171            c(
 3172                d(
 3173        ˇ
 3174                )
 3175        ˇ
 3176        ˇ    )
 3177        );
 3178    "});
 3179    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3180    cx.assert_editor_state(indoc! {"
 3181        ˇ
 3182        const a: B = (
 3183            c(
 3184                d(
 3185                    ˇ
 3186                )
 3187                ˇ
 3188            ˇ)
 3189        );
 3190    "});
 3191    // test when all cursors are at suggested indent then tab is inserted
 3192    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3193    cx.assert_editor_state(indoc! {"
 3194            ˇ
 3195        const a: B = (
 3196            c(
 3197                d(
 3198                        ˇ
 3199                )
 3200                    ˇ
 3201                ˇ)
 3202        );
 3203    "});
 3204
 3205    // test when current indent is less than suggested indent,
 3206    // we adjust line to match suggested indent and move cursor to it
 3207    //
 3208    // when no other cursor is at word boundary, all of them should move
 3209    cx.set_state(indoc! {"
 3210        const a: B = (
 3211            c(
 3212                d(
 3213        ˇ
 3214        ˇ   )
 3215        ˇ   )
 3216        );
 3217    "});
 3218    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3219    cx.assert_editor_state(indoc! {"
 3220        const a: B = (
 3221            c(
 3222                d(
 3223                    ˇ
 3224                ˇ)
 3225            ˇ)
 3226        );
 3227    "});
 3228
 3229    // test when current indent is less than suggested indent,
 3230    // we adjust line to match suggested indent and move cursor to it
 3231    //
 3232    // when some other cursor is at word boundary, it should not move
 3233    cx.set_state(indoc! {"
 3234        const a: B = (
 3235            c(
 3236                d(
 3237        ˇ
 3238        ˇ   )
 3239           ˇ)
 3240        );
 3241    "});
 3242    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3243    cx.assert_editor_state(indoc! {"
 3244        const a: B = (
 3245            c(
 3246                d(
 3247                    ˇ
 3248                ˇ)
 3249            ˇ)
 3250        );
 3251    "});
 3252
 3253    // test when current indent is more than suggested indent,
 3254    // we just move cursor to current indent instead of suggested indent
 3255    //
 3256    // when no other cursor is at word boundary, all of them should move
 3257    cx.set_state(indoc! {"
 3258        const a: B = (
 3259            c(
 3260                d(
 3261        ˇ
 3262        ˇ                )
 3263        ˇ   )
 3264        );
 3265    "});
 3266    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3267    cx.assert_editor_state(indoc! {"
 3268        const a: B = (
 3269            c(
 3270                d(
 3271                    ˇ
 3272                        ˇ)
 3273            ˇ)
 3274        );
 3275    "});
 3276    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3277    cx.assert_editor_state(indoc! {"
 3278        const a: B = (
 3279            c(
 3280                d(
 3281                        ˇ
 3282                            ˇ)
 3283                ˇ)
 3284        );
 3285    "});
 3286
 3287    // test when current indent is more than suggested indent,
 3288    // we just move cursor to current indent instead of suggested indent
 3289    //
 3290    // when some other cursor is at word boundary, it doesn't move
 3291    cx.set_state(indoc! {"
 3292        const a: B = (
 3293            c(
 3294                d(
 3295        ˇ
 3296        ˇ                )
 3297            ˇ)
 3298        );
 3299    "});
 3300    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3301    cx.assert_editor_state(indoc! {"
 3302        const a: B = (
 3303            c(
 3304                d(
 3305                    ˇ
 3306                        ˇ)
 3307            ˇ)
 3308        );
 3309    "});
 3310
 3311    // handle auto-indent when there are multiple cursors on the same line
 3312    cx.set_state(indoc! {"
 3313        const a: B = (
 3314            c(
 3315        ˇ    ˇ
 3316        ˇ    )
 3317        );
 3318    "});
 3319    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3320    cx.assert_editor_state(indoc! {"
 3321        const a: B = (
 3322            c(
 3323                ˇ
 3324            ˇ)
 3325        );
 3326    "});
 3327}
 3328
 3329#[gpui::test]
 3330async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3331    init_test(cx, |settings| {
 3332        settings.defaults.tab_size = NonZeroU32::new(3)
 3333    });
 3334
 3335    let mut cx = EditorTestContext::new(cx).await;
 3336    cx.set_state(indoc! {"
 3337         ˇ
 3338        \t ˇ
 3339        \t  ˇ
 3340        \t   ˇ
 3341         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3342    "});
 3343
 3344    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3345    cx.assert_editor_state(indoc! {"
 3346           ˇ
 3347        \t   ˇ
 3348        \t   ˇ
 3349        \t      ˇ
 3350         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3351    "});
 3352}
 3353
 3354#[gpui::test]
 3355async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3356    init_test(cx, |settings| {
 3357        settings.defaults.tab_size = NonZeroU32::new(4)
 3358    });
 3359
 3360    let language = Arc::new(
 3361        Language::new(
 3362            LanguageConfig::default(),
 3363            Some(tree_sitter_rust::LANGUAGE.into()),
 3364        )
 3365        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3366        .unwrap(),
 3367    );
 3368
 3369    let mut cx = EditorTestContext::new(cx).await;
 3370    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3371    cx.set_state(indoc! {"
 3372        fn a() {
 3373            if b {
 3374        \t ˇc
 3375            }
 3376        }
 3377    "});
 3378
 3379    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3380    cx.assert_editor_state(indoc! {"
 3381        fn a() {
 3382            if b {
 3383                ˇc
 3384            }
 3385        }
 3386    "});
 3387}
 3388
 3389#[gpui::test]
 3390async fn test_indent_outdent(cx: &mut TestAppContext) {
 3391    init_test(cx, |settings| {
 3392        settings.defaults.tab_size = NonZeroU32::new(4);
 3393    });
 3394
 3395    let mut cx = EditorTestContext::new(cx).await;
 3396
 3397    cx.set_state(indoc! {"
 3398          «oneˇ» «twoˇ»
 3399        three
 3400         four
 3401    "});
 3402    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404            «oneˇ» «twoˇ»
 3405        three
 3406         four
 3407    "});
 3408
 3409    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3410    cx.assert_editor_state(indoc! {"
 3411        «oneˇ» «twoˇ»
 3412        three
 3413         four
 3414    "});
 3415
 3416    // select across line ending
 3417    cx.set_state(indoc! {"
 3418        one two
 3419        t«hree
 3420        ˇ» four
 3421    "});
 3422    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3423    cx.assert_editor_state(indoc! {"
 3424        one two
 3425            t«hree
 3426        ˇ» four
 3427    "});
 3428
 3429    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3430    cx.assert_editor_state(indoc! {"
 3431        one two
 3432        t«hree
 3433        ˇ» four
 3434    "});
 3435
 3436    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3437    cx.set_state(indoc! {"
 3438        one two
 3439        ˇthree
 3440            four
 3441    "});
 3442    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3443    cx.assert_editor_state(indoc! {"
 3444        one two
 3445            ˇthree
 3446            four
 3447    "});
 3448
 3449    cx.set_state(indoc! {"
 3450        one two
 3451        ˇ    three
 3452            four
 3453    "});
 3454    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3455    cx.assert_editor_state(indoc! {"
 3456        one two
 3457        ˇthree
 3458            four
 3459    "});
 3460}
 3461
 3462#[gpui::test]
 3463async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3464    init_test(cx, |settings| {
 3465        settings.defaults.hard_tabs = Some(true);
 3466    });
 3467
 3468    let mut cx = EditorTestContext::new(cx).await;
 3469
 3470    // select two ranges on one line
 3471    cx.set_state(indoc! {"
 3472        «oneˇ» «twoˇ»
 3473        three
 3474        four
 3475    "});
 3476    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3477    cx.assert_editor_state(indoc! {"
 3478        \t«oneˇ» «twoˇ»
 3479        three
 3480        four
 3481    "});
 3482    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3483    cx.assert_editor_state(indoc! {"
 3484        \t\t«oneˇ» «twoˇ»
 3485        three
 3486        four
 3487    "});
 3488    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3489    cx.assert_editor_state(indoc! {"
 3490        \t«oneˇ» «twoˇ»
 3491        three
 3492        four
 3493    "});
 3494    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3495    cx.assert_editor_state(indoc! {"
 3496        «oneˇ» «twoˇ»
 3497        three
 3498        four
 3499    "});
 3500
 3501    // select across a line ending
 3502    cx.set_state(indoc! {"
 3503        one two
 3504        t«hree
 3505        ˇ»four
 3506    "});
 3507    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3508    cx.assert_editor_state(indoc! {"
 3509        one two
 3510        \tt«hree
 3511        ˇ»four
 3512    "});
 3513    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3514    cx.assert_editor_state(indoc! {"
 3515        one two
 3516        \t\tt«hree
 3517        ˇ»four
 3518    "});
 3519    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3520    cx.assert_editor_state(indoc! {"
 3521        one two
 3522        \tt«hree
 3523        ˇ»four
 3524    "});
 3525    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3526    cx.assert_editor_state(indoc! {"
 3527        one two
 3528        t«hree
 3529        ˇ»four
 3530    "});
 3531
 3532    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3533    cx.set_state(indoc! {"
 3534        one two
 3535        ˇthree
 3536        four
 3537    "});
 3538    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3539    cx.assert_editor_state(indoc! {"
 3540        one two
 3541        ˇthree
 3542        four
 3543    "});
 3544    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3545    cx.assert_editor_state(indoc! {"
 3546        one two
 3547        \tˇthree
 3548        four
 3549    "});
 3550    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3551    cx.assert_editor_state(indoc! {"
 3552        one two
 3553        ˇthree
 3554        four
 3555    "});
 3556}
 3557
 3558#[gpui::test]
 3559fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3560    init_test(cx, |settings| {
 3561        settings.languages.extend([
 3562            (
 3563                "TOML".into(),
 3564                LanguageSettingsContent {
 3565                    tab_size: NonZeroU32::new(2),
 3566                    ..Default::default()
 3567                },
 3568            ),
 3569            (
 3570                "Rust".into(),
 3571                LanguageSettingsContent {
 3572                    tab_size: NonZeroU32::new(4),
 3573                    ..Default::default()
 3574                },
 3575            ),
 3576        ]);
 3577    });
 3578
 3579    let toml_language = Arc::new(Language::new(
 3580        LanguageConfig {
 3581            name: "TOML".into(),
 3582            ..Default::default()
 3583        },
 3584        None,
 3585    ));
 3586    let rust_language = Arc::new(Language::new(
 3587        LanguageConfig {
 3588            name: "Rust".into(),
 3589            ..Default::default()
 3590        },
 3591        None,
 3592    ));
 3593
 3594    let toml_buffer =
 3595        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3596    let rust_buffer =
 3597        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3598    let multibuffer = cx.new(|cx| {
 3599        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3600        multibuffer.push_excerpts(
 3601            toml_buffer.clone(),
 3602            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3603            cx,
 3604        );
 3605        multibuffer.push_excerpts(
 3606            rust_buffer.clone(),
 3607            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3608            cx,
 3609        );
 3610        multibuffer
 3611    });
 3612
 3613    cx.add_window(|window, cx| {
 3614        let mut editor = build_editor(multibuffer, window, cx);
 3615
 3616        assert_eq!(
 3617            editor.text(cx),
 3618            indoc! {"
 3619                a = 1
 3620                b = 2
 3621
 3622                const c: usize = 3;
 3623            "}
 3624        );
 3625
 3626        select_ranges(
 3627            &mut editor,
 3628            indoc! {"
 3629                «aˇ» = 1
 3630                b = 2
 3631
 3632                «const c:ˇ» usize = 3;
 3633            "},
 3634            window,
 3635            cx,
 3636        );
 3637
 3638        editor.tab(&Tab, window, cx);
 3639        assert_text_with_selections(
 3640            &mut editor,
 3641            indoc! {"
 3642                  «aˇ» = 1
 3643                b = 2
 3644
 3645                    «const c:ˇ» usize = 3;
 3646            "},
 3647            cx,
 3648        );
 3649        editor.backtab(&Backtab, window, cx);
 3650        assert_text_with_selections(
 3651            &mut editor,
 3652            indoc! {"
 3653                «aˇ» = 1
 3654                b = 2
 3655
 3656                «const c:ˇ» usize = 3;
 3657            "},
 3658            cx,
 3659        );
 3660
 3661        editor
 3662    });
 3663}
 3664
 3665#[gpui::test]
 3666async fn test_backspace(cx: &mut TestAppContext) {
 3667    init_test(cx, |_| {});
 3668
 3669    let mut cx = EditorTestContext::new(cx).await;
 3670
 3671    // Basic backspace
 3672    cx.set_state(indoc! {"
 3673        onˇe two three
 3674        fou«rˇ» five six
 3675        seven «ˇeight nine
 3676        »ten
 3677    "});
 3678    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3679    cx.assert_editor_state(indoc! {"
 3680        oˇe two three
 3681        fouˇ five six
 3682        seven ˇten
 3683    "});
 3684
 3685    // Test backspace inside and around indents
 3686    cx.set_state(indoc! {"
 3687        zero
 3688            ˇone
 3689                ˇtwo
 3690            ˇ ˇ ˇ  three
 3691        ˇ  ˇ  four
 3692    "});
 3693    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3694    cx.assert_editor_state(indoc! {"
 3695        zero
 3696        ˇone
 3697            ˇtwo
 3698        ˇ  threeˇ  four
 3699    "});
 3700}
 3701
 3702#[gpui::test]
 3703async fn test_delete(cx: &mut TestAppContext) {
 3704    init_test(cx, |_| {});
 3705
 3706    let mut cx = EditorTestContext::new(cx).await;
 3707    cx.set_state(indoc! {"
 3708        onˇe two three
 3709        fou«rˇ» five six
 3710        seven «ˇeight nine
 3711        »ten
 3712    "});
 3713    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3714    cx.assert_editor_state(indoc! {"
 3715        onˇ two three
 3716        fouˇ five six
 3717        seven ˇten
 3718    "});
 3719}
 3720
 3721#[gpui::test]
 3722fn test_delete_line(cx: &mut TestAppContext) {
 3723    init_test(cx, |_| {});
 3724
 3725    let editor = cx.add_window(|window, cx| {
 3726        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3727        build_editor(buffer, window, cx)
 3728    });
 3729    _ = editor.update(cx, |editor, window, cx| {
 3730        editor.change_selections(None, window, cx, |s| {
 3731            s.select_display_ranges([
 3732                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3733                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3734                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3735            ])
 3736        });
 3737        editor.delete_line(&DeleteLine, window, cx);
 3738        assert_eq!(editor.display_text(cx), "ghi");
 3739        assert_eq!(
 3740            editor.selections.display_ranges(cx),
 3741            vec![
 3742                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3743                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3744            ]
 3745        );
 3746    });
 3747
 3748    let editor = cx.add_window(|window, cx| {
 3749        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3750        build_editor(buffer, window, cx)
 3751    });
 3752    _ = editor.update(cx, |editor, window, cx| {
 3753        editor.change_selections(None, window, cx, |s| {
 3754            s.select_display_ranges([
 3755                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3756            ])
 3757        });
 3758        editor.delete_line(&DeleteLine, window, cx);
 3759        assert_eq!(editor.display_text(cx), "ghi\n");
 3760        assert_eq!(
 3761            editor.selections.display_ranges(cx),
 3762            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3763        );
 3764    });
 3765}
 3766
 3767#[gpui::test]
 3768fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3769    init_test(cx, |_| {});
 3770
 3771    cx.add_window(|window, cx| {
 3772        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3773        let mut editor = build_editor(buffer.clone(), window, cx);
 3774        let buffer = buffer.read(cx).as_singleton().unwrap();
 3775
 3776        assert_eq!(
 3777            editor.selections.ranges::<Point>(cx),
 3778            &[Point::new(0, 0)..Point::new(0, 0)]
 3779        );
 3780
 3781        // When on single line, replace newline at end by space
 3782        editor.join_lines(&JoinLines, window, cx);
 3783        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3784        assert_eq!(
 3785            editor.selections.ranges::<Point>(cx),
 3786            &[Point::new(0, 3)..Point::new(0, 3)]
 3787        );
 3788
 3789        // When multiple lines are selected, remove newlines that are spanned by the selection
 3790        editor.change_selections(None, window, cx, |s| {
 3791            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3792        });
 3793        editor.join_lines(&JoinLines, window, cx);
 3794        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3795        assert_eq!(
 3796            editor.selections.ranges::<Point>(cx),
 3797            &[Point::new(0, 11)..Point::new(0, 11)]
 3798        );
 3799
 3800        // Undo should be transactional
 3801        editor.undo(&Undo, window, cx);
 3802        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3803        assert_eq!(
 3804            editor.selections.ranges::<Point>(cx),
 3805            &[Point::new(0, 5)..Point::new(2, 2)]
 3806        );
 3807
 3808        // When joining an empty line don't insert a space
 3809        editor.change_selections(None, window, cx, |s| {
 3810            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3811        });
 3812        editor.join_lines(&JoinLines, window, cx);
 3813        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3814        assert_eq!(
 3815            editor.selections.ranges::<Point>(cx),
 3816            [Point::new(2, 3)..Point::new(2, 3)]
 3817        );
 3818
 3819        // We can remove trailing newlines
 3820        editor.join_lines(&JoinLines, window, cx);
 3821        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3822        assert_eq!(
 3823            editor.selections.ranges::<Point>(cx),
 3824            [Point::new(2, 3)..Point::new(2, 3)]
 3825        );
 3826
 3827        // We don't blow up on the last line
 3828        editor.join_lines(&JoinLines, window, cx);
 3829        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3830        assert_eq!(
 3831            editor.selections.ranges::<Point>(cx),
 3832            [Point::new(2, 3)..Point::new(2, 3)]
 3833        );
 3834
 3835        // reset to test indentation
 3836        editor.buffer.update(cx, |buffer, cx| {
 3837            buffer.edit(
 3838                [
 3839                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3840                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3841                ],
 3842                None,
 3843                cx,
 3844            )
 3845        });
 3846
 3847        // We remove any leading spaces
 3848        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3849        editor.change_selections(None, window, cx, |s| {
 3850            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3851        });
 3852        editor.join_lines(&JoinLines, window, cx);
 3853        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3854
 3855        // We don't insert a space for a line containing only spaces
 3856        editor.join_lines(&JoinLines, window, cx);
 3857        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3858
 3859        // We ignore any leading tabs
 3860        editor.join_lines(&JoinLines, window, cx);
 3861        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3862
 3863        editor
 3864    });
 3865}
 3866
 3867#[gpui::test]
 3868fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3869    init_test(cx, |_| {});
 3870
 3871    cx.add_window(|window, cx| {
 3872        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3873        let mut editor = build_editor(buffer.clone(), window, cx);
 3874        let buffer = buffer.read(cx).as_singleton().unwrap();
 3875
 3876        editor.change_selections(None, window, cx, |s| {
 3877            s.select_ranges([
 3878                Point::new(0, 2)..Point::new(1, 1),
 3879                Point::new(1, 2)..Point::new(1, 2),
 3880                Point::new(3, 1)..Point::new(3, 2),
 3881            ])
 3882        });
 3883
 3884        editor.join_lines(&JoinLines, window, cx);
 3885        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3886
 3887        assert_eq!(
 3888            editor.selections.ranges::<Point>(cx),
 3889            [
 3890                Point::new(0, 7)..Point::new(0, 7),
 3891                Point::new(1, 3)..Point::new(1, 3)
 3892            ]
 3893        );
 3894        editor
 3895    });
 3896}
 3897
 3898#[gpui::test]
 3899async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3900    init_test(cx, |_| {});
 3901
 3902    let mut cx = EditorTestContext::new(cx).await;
 3903
 3904    let diff_base = r#"
 3905        Line 0
 3906        Line 1
 3907        Line 2
 3908        Line 3
 3909        "#
 3910    .unindent();
 3911
 3912    cx.set_state(
 3913        &r#"
 3914        ˇLine 0
 3915        Line 1
 3916        Line 2
 3917        Line 3
 3918        "#
 3919        .unindent(),
 3920    );
 3921
 3922    cx.set_head_text(&diff_base);
 3923    executor.run_until_parked();
 3924
 3925    // Join lines
 3926    cx.update_editor(|editor, window, cx| {
 3927        editor.join_lines(&JoinLines, window, cx);
 3928    });
 3929    executor.run_until_parked();
 3930
 3931    cx.assert_editor_state(
 3932        &r#"
 3933        Line 0ˇ Line 1
 3934        Line 2
 3935        Line 3
 3936        "#
 3937        .unindent(),
 3938    );
 3939    // Join again
 3940    cx.update_editor(|editor, window, cx| {
 3941        editor.join_lines(&JoinLines, window, cx);
 3942    });
 3943    executor.run_until_parked();
 3944
 3945    cx.assert_editor_state(
 3946        &r#"
 3947        Line 0 Line 1ˇ Line 2
 3948        Line 3
 3949        "#
 3950        .unindent(),
 3951    );
 3952}
 3953
 3954#[gpui::test]
 3955async fn test_custom_newlines_cause_no_false_positive_diffs(
 3956    executor: BackgroundExecutor,
 3957    cx: &mut TestAppContext,
 3958) {
 3959    init_test(cx, |_| {});
 3960    let mut cx = EditorTestContext::new(cx).await;
 3961    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3962    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3963    executor.run_until_parked();
 3964
 3965    cx.update_editor(|editor, window, cx| {
 3966        let snapshot = editor.snapshot(window, cx);
 3967        assert_eq!(
 3968            snapshot
 3969                .buffer_snapshot
 3970                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3971                .collect::<Vec<_>>(),
 3972            Vec::new(),
 3973            "Should not have any diffs for files with custom newlines"
 3974        );
 3975    });
 3976}
 3977
 3978#[gpui::test]
 3979async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3980    init_test(cx, |_| {});
 3981
 3982    let mut cx = EditorTestContext::new(cx).await;
 3983
 3984    // Test sort_lines_case_insensitive()
 3985    cx.set_state(indoc! {"
 3986        «z
 3987        y
 3988        x
 3989        Z
 3990        Y
 3991        Xˇ»
 3992    "});
 3993    cx.update_editor(|e, window, cx| {
 3994        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3995    });
 3996    cx.assert_editor_state(indoc! {"
 3997        «x
 3998        X
 3999        y
 4000        Y
 4001        z
 4002        Zˇ»
 4003    "});
 4004
 4005    // Test reverse_lines()
 4006    cx.set_state(indoc! {"
 4007        «5
 4008        4
 4009        3
 4010        2
 4011        1ˇ»
 4012    "});
 4013    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 4014    cx.assert_editor_state(indoc! {"
 4015        «1
 4016        2
 4017        3
 4018        4
 4019        5ˇ»
 4020    "});
 4021
 4022    // Skip testing shuffle_line()
 4023
 4024    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 4025    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 4026
 4027    // Don't manipulate when cursor is on single line, but expand the selection
 4028    cx.set_state(indoc! {"
 4029        ddˇdd
 4030        ccc
 4031        bb
 4032        a
 4033    "});
 4034    cx.update_editor(|e, window, cx| {
 4035        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4036    });
 4037    cx.assert_editor_state(indoc! {"
 4038        «ddddˇ»
 4039        ccc
 4040        bb
 4041        a
 4042    "});
 4043
 4044    // Basic manipulate case
 4045    // Start selection moves to column 0
 4046    // End of selection shrinks to fit shorter line
 4047    cx.set_state(indoc! {"
 4048        dd«d
 4049        ccc
 4050        bb
 4051        aaaaaˇ»
 4052    "});
 4053    cx.update_editor(|e, window, cx| {
 4054        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4055    });
 4056    cx.assert_editor_state(indoc! {"
 4057        «aaaaa
 4058        bb
 4059        ccc
 4060        dddˇ»
 4061    "});
 4062
 4063    // Manipulate case with newlines
 4064    cx.set_state(indoc! {"
 4065        dd«d
 4066        ccc
 4067
 4068        bb
 4069        aaaaa
 4070
 4071        ˇ»
 4072    "});
 4073    cx.update_editor(|e, window, cx| {
 4074        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4075    });
 4076    cx.assert_editor_state(indoc! {"
 4077        «
 4078
 4079        aaaaa
 4080        bb
 4081        ccc
 4082        dddˇ»
 4083
 4084    "});
 4085
 4086    // Adding new line
 4087    cx.set_state(indoc! {"
 4088        aa«a
 4089        bbˇ»b
 4090    "});
 4091    cx.update_editor(|e, window, cx| {
 4092        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4093    });
 4094    cx.assert_editor_state(indoc! {"
 4095        «aaa
 4096        bbb
 4097        added_lineˇ»
 4098    "});
 4099
 4100    // Removing line
 4101    cx.set_state(indoc! {"
 4102        aa«a
 4103        bbbˇ»
 4104    "});
 4105    cx.update_editor(|e, window, cx| {
 4106        e.manipulate_lines(window, cx, |lines| {
 4107            lines.pop();
 4108        })
 4109    });
 4110    cx.assert_editor_state(indoc! {"
 4111        «aaaˇ»
 4112    "});
 4113
 4114    // Removing all lines
 4115    cx.set_state(indoc! {"
 4116        aa«a
 4117        bbbˇ»
 4118    "});
 4119    cx.update_editor(|e, window, cx| {
 4120        e.manipulate_lines(window, cx, |lines| {
 4121            lines.drain(..);
 4122        })
 4123    });
 4124    cx.assert_editor_state(indoc! {"
 4125        ˇ
 4126    "});
 4127}
 4128
 4129#[gpui::test]
 4130async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4131    init_test(cx, |_| {});
 4132
 4133    let mut cx = EditorTestContext::new(cx).await;
 4134
 4135    // Consider continuous selection as single selection
 4136    cx.set_state(indoc! {"
 4137        Aaa«aa
 4138        cˇ»c«c
 4139        bb
 4140        aaaˇ»aa
 4141    "});
 4142    cx.update_editor(|e, window, cx| {
 4143        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4144    });
 4145    cx.assert_editor_state(indoc! {"
 4146        «Aaaaa
 4147        ccc
 4148        bb
 4149        aaaaaˇ»
 4150    "});
 4151
 4152    cx.set_state(indoc! {"
 4153        Aaa«aa
 4154        cˇ»c«c
 4155        bb
 4156        aaaˇ»aa
 4157    "});
 4158    cx.update_editor(|e, window, cx| {
 4159        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4160    });
 4161    cx.assert_editor_state(indoc! {"
 4162        «Aaaaa
 4163        ccc
 4164        bbˇ»
 4165    "});
 4166
 4167    // Consider non continuous selection as distinct dedup operations
 4168    cx.set_state(indoc! {"
 4169        «aaaaa
 4170        bb
 4171        aaaaa
 4172        aaaaaˇ»
 4173
 4174        aaa«aaˇ»
 4175    "});
 4176    cx.update_editor(|e, window, cx| {
 4177        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4178    });
 4179    cx.assert_editor_state(indoc! {"
 4180        «aaaaa
 4181        bbˇ»
 4182
 4183        «aaaaaˇ»
 4184    "});
 4185}
 4186
 4187#[gpui::test]
 4188async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4189    init_test(cx, |_| {});
 4190
 4191    let mut cx = EditorTestContext::new(cx).await;
 4192
 4193    cx.set_state(indoc! {"
 4194        «Aaa
 4195        aAa
 4196        Aaaˇ»
 4197    "});
 4198    cx.update_editor(|e, window, cx| {
 4199        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4200    });
 4201    cx.assert_editor_state(indoc! {"
 4202        «Aaa
 4203        aAaˇ»
 4204    "});
 4205
 4206    cx.set_state(indoc! {"
 4207        «Aaa
 4208        aAa
 4209        aaAˇ»
 4210    "});
 4211    cx.update_editor(|e, window, cx| {
 4212        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4213    });
 4214    cx.assert_editor_state(indoc! {"
 4215        «Aaaˇ»
 4216    "});
 4217}
 4218
 4219#[gpui::test]
 4220async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4221    init_test(cx, |_| {});
 4222
 4223    let mut cx = EditorTestContext::new(cx).await;
 4224
 4225    // Manipulate with multiple selections on a single line
 4226    cx.set_state(indoc! {"
 4227        dd«dd
 4228        cˇ»c«c
 4229        bb
 4230        aaaˇ»aa
 4231    "});
 4232    cx.update_editor(|e, window, cx| {
 4233        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4234    });
 4235    cx.assert_editor_state(indoc! {"
 4236        «aaaaa
 4237        bb
 4238        ccc
 4239        ddddˇ»
 4240    "});
 4241
 4242    // Manipulate with multiple disjoin selections
 4243    cx.set_state(indoc! {"
 4244 4245        4
 4246        3
 4247        2
 4248        1ˇ»
 4249
 4250        dd«dd
 4251        ccc
 4252        bb
 4253        aaaˇ»aa
 4254    "});
 4255    cx.update_editor(|e, window, cx| {
 4256        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4257    });
 4258    cx.assert_editor_state(indoc! {"
 4259        «1
 4260        2
 4261        3
 4262        4
 4263        5ˇ»
 4264
 4265        «aaaaa
 4266        bb
 4267        ccc
 4268        ddddˇ»
 4269    "});
 4270
 4271    // Adding lines on each selection
 4272    cx.set_state(indoc! {"
 4273 4274        1ˇ»
 4275
 4276        bb«bb
 4277        aaaˇ»aa
 4278    "});
 4279    cx.update_editor(|e, window, cx| {
 4280        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4281    });
 4282    cx.assert_editor_state(indoc! {"
 4283        «2
 4284        1
 4285        added lineˇ»
 4286
 4287        «bbbb
 4288        aaaaa
 4289        added lineˇ»
 4290    "});
 4291
 4292    // Removing lines on each selection
 4293    cx.set_state(indoc! {"
 4294 4295        1ˇ»
 4296
 4297        bb«bb
 4298        aaaˇ»aa
 4299    "});
 4300    cx.update_editor(|e, window, cx| {
 4301        e.manipulate_lines(window, cx, |lines| {
 4302            lines.pop();
 4303        })
 4304    });
 4305    cx.assert_editor_state(indoc! {"
 4306        «2ˇ»
 4307
 4308        «bbbbˇ»
 4309    "});
 4310}
 4311
 4312#[gpui::test]
 4313async fn test_toggle_case(cx: &mut TestAppContext) {
 4314    init_test(cx, |_| {});
 4315
 4316    let mut cx = EditorTestContext::new(cx).await;
 4317
 4318    // If all lower case -> upper case
 4319    cx.set_state(indoc! {"
 4320        «hello worldˇ»
 4321    "});
 4322    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4323    cx.assert_editor_state(indoc! {"
 4324        «HELLO WORLDˇ»
 4325    "});
 4326
 4327    // If all upper case -> lower case
 4328    cx.set_state(indoc! {"
 4329        «HELLO WORLDˇ»
 4330    "});
 4331    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4332    cx.assert_editor_state(indoc! {"
 4333        «hello worldˇ»
 4334    "});
 4335
 4336    // If any upper case characters are identified -> lower case
 4337    // This matches JetBrains IDEs
 4338    cx.set_state(indoc! {"
 4339        «hEllo worldˇ»
 4340    "});
 4341    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4342    cx.assert_editor_state(indoc! {"
 4343        «hello worldˇ»
 4344    "});
 4345}
 4346
 4347#[gpui::test]
 4348async fn test_manipulate_text(cx: &mut TestAppContext) {
 4349    init_test(cx, |_| {});
 4350
 4351    let mut cx = EditorTestContext::new(cx).await;
 4352
 4353    // Test convert_to_upper_case()
 4354    cx.set_state(indoc! {"
 4355        «hello worldˇ»
 4356    "});
 4357    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4358    cx.assert_editor_state(indoc! {"
 4359        «HELLO WORLDˇ»
 4360    "});
 4361
 4362    // Test convert_to_lower_case()
 4363    cx.set_state(indoc! {"
 4364        «HELLO WORLDˇ»
 4365    "});
 4366    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4367    cx.assert_editor_state(indoc! {"
 4368        «hello worldˇ»
 4369    "});
 4370
 4371    // Test multiple line, single selection case
 4372    cx.set_state(indoc! {"
 4373        «The quick brown
 4374        fox jumps over
 4375        the lazy dogˇ»
 4376    "});
 4377    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4378    cx.assert_editor_state(indoc! {"
 4379        «The Quick Brown
 4380        Fox Jumps Over
 4381        The Lazy Dogˇ»
 4382    "});
 4383
 4384    // Test multiple line, single selection case
 4385    cx.set_state(indoc! {"
 4386        «The quick brown
 4387        fox jumps over
 4388        the lazy dogˇ»
 4389    "});
 4390    cx.update_editor(|e, window, cx| {
 4391        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4392    });
 4393    cx.assert_editor_state(indoc! {"
 4394        «TheQuickBrown
 4395        FoxJumpsOver
 4396        TheLazyDogˇ»
 4397    "});
 4398
 4399    // From here on out, test more complex cases of manipulate_text()
 4400
 4401    // Test no selection case - should affect words cursors are in
 4402    // Cursor at beginning, middle, and end of word
 4403    cx.set_state(indoc! {"
 4404        ˇhello big beauˇtiful worldˇ
 4405    "});
 4406    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4407    cx.assert_editor_state(indoc! {"
 4408        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4409    "});
 4410
 4411    // Test multiple selections on a single line and across multiple lines
 4412    cx.set_state(indoc! {"
 4413        «Theˇ» quick «brown
 4414        foxˇ» jumps «overˇ»
 4415        the «lazyˇ» dog
 4416    "});
 4417    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4418    cx.assert_editor_state(indoc! {"
 4419        «THEˇ» quick «BROWN
 4420        FOXˇ» jumps «OVERˇ»
 4421        the «LAZYˇ» dog
 4422    "});
 4423
 4424    // Test case where text length grows
 4425    cx.set_state(indoc! {"
 4426        «tschüߡ»
 4427    "});
 4428    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4429    cx.assert_editor_state(indoc! {"
 4430        «TSCHÜSSˇ»
 4431    "});
 4432
 4433    // Test to make sure we don't crash when text shrinks
 4434    cx.set_state(indoc! {"
 4435        aaa_bbbˇ
 4436    "});
 4437    cx.update_editor(|e, window, cx| {
 4438        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4439    });
 4440    cx.assert_editor_state(indoc! {"
 4441        «aaaBbbˇ»
 4442    "});
 4443
 4444    // Test to make sure we all aware of the fact that each word can grow and shrink
 4445    // Final selections should be aware of this fact
 4446    cx.set_state(indoc! {"
 4447        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4448    "});
 4449    cx.update_editor(|e, window, cx| {
 4450        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4451    });
 4452    cx.assert_editor_state(indoc! {"
 4453        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4454    "});
 4455
 4456    cx.set_state(indoc! {"
 4457        «hElLo, WoRld!ˇ»
 4458    "});
 4459    cx.update_editor(|e, window, cx| {
 4460        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4461    });
 4462    cx.assert_editor_state(indoc! {"
 4463        «HeLlO, wOrLD!ˇ»
 4464    "});
 4465}
 4466
 4467#[gpui::test]
 4468fn test_duplicate_line(cx: &mut TestAppContext) {
 4469    init_test(cx, |_| {});
 4470
 4471    let editor = cx.add_window(|window, cx| {
 4472        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4473        build_editor(buffer, window, cx)
 4474    });
 4475    _ = editor.update(cx, |editor, window, cx| {
 4476        editor.change_selections(None, window, cx, |s| {
 4477            s.select_display_ranges([
 4478                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4479                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4480                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4481                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4482            ])
 4483        });
 4484        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4485        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4486        assert_eq!(
 4487            editor.selections.display_ranges(cx),
 4488            vec![
 4489                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4490                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4491                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4492                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4493            ]
 4494        );
 4495    });
 4496
 4497    let editor = cx.add_window(|window, cx| {
 4498        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4499        build_editor(buffer, window, cx)
 4500    });
 4501    _ = editor.update(cx, |editor, window, cx| {
 4502        editor.change_selections(None, window, cx, |s| {
 4503            s.select_display_ranges([
 4504                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4505                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4506            ])
 4507        });
 4508        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4509        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4510        assert_eq!(
 4511            editor.selections.display_ranges(cx),
 4512            vec![
 4513                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4514                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4515            ]
 4516        );
 4517    });
 4518
 4519    // With `move_upwards` the selections stay in place, except for
 4520    // the lines inserted above them
 4521    let editor = cx.add_window(|window, cx| {
 4522        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4523        build_editor(buffer, window, cx)
 4524    });
 4525    _ = editor.update(cx, |editor, window, cx| {
 4526        editor.change_selections(None, window, cx, |s| {
 4527            s.select_display_ranges([
 4528                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4529                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4530                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4531                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4532            ])
 4533        });
 4534        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4535        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4536        assert_eq!(
 4537            editor.selections.display_ranges(cx),
 4538            vec![
 4539                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4540                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4541                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4542                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4543            ]
 4544        );
 4545    });
 4546
 4547    let editor = cx.add_window(|window, cx| {
 4548        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4549        build_editor(buffer, window, cx)
 4550    });
 4551    _ = editor.update(cx, |editor, window, cx| {
 4552        editor.change_selections(None, window, cx, |s| {
 4553            s.select_display_ranges([
 4554                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4555                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4556            ])
 4557        });
 4558        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4559        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4560        assert_eq!(
 4561            editor.selections.display_ranges(cx),
 4562            vec![
 4563                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4564                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4565            ]
 4566        );
 4567    });
 4568
 4569    let editor = cx.add_window(|window, cx| {
 4570        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4571        build_editor(buffer, window, cx)
 4572    });
 4573    _ = editor.update(cx, |editor, window, cx| {
 4574        editor.change_selections(None, window, cx, |s| {
 4575            s.select_display_ranges([
 4576                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4577                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4578            ])
 4579        });
 4580        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4581        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4582        assert_eq!(
 4583            editor.selections.display_ranges(cx),
 4584            vec![
 4585                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4586                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4587            ]
 4588        );
 4589    });
 4590}
 4591
 4592#[gpui::test]
 4593fn test_move_line_up_down(cx: &mut TestAppContext) {
 4594    init_test(cx, |_| {});
 4595
 4596    let editor = cx.add_window(|window, cx| {
 4597        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4598        build_editor(buffer, window, cx)
 4599    });
 4600    _ = editor.update(cx, |editor, window, cx| {
 4601        editor.fold_creases(
 4602            vec![
 4603                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4604                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4605                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4606            ],
 4607            true,
 4608            window,
 4609            cx,
 4610        );
 4611        editor.change_selections(None, window, cx, |s| {
 4612            s.select_display_ranges([
 4613                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4614                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4615                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4616                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4617            ])
 4618        });
 4619        assert_eq!(
 4620            editor.display_text(cx),
 4621            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4622        );
 4623
 4624        editor.move_line_up(&MoveLineUp, window, cx);
 4625        assert_eq!(
 4626            editor.display_text(cx),
 4627            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4628        );
 4629        assert_eq!(
 4630            editor.selections.display_ranges(cx),
 4631            vec![
 4632                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4633                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4634                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4635                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 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\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4645        );
 4646        assert_eq!(
 4647            editor.selections.display_ranges(cx),
 4648            vec![
 4649                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 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_down(&MoveLineDown, window, cx);
 4659        assert_eq!(
 4660            editor.display_text(cx),
 4661            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4662        );
 4663        assert_eq!(
 4664            editor.selections.display_ranges(cx),
 4665            vec![
 4666                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4667                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4668                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4669                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4670            ]
 4671        );
 4672    });
 4673
 4674    _ = editor.update(cx, |editor, window, cx| {
 4675        editor.move_line_up(&MoveLineUp, window, cx);
 4676        assert_eq!(
 4677            editor.display_text(cx),
 4678            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4679        );
 4680        assert_eq!(
 4681            editor.selections.display_ranges(cx),
 4682            vec![
 4683                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4684                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4685                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4686                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4687            ]
 4688        );
 4689    });
 4690}
 4691
 4692#[gpui::test]
 4693fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4694    init_test(cx, |_| {});
 4695
 4696    let editor = cx.add_window(|window, cx| {
 4697        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4698        build_editor(buffer, window, cx)
 4699    });
 4700    _ = editor.update(cx, |editor, window, cx| {
 4701        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4702        editor.insert_blocks(
 4703            [BlockProperties {
 4704                style: BlockStyle::Fixed,
 4705                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4706                height: Some(1),
 4707                render: Arc::new(|_| div().into_any()),
 4708                priority: 0,
 4709                render_in_minimap: true,
 4710            }],
 4711            Some(Autoscroll::fit()),
 4712            cx,
 4713        );
 4714        editor.change_selections(None, window, cx, |s| {
 4715            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4716        });
 4717        editor.move_line_down(&MoveLineDown, window, cx);
 4718    });
 4719}
 4720
 4721#[gpui::test]
 4722async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4723    init_test(cx, |_| {});
 4724
 4725    let mut cx = EditorTestContext::new(cx).await;
 4726    cx.set_state(
 4727        &"
 4728            ˇzero
 4729            one
 4730            two
 4731            three
 4732            four
 4733            five
 4734        "
 4735        .unindent(),
 4736    );
 4737
 4738    // Create a four-line block that replaces three lines of text.
 4739    cx.update_editor(|editor, window, cx| {
 4740        let snapshot = editor.snapshot(window, cx);
 4741        let snapshot = &snapshot.buffer_snapshot;
 4742        let placement = BlockPlacement::Replace(
 4743            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4744        );
 4745        editor.insert_blocks(
 4746            [BlockProperties {
 4747                placement,
 4748                height: Some(4),
 4749                style: BlockStyle::Sticky,
 4750                render: Arc::new(|_| gpui::div().into_any_element()),
 4751                priority: 0,
 4752                render_in_minimap: true,
 4753            }],
 4754            None,
 4755            cx,
 4756        );
 4757    });
 4758
 4759    // Move down so that the cursor touches the block.
 4760    cx.update_editor(|editor, window, cx| {
 4761        editor.move_down(&Default::default(), window, cx);
 4762    });
 4763    cx.assert_editor_state(
 4764        &"
 4765            zero
 4766            «one
 4767            two
 4768            threeˇ»
 4769            four
 4770            five
 4771        "
 4772        .unindent(),
 4773    );
 4774
 4775    // Move down past the block.
 4776    cx.update_editor(|editor, window, cx| {
 4777        editor.move_down(&Default::default(), window, cx);
 4778    });
 4779    cx.assert_editor_state(
 4780        &"
 4781            zero
 4782            one
 4783            two
 4784            three
 4785            ˇfour
 4786            five
 4787        "
 4788        .unindent(),
 4789    );
 4790}
 4791
 4792#[gpui::test]
 4793fn test_transpose(cx: &mut TestAppContext) {
 4794    init_test(cx, |_| {});
 4795
 4796    _ = cx.add_window(|window, cx| {
 4797        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4798        editor.set_style(EditorStyle::default(), window, cx);
 4799        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4800        editor.transpose(&Default::default(), window, cx);
 4801        assert_eq!(editor.text(cx), "bac");
 4802        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4803
 4804        editor.transpose(&Default::default(), window, cx);
 4805        assert_eq!(editor.text(cx), "bca");
 4806        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4807
 4808        editor.transpose(&Default::default(), window, cx);
 4809        assert_eq!(editor.text(cx), "bac");
 4810        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4811
 4812        editor
 4813    });
 4814
 4815    _ = cx.add_window(|window, cx| {
 4816        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4817        editor.set_style(EditorStyle::default(), window, cx);
 4818        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4819        editor.transpose(&Default::default(), window, cx);
 4820        assert_eq!(editor.text(cx), "acb\nde");
 4821        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4822
 4823        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4824        editor.transpose(&Default::default(), window, cx);
 4825        assert_eq!(editor.text(cx), "acbd\ne");
 4826        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4827
 4828        editor.transpose(&Default::default(), window, cx);
 4829        assert_eq!(editor.text(cx), "acbde\n");
 4830        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4831
 4832        editor.transpose(&Default::default(), window, cx);
 4833        assert_eq!(editor.text(cx), "acbd\ne");
 4834        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4835
 4836        editor
 4837    });
 4838
 4839    _ = cx.add_window(|window, cx| {
 4840        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4841        editor.set_style(EditorStyle::default(), window, cx);
 4842        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4843        editor.transpose(&Default::default(), window, cx);
 4844        assert_eq!(editor.text(cx), "bacd\ne");
 4845        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4846
 4847        editor.transpose(&Default::default(), window, cx);
 4848        assert_eq!(editor.text(cx), "bcade\n");
 4849        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4850
 4851        editor.transpose(&Default::default(), window, cx);
 4852        assert_eq!(editor.text(cx), "bcda\ne");
 4853        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4854
 4855        editor.transpose(&Default::default(), window, cx);
 4856        assert_eq!(editor.text(cx), "bcade\n");
 4857        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4858
 4859        editor.transpose(&Default::default(), window, cx);
 4860        assert_eq!(editor.text(cx), "bcaed\n");
 4861        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4862
 4863        editor
 4864    });
 4865
 4866    _ = cx.add_window(|window, cx| {
 4867        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4868        editor.set_style(EditorStyle::default(), window, cx);
 4869        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4870        editor.transpose(&Default::default(), window, cx);
 4871        assert_eq!(editor.text(cx), "🏀🍐✋");
 4872        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4873
 4874        editor.transpose(&Default::default(), window, cx);
 4875        assert_eq!(editor.text(cx), "🏀✋🍐");
 4876        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4877
 4878        editor.transpose(&Default::default(), window, cx);
 4879        assert_eq!(editor.text(cx), "🏀🍐✋");
 4880        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4881
 4882        editor
 4883    });
 4884}
 4885
 4886#[gpui::test]
 4887async fn test_rewrap(cx: &mut TestAppContext) {
 4888    init_test(cx, |settings| {
 4889        settings.languages.extend([
 4890            (
 4891                "Markdown".into(),
 4892                LanguageSettingsContent {
 4893                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4894                    ..Default::default()
 4895                },
 4896            ),
 4897            (
 4898                "Plain Text".into(),
 4899                LanguageSettingsContent {
 4900                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4901                    ..Default::default()
 4902                },
 4903            ),
 4904        ])
 4905    });
 4906
 4907    let mut cx = EditorTestContext::new(cx).await;
 4908
 4909    let language_with_c_comments = Arc::new(Language::new(
 4910        LanguageConfig {
 4911            line_comments: vec!["// ".into()],
 4912            ..LanguageConfig::default()
 4913        },
 4914        None,
 4915    ));
 4916    let language_with_pound_comments = Arc::new(Language::new(
 4917        LanguageConfig {
 4918            line_comments: vec!["# ".into()],
 4919            ..LanguageConfig::default()
 4920        },
 4921        None,
 4922    ));
 4923    let markdown_language = Arc::new(Language::new(
 4924        LanguageConfig {
 4925            name: "Markdown".into(),
 4926            ..LanguageConfig::default()
 4927        },
 4928        None,
 4929    ));
 4930    let language_with_doc_comments = Arc::new(Language::new(
 4931        LanguageConfig {
 4932            line_comments: vec!["// ".into(), "/// ".into()],
 4933            ..LanguageConfig::default()
 4934        },
 4935        Some(tree_sitter_rust::LANGUAGE.into()),
 4936    ));
 4937
 4938    let plaintext_language = Arc::new(Language::new(
 4939        LanguageConfig {
 4940            name: "Plain Text".into(),
 4941            ..LanguageConfig::default()
 4942        },
 4943        None,
 4944    ));
 4945
 4946    assert_rewrap(
 4947        indoc! {"
 4948            // ˇ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.
 4949        "},
 4950        indoc! {"
 4951            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4952            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4953            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4954            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4955            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4956            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4957            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4958            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4959            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4960            // porttitor id. Aliquam id accumsan eros.
 4961        "},
 4962        language_with_c_comments.clone(),
 4963        &mut cx,
 4964    );
 4965
 4966    // Test that rewrapping works inside of a selection
 4967    assert_rewrap(
 4968        indoc! {"
 4969            «// 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.ˇ»
 4970        "},
 4971        indoc! {"
 4972            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4973            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4974            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4975            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4976            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4977            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4978            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4979            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4980            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4981            // porttitor id. Aliquam id accumsan eros.ˇ»
 4982        "},
 4983        language_with_c_comments.clone(),
 4984        &mut cx,
 4985    );
 4986
 4987    // Test that cursors that expand to the same region are collapsed.
 4988    assert_rewrap(
 4989        indoc! {"
 4990            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4991            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4992            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4993            // ˇ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.
 4994        "},
 4995        indoc! {"
 4996            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4997            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4998            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4999            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 5000            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 5001            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 5002            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 5003            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 5004            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 5005            // porttitor id. Aliquam id accumsan eros.
 5006        "},
 5007        language_with_c_comments.clone(),
 5008        &mut cx,
 5009    );
 5010
 5011    // Test that non-contiguous selections are treated separately.
 5012    assert_rewrap(
 5013        indoc! {"
 5014            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 5015            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 5016            //
 5017            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5018            // ˇ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.
 5019        "},
 5020        indoc! {"
 5021            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 5022            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 5023            // auctor, eu lacinia sapien scelerisque.
 5024            //
 5025            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 5026            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5027            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 5028            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 5029            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 5030            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 5031            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 5032        "},
 5033        language_with_c_comments.clone(),
 5034        &mut cx,
 5035    );
 5036
 5037    // Test that different comment prefixes are supported.
 5038    assert_rewrap(
 5039        indoc! {"
 5040            # ˇ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.
 5041        "},
 5042        indoc! {"
 5043            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 5044            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5045            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5046            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5047            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 5048            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 5049            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 5050            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 5051            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 5052            # accumsan eros.
 5053        "},
 5054        language_with_pound_comments.clone(),
 5055        &mut cx,
 5056    );
 5057
 5058    // Test that rewrapping is ignored outside of comments in most languages.
 5059    assert_rewrap(
 5060        indoc! {"
 5061            /// Adds two numbers.
 5062            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5063            fn add(a: u32, b: u32) -> u32 {
 5064                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ˇ
 5065            }
 5066        "},
 5067        indoc! {"
 5068            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 5069            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5070            fn add(a: u32, b: u32) -> u32 {
 5071                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ˇ
 5072            }
 5073        "},
 5074        language_with_doc_comments.clone(),
 5075        &mut cx,
 5076    );
 5077
 5078    // Test that rewrapping works in Markdown and Plain Text languages.
 5079    assert_rewrap(
 5080        indoc! {"
 5081            # Hello
 5082
 5083            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.
 5084        "},
 5085        indoc! {"
 5086            # Hello
 5087
 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        markdown_language,
 5097        &mut cx,
 5098    );
 5099
 5100    assert_rewrap(
 5101        indoc! {"
 5102            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.
 5103        "},
 5104        indoc! {"
 5105            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5106            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5107            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5108            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5109            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5110            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5111            Integer sit amet scelerisque nisi.
 5112        "},
 5113        plaintext_language,
 5114        &mut cx,
 5115    );
 5116
 5117    // Test rewrapping unaligned comments in a selection.
 5118    assert_rewrap(
 5119        indoc! {"
 5120            fn foo() {
 5121                if true {
 5122            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5123            // Praesent semper egestas tellus id dignissim.ˇ»
 5124                    do_something();
 5125                } else {
 5126                    //
 5127                }
 5128            }
 5129        "},
 5130        indoc! {"
 5131            fn foo() {
 5132                if true {
 5133            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5134                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5135                    // egestas tellus id dignissim.ˇ»
 5136                    do_something();
 5137                } else {
 5138                    //
 5139                }
 5140            }
 5141        "},
 5142        language_with_doc_comments.clone(),
 5143        &mut cx,
 5144    );
 5145
 5146    assert_rewrap(
 5147        indoc! {"
 5148            fn foo() {
 5149                if true {
 5150            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5151            // Praesent semper egestas tellus id dignissim.»
 5152                    do_something();
 5153                } else {
 5154                    //
 5155                }
 5156
 5157            }
 5158        "},
 5159        indoc! {"
 5160            fn foo() {
 5161                if true {
 5162            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5163                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5164                    // egestas tellus id dignissim.»
 5165                    do_something();
 5166                } else {
 5167                    //
 5168                }
 5169
 5170            }
 5171        "},
 5172        language_with_doc_comments.clone(),
 5173        &mut cx,
 5174    );
 5175
 5176    #[track_caller]
 5177    fn assert_rewrap(
 5178        unwrapped_text: &str,
 5179        wrapped_text: &str,
 5180        language: Arc<Language>,
 5181        cx: &mut EditorTestContext,
 5182    ) {
 5183        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5184        cx.set_state(unwrapped_text);
 5185        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5186        cx.assert_editor_state(wrapped_text);
 5187    }
 5188}
 5189
 5190#[gpui::test]
 5191async fn test_hard_wrap(cx: &mut TestAppContext) {
 5192    init_test(cx, |_| {});
 5193    let mut cx = EditorTestContext::new(cx).await;
 5194
 5195    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5196    cx.update_editor(|editor, _, cx| {
 5197        editor.set_hard_wrap(Some(14), cx);
 5198    });
 5199
 5200    cx.set_state(indoc!(
 5201        "
 5202        one two three ˇ
 5203        "
 5204    ));
 5205    cx.simulate_input("four");
 5206    cx.run_until_parked();
 5207
 5208    cx.assert_editor_state(indoc!(
 5209        "
 5210        one two three
 5211        fourˇ
 5212        "
 5213    ));
 5214
 5215    cx.update_editor(|editor, window, cx| {
 5216        editor.newline(&Default::default(), window, cx);
 5217    });
 5218    cx.run_until_parked();
 5219    cx.assert_editor_state(indoc!(
 5220        "
 5221        one two three
 5222        four
 5223        ˇ
 5224        "
 5225    ));
 5226
 5227    cx.simulate_input("five");
 5228    cx.run_until_parked();
 5229    cx.assert_editor_state(indoc!(
 5230        "
 5231        one two three
 5232        four
 5233        fiveˇ
 5234        "
 5235    ));
 5236
 5237    cx.update_editor(|editor, window, cx| {
 5238        editor.newline(&Default::default(), window, cx);
 5239    });
 5240    cx.run_until_parked();
 5241    cx.simulate_input("# ");
 5242    cx.run_until_parked();
 5243    cx.assert_editor_state(indoc!(
 5244        "
 5245        one two three
 5246        four
 5247        five
 5248        # ˇ
 5249        "
 5250    ));
 5251
 5252    cx.update_editor(|editor, window, cx| {
 5253        editor.newline(&Default::default(), window, cx);
 5254    });
 5255    cx.run_until_parked();
 5256    cx.assert_editor_state(indoc!(
 5257        "
 5258        one two three
 5259        four
 5260        five
 5261        #\x20
 5262 5263        "
 5264    ));
 5265
 5266    cx.simulate_input(" 6");
 5267    cx.run_until_parked();
 5268    cx.assert_editor_state(indoc!(
 5269        "
 5270        one two three
 5271        four
 5272        five
 5273        #
 5274        # 6ˇ
 5275        "
 5276    ));
 5277}
 5278
 5279#[gpui::test]
 5280async fn test_clipboard(cx: &mut TestAppContext) {
 5281    init_test(cx, |_| {});
 5282
 5283    let mut cx = EditorTestContext::new(cx).await;
 5284
 5285    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5286    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5287    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5288
 5289    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5290    cx.set_state("two ˇfour ˇsix ˇ");
 5291    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5292    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5293
 5294    // Paste again but with only two cursors. Since the number of cursors doesn't
 5295    // match the number of slices in the clipboard, the entire clipboard text
 5296    // is pasted at each cursor.
 5297    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5298    cx.update_editor(|e, window, cx| {
 5299        e.handle_input("( ", window, cx);
 5300        e.paste(&Paste, window, cx);
 5301        e.handle_input(") ", window, cx);
 5302    });
 5303    cx.assert_editor_state(
 5304        &([
 5305            "( one✅ ",
 5306            "three ",
 5307            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5308            "three ",
 5309            "five ) ˇ",
 5310        ]
 5311        .join("\n")),
 5312    );
 5313
 5314    // Cut with three selections, one of which is full-line.
 5315    cx.set_state(indoc! {"
 5316        1«2ˇ»3
 5317        4ˇ567
 5318        «8ˇ»9"});
 5319    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5320    cx.assert_editor_state(indoc! {"
 5321        1ˇ3
 5322        ˇ9"});
 5323
 5324    // Paste with three selections, noticing how the copied selection that was full-line
 5325    // gets inserted before the second cursor.
 5326    cx.set_state(indoc! {"
 5327        1ˇ3
 5328 5329        «oˇ»ne"});
 5330    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5331    cx.assert_editor_state(indoc! {"
 5332        12ˇ3
 5333        4567
 5334 5335        8ˇne"});
 5336
 5337    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5338    cx.set_state(indoc! {"
 5339        The quick brown
 5340        fox juˇmps over
 5341        the lazy dog"});
 5342    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5343    assert_eq!(
 5344        cx.read_from_clipboard()
 5345            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5346        Some("fox jumps over\n".to_string())
 5347    );
 5348
 5349    // Paste with three selections, noticing how the copied full-line selection is inserted
 5350    // before the empty selections but replaces the selection that is non-empty.
 5351    cx.set_state(indoc! {"
 5352        Tˇhe quick brown
 5353        «foˇ»x jumps over
 5354        tˇhe lazy dog"});
 5355    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5356    cx.assert_editor_state(indoc! {"
 5357        fox jumps over
 5358        Tˇhe quick brown
 5359        fox jumps over
 5360        ˇx jumps over
 5361        fox jumps over
 5362        tˇhe lazy dog"});
 5363}
 5364
 5365#[gpui::test]
 5366async fn test_copy_trim(cx: &mut TestAppContext) {
 5367    init_test(cx, |_| {});
 5368
 5369    let mut cx = EditorTestContext::new(cx).await;
 5370    cx.set_state(
 5371        r#"            «for selection in selections.iter() {
 5372            let mut start = selection.start;
 5373            let mut end = selection.end;
 5374            let is_entire_line = selection.is_empty();
 5375            if is_entire_line {
 5376                start = Point::new(start.row, 0);ˇ»
 5377                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5378            }
 5379        "#,
 5380    );
 5381    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5382    assert_eq!(
 5383        cx.read_from_clipboard()
 5384            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5385        Some(
 5386            "for selection in selections.iter() {
 5387            let mut start = selection.start;
 5388            let mut end = selection.end;
 5389            let is_entire_line = selection.is_empty();
 5390            if is_entire_line {
 5391                start = Point::new(start.row, 0);"
 5392                .to_string()
 5393        ),
 5394        "Regular copying preserves all indentation selected",
 5395    );
 5396    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5397    assert_eq!(
 5398        cx.read_from_clipboard()
 5399            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5400        Some(
 5401            "for selection in selections.iter() {
 5402let mut start = selection.start;
 5403let mut end = selection.end;
 5404let is_entire_line = selection.is_empty();
 5405if is_entire_line {
 5406    start = Point::new(start.row, 0);"
 5407                .to_string()
 5408        ),
 5409        "Copying with stripping should strip all leading whitespaces"
 5410    );
 5411
 5412    cx.set_state(
 5413        r#"       «     for selection in selections.iter() {
 5414            let mut start = selection.start;
 5415            let mut end = selection.end;
 5416            let is_entire_line = selection.is_empty();
 5417            if is_entire_line {
 5418                start = Point::new(start.row, 0);ˇ»
 5419                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5420            }
 5421        "#,
 5422    );
 5423    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5424    assert_eq!(
 5425        cx.read_from_clipboard()
 5426            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5427        Some(
 5428            "     for selection in selections.iter() {
 5429            let mut start = selection.start;
 5430            let mut end = selection.end;
 5431            let is_entire_line = selection.is_empty();
 5432            if is_entire_line {
 5433                start = Point::new(start.row, 0);"
 5434                .to_string()
 5435        ),
 5436        "Regular copying preserves all indentation selected",
 5437    );
 5438    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5439    assert_eq!(
 5440        cx.read_from_clipboard()
 5441            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5442        Some(
 5443            "for selection in selections.iter() {
 5444let mut start = selection.start;
 5445let mut end = selection.end;
 5446let is_entire_line = selection.is_empty();
 5447if is_entire_line {
 5448    start = Point::new(start.row, 0);"
 5449                .to_string()
 5450        ),
 5451        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5452    );
 5453
 5454    cx.set_state(
 5455        r#"       «ˇ     for selection in selections.iter() {
 5456            let mut start = selection.start;
 5457            let mut end = selection.end;
 5458            let is_entire_line = selection.is_empty();
 5459            if is_entire_line {
 5460                start = Point::new(start.row, 0);»
 5461                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5462            }
 5463        "#,
 5464    );
 5465    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5466    assert_eq!(
 5467        cx.read_from_clipboard()
 5468            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5469        Some(
 5470            "     for selection in selections.iter() {
 5471            let mut start = selection.start;
 5472            let mut end = selection.end;
 5473            let is_entire_line = selection.is_empty();
 5474            if is_entire_line {
 5475                start = Point::new(start.row, 0);"
 5476                .to_string()
 5477        ),
 5478        "Regular copying for reverse selection works the same",
 5479    );
 5480    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5481    assert_eq!(
 5482        cx.read_from_clipboard()
 5483            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5484        Some(
 5485            "for selection in selections.iter() {
 5486let mut start = selection.start;
 5487let mut end = selection.end;
 5488let is_entire_line = selection.is_empty();
 5489if is_entire_line {
 5490    start = Point::new(start.row, 0);"
 5491                .to_string()
 5492        ),
 5493        "Copying with stripping for reverse selection works the same"
 5494    );
 5495
 5496    cx.set_state(
 5497        r#"            for selection «in selections.iter() {
 5498            let mut start = selection.start;
 5499            let mut end = selection.end;
 5500            let is_entire_line = selection.is_empty();
 5501            if is_entire_line {
 5502                start = Point::new(start.row, 0);ˇ»
 5503                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5504            }
 5505        "#,
 5506    );
 5507    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5508    assert_eq!(
 5509        cx.read_from_clipboard()
 5510            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5511        Some(
 5512            "in selections.iter() {
 5513            let mut start = selection.start;
 5514            let mut end = selection.end;
 5515            let is_entire_line = selection.is_empty();
 5516            if is_entire_line {
 5517                start = Point::new(start.row, 0);"
 5518                .to_string()
 5519        ),
 5520        "When selecting past the indent, the copying works as usual",
 5521    );
 5522    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5523    assert_eq!(
 5524        cx.read_from_clipboard()
 5525            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5526        Some(
 5527            "in selections.iter() {
 5528            let mut start = selection.start;
 5529            let mut end = selection.end;
 5530            let is_entire_line = selection.is_empty();
 5531            if is_entire_line {
 5532                start = Point::new(start.row, 0);"
 5533                .to_string()
 5534        ),
 5535        "When selecting past the indent, nothing is trimmed"
 5536    );
 5537
 5538    cx.set_state(
 5539        r#"            «for selection in selections.iter() {
 5540            let mut start = selection.start;
 5541
 5542            let mut end = selection.end;
 5543            let is_entire_line = selection.is_empty();
 5544            if is_entire_line {
 5545                start = Point::new(start.row, 0);
 5546ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5547            }
 5548        "#,
 5549    );
 5550    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5551    assert_eq!(
 5552        cx.read_from_clipboard()
 5553            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5554        Some(
 5555            "for selection in selections.iter() {
 5556let mut start = selection.start;
 5557
 5558let mut end = selection.end;
 5559let is_entire_line = selection.is_empty();
 5560if is_entire_line {
 5561    start = Point::new(start.row, 0);
 5562"
 5563            .to_string()
 5564        ),
 5565        "Copying with stripping should ignore empty lines"
 5566    );
 5567}
 5568
 5569#[gpui::test]
 5570async fn test_paste_multiline(cx: &mut TestAppContext) {
 5571    init_test(cx, |_| {});
 5572
 5573    let mut cx = EditorTestContext::new(cx).await;
 5574    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5575
 5576    // Cut an indented block, without the leading whitespace.
 5577    cx.set_state(indoc! {"
 5578        const a: B = (
 5579            c(),
 5580            «d(
 5581                e,
 5582                f
 5583            )ˇ»
 5584        );
 5585    "});
 5586    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5587    cx.assert_editor_state(indoc! {"
 5588        const a: B = (
 5589            c(),
 5590            ˇ
 5591        );
 5592    "});
 5593
 5594    // Paste it at the same position.
 5595    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5596    cx.assert_editor_state(indoc! {"
 5597        const a: B = (
 5598            c(),
 5599            d(
 5600                e,
 5601                f
 5602 5603        );
 5604    "});
 5605
 5606    // Paste it at a line with a lower indent level.
 5607    cx.set_state(indoc! {"
 5608        ˇ
 5609        const a: B = (
 5610            c(),
 5611        );
 5612    "});
 5613    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5614    cx.assert_editor_state(indoc! {"
 5615        d(
 5616            e,
 5617            f
 5618 5619        const a: B = (
 5620            c(),
 5621        );
 5622    "});
 5623
 5624    // Cut an indented block, with the leading whitespace.
 5625    cx.set_state(indoc! {"
 5626        const a: B = (
 5627            c(),
 5628        «    d(
 5629                e,
 5630                f
 5631            )
 5632        ˇ»);
 5633    "});
 5634    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5635    cx.assert_editor_state(indoc! {"
 5636        const a: B = (
 5637            c(),
 5638        ˇ);
 5639    "});
 5640
 5641    // Paste it at the same position.
 5642    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5643    cx.assert_editor_state(indoc! {"
 5644        const a: B = (
 5645            c(),
 5646            d(
 5647                e,
 5648                f
 5649            )
 5650        ˇ);
 5651    "});
 5652
 5653    // Paste it at a line with a higher indent level.
 5654    cx.set_state(indoc! {"
 5655        const a: B = (
 5656            c(),
 5657            d(
 5658                e,
 5659 5660            )
 5661        );
 5662    "});
 5663    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5664    cx.assert_editor_state(indoc! {"
 5665        const a: B = (
 5666            c(),
 5667            d(
 5668                e,
 5669                f    d(
 5670                    e,
 5671                    f
 5672                )
 5673        ˇ
 5674            )
 5675        );
 5676    "});
 5677
 5678    // Copy an indented block, starting mid-line
 5679    cx.set_state(indoc! {"
 5680        const a: B = (
 5681            c(),
 5682            somethin«g(
 5683                e,
 5684                f
 5685            )ˇ»
 5686        );
 5687    "});
 5688    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5689
 5690    // Paste it on a line with a lower indent level
 5691    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5692    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5693    cx.assert_editor_state(indoc! {"
 5694        const a: B = (
 5695            c(),
 5696            something(
 5697                e,
 5698                f
 5699            )
 5700        );
 5701        g(
 5702            e,
 5703            f
 5704"});
 5705}
 5706
 5707#[gpui::test]
 5708async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5709    init_test(cx, |_| {});
 5710
 5711    cx.write_to_clipboard(ClipboardItem::new_string(
 5712        "    d(\n        e\n    );\n".into(),
 5713    ));
 5714
 5715    let mut cx = EditorTestContext::new(cx).await;
 5716    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5717
 5718    cx.set_state(indoc! {"
 5719        fn a() {
 5720            b();
 5721            if c() {
 5722                ˇ
 5723            }
 5724        }
 5725    "});
 5726
 5727    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5728    cx.assert_editor_state(indoc! {"
 5729        fn a() {
 5730            b();
 5731            if c() {
 5732                d(
 5733                    e
 5734                );
 5735        ˇ
 5736            }
 5737        }
 5738    "});
 5739
 5740    cx.set_state(indoc! {"
 5741        fn a() {
 5742            b();
 5743            ˇ
 5744        }
 5745    "});
 5746
 5747    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5748    cx.assert_editor_state(indoc! {"
 5749        fn a() {
 5750            b();
 5751            d(
 5752                e
 5753            );
 5754        ˇ
 5755        }
 5756    "});
 5757}
 5758
 5759#[gpui::test]
 5760fn test_select_all(cx: &mut TestAppContext) {
 5761    init_test(cx, |_| {});
 5762
 5763    let editor = cx.add_window(|window, cx| {
 5764        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5765        build_editor(buffer, window, cx)
 5766    });
 5767    _ = editor.update(cx, |editor, window, cx| {
 5768        editor.select_all(&SelectAll, window, cx);
 5769        assert_eq!(
 5770            editor.selections.display_ranges(cx),
 5771            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5772        );
 5773    });
 5774}
 5775
 5776#[gpui::test]
 5777fn test_select_line(cx: &mut TestAppContext) {
 5778    init_test(cx, |_| {});
 5779
 5780    let editor = cx.add_window(|window, cx| {
 5781        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5782        build_editor(buffer, window, cx)
 5783    });
 5784    _ = editor.update(cx, |editor, window, cx| {
 5785        editor.change_selections(None, window, cx, |s| {
 5786            s.select_display_ranges([
 5787                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5788                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5789                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5790                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5791            ])
 5792        });
 5793        editor.select_line(&SelectLine, window, cx);
 5794        assert_eq!(
 5795            editor.selections.display_ranges(cx),
 5796            vec![
 5797                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5798                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5799            ]
 5800        );
 5801    });
 5802
 5803    _ = editor.update(cx, |editor, window, cx| {
 5804        editor.select_line(&SelectLine, window, cx);
 5805        assert_eq!(
 5806            editor.selections.display_ranges(cx),
 5807            vec![
 5808                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5809                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5810            ]
 5811        );
 5812    });
 5813
 5814    _ = editor.update(cx, |editor, window, cx| {
 5815        editor.select_line(&SelectLine, window, cx);
 5816        assert_eq!(
 5817            editor.selections.display_ranges(cx),
 5818            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5819        );
 5820    });
 5821}
 5822
 5823#[gpui::test]
 5824async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5825    init_test(cx, |_| {});
 5826    let mut cx = EditorTestContext::new(cx).await;
 5827
 5828    #[track_caller]
 5829    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5830        cx.set_state(initial_state);
 5831        cx.update_editor(|e, window, cx| {
 5832            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5833        });
 5834        cx.assert_editor_state(expected_state);
 5835    }
 5836
 5837    // Selection starts and ends at the middle of lines, left-to-right
 5838    test(
 5839        &mut cx,
 5840        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5841        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5842    );
 5843    // Same thing, right-to-left
 5844    test(
 5845        &mut cx,
 5846        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5847        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5848    );
 5849
 5850    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5851    test(
 5852        &mut cx,
 5853        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5854        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5855    );
 5856    // Same thing, right-to-left
 5857    test(
 5858        &mut cx,
 5859        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5860        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5861    );
 5862
 5863    // Whole buffer, left-to-right, last line ends with newline
 5864    test(
 5865        &mut cx,
 5866        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5867        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5868    );
 5869    // Same thing, right-to-left
 5870    test(
 5871        &mut cx,
 5872        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5873        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5874    );
 5875
 5876    // Starts at the end of a line, ends at the start of another
 5877    test(
 5878        &mut cx,
 5879        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5880        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5881    );
 5882}
 5883
 5884#[gpui::test]
 5885async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5886    init_test(cx, |_| {});
 5887
 5888    let editor = cx.add_window(|window, cx| {
 5889        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5890        build_editor(buffer, window, cx)
 5891    });
 5892
 5893    // setup
 5894    _ = editor.update(cx, |editor, window, cx| {
 5895        editor.fold_creases(
 5896            vec![
 5897                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5898                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5899                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5900            ],
 5901            true,
 5902            window,
 5903            cx,
 5904        );
 5905        assert_eq!(
 5906            editor.display_text(cx),
 5907            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5908        );
 5909    });
 5910
 5911    _ = editor.update(cx, |editor, window, cx| {
 5912        editor.change_selections(None, window, cx, |s| {
 5913            s.select_display_ranges([
 5914                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5915                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5916                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5917                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5918            ])
 5919        });
 5920        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5921        assert_eq!(
 5922            editor.display_text(cx),
 5923            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5924        );
 5925    });
 5926    EditorTestContext::for_editor(editor, cx)
 5927        .await
 5928        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5929
 5930    _ = editor.update(cx, |editor, window, cx| {
 5931        editor.change_selections(None, window, cx, |s| {
 5932            s.select_display_ranges([
 5933                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5934            ])
 5935        });
 5936        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5937        assert_eq!(
 5938            editor.display_text(cx),
 5939            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5940        );
 5941        assert_eq!(
 5942            editor.selections.display_ranges(cx),
 5943            [
 5944                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5945                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5946                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5947                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5948                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5949                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5950                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5951            ]
 5952        );
 5953    });
 5954    EditorTestContext::for_editor(editor, cx)
 5955        .await
 5956        .assert_editor_state(
 5957            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5958        );
 5959}
 5960
 5961#[gpui::test]
 5962async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5963    init_test(cx, |_| {});
 5964
 5965    let mut cx = EditorTestContext::new(cx).await;
 5966
 5967    cx.set_state(indoc!(
 5968        r#"abc
 5969           defˇghi
 5970
 5971           jk
 5972           nlmo
 5973           "#
 5974    ));
 5975
 5976    cx.update_editor(|editor, window, cx| {
 5977        editor.add_selection_above(&Default::default(), window, cx);
 5978    });
 5979
 5980    cx.assert_editor_state(indoc!(
 5981        r#"abcˇ
 5982           defˇghi
 5983
 5984           jk
 5985           nlmo
 5986           "#
 5987    ));
 5988
 5989    cx.update_editor(|editor, window, cx| {
 5990        editor.add_selection_above(&Default::default(), window, cx);
 5991    });
 5992
 5993    cx.assert_editor_state(indoc!(
 5994        r#"abcˇ
 5995            defˇghi
 5996
 5997            jk
 5998            nlmo
 5999            "#
 6000    ));
 6001
 6002    cx.update_editor(|editor, window, cx| {
 6003        editor.add_selection_below(&Default::default(), window, cx);
 6004    });
 6005
 6006    cx.assert_editor_state(indoc!(
 6007        r#"abc
 6008           defˇghi
 6009
 6010           jk
 6011           nlmo
 6012           "#
 6013    ));
 6014
 6015    cx.update_editor(|editor, window, cx| {
 6016        editor.undo_selection(&Default::default(), window, cx);
 6017    });
 6018
 6019    cx.assert_editor_state(indoc!(
 6020        r#"abcˇ
 6021           defˇghi
 6022
 6023           jk
 6024           nlmo
 6025           "#
 6026    ));
 6027
 6028    cx.update_editor(|editor, window, cx| {
 6029        editor.redo_selection(&Default::default(), window, cx);
 6030    });
 6031
 6032    cx.assert_editor_state(indoc!(
 6033        r#"abc
 6034           defˇghi
 6035
 6036           jk
 6037           nlmo
 6038           "#
 6039    ));
 6040
 6041    cx.update_editor(|editor, window, cx| {
 6042        editor.add_selection_below(&Default::default(), window, cx);
 6043    });
 6044
 6045    cx.assert_editor_state(indoc!(
 6046        r#"abc
 6047           defˇghi
 6048           ˇ
 6049           jk
 6050           nlmo
 6051           "#
 6052    ));
 6053
 6054    cx.update_editor(|editor, window, cx| {
 6055        editor.add_selection_below(&Default::default(), window, cx);
 6056    });
 6057
 6058    cx.assert_editor_state(indoc!(
 6059        r#"abc
 6060           defˇghi
 6061           ˇ
 6062           jkˇ
 6063           nlmo
 6064           "#
 6065    ));
 6066
 6067    cx.update_editor(|editor, window, cx| {
 6068        editor.add_selection_below(&Default::default(), window, cx);
 6069    });
 6070
 6071    cx.assert_editor_state(indoc!(
 6072        r#"abc
 6073           defˇghi
 6074           ˇ
 6075           jkˇ
 6076           nlmˇo
 6077           "#
 6078    ));
 6079
 6080    cx.update_editor(|editor, window, cx| {
 6081        editor.add_selection_below(&Default::default(), window, cx);
 6082    });
 6083
 6084    cx.assert_editor_state(indoc!(
 6085        r#"abc
 6086           defˇghi
 6087           ˇ
 6088           jkˇ
 6089           nlmˇo
 6090           ˇ"#
 6091    ));
 6092
 6093    // change selections
 6094    cx.set_state(indoc!(
 6095        r#"abc
 6096           def«ˇg»hi
 6097
 6098           jk
 6099           nlmo
 6100           "#
 6101    ));
 6102
 6103    cx.update_editor(|editor, window, cx| {
 6104        editor.add_selection_below(&Default::default(), window, cx);
 6105    });
 6106
 6107    cx.assert_editor_state(indoc!(
 6108        r#"abc
 6109           def«ˇg»hi
 6110
 6111           jk
 6112           nlm«ˇo»
 6113           "#
 6114    ));
 6115
 6116    cx.update_editor(|editor, window, cx| {
 6117        editor.add_selection_below(&Default::default(), window, cx);
 6118    });
 6119
 6120    cx.assert_editor_state(indoc!(
 6121        r#"abc
 6122           def«ˇg»hi
 6123
 6124           jk
 6125           nlm«ˇo»
 6126           "#
 6127    ));
 6128
 6129    cx.update_editor(|editor, window, cx| {
 6130        editor.add_selection_above(&Default::default(), window, cx);
 6131    });
 6132
 6133    cx.assert_editor_state(indoc!(
 6134        r#"abc
 6135           def«ˇg»hi
 6136
 6137           jk
 6138           nlmo
 6139           "#
 6140    ));
 6141
 6142    cx.update_editor(|editor, window, cx| {
 6143        editor.add_selection_above(&Default::default(), window, cx);
 6144    });
 6145
 6146    cx.assert_editor_state(indoc!(
 6147        r#"abc
 6148           def«ˇg»hi
 6149
 6150           jk
 6151           nlmo
 6152           "#
 6153    ));
 6154
 6155    // Change selections again
 6156    cx.set_state(indoc!(
 6157        r#"a«bc
 6158           defgˇ»hi
 6159
 6160           jk
 6161           nlmo
 6162           "#
 6163    ));
 6164
 6165    cx.update_editor(|editor, window, cx| {
 6166        editor.add_selection_below(&Default::default(), window, cx);
 6167    });
 6168
 6169    cx.assert_editor_state(indoc!(
 6170        r#"a«bcˇ»
 6171           d«efgˇ»hi
 6172
 6173           j«kˇ»
 6174           nlmo
 6175           "#
 6176    ));
 6177
 6178    cx.update_editor(|editor, window, cx| {
 6179        editor.add_selection_below(&Default::default(), window, cx);
 6180    });
 6181    cx.assert_editor_state(indoc!(
 6182        r#"a«bcˇ»
 6183           d«efgˇ»hi
 6184
 6185           j«kˇ»
 6186           n«lmoˇ»
 6187           "#
 6188    ));
 6189    cx.update_editor(|editor, window, cx| {
 6190        editor.add_selection_above(&Default::default(), window, cx);
 6191    });
 6192
 6193    cx.assert_editor_state(indoc!(
 6194        r#"a«bcˇ»
 6195           d«efgˇ»hi
 6196
 6197           j«kˇ»
 6198           nlmo
 6199           "#
 6200    ));
 6201
 6202    // Change selections again
 6203    cx.set_state(indoc!(
 6204        r#"abc
 6205           d«ˇefghi
 6206
 6207           jk
 6208           nlm»o
 6209           "#
 6210    ));
 6211
 6212    cx.update_editor(|editor, window, cx| {
 6213        editor.add_selection_above(&Default::default(), window, cx);
 6214    });
 6215
 6216    cx.assert_editor_state(indoc!(
 6217        r#"a«ˇbc»
 6218           d«ˇef»ghi
 6219
 6220           j«ˇk»
 6221           n«ˇlm»o
 6222           "#
 6223    ));
 6224
 6225    cx.update_editor(|editor, window, cx| {
 6226        editor.add_selection_below(&Default::default(), window, cx);
 6227    });
 6228
 6229    cx.assert_editor_state(indoc!(
 6230        r#"abc
 6231           d«ˇef»ghi
 6232
 6233           j«ˇk»
 6234           n«ˇlm»o
 6235           "#
 6236    ));
 6237}
 6238
 6239#[gpui::test]
 6240async fn test_select_next(cx: &mut TestAppContext) {
 6241    init_test(cx, |_| {});
 6242
 6243    let mut cx = EditorTestContext::new(cx).await;
 6244    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6245
 6246    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6247        .unwrap();
 6248    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6249
 6250    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6251        .unwrap();
 6252    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6253
 6254    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6255    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6256
 6257    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6258    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6259
 6260    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6261        .unwrap();
 6262    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6263
 6264    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6265        .unwrap();
 6266    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6267
 6268    // Test selection direction should be preserved
 6269    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6270
 6271    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6272        .unwrap();
 6273    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6274}
 6275
 6276#[gpui::test]
 6277async fn test_select_all_matches(cx: &mut TestAppContext) {
 6278    init_test(cx, |_| {});
 6279
 6280    let mut cx = EditorTestContext::new(cx).await;
 6281
 6282    // Test caret-only selections
 6283    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6284    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6285        .unwrap();
 6286    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6287
 6288    // Test left-to-right selections
 6289    cx.set_state("abc\n«abcˇ»\nabc");
 6290    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6291        .unwrap();
 6292    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6293
 6294    // Test right-to-left selections
 6295    cx.set_state("abc\n«ˇabc»\nabc");
 6296    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6297        .unwrap();
 6298    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6299
 6300    // Test selecting whitespace with caret selection
 6301    cx.set_state("abc\nˇ   abc\nabc");
 6302    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6303        .unwrap();
 6304    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6305
 6306    // Test selecting whitespace with left-to-right selection
 6307    cx.set_state("abc\n«ˇ  »abc\nabc");
 6308    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6309        .unwrap();
 6310    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6311
 6312    // Test no matches with right-to-left selection
 6313    cx.set_state("abc\n«  ˇ»abc\nabc");
 6314    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6315        .unwrap();
 6316    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6317}
 6318
 6319#[gpui::test]
 6320async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6321    init_test(cx, |_| {});
 6322
 6323    let mut cx = EditorTestContext::new(cx).await;
 6324
 6325    let large_body_1 = "\nd".repeat(200);
 6326    let large_body_2 = "\ne".repeat(200);
 6327
 6328    cx.set_state(&format!(
 6329        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6330    ));
 6331    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6332        let scroll_position = editor.scroll_position(cx);
 6333        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6334        scroll_position
 6335    });
 6336
 6337    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6338        .unwrap();
 6339    cx.assert_editor_state(&format!(
 6340        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6341    ));
 6342    let scroll_position_after_selection =
 6343        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6344    assert_eq!(
 6345        initial_scroll_position, scroll_position_after_selection,
 6346        "Scroll position should not change after selecting all matches"
 6347    );
 6348}
 6349
 6350#[gpui::test]
 6351async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6352    init_test(cx, |_| {});
 6353
 6354    let mut cx = EditorLspTestContext::new_rust(
 6355        lsp::ServerCapabilities {
 6356            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6357            ..Default::default()
 6358        },
 6359        cx,
 6360    )
 6361    .await;
 6362
 6363    cx.set_state(indoc! {"
 6364        line 1
 6365        line 2
 6366        linˇe 3
 6367        line 4
 6368        line 5
 6369    "});
 6370
 6371    // Make an edit
 6372    cx.update_editor(|editor, window, cx| {
 6373        editor.handle_input("X", window, cx);
 6374    });
 6375
 6376    // Move cursor to a different position
 6377    cx.update_editor(|editor, window, cx| {
 6378        editor.change_selections(None, window, cx, |s| {
 6379            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6380        });
 6381    });
 6382
 6383    cx.assert_editor_state(indoc! {"
 6384        line 1
 6385        line 2
 6386        linXe 3
 6387        line 4
 6388        liˇne 5
 6389    "});
 6390
 6391    cx.lsp
 6392        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6393            Ok(Some(vec![lsp::TextEdit::new(
 6394                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6395                "PREFIX ".to_string(),
 6396            )]))
 6397        });
 6398
 6399    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6400        .unwrap()
 6401        .await
 6402        .unwrap();
 6403
 6404    cx.assert_editor_state(indoc! {"
 6405        PREFIX line 1
 6406        line 2
 6407        linXe 3
 6408        line 4
 6409        liˇne 5
 6410    "});
 6411
 6412    // Undo formatting
 6413    cx.update_editor(|editor, window, cx| {
 6414        editor.undo(&Default::default(), window, cx);
 6415    });
 6416
 6417    // Verify cursor moved back to position after edit
 6418    cx.assert_editor_state(indoc! {"
 6419        line 1
 6420        line 2
 6421        linXˇe 3
 6422        line 4
 6423        line 5
 6424    "});
 6425}
 6426
 6427#[gpui::test]
 6428async fn test_undo_inline_completion_scrolls_to_edit_pos(cx: &mut TestAppContext) {
 6429    init_test(cx, |_| {});
 6430
 6431    let mut cx = EditorTestContext::new(cx).await;
 6432
 6433    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
 6434    cx.update_editor(|editor, window, cx| {
 6435        editor.set_edit_prediction_provider(Some(provider.clone()), window, cx);
 6436    });
 6437
 6438    cx.set_state(indoc! {"
 6439        line 1
 6440        line 2
 6441        linˇe 3
 6442        line 4
 6443        line 5
 6444        line 6
 6445        line 7
 6446        line 8
 6447        line 9
 6448        line 10
 6449    "});
 6450
 6451    let snapshot = cx.buffer_snapshot();
 6452    let edit_position = snapshot.anchor_after(Point::new(2, 4));
 6453
 6454    cx.update(|_, cx| {
 6455        provider.update(cx, |provider, _| {
 6456            provider.set_inline_completion(Some(inline_completion::InlineCompletion {
 6457                id: None,
 6458                edits: vec![(edit_position..edit_position, "X".into())],
 6459                edit_preview: None,
 6460            }))
 6461        })
 6462    });
 6463
 6464    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 6465    cx.update_editor(|editor, window, cx| {
 6466        editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)
 6467    });
 6468
 6469    cx.assert_editor_state(indoc! {"
 6470        line 1
 6471        line 2
 6472        lineXˇ 3
 6473        line 4
 6474        line 5
 6475        line 6
 6476        line 7
 6477        line 8
 6478        line 9
 6479        line 10
 6480    "});
 6481
 6482    cx.update_editor(|editor, window, cx| {
 6483        editor.change_selections(None, window, cx, |s| {
 6484            s.select_ranges([Point::new(9, 2)..Point::new(9, 2)]);
 6485        });
 6486    });
 6487
 6488    cx.assert_editor_state(indoc! {"
 6489        line 1
 6490        line 2
 6491        lineX 3
 6492        line 4
 6493        line 5
 6494        line 6
 6495        line 7
 6496        line 8
 6497        line 9
 6498        liˇne 10
 6499    "});
 6500
 6501    cx.update_editor(|editor, window, cx| {
 6502        editor.undo(&Default::default(), window, cx);
 6503    });
 6504
 6505    cx.assert_editor_state(indoc! {"
 6506        line 1
 6507        line 2
 6508        lineˇ 3
 6509        line 4
 6510        line 5
 6511        line 6
 6512        line 7
 6513        line 8
 6514        line 9
 6515        line 10
 6516    "});
 6517}
 6518
 6519#[gpui::test]
 6520async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6521    init_test(cx, |_| {});
 6522
 6523    let mut cx = EditorTestContext::new(cx).await;
 6524    cx.set_state(
 6525        r#"let foo = 2;
 6526lˇet foo = 2;
 6527let fooˇ = 2;
 6528let foo = 2;
 6529let foo = ˇ2;"#,
 6530    );
 6531
 6532    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6533        .unwrap();
 6534    cx.assert_editor_state(
 6535        r#"let foo = 2;
 6536«letˇ» foo = 2;
 6537let «fooˇ» = 2;
 6538let foo = 2;
 6539let foo = «2ˇ»;"#,
 6540    );
 6541
 6542    // noop for multiple selections with different contents
 6543    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6544        .unwrap();
 6545    cx.assert_editor_state(
 6546        r#"let foo = 2;
 6547«letˇ» foo = 2;
 6548let «fooˇ» = 2;
 6549let foo = 2;
 6550let foo = «2ˇ»;"#,
 6551    );
 6552
 6553    // Test last selection direction should be preserved
 6554    cx.set_state(
 6555        r#"let foo = 2;
 6556let foo = 2;
 6557let «fooˇ» = 2;
 6558let «ˇfoo» = 2;
 6559let foo = 2;"#,
 6560    );
 6561
 6562    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6563        .unwrap();
 6564    cx.assert_editor_state(
 6565        r#"let foo = 2;
 6566let foo = 2;
 6567let «fooˇ» = 2;
 6568let «ˇfoo» = 2;
 6569let «ˇfoo» = 2;"#,
 6570    );
 6571}
 6572
 6573#[gpui::test]
 6574async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6575    init_test(cx, |_| {});
 6576
 6577    let mut cx =
 6578        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6579
 6580    cx.assert_editor_state(indoc! {"
 6581        ˇbbb
 6582        ccc
 6583
 6584        bbb
 6585        ccc
 6586        "});
 6587    cx.dispatch_action(SelectPrevious::default());
 6588    cx.assert_editor_state(indoc! {"
 6589                «bbbˇ»
 6590                ccc
 6591
 6592                bbb
 6593                ccc
 6594                "});
 6595    cx.dispatch_action(SelectPrevious::default());
 6596    cx.assert_editor_state(indoc! {"
 6597                «bbbˇ»
 6598                ccc
 6599
 6600                «bbbˇ»
 6601                ccc
 6602                "});
 6603}
 6604
 6605#[gpui::test]
 6606async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6607    init_test(cx, |_| {});
 6608
 6609    let mut cx = EditorTestContext::new(cx).await;
 6610    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6611
 6612    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6613        .unwrap();
 6614    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6615
 6616    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6617        .unwrap();
 6618    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6619
 6620    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6621    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6622
 6623    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6624    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6625
 6626    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6627        .unwrap();
 6628    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6629
 6630    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6631        .unwrap();
 6632    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6633}
 6634
 6635#[gpui::test]
 6636async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6637    init_test(cx, |_| {});
 6638
 6639    let mut cx = EditorTestContext::new(cx).await;
 6640    cx.set_state("");
 6641
 6642    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6643        .unwrap();
 6644    cx.assert_editor_state("«aˇ»");
 6645    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6646        .unwrap();
 6647    cx.assert_editor_state("«aˇ»");
 6648}
 6649
 6650#[gpui::test]
 6651async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6652    init_test(cx, |_| {});
 6653
 6654    let mut cx = EditorTestContext::new(cx).await;
 6655    cx.set_state(
 6656        r#"let foo = 2;
 6657lˇet foo = 2;
 6658let fooˇ = 2;
 6659let foo = 2;
 6660let foo = ˇ2;"#,
 6661    );
 6662
 6663    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6664        .unwrap();
 6665    cx.assert_editor_state(
 6666        r#"let foo = 2;
 6667«letˇ» foo = 2;
 6668let «fooˇ» = 2;
 6669let foo = 2;
 6670let foo = «2ˇ»;"#,
 6671    );
 6672
 6673    // noop for multiple selections with different contents
 6674    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6675        .unwrap();
 6676    cx.assert_editor_state(
 6677        r#"let foo = 2;
 6678«letˇ» foo = 2;
 6679let «fooˇ» = 2;
 6680let foo = 2;
 6681let foo = «2ˇ»;"#,
 6682    );
 6683}
 6684
 6685#[gpui::test]
 6686async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6687    init_test(cx, |_| {});
 6688
 6689    let mut cx = EditorTestContext::new(cx).await;
 6690    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6691
 6692    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6693        .unwrap();
 6694    // selection direction is preserved
 6695    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6696
 6697    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6698        .unwrap();
 6699    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6700
 6701    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6702    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6703
 6704    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6705    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6706
 6707    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6708        .unwrap();
 6709    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6710
 6711    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6712        .unwrap();
 6713    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6714}
 6715
 6716#[gpui::test]
 6717async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6718    init_test(cx, |_| {});
 6719
 6720    let language = Arc::new(Language::new(
 6721        LanguageConfig::default(),
 6722        Some(tree_sitter_rust::LANGUAGE.into()),
 6723    ));
 6724
 6725    let text = r#"
 6726        use mod1::mod2::{mod3, mod4};
 6727
 6728        fn fn_1(param1: bool, param2: &str) {
 6729            let var1 = "text";
 6730        }
 6731    "#
 6732    .unindent();
 6733
 6734    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6735    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6736    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6737
 6738    editor
 6739        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6740        .await;
 6741
 6742    editor.update_in(cx, |editor, window, cx| {
 6743        editor.change_selections(None, window, cx, |s| {
 6744            s.select_display_ranges([
 6745                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6746                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6747                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6748            ]);
 6749        });
 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    editor.update(cx, |editor, cx| {
 6770        assert_text_with_selections(
 6771            editor,
 6772            indoc! {r#"
 6773                use mod1::mod2::«{mod3, mod4}ˇ»;
 6774
 6775                «ˇfn fn_1(param1: bool, param2: &str) {
 6776                    let var1 = "text";
 6777 6778            "#},
 6779            cx,
 6780        );
 6781    });
 6782
 6783    editor.update_in(cx, |editor, window, cx| {
 6784        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6785    });
 6786    assert_eq!(
 6787        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6788        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6789    );
 6790
 6791    // Trying to expand the selected syntax node one more time has no effect.
 6792    editor.update_in(cx, |editor, window, cx| {
 6793        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6794    });
 6795    assert_eq!(
 6796        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6797        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 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, «mod4ˇ»};
 6825
 6826                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6827                    let var1 = "«ˇtext»";
 6828                }
 6829            "#},
 6830            cx,
 6831        );
 6832    });
 6833
 6834    editor.update_in(cx, |editor, window, cx| {
 6835        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6836    });
 6837    editor.update(cx, |editor, cx| {
 6838        assert_text_with_selections(
 6839            editor,
 6840            indoc! {r#"
 6841                use mod1::mod2::{mod3, mo«ˇ»d4};
 6842
 6843                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6844                    let var1 = "te«ˇ»xt";
 6845                }
 6846            "#},
 6847            cx,
 6848        );
 6849    });
 6850
 6851    // Trying to shrink the selected syntax node one more time has no effect.
 6852    editor.update_in(cx, |editor, window, cx| {
 6853        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6854    });
 6855    editor.update_in(cx, |editor, _, cx| {
 6856        assert_text_with_selections(
 6857            editor,
 6858            indoc! {r#"
 6859                use mod1::mod2::{mod3, mo«ˇ»d4};
 6860
 6861                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6862                    let var1 = "te«ˇ»xt";
 6863                }
 6864            "#},
 6865            cx,
 6866        );
 6867    });
 6868
 6869    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6870    // a fold.
 6871    editor.update_in(cx, |editor, window, cx| {
 6872        editor.fold_creases(
 6873            vec![
 6874                Crease::simple(
 6875                    Point::new(0, 21)..Point::new(0, 24),
 6876                    FoldPlaceholder::test(),
 6877                ),
 6878                Crease::simple(
 6879                    Point::new(3, 20)..Point::new(3, 22),
 6880                    FoldPlaceholder::test(),
 6881                ),
 6882            ],
 6883            true,
 6884            window,
 6885            cx,
 6886        );
 6887        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6888    });
 6889    editor.update(cx, |editor, cx| {
 6890        assert_text_with_selections(
 6891            editor,
 6892            indoc! {r#"
 6893                use mod1::mod2::«{mod3, mod4}ˇ»;
 6894
 6895                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6896                    let var1 = "«ˇtext»";
 6897                }
 6898            "#},
 6899            cx,
 6900        );
 6901    });
 6902}
 6903
 6904#[gpui::test]
 6905async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6906    init_test(cx, |_| {});
 6907
 6908    let language = Arc::new(Language::new(
 6909        LanguageConfig::default(),
 6910        Some(tree_sitter_rust::LANGUAGE.into()),
 6911    ));
 6912
 6913    let text = "let a = 2;";
 6914
 6915    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6916    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6917    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6918
 6919    editor
 6920        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6921        .await;
 6922
 6923    // Test case 1: Cursor at end of word
 6924    editor.update_in(cx, |editor, window, cx| {
 6925        editor.change_selections(None, window, cx, |s| {
 6926            s.select_display_ranges([
 6927                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6928            ]);
 6929        });
 6930    });
 6931    editor.update(cx, |editor, cx| {
 6932        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6933    });
 6934    editor.update_in(cx, |editor, window, cx| {
 6935        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6936    });
 6937    editor.update(cx, |editor, cx| {
 6938        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6939    });
 6940    editor.update_in(cx, |editor, window, cx| {
 6941        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6942    });
 6943    editor.update(cx, |editor, cx| {
 6944        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6945    });
 6946
 6947    // Test case 2: Cursor at end of statement
 6948    editor.update_in(cx, |editor, window, cx| {
 6949        editor.change_selections(None, window, cx, |s| {
 6950            s.select_display_ranges([
 6951                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6952            ]);
 6953        });
 6954    });
 6955    editor.update(cx, |editor, cx| {
 6956        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6957    });
 6958    editor.update_in(cx, |editor, window, cx| {
 6959        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6960    });
 6961    editor.update(cx, |editor, cx| {
 6962        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6963    });
 6964}
 6965
 6966#[gpui::test]
 6967async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6968    init_test(cx, |_| {});
 6969
 6970    let language = Arc::new(Language::new(
 6971        LanguageConfig::default(),
 6972        Some(tree_sitter_rust::LANGUAGE.into()),
 6973    ));
 6974
 6975    let text = r#"
 6976        use mod1::mod2::{mod3, mod4};
 6977
 6978        fn fn_1(param1: bool, param2: &str) {
 6979            let var1 = "hello world";
 6980        }
 6981    "#
 6982    .unindent();
 6983
 6984    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6985    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6986    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6987
 6988    editor
 6989        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6990        .await;
 6991
 6992    // Test 1: Cursor on a letter of a string word
 6993    editor.update_in(cx, |editor, window, cx| {
 6994        editor.change_selections(None, window, cx, |s| {
 6995            s.select_display_ranges([
 6996                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6997            ]);
 6998        });
 6999    });
 7000    editor.update_in(cx, |editor, window, cx| {
 7001        assert_text_with_selections(
 7002            editor,
 7003            indoc! {r#"
 7004                use mod1::mod2::{mod3, mod4};
 7005
 7006                fn fn_1(param1: bool, param2: &str) {
 7007                    let var1 = "hˇello world";
 7008                }
 7009            "#},
 7010            cx,
 7011        );
 7012        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7013        assert_text_with_selections(
 7014            editor,
 7015            indoc! {r#"
 7016                use mod1::mod2::{mod3, mod4};
 7017
 7018                fn fn_1(param1: bool, param2: &str) {
 7019                    let var1 = "«ˇhello» world";
 7020                }
 7021            "#},
 7022            cx,
 7023        );
 7024    });
 7025
 7026    // Test 2: Partial selection within a word
 7027    editor.update_in(cx, |editor, window, cx| {
 7028        editor.change_selections(None, window, cx, |s| {
 7029            s.select_display_ranges([
 7030                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 7031            ]);
 7032        });
 7033    });
 7034    editor.update_in(cx, |editor, window, cx| {
 7035        assert_text_with_selections(
 7036            editor,
 7037            indoc! {r#"
 7038                use mod1::mod2::{mod3, mod4};
 7039
 7040                fn fn_1(param1: bool, param2: &str) {
 7041                    let var1 = "h«elˇ»lo world";
 7042                }
 7043            "#},
 7044            cx,
 7045        );
 7046        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7047        assert_text_with_selections(
 7048            editor,
 7049            indoc! {r#"
 7050                use mod1::mod2::{mod3, mod4};
 7051
 7052                fn fn_1(param1: bool, param2: &str) {
 7053                    let var1 = "«ˇhello» world";
 7054                }
 7055            "#},
 7056            cx,
 7057        );
 7058    });
 7059
 7060    // Test 3: Complete word already selected
 7061    editor.update_in(cx, |editor, window, cx| {
 7062        editor.change_selections(None, window, cx, |s| {
 7063            s.select_display_ranges([
 7064                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 7065            ]);
 7066        });
 7067    });
 7068    editor.update_in(cx, |editor, window, cx| {
 7069        assert_text_with_selections(
 7070            editor,
 7071            indoc! {r#"
 7072                use mod1::mod2::{mod3, mod4};
 7073
 7074                fn fn_1(param1: bool, param2: &str) {
 7075                    let var1 = "«helloˇ» world";
 7076                }
 7077            "#},
 7078            cx,
 7079        );
 7080        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7081        assert_text_with_selections(
 7082            editor,
 7083            indoc! {r#"
 7084                use mod1::mod2::{mod3, mod4};
 7085
 7086                fn fn_1(param1: bool, param2: &str) {
 7087                    let var1 = "«hello worldˇ»";
 7088                }
 7089            "#},
 7090            cx,
 7091        );
 7092    });
 7093
 7094    // Test 4: Selection spanning across words
 7095    editor.update_in(cx, |editor, window, cx| {
 7096        editor.change_selections(None, window, cx, |s| {
 7097            s.select_display_ranges([
 7098                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 7099            ]);
 7100        });
 7101    });
 7102    editor.update_in(cx, |editor, window, cx| {
 7103        assert_text_with_selections(
 7104            editor,
 7105            indoc! {r#"
 7106                use mod1::mod2::{mod3, mod4};
 7107
 7108                fn fn_1(param1: bool, param2: &str) {
 7109                    let var1 = "hel«lo woˇ»rld";
 7110                }
 7111            "#},
 7112            cx,
 7113        );
 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    // Test 5: Expansion beyond string
 7129    editor.update_in(cx, |editor, window, cx| {
 7130        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7131        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7132        assert_text_with_selections(
 7133            editor,
 7134            indoc! {r#"
 7135                use mod1::mod2::{mod3, mod4};
 7136
 7137                fn fn_1(param1: bool, param2: &str) {
 7138                    «ˇlet var1 = "hello world";»
 7139                }
 7140            "#},
 7141            cx,
 7142        );
 7143    });
 7144}
 7145
 7146#[gpui::test]
 7147async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 7148    init_test(cx, |_| {});
 7149
 7150    let base_text = r#"
 7151        impl A {
 7152            // this is an uncommitted comment
 7153
 7154            fn b() {
 7155                c();
 7156            }
 7157
 7158            // this is another uncommitted comment
 7159
 7160            fn d() {
 7161                // e
 7162                // f
 7163            }
 7164        }
 7165
 7166        fn g() {
 7167            // h
 7168        }
 7169    "#
 7170    .unindent();
 7171
 7172    let text = r#"
 7173        ˇimpl A {
 7174
 7175            fn b() {
 7176                c();
 7177            }
 7178
 7179            fn d() {
 7180                // e
 7181                // f
 7182            }
 7183        }
 7184
 7185        fn g() {
 7186            // h
 7187        }
 7188    "#
 7189    .unindent();
 7190
 7191    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7192    cx.set_state(&text);
 7193    cx.set_head_text(&base_text);
 7194    cx.update_editor(|editor, window, cx| {
 7195        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7196    });
 7197
 7198    cx.assert_state_with_diff(
 7199        "
 7200        ˇimpl A {
 7201      -     // this is an uncommitted comment
 7202
 7203            fn b() {
 7204                c();
 7205            }
 7206
 7207      -     // this is another uncommitted comment
 7208      -
 7209            fn d() {
 7210                // e
 7211                // f
 7212            }
 7213        }
 7214
 7215        fn g() {
 7216            // h
 7217        }
 7218    "
 7219        .unindent(),
 7220    );
 7221
 7222    let expected_display_text = "
 7223        impl A {
 7224            // this is an uncommitted comment
 7225
 7226            fn b() {
 7227 7228            }
 7229
 7230            // this is another uncommitted comment
 7231
 7232            fn d() {
 7233 7234            }
 7235        }
 7236
 7237        fn g() {
 7238 7239        }
 7240        "
 7241    .unindent();
 7242
 7243    cx.update_editor(|editor, window, cx| {
 7244        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7245        assert_eq!(editor.display_text(cx), expected_display_text);
 7246    });
 7247}
 7248
 7249#[gpui::test]
 7250async fn test_autoindent(cx: &mut TestAppContext) {
 7251    init_test(cx, |_| {});
 7252
 7253    let language = Arc::new(
 7254        Language::new(
 7255            LanguageConfig {
 7256                brackets: BracketPairConfig {
 7257                    pairs: vec![
 7258                        BracketPair {
 7259                            start: "{".to_string(),
 7260                            end: "}".to_string(),
 7261                            close: false,
 7262                            surround: false,
 7263                            newline: true,
 7264                        },
 7265                        BracketPair {
 7266                            start: "(".to_string(),
 7267                            end: ")".to_string(),
 7268                            close: false,
 7269                            surround: false,
 7270                            newline: true,
 7271                        },
 7272                    ],
 7273                    ..Default::default()
 7274                },
 7275                ..Default::default()
 7276            },
 7277            Some(tree_sitter_rust::LANGUAGE.into()),
 7278        )
 7279        .with_indents_query(
 7280            r#"
 7281                (_ "(" ")" @end) @indent
 7282                (_ "{" "}" @end) @indent
 7283            "#,
 7284        )
 7285        .unwrap(),
 7286    );
 7287
 7288    let text = "fn a() {}";
 7289
 7290    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7291    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7292    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7293    editor
 7294        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7295        .await;
 7296
 7297    editor.update_in(cx, |editor, window, cx| {
 7298        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7299        editor.newline(&Newline, window, cx);
 7300        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7301        assert_eq!(
 7302            editor.selections.ranges(cx),
 7303            &[
 7304                Point::new(1, 4)..Point::new(1, 4),
 7305                Point::new(3, 4)..Point::new(3, 4),
 7306                Point::new(5, 0)..Point::new(5, 0)
 7307            ]
 7308        );
 7309    });
 7310}
 7311
 7312#[gpui::test]
 7313async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7314    init_test(cx, |_| {});
 7315
 7316    {
 7317        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7318        cx.set_state(indoc! {"
 7319            impl A {
 7320
 7321                fn b() {}
 7322
 7323            «fn c() {
 7324
 7325            }ˇ»
 7326            }
 7327        "});
 7328
 7329        cx.update_editor(|editor, window, cx| {
 7330            editor.autoindent(&Default::default(), window, cx);
 7331        });
 7332
 7333        cx.assert_editor_state(indoc! {"
 7334            impl A {
 7335
 7336                fn b() {}
 7337
 7338                «fn c() {
 7339
 7340                }ˇ»
 7341            }
 7342        "});
 7343    }
 7344
 7345    {
 7346        let mut cx = EditorTestContext::new_multibuffer(
 7347            cx,
 7348            [indoc! { "
 7349                impl A {
 7350                «
 7351                // a
 7352                fn b(){}
 7353                »
 7354                «
 7355                    }
 7356                    fn c(){}
 7357                »
 7358            "}],
 7359        );
 7360
 7361        let buffer = cx.update_editor(|editor, _, cx| {
 7362            let buffer = editor.buffer().update(cx, |buffer, _| {
 7363                buffer.all_buffers().iter().next().unwrap().clone()
 7364            });
 7365            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7366            buffer
 7367        });
 7368
 7369        cx.run_until_parked();
 7370        cx.update_editor(|editor, window, cx| {
 7371            editor.select_all(&Default::default(), window, cx);
 7372            editor.autoindent(&Default::default(), window, cx)
 7373        });
 7374        cx.run_until_parked();
 7375
 7376        cx.update(|_, cx| {
 7377            assert_eq!(
 7378                buffer.read(cx).text(),
 7379                indoc! { "
 7380                    impl A {
 7381
 7382                        // a
 7383                        fn b(){}
 7384
 7385
 7386                    }
 7387                    fn c(){}
 7388
 7389                " }
 7390            )
 7391        });
 7392    }
 7393}
 7394
 7395#[gpui::test]
 7396async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7397    init_test(cx, |_| {});
 7398
 7399    let mut cx = EditorTestContext::new(cx).await;
 7400
 7401    let language = Arc::new(Language::new(
 7402        LanguageConfig {
 7403            brackets: BracketPairConfig {
 7404                pairs: vec![
 7405                    BracketPair {
 7406                        start: "{".to_string(),
 7407                        end: "}".to_string(),
 7408                        close: true,
 7409                        surround: true,
 7410                        newline: true,
 7411                    },
 7412                    BracketPair {
 7413                        start: "(".to_string(),
 7414                        end: ")".to_string(),
 7415                        close: true,
 7416                        surround: true,
 7417                        newline: true,
 7418                    },
 7419                    BracketPair {
 7420                        start: "/*".to_string(),
 7421                        end: " */".to_string(),
 7422                        close: true,
 7423                        surround: true,
 7424                        newline: true,
 7425                    },
 7426                    BracketPair {
 7427                        start: "[".to_string(),
 7428                        end: "]".to_string(),
 7429                        close: false,
 7430                        surround: false,
 7431                        newline: true,
 7432                    },
 7433                    BracketPair {
 7434                        start: "\"".to_string(),
 7435                        end: "\"".to_string(),
 7436                        close: true,
 7437                        surround: true,
 7438                        newline: false,
 7439                    },
 7440                    BracketPair {
 7441                        start: "<".to_string(),
 7442                        end: ">".to_string(),
 7443                        close: false,
 7444                        surround: true,
 7445                        newline: true,
 7446                    },
 7447                ],
 7448                ..Default::default()
 7449            },
 7450            autoclose_before: "})]".to_string(),
 7451            ..Default::default()
 7452        },
 7453        Some(tree_sitter_rust::LANGUAGE.into()),
 7454    ));
 7455
 7456    cx.language_registry().add(language.clone());
 7457    cx.update_buffer(|buffer, cx| {
 7458        buffer.set_language(Some(language), cx);
 7459    });
 7460
 7461    cx.set_state(
 7462        &r#"
 7463            🏀ˇ
 7464            εˇ
 7465            ❤️ˇ
 7466        "#
 7467        .unindent(),
 7468    );
 7469
 7470    // autoclose multiple nested brackets at multiple cursors
 7471    cx.update_editor(|editor, window, cx| {
 7472        editor.handle_input("{", window, cx);
 7473        editor.handle_input("{", window, cx);
 7474        editor.handle_input("{", window, cx);
 7475    });
 7476    cx.assert_editor_state(
 7477        &"
 7478            🏀{{{ˇ}}}
 7479            ε{{{ˇ}}}
 7480            ❤️{{{ˇ}}}
 7481        "
 7482        .unindent(),
 7483    );
 7484
 7485    // insert a different closing bracket
 7486    cx.update_editor(|editor, window, cx| {
 7487        editor.handle_input(")", window, cx);
 7488    });
 7489    cx.assert_editor_state(
 7490        &"
 7491            🏀{{{)ˇ}}}
 7492            ε{{{)ˇ}}}
 7493            ❤️{{{)ˇ}}}
 7494        "
 7495        .unindent(),
 7496    );
 7497
 7498    // skip over the auto-closed brackets when typing a closing bracket
 7499    cx.update_editor(|editor, window, cx| {
 7500        editor.move_right(&MoveRight, window, cx);
 7501        editor.handle_input("}", window, cx);
 7502        editor.handle_input("}", window, cx);
 7503        editor.handle_input("}", window, cx);
 7504    });
 7505    cx.assert_editor_state(
 7506        &"
 7507            🏀{{{)}}}}ˇ
 7508            ε{{{)}}}}ˇ
 7509            ❤️{{{)}}}}ˇ
 7510        "
 7511        .unindent(),
 7512    );
 7513
 7514    // autoclose multi-character pairs
 7515    cx.set_state(
 7516        &"
 7517            ˇ
 7518            ˇ
 7519        "
 7520        .unindent(),
 7521    );
 7522    cx.update_editor(|editor, window, cx| {
 7523        editor.handle_input("/", window, cx);
 7524        editor.handle_input("*", window, cx);
 7525    });
 7526    cx.assert_editor_state(
 7527        &"
 7528            /*ˇ */
 7529            /*ˇ */
 7530        "
 7531        .unindent(),
 7532    );
 7533
 7534    // one cursor autocloses a multi-character pair, one cursor
 7535    // does not autoclose.
 7536    cx.set_state(
 7537        &"
 7538 7539            ˇ
 7540        "
 7541        .unindent(),
 7542    );
 7543    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7544    cx.assert_editor_state(
 7545        &"
 7546            /*ˇ */
 7547 7548        "
 7549        .unindent(),
 7550    );
 7551
 7552    // Don't autoclose if the next character isn't whitespace and isn't
 7553    // listed in the language's "autoclose_before" section.
 7554    cx.set_state("ˇa b");
 7555    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7556    cx.assert_editor_state("{ˇa b");
 7557
 7558    // Don't autoclose if `close` is false for the bracket pair
 7559    cx.set_state("ˇ");
 7560    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7561    cx.assert_editor_state("");
 7562
 7563    // Surround with brackets if text is selected
 7564    cx.set_state("«aˇ» b");
 7565    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7566    cx.assert_editor_state("{«aˇ»} b");
 7567
 7568    // Autoclose when not immediately after a word character
 7569    cx.set_state("a ˇ");
 7570    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7571    cx.assert_editor_state("a \"ˇ\"");
 7572
 7573    // Autoclose pair where the start and end characters are the same
 7574    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7575    cx.assert_editor_state("a \"\"ˇ");
 7576
 7577    // Don't autoclose when immediately after a word character
 7578    cx.set_state("");
 7579    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7580    cx.assert_editor_state("a\"ˇ");
 7581
 7582    // Do autoclose when after a non-word character
 7583    cx.set_state("");
 7584    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7585    cx.assert_editor_state("{\"ˇ\"");
 7586
 7587    // Non identical pairs autoclose regardless of preceding character
 7588    cx.set_state("");
 7589    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7590    cx.assert_editor_state("a{ˇ}");
 7591
 7592    // Don't autoclose pair if autoclose is disabled
 7593    cx.set_state("ˇ");
 7594    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7595    cx.assert_editor_state("");
 7596
 7597    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7598    cx.set_state("«aˇ» b");
 7599    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7600    cx.assert_editor_state("<«aˇ»> b");
 7601}
 7602
 7603#[gpui::test]
 7604async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7605    init_test(cx, |settings| {
 7606        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7607    });
 7608
 7609    let mut cx = EditorTestContext::new(cx).await;
 7610
 7611    let language = Arc::new(Language::new(
 7612        LanguageConfig {
 7613            brackets: BracketPairConfig {
 7614                pairs: vec![
 7615                    BracketPair {
 7616                        start: "{".to_string(),
 7617                        end: "}".to_string(),
 7618                        close: true,
 7619                        surround: true,
 7620                        newline: true,
 7621                    },
 7622                    BracketPair {
 7623                        start: "(".to_string(),
 7624                        end: ")".to_string(),
 7625                        close: true,
 7626                        surround: true,
 7627                        newline: true,
 7628                    },
 7629                    BracketPair {
 7630                        start: "[".to_string(),
 7631                        end: "]".to_string(),
 7632                        close: false,
 7633                        surround: false,
 7634                        newline: true,
 7635                    },
 7636                ],
 7637                ..Default::default()
 7638            },
 7639            autoclose_before: "})]".to_string(),
 7640            ..Default::default()
 7641        },
 7642        Some(tree_sitter_rust::LANGUAGE.into()),
 7643    ));
 7644
 7645    cx.language_registry().add(language.clone());
 7646    cx.update_buffer(|buffer, cx| {
 7647        buffer.set_language(Some(language), cx);
 7648    });
 7649
 7650    cx.set_state(
 7651        &"
 7652            ˇ
 7653            ˇ
 7654            ˇ
 7655        "
 7656        .unindent(),
 7657    );
 7658
 7659    // ensure only matching closing brackets are skipped over
 7660    cx.update_editor(|editor, window, cx| {
 7661        editor.handle_input("}", window, cx);
 7662        editor.move_left(&MoveLeft, window, cx);
 7663        editor.handle_input(")", window, cx);
 7664        editor.move_left(&MoveLeft, window, cx);
 7665    });
 7666    cx.assert_editor_state(
 7667        &"
 7668            ˇ)}
 7669            ˇ)}
 7670            ˇ)}
 7671        "
 7672        .unindent(),
 7673    );
 7674
 7675    // skip-over closing brackets at multiple cursors
 7676    cx.update_editor(|editor, window, cx| {
 7677        editor.handle_input(")", window, cx);
 7678        editor.handle_input("}", window, cx);
 7679    });
 7680    cx.assert_editor_state(
 7681        &"
 7682            )}ˇ
 7683            )}ˇ
 7684            )}ˇ
 7685        "
 7686        .unindent(),
 7687    );
 7688
 7689    // ignore non-close brackets
 7690    cx.update_editor(|editor, window, cx| {
 7691        editor.handle_input("]", window, cx);
 7692        editor.move_left(&MoveLeft, window, cx);
 7693        editor.handle_input("]", window, cx);
 7694    });
 7695    cx.assert_editor_state(
 7696        &"
 7697            )}]ˇ]
 7698            )}]ˇ]
 7699            )}]ˇ]
 7700        "
 7701        .unindent(),
 7702    );
 7703}
 7704
 7705#[gpui::test]
 7706async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7707    init_test(cx, |_| {});
 7708
 7709    let mut cx = EditorTestContext::new(cx).await;
 7710
 7711    let html_language = Arc::new(
 7712        Language::new(
 7713            LanguageConfig {
 7714                name: "HTML".into(),
 7715                brackets: BracketPairConfig {
 7716                    pairs: vec![
 7717                        BracketPair {
 7718                            start: "<".into(),
 7719                            end: ">".into(),
 7720                            close: true,
 7721                            ..Default::default()
 7722                        },
 7723                        BracketPair {
 7724                            start: "{".into(),
 7725                            end: "}".into(),
 7726                            close: true,
 7727                            ..Default::default()
 7728                        },
 7729                        BracketPair {
 7730                            start: "(".into(),
 7731                            end: ")".into(),
 7732                            close: true,
 7733                            ..Default::default()
 7734                        },
 7735                    ],
 7736                    ..Default::default()
 7737                },
 7738                autoclose_before: "})]>".into(),
 7739                ..Default::default()
 7740            },
 7741            Some(tree_sitter_html::LANGUAGE.into()),
 7742        )
 7743        .with_injection_query(
 7744            r#"
 7745            (script_element
 7746                (raw_text) @injection.content
 7747                (#set! injection.language "javascript"))
 7748            "#,
 7749        )
 7750        .unwrap(),
 7751    );
 7752
 7753    let javascript_language = Arc::new(Language::new(
 7754        LanguageConfig {
 7755            name: "JavaScript".into(),
 7756            brackets: BracketPairConfig {
 7757                pairs: vec![
 7758                    BracketPair {
 7759                        start: "/*".into(),
 7760                        end: " */".into(),
 7761                        close: true,
 7762                        ..Default::default()
 7763                    },
 7764                    BracketPair {
 7765                        start: "{".into(),
 7766                        end: "}".into(),
 7767                        close: true,
 7768                        ..Default::default()
 7769                    },
 7770                    BracketPair {
 7771                        start: "(".into(),
 7772                        end: ")".into(),
 7773                        close: true,
 7774                        ..Default::default()
 7775                    },
 7776                ],
 7777                ..Default::default()
 7778            },
 7779            autoclose_before: "})]>".into(),
 7780            ..Default::default()
 7781        },
 7782        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7783    ));
 7784
 7785    cx.language_registry().add(html_language.clone());
 7786    cx.language_registry().add(javascript_language.clone());
 7787
 7788    cx.update_buffer(|buffer, cx| {
 7789        buffer.set_language(Some(html_language), cx);
 7790    });
 7791
 7792    cx.set_state(
 7793        &r#"
 7794            <body>ˇ
 7795                <script>
 7796                    var x = 1;ˇ
 7797                </script>
 7798            </body>ˇ
 7799        "#
 7800        .unindent(),
 7801    );
 7802
 7803    // Precondition: different languages are active at different locations.
 7804    cx.update_editor(|editor, window, cx| {
 7805        let snapshot = editor.snapshot(window, cx);
 7806        let cursors = editor.selections.ranges::<usize>(cx);
 7807        let languages = cursors
 7808            .iter()
 7809            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7810            .collect::<Vec<_>>();
 7811        assert_eq!(
 7812            languages,
 7813            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7814        );
 7815    });
 7816
 7817    // Angle brackets autoclose in HTML, but not JavaScript.
 7818    cx.update_editor(|editor, window, cx| {
 7819        editor.handle_input("<", window, cx);
 7820        editor.handle_input("a", window, cx);
 7821    });
 7822    cx.assert_editor_state(
 7823        &r#"
 7824            <body><aˇ>
 7825                <script>
 7826                    var x = 1;<aˇ
 7827                </script>
 7828            </body><aˇ>
 7829        "#
 7830        .unindent(),
 7831    );
 7832
 7833    // Curly braces and parens autoclose in both HTML and JavaScript.
 7834    cx.update_editor(|editor, window, cx| {
 7835        editor.handle_input(" b=", window, cx);
 7836        editor.handle_input("{", window, cx);
 7837        editor.handle_input("c", window, cx);
 7838        editor.handle_input("(", window, cx);
 7839    });
 7840    cx.assert_editor_state(
 7841        &r#"
 7842            <body><a b={c(ˇ)}>
 7843                <script>
 7844                    var x = 1;<a b={c(ˇ)}
 7845                </script>
 7846            </body><a b={c(ˇ)}>
 7847        "#
 7848        .unindent(),
 7849    );
 7850
 7851    // Brackets that were already autoclosed are skipped.
 7852    cx.update_editor(|editor, window, cx| {
 7853        editor.handle_input(")", window, cx);
 7854        editor.handle_input("d", window, cx);
 7855        editor.handle_input("}", window, cx);
 7856    });
 7857    cx.assert_editor_state(
 7858        &r#"
 7859            <body><a b={c()d}ˇ>
 7860                <script>
 7861                    var x = 1;<a b={c()d}ˇ
 7862                </script>
 7863            </body><a b={c()d}ˇ>
 7864        "#
 7865        .unindent(),
 7866    );
 7867    cx.update_editor(|editor, window, cx| {
 7868        editor.handle_input(">", window, cx);
 7869    });
 7870    cx.assert_editor_state(
 7871        &r#"
 7872            <body><a b={c()d}>ˇ
 7873                <script>
 7874                    var x = 1;<a b={c()d}>ˇ
 7875                </script>
 7876            </body><a b={c()d}>ˇ
 7877        "#
 7878        .unindent(),
 7879    );
 7880
 7881    // Reset
 7882    cx.set_state(
 7883        &r#"
 7884            <body>ˇ
 7885                <script>
 7886                    var x = 1;ˇ
 7887                </script>
 7888            </body>ˇ
 7889        "#
 7890        .unindent(),
 7891    );
 7892
 7893    cx.update_editor(|editor, window, cx| {
 7894        editor.handle_input("<", window, cx);
 7895    });
 7896    cx.assert_editor_state(
 7897        &r#"
 7898            <body><ˇ>
 7899                <script>
 7900                    var x = 1;<ˇ
 7901                </script>
 7902            </body><ˇ>
 7903        "#
 7904        .unindent(),
 7905    );
 7906
 7907    // When backspacing, the closing angle brackets are removed.
 7908    cx.update_editor(|editor, window, cx| {
 7909        editor.backspace(&Backspace, window, cx);
 7910    });
 7911    cx.assert_editor_state(
 7912        &r#"
 7913            <body>ˇ
 7914                <script>
 7915                    var x = 1;ˇ
 7916                </script>
 7917            </body>ˇ
 7918        "#
 7919        .unindent(),
 7920    );
 7921
 7922    // Block comments autoclose in JavaScript, but not HTML.
 7923    cx.update_editor(|editor, window, cx| {
 7924        editor.handle_input("/", window, cx);
 7925        editor.handle_input("*", window, cx);
 7926    });
 7927    cx.assert_editor_state(
 7928        &r#"
 7929            <body>/*ˇ
 7930                <script>
 7931                    var x = 1;/*ˇ */
 7932                </script>
 7933            </body>/*ˇ
 7934        "#
 7935        .unindent(),
 7936    );
 7937}
 7938
 7939#[gpui::test]
 7940async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7941    init_test(cx, |_| {});
 7942
 7943    let mut cx = EditorTestContext::new(cx).await;
 7944
 7945    let rust_language = Arc::new(
 7946        Language::new(
 7947            LanguageConfig {
 7948                name: "Rust".into(),
 7949                brackets: serde_json::from_value(json!([
 7950                    { "start": "{", "end": "}", "close": true, "newline": true },
 7951                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7952                ]))
 7953                .unwrap(),
 7954                autoclose_before: "})]>".into(),
 7955                ..Default::default()
 7956            },
 7957            Some(tree_sitter_rust::LANGUAGE.into()),
 7958        )
 7959        .with_override_query("(string_literal) @string")
 7960        .unwrap(),
 7961    );
 7962
 7963    cx.language_registry().add(rust_language.clone());
 7964    cx.update_buffer(|buffer, cx| {
 7965        buffer.set_language(Some(rust_language), cx);
 7966    });
 7967
 7968    cx.set_state(
 7969        &r#"
 7970            let x = ˇ
 7971        "#
 7972        .unindent(),
 7973    );
 7974
 7975    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7976    cx.update_editor(|editor, window, cx| {
 7977        editor.handle_input("\"", window, cx);
 7978    });
 7979    cx.assert_editor_state(
 7980        &r#"
 7981            let x = "ˇ"
 7982        "#
 7983        .unindent(),
 7984    );
 7985
 7986    // Inserting another quotation mark. The cursor moves across the existing
 7987    // automatically-inserted quotation mark.
 7988    cx.update_editor(|editor, window, cx| {
 7989        editor.handle_input("\"", window, cx);
 7990    });
 7991    cx.assert_editor_state(
 7992        &r#"
 7993            let x = ""ˇ
 7994        "#
 7995        .unindent(),
 7996    );
 7997
 7998    // Reset
 7999    cx.set_state(
 8000        &r#"
 8001            let x = ˇ
 8002        "#
 8003        .unindent(),
 8004    );
 8005
 8006    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 8007    cx.update_editor(|editor, window, cx| {
 8008        editor.handle_input("\"", window, cx);
 8009        editor.handle_input(" ", window, cx);
 8010        editor.move_left(&Default::default(), window, cx);
 8011        editor.handle_input("\\", window, cx);
 8012        editor.handle_input("\"", window, cx);
 8013    });
 8014    cx.assert_editor_state(
 8015        &r#"
 8016            let x = "\"ˇ "
 8017        "#
 8018        .unindent(),
 8019    );
 8020
 8021    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 8022    // mark. Nothing is inserted.
 8023    cx.update_editor(|editor, window, cx| {
 8024        editor.move_right(&Default::default(), window, cx);
 8025        editor.handle_input("\"", window, cx);
 8026    });
 8027    cx.assert_editor_state(
 8028        &r#"
 8029            let x = "\" "ˇ
 8030        "#
 8031        .unindent(),
 8032    );
 8033}
 8034
 8035#[gpui::test]
 8036async fn test_surround_with_pair(cx: &mut TestAppContext) {
 8037    init_test(cx, |_| {});
 8038
 8039    let language = Arc::new(Language::new(
 8040        LanguageConfig {
 8041            brackets: BracketPairConfig {
 8042                pairs: vec![
 8043                    BracketPair {
 8044                        start: "{".to_string(),
 8045                        end: "}".to_string(),
 8046                        close: true,
 8047                        surround: true,
 8048                        newline: true,
 8049                    },
 8050                    BracketPair {
 8051                        start: "/* ".to_string(),
 8052                        end: "*/".to_string(),
 8053                        close: true,
 8054                        surround: true,
 8055                        ..Default::default()
 8056                    },
 8057                ],
 8058                ..Default::default()
 8059            },
 8060            ..Default::default()
 8061        },
 8062        Some(tree_sitter_rust::LANGUAGE.into()),
 8063    ));
 8064
 8065    let text = r#"
 8066        a
 8067        b
 8068        c
 8069    "#
 8070    .unindent();
 8071
 8072    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8073    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8074    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8075    editor
 8076        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8077        .await;
 8078
 8079    editor.update_in(cx, |editor, window, cx| {
 8080        editor.change_selections(None, window, cx, |s| {
 8081            s.select_display_ranges([
 8082                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8083                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8084                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 8085            ])
 8086        });
 8087
 8088        editor.handle_input("{", window, cx);
 8089        editor.handle_input("{", window, cx);
 8090        editor.handle_input("{", window, cx);
 8091        assert_eq!(
 8092            editor.text(cx),
 8093            "
 8094                {{{a}}}
 8095                {{{b}}}
 8096                {{{c}}}
 8097            "
 8098            .unindent()
 8099        );
 8100        assert_eq!(
 8101            editor.selections.display_ranges(cx),
 8102            [
 8103                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 8104                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 8105                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 8106            ]
 8107        );
 8108
 8109        editor.undo(&Undo, window, cx);
 8110        editor.undo(&Undo, window, cx);
 8111        editor.undo(&Undo, window, cx);
 8112        assert_eq!(
 8113            editor.text(cx),
 8114            "
 8115                a
 8116                b
 8117                c
 8118            "
 8119            .unindent()
 8120        );
 8121        assert_eq!(
 8122            editor.selections.display_ranges(cx),
 8123            [
 8124                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8125                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8126                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8127            ]
 8128        );
 8129
 8130        // Ensure inserting the first character of a multi-byte bracket pair
 8131        // doesn't surround the selections with the bracket.
 8132        editor.handle_input("/", window, cx);
 8133        assert_eq!(
 8134            editor.text(cx),
 8135            "
 8136                /
 8137                /
 8138                /
 8139            "
 8140            .unindent()
 8141        );
 8142        assert_eq!(
 8143            editor.selections.display_ranges(cx),
 8144            [
 8145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8146                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8147                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8148            ]
 8149        );
 8150
 8151        editor.undo(&Undo, window, cx);
 8152        assert_eq!(
 8153            editor.text(cx),
 8154            "
 8155                a
 8156                b
 8157                c
 8158            "
 8159            .unindent()
 8160        );
 8161        assert_eq!(
 8162            editor.selections.display_ranges(cx),
 8163            [
 8164                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8165                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8166                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8167            ]
 8168        );
 8169
 8170        // Ensure inserting the last character of a multi-byte bracket pair
 8171        // doesn't surround the selections with the bracket.
 8172        editor.handle_input("*", window, cx);
 8173        assert_eq!(
 8174            editor.text(cx),
 8175            "
 8176                *
 8177                *
 8178                *
 8179            "
 8180            .unindent()
 8181        );
 8182        assert_eq!(
 8183            editor.selections.display_ranges(cx),
 8184            [
 8185                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8186                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8187                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8188            ]
 8189        );
 8190    });
 8191}
 8192
 8193#[gpui::test]
 8194async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8195    init_test(cx, |_| {});
 8196
 8197    let language = Arc::new(Language::new(
 8198        LanguageConfig {
 8199            brackets: BracketPairConfig {
 8200                pairs: vec![BracketPair {
 8201                    start: "{".to_string(),
 8202                    end: "}".to_string(),
 8203                    close: true,
 8204                    surround: true,
 8205                    newline: true,
 8206                }],
 8207                ..Default::default()
 8208            },
 8209            autoclose_before: "}".to_string(),
 8210            ..Default::default()
 8211        },
 8212        Some(tree_sitter_rust::LANGUAGE.into()),
 8213    ));
 8214
 8215    let text = r#"
 8216        a
 8217        b
 8218        c
 8219    "#
 8220    .unindent();
 8221
 8222    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8223    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8224    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8225    editor
 8226        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8227        .await;
 8228
 8229    editor.update_in(cx, |editor, window, cx| {
 8230        editor.change_selections(None, window, cx, |s| {
 8231            s.select_ranges([
 8232                Point::new(0, 1)..Point::new(0, 1),
 8233                Point::new(1, 1)..Point::new(1, 1),
 8234                Point::new(2, 1)..Point::new(2, 1),
 8235            ])
 8236        });
 8237
 8238        editor.handle_input("{", window, cx);
 8239        editor.handle_input("{", window, cx);
 8240        editor.handle_input("_", window, cx);
 8241        assert_eq!(
 8242            editor.text(cx),
 8243            "
 8244                a{{_}}
 8245                b{{_}}
 8246                c{{_}}
 8247            "
 8248            .unindent()
 8249        );
 8250        assert_eq!(
 8251            editor.selections.ranges::<Point>(cx),
 8252            [
 8253                Point::new(0, 4)..Point::new(0, 4),
 8254                Point::new(1, 4)..Point::new(1, 4),
 8255                Point::new(2, 4)..Point::new(2, 4)
 8256            ]
 8257        );
 8258
 8259        editor.backspace(&Default::default(), window, cx);
 8260        editor.backspace(&Default::default(), window, cx);
 8261        assert_eq!(
 8262            editor.text(cx),
 8263            "
 8264                a{}
 8265                b{}
 8266                c{}
 8267            "
 8268            .unindent()
 8269        );
 8270        assert_eq!(
 8271            editor.selections.ranges::<Point>(cx),
 8272            [
 8273                Point::new(0, 2)..Point::new(0, 2),
 8274                Point::new(1, 2)..Point::new(1, 2),
 8275                Point::new(2, 2)..Point::new(2, 2)
 8276            ]
 8277        );
 8278
 8279        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8280        assert_eq!(
 8281            editor.text(cx),
 8282            "
 8283                a
 8284                b
 8285                c
 8286            "
 8287            .unindent()
 8288        );
 8289        assert_eq!(
 8290            editor.selections.ranges::<Point>(cx),
 8291            [
 8292                Point::new(0, 1)..Point::new(0, 1),
 8293                Point::new(1, 1)..Point::new(1, 1),
 8294                Point::new(2, 1)..Point::new(2, 1)
 8295            ]
 8296        );
 8297    });
 8298}
 8299
 8300#[gpui::test]
 8301async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8302    init_test(cx, |settings| {
 8303        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8304    });
 8305
 8306    let mut cx = EditorTestContext::new(cx).await;
 8307
 8308    let language = Arc::new(Language::new(
 8309        LanguageConfig {
 8310            brackets: BracketPairConfig {
 8311                pairs: vec![
 8312                    BracketPair {
 8313                        start: "{".to_string(),
 8314                        end: "}".to_string(),
 8315                        close: true,
 8316                        surround: true,
 8317                        newline: true,
 8318                    },
 8319                    BracketPair {
 8320                        start: "(".to_string(),
 8321                        end: ")".to_string(),
 8322                        close: true,
 8323                        surround: true,
 8324                        newline: true,
 8325                    },
 8326                    BracketPair {
 8327                        start: "[".to_string(),
 8328                        end: "]".to_string(),
 8329                        close: false,
 8330                        surround: true,
 8331                        newline: true,
 8332                    },
 8333                ],
 8334                ..Default::default()
 8335            },
 8336            autoclose_before: "})]".to_string(),
 8337            ..Default::default()
 8338        },
 8339        Some(tree_sitter_rust::LANGUAGE.into()),
 8340    ));
 8341
 8342    cx.language_registry().add(language.clone());
 8343    cx.update_buffer(|buffer, cx| {
 8344        buffer.set_language(Some(language), cx);
 8345    });
 8346
 8347    cx.set_state(
 8348        &"
 8349            {(ˇ)}
 8350            [[ˇ]]
 8351            {(ˇ)}
 8352        "
 8353        .unindent(),
 8354    );
 8355
 8356    cx.update_editor(|editor, window, cx| {
 8357        editor.backspace(&Default::default(), window, cx);
 8358        editor.backspace(&Default::default(), window, cx);
 8359    });
 8360
 8361    cx.assert_editor_state(
 8362        &"
 8363            ˇ
 8364            ˇ]]
 8365            ˇ
 8366        "
 8367        .unindent(),
 8368    );
 8369
 8370    cx.update_editor(|editor, window, cx| {
 8371        editor.handle_input("{", window, cx);
 8372        editor.handle_input("{", window, cx);
 8373        editor.move_right(&MoveRight, window, cx);
 8374        editor.move_right(&MoveRight, window, cx);
 8375        editor.move_left(&MoveLeft, window, cx);
 8376        editor.move_left(&MoveLeft, window, cx);
 8377        editor.backspace(&Default::default(), window, cx);
 8378    });
 8379
 8380    cx.assert_editor_state(
 8381        &"
 8382            {ˇ}
 8383            {ˇ}]]
 8384            {ˇ}
 8385        "
 8386        .unindent(),
 8387    );
 8388
 8389    cx.update_editor(|editor, window, cx| {
 8390        editor.backspace(&Default::default(), window, cx);
 8391    });
 8392
 8393    cx.assert_editor_state(
 8394        &"
 8395            ˇ
 8396            ˇ]]
 8397            ˇ
 8398        "
 8399        .unindent(),
 8400    );
 8401}
 8402
 8403#[gpui::test]
 8404async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8405    init_test(cx, |_| {});
 8406
 8407    let language = Arc::new(Language::new(
 8408        LanguageConfig::default(),
 8409        Some(tree_sitter_rust::LANGUAGE.into()),
 8410    ));
 8411
 8412    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8413    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8414    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8415    editor
 8416        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8417        .await;
 8418
 8419    editor.update_in(cx, |editor, window, cx| {
 8420        editor.set_auto_replace_emoji_shortcode(true);
 8421
 8422        editor.handle_input("Hello ", window, cx);
 8423        editor.handle_input(":wave", window, cx);
 8424        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8425
 8426        editor.handle_input(":", window, cx);
 8427        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8428
 8429        editor.handle_input(" :smile", window, cx);
 8430        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8431
 8432        editor.handle_input(":", window, cx);
 8433        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8434
 8435        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8436        editor.handle_input(":wave", window, cx);
 8437        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8438
 8439        editor.handle_input(":", window, cx);
 8440        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8441
 8442        editor.handle_input(":1", window, cx);
 8443        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8444
 8445        editor.handle_input(":", window, cx);
 8446        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8447
 8448        // Ensure shortcode does not get replaced when it is part of a word
 8449        editor.handle_input(" Test:wave", window, cx);
 8450        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8451
 8452        editor.handle_input(":", window, cx);
 8453        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8454
 8455        editor.set_auto_replace_emoji_shortcode(false);
 8456
 8457        // Ensure shortcode does not get replaced when auto replace is off
 8458        editor.handle_input(" :wave", window, cx);
 8459        assert_eq!(
 8460            editor.text(cx),
 8461            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8462        );
 8463
 8464        editor.handle_input(":", window, cx);
 8465        assert_eq!(
 8466            editor.text(cx),
 8467            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8468        );
 8469    });
 8470}
 8471
 8472#[gpui::test]
 8473async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8474    init_test(cx, |_| {});
 8475
 8476    let (text, insertion_ranges) = marked_text_ranges(
 8477        indoc! {"
 8478            ˇ
 8479        "},
 8480        false,
 8481    );
 8482
 8483    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8484    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8485
 8486    _ = editor.update_in(cx, |editor, window, cx| {
 8487        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8488
 8489        editor
 8490            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8491            .unwrap();
 8492
 8493        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8494            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8495            assert_eq!(editor.text(cx), expected_text);
 8496            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8497        }
 8498
 8499        assert(
 8500            editor,
 8501            cx,
 8502            indoc! {"
 8503            type «» =•
 8504            "},
 8505        );
 8506
 8507        assert!(editor.context_menu_visible(), "There should be a matches");
 8508    });
 8509}
 8510
 8511#[gpui::test]
 8512async fn test_snippets(cx: &mut TestAppContext) {
 8513    init_test(cx, |_| {});
 8514
 8515    let (text, insertion_ranges) = marked_text_ranges(
 8516        indoc! {"
 8517            a.ˇ b
 8518            a.ˇ b
 8519            a.ˇ b
 8520        "},
 8521        false,
 8522    );
 8523
 8524    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8525    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8526
 8527    editor.update_in(cx, |editor, window, cx| {
 8528        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8529
 8530        editor
 8531            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8532            .unwrap();
 8533
 8534        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8535            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8536            assert_eq!(editor.text(cx), expected_text);
 8537            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8538        }
 8539
 8540        assert(
 8541            editor,
 8542            cx,
 8543            indoc! {"
 8544                a.f(«one», two, «three») b
 8545                a.f(«one», two, «three») b
 8546                a.f(«one», two, «three») b
 8547            "},
 8548        );
 8549
 8550        // Can't move earlier than the first tab stop
 8551        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8552        assert(
 8553            editor,
 8554            cx,
 8555            indoc! {"
 8556                a.f(«one», two, «three») b
 8557                a.f(«one», two, «three») b
 8558                a.f(«one», two, «three») b
 8559            "},
 8560        );
 8561
 8562        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8563        assert(
 8564            editor,
 8565            cx,
 8566            indoc! {"
 8567                a.f(one, «two», three) b
 8568                a.f(one, «two», three) b
 8569                a.f(one, «two», three) b
 8570            "},
 8571        );
 8572
 8573        editor.move_to_prev_snippet_tabstop(window, cx);
 8574        assert(
 8575            editor,
 8576            cx,
 8577            indoc! {"
 8578                a.f(«one», two, «three») b
 8579                a.f(«one», two, «three») b
 8580                a.f(«one», two, «three») b
 8581            "},
 8582        );
 8583
 8584        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8585        assert(
 8586            editor,
 8587            cx,
 8588            indoc! {"
 8589                a.f(one, «two», three) b
 8590                a.f(one, «two», three) b
 8591                a.f(one, «two», three) b
 8592            "},
 8593        );
 8594        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8595        assert(
 8596            editor,
 8597            cx,
 8598            indoc! {"
 8599                a.f(one, two, three)ˇ b
 8600                a.f(one, two, three)ˇ b
 8601                a.f(one, two, three)ˇ b
 8602            "},
 8603        );
 8604
 8605        // As soon as the last tab stop is reached, snippet state is gone
 8606        editor.move_to_prev_snippet_tabstop(window, cx);
 8607        assert(
 8608            editor,
 8609            cx,
 8610            indoc! {"
 8611                a.f(one, two, three)ˇ b
 8612                a.f(one, two, three)ˇ b
 8613                a.f(one, two, three)ˇ b
 8614            "},
 8615        );
 8616    });
 8617}
 8618
 8619#[gpui::test]
 8620async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8621    init_test(cx, |_| {});
 8622
 8623    let fs = FakeFs::new(cx.executor());
 8624    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8625
 8626    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8627
 8628    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8629    language_registry.add(rust_lang());
 8630    let mut fake_servers = language_registry.register_fake_lsp(
 8631        "Rust",
 8632        FakeLspAdapter {
 8633            capabilities: lsp::ServerCapabilities {
 8634                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8635                ..Default::default()
 8636            },
 8637            ..Default::default()
 8638        },
 8639    );
 8640
 8641    let buffer = project
 8642        .update(cx, |project, cx| {
 8643            project.open_local_buffer(path!("/file.rs"), cx)
 8644        })
 8645        .await
 8646        .unwrap();
 8647
 8648    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8649    let (editor, cx) = cx.add_window_view(|window, cx| {
 8650        build_editor_with_project(project.clone(), buffer, window, cx)
 8651    });
 8652    editor.update_in(cx, |editor, window, cx| {
 8653        editor.set_text("one\ntwo\nthree\n", window, cx)
 8654    });
 8655    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8656
 8657    cx.executor().start_waiting();
 8658    let fake_server = fake_servers.next().await.unwrap();
 8659
 8660    {
 8661        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8662            move |params, _| async move {
 8663                assert_eq!(
 8664                    params.text_document.uri,
 8665                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8666                );
 8667                assert_eq!(params.options.tab_size, 4);
 8668                Ok(Some(vec![lsp::TextEdit::new(
 8669                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8670                    ", ".to_string(),
 8671                )]))
 8672            },
 8673        );
 8674        let save = editor
 8675            .update_in(cx, |editor, window, cx| {
 8676                editor.save(true, project.clone(), window, cx)
 8677            })
 8678            .unwrap();
 8679        cx.executor().start_waiting();
 8680        save.await;
 8681
 8682        assert_eq!(
 8683            editor.update(cx, |editor, cx| editor.text(cx)),
 8684            "one, two\nthree\n"
 8685        );
 8686        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8687    }
 8688
 8689    {
 8690        editor.update_in(cx, |editor, window, cx| {
 8691            editor.set_text("one\ntwo\nthree\n", window, cx)
 8692        });
 8693        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8694
 8695        // Ensure we can still save even if formatting hangs.
 8696        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8697            move |params, _| async move {
 8698                assert_eq!(
 8699                    params.text_document.uri,
 8700                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8701                );
 8702                futures::future::pending::<()>().await;
 8703                unreachable!()
 8704            },
 8705        );
 8706        let save = editor
 8707            .update_in(cx, |editor, window, cx| {
 8708                editor.save(true, project.clone(), window, cx)
 8709            })
 8710            .unwrap();
 8711        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8712        cx.executor().start_waiting();
 8713        save.await;
 8714        assert_eq!(
 8715            editor.update(cx, |editor, cx| editor.text(cx)),
 8716            "one\ntwo\nthree\n"
 8717        );
 8718    }
 8719
 8720    // For non-dirty buffer, no formatting request should be sent
 8721    {
 8722        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8723
 8724        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8725            panic!("Should not be invoked on non-dirty buffer");
 8726        });
 8727        let save = editor
 8728            .update_in(cx, |editor, window, cx| {
 8729                editor.save(true, project.clone(), window, cx)
 8730            })
 8731            .unwrap();
 8732        cx.executor().start_waiting();
 8733        save.await;
 8734    }
 8735
 8736    // Set rust language override and assert overridden tabsize is sent to language server
 8737    update_test_language_settings(cx, |settings| {
 8738        settings.languages.insert(
 8739            "Rust".into(),
 8740            LanguageSettingsContent {
 8741                tab_size: NonZeroU32::new(8),
 8742                ..Default::default()
 8743            },
 8744        );
 8745    });
 8746
 8747    {
 8748        editor.update_in(cx, |editor, window, cx| {
 8749            editor.set_text("somehting_new\n", window, cx)
 8750        });
 8751        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8752        let _formatting_request_signal = fake_server
 8753            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8754                assert_eq!(
 8755                    params.text_document.uri,
 8756                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8757                );
 8758                assert_eq!(params.options.tab_size, 8);
 8759                Ok(Some(vec![]))
 8760            });
 8761        let save = editor
 8762            .update_in(cx, |editor, window, cx| {
 8763                editor.save(true, project.clone(), window, cx)
 8764            })
 8765            .unwrap();
 8766        cx.executor().start_waiting();
 8767        save.await;
 8768    }
 8769}
 8770
 8771#[gpui::test]
 8772async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8773    init_test(cx, |_| {});
 8774
 8775    let cols = 4;
 8776    let rows = 10;
 8777    let sample_text_1 = sample_text(rows, cols, 'a');
 8778    assert_eq!(
 8779        sample_text_1,
 8780        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8781    );
 8782    let sample_text_2 = sample_text(rows, cols, 'l');
 8783    assert_eq!(
 8784        sample_text_2,
 8785        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8786    );
 8787    let sample_text_3 = sample_text(rows, cols, 'v');
 8788    assert_eq!(
 8789        sample_text_3,
 8790        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8791    );
 8792
 8793    let fs = FakeFs::new(cx.executor());
 8794    fs.insert_tree(
 8795        path!("/a"),
 8796        json!({
 8797            "main.rs": sample_text_1,
 8798            "other.rs": sample_text_2,
 8799            "lib.rs": sample_text_3,
 8800        }),
 8801    )
 8802    .await;
 8803
 8804    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8805    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8806    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8807
 8808    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8809    language_registry.add(rust_lang());
 8810    let mut fake_servers = language_registry.register_fake_lsp(
 8811        "Rust",
 8812        FakeLspAdapter {
 8813            capabilities: lsp::ServerCapabilities {
 8814                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8815                ..Default::default()
 8816            },
 8817            ..Default::default()
 8818        },
 8819    );
 8820
 8821    let worktree = project.update(cx, |project, cx| {
 8822        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8823        assert_eq!(worktrees.len(), 1);
 8824        worktrees.pop().unwrap()
 8825    });
 8826    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8827
 8828    let buffer_1 = project
 8829        .update(cx, |project, cx| {
 8830            project.open_buffer((worktree_id, "main.rs"), cx)
 8831        })
 8832        .await
 8833        .unwrap();
 8834    let buffer_2 = project
 8835        .update(cx, |project, cx| {
 8836            project.open_buffer((worktree_id, "other.rs"), cx)
 8837        })
 8838        .await
 8839        .unwrap();
 8840    let buffer_3 = project
 8841        .update(cx, |project, cx| {
 8842            project.open_buffer((worktree_id, "lib.rs"), cx)
 8843        })
 8844        .await
 8845        .unwrap();
 8846
 8847    let multi_buffer = cx.new(|cx| {
 8848        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8849        multi_buffer.push_excerpts(
 8850            buffer_1.clone(),
 8851            [
 8852                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8853                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8854                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8855            ],
 8856            cx,
 8857        );
 8858        multi_buffer.push_excerpts(
 8859            buffer_2.clone(),
 8860            [
 8861                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8862                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8863                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8864            ],
 8865            cx,
 8866        );
 8867        multi_buffer.push_excerpts(
 8868            buffer_3.clone(),
 8869            [
 8870                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8871                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8872                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8873            ],
 8874            cx,
 8875        );
 8876        multi_buffer
 8877    });
 8878    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8879        Editor::new(
 8880            EditorMode::full(),
 8881            multi_buffer,
 8882            Some(project.clone()),
 8883            window,
 8884            cx,
 8885        )
 8886    });
 8887
 8888    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8889        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8890            s.select_ranges(Some(1..2))
 8891        });
 8892        editor.insert("|one|two|three|", window, cx);
 8893    });
 8894    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8895    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8896        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8897            s.select_ranges(Some(60..70))
 8898        });
 8899        editor.insert("|four|five|six|", window, cx);
 8900    });
 8901    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8902
 8903    // First two buffers should be edited, but not the third one.
 8904    assert_eq!(
 8905        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8906        "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}",
 8907    );
 8908    buffer_1.update(cx, |buffer, _| {
 8909        assert!(buffer.is_dirty());
 8910        assert_eq!(
 8911            buffer.text(),
 8912            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8913        )
 8914    });
 8915    buffer_2.update(cx, |buffer, _| {
 8916        assert!(buffer.is_dirty());
 8917        assert_eq!(
 8918            buffer.text(),
 8919            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8920        )
 8921    });
 8922    buffer_3.update(cx, |buffer, _| {
 8923        assert!(!buffer.is_dirty());
 8924        assert_eq!(buffer.text(), sample_text_3,)
 8925    });
 8926    cx.executor().run_until_parked();
 8927
 8928    cx.executor().start_waiting();
 8929    let save = multi_buffer_editor
 8930        .update_in(cx, |editor, window, cx| {
 8931            editor.save(true, project.clone(), window, cx)
 8932        })
 8933        .unwrap();
 8934
 8935    let fake_server = fake_servers.next().await.unwrap();
 8936    fake_server
 8937        .server
 8938        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8939            Ok(Some(vec![lsp::TextEdit::new(
 8940                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8941                format!("[{} formatted]", params.text_document.uri),
 8942            )]))
 8943        })
 8944        .detach();
 8945    save.await;
 8946
 8947    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8948    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8949    assert_eq!(
 8950        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8951        uri!(
 8952            "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}"
 8953        ),
 8954    );
 8955    buffer_1.update(cx, |buffer, _| {
 8956        assert!(!buffer.is_dirty());
 8957        assert_eq!(
 8958            buffer.text(),
 8959            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8960        )
 8961    });
 8962    buffer_2.update(cx, |buffer, _| {
 8963        assert!(!buffer.is_dirty());
 8964        assert_eq!(
 8965            buffer.text(),
 8966            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8967        )
 8968    });
 8969    buffer_3.update(cx, |buffer, _| {
 8970        assert!(!buffer.is_dirty());
 8971        assert_eq!(buffer.text(), sample_text_3,)
 8972    });
 8973}
 8974
 8975#[gpui::test]
 8976async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8977    init_test(cx, |_| {});
 8978
 8979    let fs = FakeFs::new(cx.executor());
 8980    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8981
 8982    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8983
 8984    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8985    language_registry.add(rust_lang());
 8986    let mut fake_servers = language_registry.register_fake_lsp(
 8987        "Rust",
 8988        FakeLspAdapter {
 8989            capabilities: lsp::ServerCapabilities {
 8990                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8991                ..Default::default()
 8992            },
 8993            ..Default::default()
 8994        },
 8995    );
 8996
 8997    let buffer = project
 8998        .update(cx, |project, cx| {
 8999            project.open_local_buffer(path!("/file.rs"), cx)
 9000        })
 9001        .await
 9002        .unwrap();
 9003
 9004    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9005    let (editor, cx) = cx.add_window_view(|window, cx| {
 9006        build_editor_with_project(project.clone(), buffer, window, cx)
 9007    });
 9008    editor.update_in(cx, |editor, window, cx| {
 9009        editor.set_text("one\ntwo\nthree\n", window, cx)
 9010    });
 9011    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9012
 9013    cx.executor().start_waiting();
 9014    let fake_server = fake_servers.next().await.unwrap();
 9015
 9016    let save = editor
 9017        .update_in(cx, |editor, window, cx| {
 9018            editor.save(true, project.clone(), window, cx)
 9019        })
 9020        .unwrap();
 9021    fake_server
 9022        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9023            assert_eq!(
 9024                params.text_document.uri,
 9025                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9026            );
 9027            assert_eq!(params.options.tab_size, 4);
 9028            Ok(Some(vec![lsp::TextEdit::new(
 9029                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9030                ", ".to_string(),
 9031            )]))
 9032        })
 9033        .next()
 9034        .await;
 9035    cx.executor().start_waiting();
 9036    save.await;
 9037    assert_eq!(
 9038        editor.update(cx, |editor, cx| editor.text(cx)),
 9039        "one, two\nthree\n"
 9040    );
 9041    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9042
 9043    editor.update_in(cx, |editor, window, cx| {
 9044        editor.set_text("one\ntwo\nthree\n", window, cx)
 9045    });
 9046    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9047
 9048    // Ensure we can still save even if formatting hangs.
 9049    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 9050        move |params, _| async move {
 9051            assert_eq!(
 9052                params.text_document.uri,
 9053                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9054            );
 9055            futures::future::pending::<()>().await;
 9056            unreachable!()
 9057        },
 9058    );
 9059    let save = editor
 9060        .update_in(cx, |editor, window, cx| {
 9061            editor.save(true, project.clone(), window, cx)
 9062        })
 9063        .unwrap();
 9064    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9065    cx.executor().start_waiting();
 9066    save.await;
 9067    assert_eq!(
 9068        editor.update(cx, |editor, cx| editor.text(cx)),
 9069        "one\ntwo\nthree\n"
 9070    );
 9071    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9072
 9073    // For non-dirty buffer, no formatting request should be sent
 9074    let save = editor
 9075        .update_in(cx, |editor, window, cx| {
 9076            editor.save(true, project.clone(), window, cx)
 9077        })
 9078        .unwrap();
 9079    let _pending_format_request = fake_server
 9080        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 9081            panic!("Should not be invoked on non-dirty buffer");
 9082        })
 9083        .next();
 9084    cx.executor().start_waiting();
 9085    save.await;
 9086
 9087    // Set Rust language override and assert overridden tabsize is sent to language server
 9088    update_test_language_settings(cx, |settings| {
 9089        settings.languages.insert(
 9090            "Rust".into(),
 9091            LanguageSettingsContent {
 9092                tab_size: NonZeroU32::new(8),
 9093                ..Default::default()
 9094            },
 9095        );
 9096    });
 9097
 9098    editor.update_in(cx, |editor, window, cx| {
 9099        editor.set_text("somehting_new\n", window, cx)
 9100    });
 9101    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9102    let save = editor
 9103        .update_in(cx, |editor, window, cx| {
 9104            editor.save(true, project.clone(), window, cx)
 9105        })
 9106        .unwrap();
 9107    fake_server
 9108        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9109            assert_eq!(
 9110                params.text_document.uri,
 9111                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9112            );
 9113            assert_eq!(params.options.tab_size, 8);
 9114            Ok(Some(Vec::new()))
 9115        })
 9116        .next()
 9117        .await;
 9118    save.await;
 9119}
 9120
 9121#[gpui::test]
 9122async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 9123    init_test(cx, |settings| {
 9124        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9125            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9126        ))
 9127    });
 9128
 9129    let fs = FakeFs::new(cx.executor());
 9130    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9131
 9132    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9133
 9134    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9135    language_registry.add(Arc::new(Language::new(
 9136        LanguageConfig {
 9137            name: "Rust".into(),
 9138            matcher: LanguageMatcher {
 9139                path_suffixes: vec!["rs".to_string()],
 9140                ..Default::default()
 9141            },
 9142            ..LanguageConfig::default()
 9143        },
 9144        Some(tree_sitter_rust::LANGUAGE.into()),
 9145    )));
 9146    update_test_language_settings(cx, |settings| {
 9147        // Enable Prettier formatting for the same buffer, and ensure
 9148        // LSP is called instead of Prettier.
 9149        settings.defaults.prettier = Some(PrettierSettings {
 9150            allowed: true,
 9151            ..PrettierSettings::default()
 9152        });
 9153    });
 9154    let mut fake_servers = language_registry.register_fake_lsp(
 9155        "Rust",
 9156        FakeLspAdapter {
 9157            capabilities: lsp::ServerCapabilities {
 9158                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9159                ..Default::default()
 9160            },
 9161            ..Default::default()
 9162        },
 9163    );
 9164
 9165    let buffer = project
 9166        .update(cx, |project, cx| {
 9167            project.open_local_buffer(path!("/file.rs"), cx)
 9168        })
 9169        .await
 9170        .unwrap();
 9171
 9172    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9173    let (editor, cx) = cx.add_window_view(|window, cx| {
 9174        build_editor_with_project(project.clone(), buffer, window, cx)
 9175    });
 9176    editor.update_in(cx, |editor, window, cx| {
 9177        editor.set_text("one\ntwo\nthree\n", window, cx)
 9178    });
 9179
 9180    cx.executor().start_waiting();
 9181    let fake_server = fake_servers.next().await.unwrap();
 9182
 9183    let format = editor
 9184        .update_in(cx, |editor, window, cx| {
 9185            editor.perform_format(
 9186                project.clone(),
 9187                FormatTrigger::Manual,
 9188                FormatTarget::Buffers,
 9189                window,
 9190                cx,
 9191            )
 9192        })
 9193        .unwrap();
 9194    fake_server
 9195        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9196            assert_eq!(
 9197                params.text_document.uri,
 9198                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9199            );
 9200            assert_eq!(params.options.tab_size, 4);
 9201            Ok(Some(vec![lsp::TextEdit::new(
 9202                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9203                ", ".to_string(),
 9204            )]))
 9205        })
 9206        .next()
 9207        .await;
 9208    cx.executor().start_waiting();
 9209    format.await;
 9210    assert_eq!(
 9211        editor.update(cx, |editor, cx| editor.text(cx)),
 9212        "one, two\nthree\n"
 9213    );
 9214
 9215    editor.update_in(cx, |editor, window, cx| {
 9216        editor.set_text("one\ntwo\nthree\n", window, cx)
 9217    });
 9218    // Ensure we don't lock if formatting hangs.
 9219    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9220        move |params, _| async move {
 9221            assert_eq!(
 9222                params.text_document.uri,
 9223                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9224            );
 9225            futures::future::pending::<()>().await;
 9226            unreachable!()
 9227        },
 9228    );
 9229    let format = editor
 9230        .update_in(cx, |editor, window, cx| {
 9231            editor.perform_format(
 9232                project,
 9233                FormatTrigger::Manual,
 9234                FormatTarget::Buffers,
 9235                window,
 9236                cx,
 9237            )
 9238        })
 9239        .unwrap();
 9240    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9241    cx.executor().start_waiting();
 9242    format.await;
 9243    assert_eq!(
 9244        editor.update(cx, |editor, cx| editor.text(cx)),
 9245        "one\ntwo\nthree\n"
 9246    );
 9247}
 9248
 9249#[gpui::test]
 9250async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9251    init_test(cx, |settings| {
 9252        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9253        settings.defaults.formatter =
 9254            Some(language_settings::SelectedFormatter::List(FormatterList(
 9255                vec![
 9256                    Formatter::LanguageServer { name: None },
 9257                    Formatter::CodeActions(
 9258                        [
 9259                            ("code-action-1".into(), true),
 9260                            ("code-action-2".into(), true),
 9261                        ]
 9262                        .into_iter()
 9263                        .collect(),
 9264                    ),
 9265                ]
 9266                .into(),
 9267            )))
 9268    });
 9269
 9270    let fs = FakeFs::new(cx.executor());
 9271    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9272        .await;
 9273
 9274    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9275    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9276    language_registry.add(rust_lang());
 9277
 9278    let mut fake_servers = language_registry.register_fake_lsp(
 9279        "Rust",
 9280        FakeLspAdapter {
 9281            capabilities: lsp::ServerCapabilities {
 9282                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9283                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9284                    commands: vec!["the-command-for-code-action-1".into()],
 9285                    ..Default::default()
 9286                }),
 9287                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9288                ..Default::default()
 9289            },
 9290            ..Default::default()
 9291        },
 9292    );
 9293
 9294    let buffer = project
 9295        .update(cx, |project, cx| {
 9296            project.open_local_buffer(path!("/file.rs"), cx)
 9297        })
 9298        .await
 9299        .unwrap();
 9300
 9301    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9302    let (editor, cx) = cx.add_window_view(|window, cx| {
 9303        build_editor_with_project(project.clone(), buffer, window, cx)
 9304    });
 9305
 9306    cx.executor().start_waiting();
 9307
 9308    let fake_server = fake_servers.next().await.unwrap();
 9309    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9310        move |_params, _| async move {
 9311            Ok(Some(vec![lsp::TextEdit::new(
 9312                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9313                "applied-formatting\n".to_string(),
 9314            )]))
 9315        },
 9316    );
 9317    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9318        move |params, _| async move {
 9319            assert_eq!(
 9320                params.context.only,
 9321                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9322            );
 9323            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9324            Ok(Some(vec![
 9325                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9326                    kind: Some("code-action-1".into()),
 9327                    edit: Some(lsp::WorkspaceEdit::new(
 9328                        [(
 9329                            uri.clone(),
 9330                            vec![lsp::TextEdit::new(
 9331                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9332                                "applied-code-action-1-edit\n".to_string(),
 9333                            )],
 9334                        )]
 9335                        .into_iter()
 9336                        .collect(),
 9337                    )),
 9338                    command: Some(lsp::Command {
 9339                        command: "the-command-for-code-action-1".into(),
 9340                        ..Default::default()
 9341                    }),
 9342                    ..Default::default()
 9343                }),
 9344                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9345                    kind: Some("code-action-2".into()),
 9346                    edit: Some(lsp::WorkspaceEdit::new(
 9347                        [(
 9348                            uri.clone(),
 9349                            vec![lsp::TextEdit::new(
 9350                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9351                                "applied-code-action-2-edit\n".to_string(),
 9352                            )],
 9353                        )]
 9354                        .into_iter()
 9355                        .collect(),
 9356                    )),
 9357                    ..Default::default()
 9358                }),
 9359            ]))
 9360        },
 9361    );
 9362
 9363    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9364        move |params, _| async move { Ok(params) }
 9365    });
 9366
 9367    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9368    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9369        let fake = fake_server.clone();
 9370        let lock = command_lock.clone();
 9371        move |params, _| {
 9372            assert_eq!(params.command, "the-command-for-code-action-1");
 9373            let fake = fake.clone();
 9374            let lock = lock.clone();
 9375            async move {
 9376                lock.lock().await;
 9377                fake.server
 9378                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9379                        label: None,
 9380                        edit: lsp::WorkspaceEdit {
 9381                            changes: Some(
 9382                                [(
 9383                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9384                                    vec![lsp::TextEdit {
 9385                                        range: lsp::Range::new(
 9386                                            lsp::Position::new(0, 0),
 9387                                            lsp::Position::new(0, 0),
 9388                                        ),
 9389                                        new_text: "applied-code-action-1-command\n".into(),
 9390                                    }],
 9391                                )]
 9392                                .into_iter()
 9393                                .collect(),
 9394                            ),
 9395                            ..Default::default()
 9396                        },
 9397                    })
 9398                    .await
 9399                    .into_response()
 9400                    .unwrap();
 9401                Ok(Some(json!(null)))
 9402            }
 9403        }
 9404    });
 9405
 9406    cx.executor().start_waiting();
 9407    editor
 9408        .update_in(cx, |editor, window, cx| {
 9409            editor.perform_format(
 9410                project.clone(),
 9411                FormatTrigger::Manual,
 9412                FormatTarget::Buffers,
 9413                window,
 9414                cx,
 9415            )
 9416        })
 9417        .unwrap()
 9418        .await;
 9419    editor.update(cx, |editor, cx| {
 9420        assert_eq!(
 9421            editor.text(cx),
 9422            r#"
 9423                applied-code-action-2-edit
 9424                applied-code-action-1-command
 9425                applied-code-action-1-edit
 9426                applied-formatting
 9427                one
 9428                two
 9429                three
 9430            "#
 9431            .unindent()
 9432        );
 9433    });
 9434
 9435    editor.update_in(cx, |editor, window, cx| {
 9436        editor.undo(&Default::default(), window, cx);
 9437        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9438    });
 9439
 9440    // Perform a manual edit while waiting for an LSP command
 9441    // that's being run as part of a formatting code action.
 9442    let lock_guard = command_lock.lock().await;
 9443    let format = editor
 9444        .update_in(cx, |editor, window, cx| {
 9445            editor.perform_format(
 9446                project.clone(),
 9447                FormatTrigger::Manual,
 9448                FormatTarget::Buffers,
 9449                window,
 9450                cx,
 9451            )
 9452        })
 9453        .unwrap();
 9454    cx.run_until_parked();
 9455    editor.update(cx, |editor, cx| {
 9456        assert_eq!(
 9457            editor.text(cx),
 9458            r#"
 9459                applied-code-action-1-edit
 9460                applied-formatting
 9461                one
 9462                two
 9463                three
 9464            "#
 9465            .unindent()
 9466        );
 9467
 9468        editor.buffer.update(cx, |buffer, cx| {
 9469            let ix = buffer.len(cx);
 9470            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9471        });
 9472    });
 9473
 9474    // Allow the LSP command to proceed. Because the buffer was edited,
 9475    // the second code action will not be run.
 9476    drop(lock_guard);
 9477    format.await;
 9478    editor.update_in(cx, |editor, window, cx| {
 9479        assert_eq!(
 9480            editor.text(cx),
 9481            r#"
 9482                applied-code-action-1-command
 9483                applied-code-action-1-edit
 9484                applied-formatting
 9485                one
 9486                two
 9487                three
 9488                edited
 9489            "#
 9490            .unindent()
 9491        );
 9492
 9493        // The manual edit is undone first, because it is the last thing the user did
 9494        // (even though the command completed afterwards).
 9495        editor.undo(&Default::default(), window, cx);
 9496        assert_eq!(
 9497            editor.text(cx),
 9498            r#"
 9499                applied-code-action-1-command
 9500                applied-code-action-1-edit
 9501                applied-formatting
 9502                one
 9503                two
 9504                three
 9505            "#
 9506            .unindent()
 9507        );
 9508
 9509        // All the formatting (including the command, which completed after the manual edit)
 9510        // is undone together.
 9511        editor.undo(&Default::default(), window, cx);
 9512        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9513    });
 9514}
 9515
 9516#[gpui::test]
 9517async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9518    init_test(cx, |settings| {
 9519        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9520            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9521        ))
 9522    });
 9523
 9524    let fs = FakeFs::new(cx.executor());
 9525    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9526
 9527    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9528
 9529    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9530    language_registry.add(Arc::new(Language::new(
 9531        LanguageConfig {
 9532            name: "TypeScript".into(),
 9533            matcher: LanguageMatcher {
 9534                path_suffixes: vec!["ts".to_string()],
 9535                ..Default::default()
 9536            },
 9537            ..LanguageConfig::default()
 9538        },
 9539        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9540    )));
 9541    update_test_language_settings(cx, |settings| {
 9542        settings.defaults.prettier = Some(PrettierSettings {
 9543            allowed: true,
 9544            ..PrettierSettings::default()
 9545        });
 9546    });
 9547    let mut fake_servers = language_registry.register_fake_lsp(
 9548        "TypeScript",
 9549        FakeLspAdapter {
 9550            capabilities: lsp::ServerCapabilities {
 9551                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9552                ..Default::default()
 9553            },
 9554            ..Default::default()
 9555        },
 9556    );
 9557
 9558    let buffer = project
 9559        .update(cx, |project, cx| {
 9560            project.open_local_buffer(path!("/file.ts"), cx)
 9561        })
 9562        .await
 9563        .unwrap();
 9564
 9565    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9566    let (editor, cx) = cx.add_window_view(|window, cx| {
 9567        build_editor_with_project(project.clone(), buffer, window, cx)
 9568    });
 9569    editor.update_in(cx, |editor, window, cx| {
 9570        editor.set_text(
 9571            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9572            window,
 9573            cx,
 9574        )
 9575    });
 9576
 9577    cx.executor().start_waiting();
 9578    let fake_server = fake_servers.next().await.unwrap();
 9579
 9580    let format = editor
 9581        .update_in(cx, |editor, window, cx| {
 9582            editor.perform_code_action_kind(
 9583                project.clone(),
 9584                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9585                window,
 9586                cx,
 9587            )
 9588        })
 9589        .unwrap();
 9590    fake_server
 9591        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9592            assert_eq!(
 9593                params.text_document.uri,
 9594                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9595            );
 9596            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9597                lsp::CodeAction {
 9598                    title: "Organize Imports".to_string(),
 9599                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9600                    edit: Some(lsp::WorkspaceEdit {
 9601                        changes: Some(
 9602                            [(
 9603                                params.text_document.uri.clone(),
 9604                                vec![lsp::TextEdit::new(
 9605                                    lsp::Range::new(
 9606                                        lsp::Position::new(1, 0),
 9607                                        lsp::Position::new(2, 0),
 9608                                    ),
 9609                                    "".to_string(),
 9610                                )],
 9611                            )]
 9612                            .into_iter()
 9613                            .collect(),
 9614                        ),
 9615                        ..Default::default()
 9616                    }),
 9617                    ..Default::default()
 9618                },
 9619            )]))
 9620        })
 9621        .next()
 9622        .await;
 9623    cx.executor().start_waiting();
 9624    format.await;
 9625    assert_eq!(
 9626        editor.update(cx, |editor, cx| editor.text(cx)),
 9627        "import { a } from 'module';\n\nconst x = a;\n"
 9628    );
 9629
 9630    editor.update_in(cx, |editor, window, cx| {
 9631        editor.set_text(
 9632            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9633            window,
 9634            cx,
 9635        )
 9636    });
 9637    // Ensure we don't lock if code action hangs.
 9638    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9639        move |params, _| async move {
 9640            assert_eq!(
 9641                params.text_document.uri,
 9642                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9643            );
 9644            futures::future::pending::<()>().await;
 9645            unreachable!()
 9646        },
 9647    );
 9648    let format = editor
 9649        .update_in(cx, |editor, window, cx| {
 9650            editor.perform_code_action_kind(
 9651                project,
 9652                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9653                window,
 9654                cx,
 9655            )
 9656        })
 9657        .unwrap();
 9658    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9659    cx.executor().start_waiting();
 9660    format.await;
 9661    assert_eq!(
 9662        editor.update(cx, |editor, cx| editor.text(cx)),
 9663        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9664    );
 9665}
 9666
 9667#[gpui::test]
 9668async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9669    init_test(cx, |_| {});
 9670
 9671    let mut cx = EditorLspTestContext::new_rust(
 9672        lsp::ServerCapabilities {
 9673            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9674            ..Default::default()
 9675        },
 9676        cx,
 9677    )
 9678    .await;
 9679
 9680    cx.set_state(indoc! {"
 9681        one.twoˇ
 9682    "});
 9683
 9684    // The format request takes a long time. When it completes, it inserts
 9685    // a newline and an indent before the `.`
 9686    cx.lsp
 9687        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9688            let executor = cx.background_executor().clone();
 9689            async move {
 9690                executor.timer(Duration::from_millis(100)).await;
 9691                Ok(Some(vec![lsp::TextEdit {
 9692                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9693                    new_text: "\n    ".into(),
 9694                }]))
 9695            }
 9696        });
 9697
 9698    // Submit a format request.
 9699    let format_1 = cx
 9700        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9701        .unwrap();
 9702    cx.executor().run_until_parked();
 9703
 9704    // Submit a second format request.
 9705    let format_2 = cx
 9706        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9707        .unwrap();
 9708    cx.executor().run_until_parked();
 9709
 9710    // Wait for both format requests to complete
 9711    cx.executor().advance_clock(Duration::from_millis(200));
 9712    cx.executor().start_waiting();
 9713    format_1.await.unwrap();
 9714    cx.executor().start_waiting();
 9715    format_2.await.unwrap();
 9716
 9717    // The formatting edits only happens once.
 9718    cx.assert_editor_state(indoc! {"
 9719        one
 9720            .twoˇ
 9721    "});
 9722}
 9723
 9724#[gpui::test]
 9725async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9726    init_test(cx, |settings| {
 9727        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9728    });
 9729
 9730    let mut cx = EditorLspTestContext::new_rust(
 9731        lsp::ServerCapabilities {
 9732            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9733            ..Default::default()
 9734        },
 9735        cx,
 9736    )
 9737    .await;
 9738
 9739    // Set up a buffer white some trailing whitespace and no trailing newline.
 9740    cx.set_state(
 9741        &[
 9742            "one ",   //
 9743            "twoˇ",   //
 9744            "three ", //
 9745            "four",   //
 9746        ]
 9747        .join("\n"),
 9748    );
 9749
 9750    // Submit a format request.
 9751    let format = cx
 9752        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9753        .unwrap();
 9754
 9755    // Record which buffer changes have been sent to the language server
 9756    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9757    cx.lsp
 9758        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9759            let buffer_changes = buffer_changes.clone();
 9760            move |params, _| {
 9761                buffer_changes.lock().extend(
 9762                    params
 9763                        .content_changes
 9764                        .into_iter()
 9765                        .map(|e| (e.range.unwrap(), e.text)),
 9766                );
 9767            }
 9768        });
 9769
 9770    // Handle formatting requests to the language server.
 9771    cx.lsp
 9772        .set_request_handler::<lsp::request::Formatting, _, _>({
 9773            let buffer_changes = buffer_changes.clone();
 9774            move |_, _| {
 9775                // When formatting is requested, trailing whitespace has already been stripped,
 9776                // and the trailing newline has already been added.
 9777                assert_eq!(
 9778                    &buffer_changes.lock()[1..],
 9779                    &[
 9780                        (
 9781                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9782                            "".into()
 9783                        ),
 9784                        (
 9785                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9786                            "".into()
 9787                        ),
 9788                        (
 9789                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9790                            "\n".into()
 9791                        ),
 9792                    ]
 9793                );
 9794
 9795                // Insert blank lines between each line of the buffer.
 9796                async move {
 9797                    Ok(Some(vec![
 9798                        lsp::TextEdit {
 9799                            range: lsp::Range::new(
 9800                                lsp::Position::new(1, 0),
 9801                                lsp::Position::new(1, 0),
 9802                            ),
 9803                            new_text: "\n".into(),
 9804                        },
 9805                        lsp::TextEdit {
 9806                            range: lsp::Range::new(
 9807                                lsp::Position::new(2, 0),
 9808                                lsp::Position::new(2, 0),
 9809                            ),
 9810                            new_text: "\n".into(),
 9811                        },
 9812                    ]))
 9813                }
 9814            }
 9815        });
 9816
 9817    // After formatting the buffer, the trailing whitespace is stripped,
 9818    // a newline is appended, and the edits provided by the language server
 9819    // have been applied.
 9820    format.await.unwrap();
 9821    cx.assert_editor_state(
 9822        &[
 9823            "one",   //
 9824            "",      //
 9825            "twoˇ",  //
 9826            "",      //
 9827            "three", //
 9828            "four",  //
 9829            "",      //
 9830        ]
 9831        .join("\n"),
 9832    );
 9833
 9834    // Undoing the formatting undoes the trailing whitespace removal, the
 9835    // trailing newline, and the LSP edits.
 9836    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9837    cx.assert_editor_state(
 9838        &[
 9839            "one ",   //
 9840            "twoˇ",   //
 9841            "three ", //
 9842            "four",   //
 9843        ]
 9844        .join("\n"),
 9845    );
 9846}
 9847
 9848#[gpui::test]
 9849async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9850    cx: &mut TestAppContext,
 9851) {
 9852    init_test(cx, |_| {});
 9853
 9854    cx.update(|cx| {
 9855        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9856            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9857                settings.auto_signature_help = Some(true);
 9858            });
 9859        });
 9860    });
 9861
 9862    let mut cx = EditorLspTestContext::new_rust(
 9863        lsp::ServerCapabilities {
 9864            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9865                ..Default::default()
 9866            }),
 9867            ..Default::default()
 9868        },
 9869        cx,
 9870    )
 9871    .await;
 9872
 9873    let language = Language::new(
 9874        LanguageConfig {
 9875            name: "Rust".into(),
 9876            brackets: BracketPairConfig {
 9877                pairs: vec![
 9878                    BracketPair {
 9879                        start: "{".to_string(),
 9880                        end: "}".to_string(),
 9881                        close: true,
 9882                        surround: true,
 9883                        newline: true,
 9884                    },
 9885                    BracketPair {
 9886                        start: "(".to_string(),
 9887                        end: ")".to_string(),
 9888                        close: true,
 9889                        surround: true,
 9890                        newline: true,
 9891                    },
 9892                    BracketPair {
 9893                        start: "/*".to_string(),
 9894                        end: " */".to_string(),
 9895                        close: true,
 9896                        surround: true,
 9897                        newline: true,
 9898                    },
 9899                    BracketPair {
 9900                        start: "[".to_string(),
 9901                        end: "]".to_string(),
 9902                        close: false,
 9903                        surround: false,
 9904                        newline: true,
 9905                    },
 9906                    BracketPair {
 9907                        start: "\"".to_string(),
 9908                        end: "\"".to_string(),
 9909                        close: true,
 9910                        surround: true,
 9911                        newline: false,
 9912                    },
 9913                    BracketPair {
 9914                        start: "<".to_string(),
 9915                        end: ">".to_string(),
 9916                        close: false,
 9917                        surround: true,
 9918                        newline: true,
 9919                    },
 9920                ],
 9921                ..Default::default()
 9922            },
 9923            autoclose_before: "})]".to_string(),
 9924            ..Default::default()
 9925        },
 9926        Some(tree_sitter_rust::LANGUAGE.into()),
 9927    );
 9928    let language = Arc::new(language);
 9929
 9930    cx.language_registry().add(language.clone());
 9931    cx.update_buffer(|buffer, cx| {
 9932        buffer.set_language(Some(language), cx);
 9933    });
 9934
 9935    cx.set_state(
 9936        &r#"
 9937            fn main() {
 9938                sampleˇ
 9939            }
 9940        "#
 9941        .unindent(),
 9942    );
 9943
 9944    cx.update_editor(|editor, window, cx| {
 9945        editor.handle_input("(", window, cx);
 9946    });
 9947    cx.assert_editor_state(
 9948        &"
 9949            fn main() {
 9950                sample(ˇ)
 9951            }
 9952        "
 9953        .unindent(),
 9954    );
 9955
 9956    let mocked_response = lsp::SignatureHelp {
 9957        signatures: vec![lsp::SignatureInformation {
 9958            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9959            documentation: None,
 9960            parameters: Some(vec![
 9961                lsp::ParameterInformation {
 9962                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9963                    documentation: None,
 9964                },
 9965                lsp::ParameterInformation {
 9966                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9967                    documentation: None,
 9968                },
 9969            ]),
 9970            active_parameter: None,
 9971        }],
 9972        active_signature: Some(0),
 9973        active_parameter: Some(0),
 9974    };
 9975    handle_signature_help_request(&mut cx, mocked_response).await;
 9976
 9977    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9978        .await;
 9979
 9980    cx.editor(|editor, _, _| {
 9981        let signature_help_state = editor.signature_help_state.popover().cloned();
 9982        assert_eq!(
 9983            signature_help_state.unwrap().label,
 9984            "param1: u8, param2: u8"
 9985        );
 9986    });
 9987}
 9988
 9989#[gpui::test]
 9990async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9991    init_test(cx, |_| {});
 9992
 9993    cx.update(|cx| {
 9994        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9995            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9996                settings.auto_signature_help = Some(false);
 9997                settings.show_signature_help_after_edits = Some(false);
 9998            });
 9999        });
10000    });
10001
10002    let mut cx = EditorLspTestContext::new_rust(
10003        lsp::ServerCapabilities {
10004            signature_help_provider: Some(lsp::SignatureHelpOptions {
10005                ..Default::default()
10006            }),
10007            ..Default::default()
10008        },
10009        cx,
10010    )
10011    .await;
10012
10013    let language = Language::new(
10014        LanguageConfig {
10015            name: "Rust".into(),
10016            brackets: BracketPairConfig {
10017                pairs: vec![
10018                    BracketPair {
10019                        start: "{".to_string(),
10020                        end: "}".to_string(),
10021                        close: true,
10022                        surround: true,
10023                        newline: true,
10024                    },
10025                    BracketPair {
10026                        start: "(".to_string(),
10027                        end: ")".to_string(),
10028                        close: true,
10029                        surround: true,
10030                        newline: true,
10031                    },
10032                    BracketPair {
10033                        start: "/*".to_string(),
10034                        end: " */".to_string(),
10035                        close: true,
10036                        surround: true,
10037                        newline: true,
10038                    },
10039                    BracketPair {
10040                        start: "[".to_string(),
10041                        end: "]".to_string(),
10042                        close: false,
10043                        surround: false,
10044                        newline: true,
10045                    },
10046                    BracketPair {
10047                        start: "\"".to_string(),
10048                        end: "\"".to_string(),
10049                        close: true,
10050                        surround: true,
10051                        newline: false,
10052                    },
10053                    BracketPair {
10054                        start: "<".to_string(),
10055                        end: ">".to_string(),
10056                        close: false,
10057                        surround: true,
10058                        newline: true,
10059                    },
10060                ],
10061                ..Default::default()
10062            },
10063            autoclose_before: "})]".to_string(),
10064            ..Default::default()
10065        },
10066        Some(tree_sitter_rust::LANGUAGE.into()),
10067    );
10068    let language = Arc::new(language);
10069
10070    cx.language_registry().add(language.clone());
10071    cx.update_buffer(|buffer, cx| {
10072        buffer.set_language(Some(language), cx);
10073    });
10074
10075    // Ensure that signature_help is not called when no signature help is enabled.
10076    cx.set_state(
10077        &r#"
10078            fn main() {
10079                sampleˇ
10080            }
10081        "#
10082        .unindent(),
10083    );
10084    cx.update_editor(|editor, window, cx| {
10085        editor.handle_input("(", window, cx);
10086    });
10087    cx.assert_editor_state(
10088        &"
10089            fn main() {
10090                sample(ˇ)
10091            }
10092        "
10093        .unindent(),
10094    );
10095    cx.editor(|editor, _, _| {
10096        assert!(editor.signature_help_state.task().is_none());
10097    });
10098
10099    let mocked_response = lsp::SignatureHelp {
10100        signatures: vec![lsp::SignatureInformation {
10101            label: "fn sample(param1: u8, param2: u8)".to_string(),
10102            documentation: None,
10103            parameters: Some(vec![
10104                lsp::ParameterInformation {
10105                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10106                    documentation: None,
10107                },
10108                lsp::ParameterInformation {
10109                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10110                    documentation: None,
10111                },
10112            ]),
10113            active_parameter: None,
10114        }],
10115        active_signature: Some(0),
10116        active_parameter: Some(0),
10117    };
10118
10119    // Ensure that signature_help is called when enabled afte edits
10120    cx.update(|_, cx| {
10121        cx.update_global::<SettingsStore, _>(|settings, cx| {
10122            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10123                settings.auto_signature_help = Some(false);
10124                settings.show_signature_help_after_edits = Some(true);
10125            });
10126        });
10127    });
10128    cx.set_state(
10129        &r#"
10130            fn main() {
10131                sampleˇ
10132            }
10133        "#
10134        .unindent(),
10135    );
10136    cx.update_editor(|editor, window, cx| {
10137        editor.handle_input("(", window, cx);
10138    });
10139    cx.assert_editor_state(
10140        &"
10141            fn main() {
10142                sample(ˇ)
10143            }
10144        "
10145        .unindent(),
10146    );
10147    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10148    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10149        .await;
10150    cx.update_editor(|editor, _, _| {
10151        let signature_help_state = editor.signature_help_state.popover().cloned();
10152        assert!(signature_help_state.is_some());
10153        assert_eq!(
10154            signature_help_state.unwrap().label,
10155            "param1: u8, param2: u8"
10156        );
10157        editor.signature_help_state = SignatureHelpState::default();
10158    });
10159
10160    // Ensure that signature_help is called when auto signature help override is enabled
10161    cx.update(|_, cx| {
10162        cx.update_global::<SettingsStore, _>(|settings, cx| {
10163            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10164                settings.auto_signature_help = Some(true);
10165                settings.show_signature_help_after_edits = Some(false);
10166            });
10167        });
10168    });
10169    cx.set_state(
10170        &r#"
10171            fn main() {
10172                sampleˇ
10173            }
10174        "#
10175        .unindent(),
10176    );
10177    cx.update_editor(|editor, window, cx| {
10178        editor.handle_input("(", window, cx);
10179    });
10180    cx.assert_editor_state(
10181        &"
10182            fn main() {
10183                sample(ˇ)
10184            }
10185        "
10186        .unindent(),
10187    );
10188    handle_signature_help_request(&mut cx, mocked_response).await;
10189    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10190        .await;
10191    cx.editor(|editor, _, _| {
10192        let signature_help_state = editor.signature_help_state.popover().cloned();
10193        assert!(signature_help_state.is_some());
10194        assert_eq!(
10195            signature_help_state.unwrap().label,
10196            "param1: u8, param2: u8"
10197        );
10198    });
10199}
10200
10201#[gpui::test]
10202async fn test_signature_help(cx: &mut TestAppContext) {
10203    init_test(cx, |_| {});
10204    cx.update(|cx| {
10205        cx.update_global::<SettingsStore, _>(|settings, cx| {
10206            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10207                settings.auto_signature_help = Some(true);
10208            });
10209        });
10210    });
10211
10212    let mut cx = EditorLspTestContext::new_rust(
10213        lsp::ServerCapabilities {
10214            signature_help_provider: Some(lsp::SignatureHelpOptions {
10215                ..Default::default()
10216            }),
10217            ..Default::default()
10218        },
10219        cx,
10220    )
10221    .await;
10222
10223    // A test that directly calls `show_signature_help`
10224    cx.update_editor(|editor, window, cx| {
10225        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10226    });
10227
10228    let mocked_response = lsp::SignatureHelp {
10229        signatures: vec![lsp::SignatureInformation {
10230            label: "fn sample(param1: u8, param2: u8)".to_string(),
10231            documentation: None,
10232            parameters: Some(vec![
10233                lsp::ParameterInformation {
10234                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10235                    documentation: None,
10236                },
10237                lsp::ParameterInformation {
10238                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10239                    documentation: None,
10240                },
10241            ]),
10242            active_parameter: None,
10243        }],
10244        active_signature: Some(0),
10245        active_parameter: Some(0),
10246    };
10247    handle_signature_help_request(&mut cx, mocked_response).await;
10248
10249    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10250        .await;
10251
10252    cx.editor(|editor, _, _| {
10253        let signature_help_state = editor.signature_help_state.popover().cloned();
10254        assert!(signature_help_state.is_some());
10255        assert_eq!(
10256            signature_help_state.unwrap().label,
10257            "param1: u8, param2: u8"
10258        );
10259    });
10260
10261    // When exiting outside from inside the brackets, `signature_help` is closed.
10262    cx.set_state(indoc! {"
10263        fn main() {
10264            sample(ˇ);
10265        }
10266
10267        fn sample(param1: u8, param2: u8) {}
10268    "});
10269
10270    cx.update_editor(|editor, window, cx| {
10271        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10272    });
10273
10274    let mocked_response = lsp::SignatureHelp {
10275        signatures: Vec::new(),
10276        active_signature: None,
10277        active_parameter: None,
10278    };
10279    handle_signature_help_request(&mut cx, mocked_response).await;
10280
10281    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10282        .await;
10283
10284    cx.editor(|editor, _, _| {
10285        assert!(!editor.signature_help_state.is_shown());
10286    });
10287
10288    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10289    cx.set_state(indoc! {"
10290        fn main() {
10291            sample(ˇ);
10292        }
10293
10294        fn sample(param1: u8, param2: u8) {}
10295    "});
10296
10297    let mocked_response = lsp::SignatureHelp {
10298        signatures: vec![lsp::SignatureInformation {
10299            label: "fn sample(param1: u8, param2: u8)".to_string(),
10300            documentation: None,
10301            parameters: Some(vec![
10302                lsp::ParameterInformation {
10303                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10304                    documentation: None,
10305                },
10306                lsp::ParameterInformation {
10307                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10308                    documentation: None,
10309                },
10310            ]),
10311            active_parameter: None,
10312        }],
10313        active_signature: Some(0),
10314        active_parameter: Some(0),
10315    };
10316    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10317    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10318        .await;
10319    cx.editor(|editor, _, _| {
10320        assert!(editor.signature_help_state.is_shown());
10321    });
10322
10323    // Restore the popover with more parameter input
10324    cx.set_state(indoc! {"
10325        fn main() {
10326            sample(param1, param2ˇ);
10327        }
10328
10329        fn sample(param1: u8, param2: u8) {}
10330    "});
10331
10332    let mocked_response = lsp::SignatureHelp {
10333        signatures: vec![lsp::SignatureInformation {
10334            label: "fn sample(param1: u8, param2: u8)".to_string(),
10335            documentation: None,
10336            parameters: Some(vec![
10337                lsp::ParameterInformation {
10338                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10339                    documentation: None,
10340                },
10341                lsp::ParameterInformation {
10342                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10343                    documentation: None,
10344                },
10345            ]),
10346            active_parameter: None,
10347        }],
10348        active_signature: Some(0),
10349        active_parameter: Some(1),
10350    };
10351    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10352    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10353        .await;
10354
10355    // When selecting a range, the popover is gone.
10356    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10357    cx.update_editor(|editor, window, cx| {
10358        editor.change_selections(None, window, cx, |s| {
10359            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10360        })
10361    });
10362    cx.assert_editor_state(indoc! {"
10363        fn main() {
10364            sample(param1, «ˇparam2»);
10365        }
10366
10367        fn sample(param1: u8, param2: u8) {}
10368    "});
10369    cx.editor(|editor, _, _| {
10370        assert!(!editor.signature_help_state.is_shown());
10371    });
10372
10373    // When unselecting again, the popover is back if within the brackets.
10374    cx.update_editor(|editor, window, cx| {
10375        editor.change_selections(None, window, cx, |s| {
10376            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10377        })
10378    });
10379    cx.assert_editor_state(indoc! {"
10380        fn main() {
10381            sample(param1, ˇparam2);
10382        }
10383
10384        fn sample(param1: u8, param2: u8) {}
10385    "});
10386    handle_signature_help_request(&mut cx, mocked_response).await;
10387    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10388        .await;
10389    cx.editor(|editor, _, _| {
10390        assert!(editor.signature_help_state.is_shown());
10391    });
10392
10393    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10394    cx.update_editor(|editor, window, cx| {
10395        editor.change_selections(None, window, cx, |s| {
10396            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10397            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10398        })
10399    });
10400    cx.assert_editor_state(indoc! {"
10401        fn main() {
10402            sample(param1, ˇparam2);
10403        }
10404
10405        fn sample(param1: u8, param2: u8) {}
10406    "});
10407
10408    let mocked_response = lsp::SignatureHelp {
10409        signatures: vec![lsp::SignatureInformation {
10410            label: "fn sample(param1: u8, param2: u8)".to_string(),
10411            documentation: None,
10412            parameters: Some(vec![
10413                lsp::ParameterInformation {
10414                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10415                    documentation: None,
10416                },
10417                lsp::ParameterInformation {
10418                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10419                    documentation: None,
10420                },
10421            ]),
10422            active_parameter: None,
10423        }],
10424        active_signature: Some(0),
10425        active_parameter: Some(1),
10426    };
10427    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10428    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10429        .await;
10430    cx.update_editor(|editor, _, cx| {
10431        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10432    });
10433    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10434        .await;
10435    cx.update_editor(|editor, window, cx| {
10436        editor.change_selections(None, window, cx, |s| {
10437            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10438        })
10439    });
10440    cx.assert_editor_state(indoc! {"
10441        fn main() {
10442            sample(param1, «ˇparam2»);
10443        }
10444
10445        fn sample(param1: u8, param2: u8) {}
10446    "});
10447    cx.update_editor(|editor, window, cx| {
10448        editor.change_selections(None, window, cx, |s| {
10449            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10450        })
10451    });
10452    cx.assert_editor_state(indoc! {"
10453        fn main() {
10454            sample(param1, ˇparam2);
10455        }
10456
10457        fn sample(param1: u8, param2: u8) {}
10458    "});
10459    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10460        .await;
10461}
10462
10463#[gpui::test]
10464async fn test_completion_mode(cx: &mut TestAppContext) {
10465    init_test(cx, |_| {});
10466    let mut cx = EditorLspTestContext::new_rust(
10467        lsp::ServerCapabilities {
10468            completion_provider: Some(lsp::CompletionOptions {
10469                resolve_provider: Some(true),
10470                ..Default::default()
10471            }),
10472            ..Default::default()
10473        },
10474        cx,
10475    )
10476    .await;
10477
10478    struct Run {
10479        run_description: &'static str,
10480        initial_state: String,
10481        buffer_marked_text: String,
10482        completion_label: &'static str,
10483        completion_text: &'static str,
10484        expected_with_insert_mode: String,
10485        expected_with_replace_mode: String,
10486        expected_with_replace_subsequence_mode: String,
10487        expected_with_replace_suffix_mode: String,
10488    }
10489
10490    let runs = [
10491        Run {
10492            run_description: "Start of word matches completion text",
10493            initial_state: "before ediˇ after".into(),
10494            buffer_marked_text: "before <edi|> after".into(),
10495            completion_label: "editor",
10496            completion_text: "editor",
10497            expected_with_insert_mode: "before editorˇ after".into(),
10498            expected_with_replace_mode: "before editorˇ after".into(),
10499            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10500            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10501        },
10502        Run {
10503            run_description: "Accept same text at the middle of the word",
10504            initial_state: "before ediˇtor after".into(),
10505            buffer_marked_text: "before <edi|tor> after".into(),
10506            completion_label: "editor",
10507            completion_text: "editor",
10508            expected_with_insert_mode: "before editorˇtor after".into(),
10509            expected_with_replace_mode: "before editorˇ after".into(),
10510            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10511            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10512        },
10513        Run {
10514            run_description: "End of word matches completion text -- cursor at end",
10515            initial_state: "before torˇ after".into(),
10516            buffer_marked_text: "before <tor|> after".into(),
10517            completion_label: "editor",
10518            completion_text: "editor",
10519            expected_with_insert_mode: "before editorˇ after".into(),
10520            expected_with_replace_mode: "before editorˇ after".into(),
10521            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10522            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10523        },
10524        Run {
10525            run_description: "End of word matches completion text -- cursor at start",
10526            initial_state: "before ˇtor after".into(),
10527            buffer_marked_text: "before <|tor> after".into(),
10528            completion_label: "editor",
10529            completion_text: "editor",
10530            expected_with_insert_mode: "before editorˇtor after".into(),
10531            expected_with_replace_mode: "before editorˇ after".into(),
10532            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10533            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10534        },
10535        Run {
10536            run_description: "Prepend text containing whitespace",
10537            initial_state: "pˇfield: bool".into(),
10538            buffer_marked_text: "<p|field>: bool".into(),
10539            completion_label: "pub ",
10540            completion_text: "pub ",
10541            expected_with_insert_mode: "pub ˇfield: bool".into(),
10542            expected_with_replace_mode: "pub ˇ: bool".into(),
10543            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10544            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10545        },
10546        Run {
10547            run_description: "Add element to start of list",
10548            initial_state: "[element_ˇelement_2]".into(),
10549            buffer_marked_text: "[<element_|element_2>]".into(),
10550            completion_label: "element_1",
10551            completion_text: "element_1",
10552            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10553            expected_with_replace_mode: "[element_1ˇ]".into(),
10554            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10555            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10556        },
10557        Run {
10558            run_description: "Add element to start of list -- first and second elements are equal",
10559            initial_state: "[elˇelement]".into(),
10560            buffer_marked_text: "[<el|element>]".into(),
10561            completion_label: "element",
10562            completion_text: "element",
10563            expected_with_insert_mode: "[elementˇelement]".into(),
10564            expected_with_replace_mode: "[elementˇ]".into(),
10565            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10566            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10567        },
10568        Run {
10569            run_description: "Ends with matching suffix",
10570            initial_state: "SubˇError".into(),
10571            buffer_marked_text: "<Sub|Error>".into(),
10572            completion_label: "SubscriptionError",
10573            completion_text: "SubscriptionError",
10574            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10575            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10576            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10577            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10578        },
10579        Run {
10580            run_description: "Suffix is a subsequence -- contiguous",
10581            initial_state: "SubˇErr".into(),
10582            buffer_marked_text: "<Sub|Err>".into(),
10583            completion_label: "SubscriptionError",
10584            completion_text: "SubscriptionError",
10585            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10586            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10587            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10588            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10589        },
10590        Run {
10591            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10592            initial_state: "Suˇscrirr".into(),
10593            buffer_marked_text: "<Su|scrirr>".into(),
10594            completion_label: "SubscriptionError",
10595            completion_text: "SubscriptionError",
10596            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10597            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10598            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10599            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10600        },
10601        Run {
10602            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10603            initial_state: "foo(indˇix)".into(),
10604            buffer_marked_text: "foo(<ind|ix>)".into(),
10605            completion_label: "node_index",
10606            completion_text: "node_index",
10607            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10608            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10609            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10610            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10611        },
10612        Run {
10613            run_description: "Replace range ends before cursor - should extend to cursor",
10614            initial_state: "before editˇo after".into(),
10615            buffer_marked_text: "before <{ed}>it|o after".into(),
10616            completion_label: "editor",
10617            completion_text: "editor",
10618            expected_with_insert_mode: "before editorˇo after".into(),
10619            expected_with_replace_mode: "before editorˇo after".into(),
10620            expected_with_replace_subsequence_mode: "before editorˇo after".into(),
10621            expected_with_replace_suffix_mode: "before editorˇo after".into(),
10622        },
10623        Run {
10624            run_description: "Uses label for suffix matching",
10625            initial_state: "before ediˇtor after".into(),
10626            buffer_marked_text: "before <edi|tor> after".into(),
10627            completion_label: "editor",
10628            completion_text: "editor()",
10629            expected_with_insert_mode: "before editor()ˇtor after".into(),
10630            expected_with_replace_mode: "before editor()ˇ after".into(),
10631            expected_with_replace_subsequence_mode: "before editor()ˇ after".into(),
10632            expected_with_replace_suffix_mode: "before editor()ˇ after".into(),
10633        },
10634        Run {
10635            run_description: "Case insensitive subsequence and suffix matching",
10636            initial_state: "before EDiˇtoR after".into(),
10637            buffer_marked_text: "before <EDi|toR> after".into(),
10638            completion_label: "editor",
10639            completion_text: "editor",
10640            expected_with_insert_mode: "before editorˇtoR after".into(),
10641            expected_with_replace_mode: "before editorˇ after".into(),
10642            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10643            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10644        },
10645    ];
10646
10647    for run in runs {
10648        let run_variations = [
10649            (LspInsertMode::Insert, run.expected_with_insert_mode),
10650            (LspInsertMode::Replace, run.expected_with_replace_mode),
10651            (
10652                LspInsertMode::ReplaceSubsequence,
10653                run.expected_with_replace_subsequence_mode,
10654            ),
10655            (
10656                LspInsertMode::ReplaceSuffix,
10657                run.expected_with_replace_suffix_mode,
10658            ),
10659        ];
10660
10661        for (lsp_insert_mode, expected_text) in run_variations {
10662            eprintln!(
10663                "run = {:?}, mode = {lsp_insert_mode:.?}",
10664                run.run_description,
10665            );
10666
10667            update_test_language_settings(&mut cx, |settings| {
10668                settings.defaults.completions = Some(CompletionSettings {
10669                    lsp_insert_mode,
10670                    words: WordsCompletionMode::Disabled,
10671                    lsp: true,
10672                    lsp_fetch_timeout_ms: 0,
10673                });
10674            });
10675
10676            cx.set_state(&run.initial_state);
10677            cx.update_editor(|editor, window, cx| {
10678                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10679            });
10680
10681            let counter = Arc::new(AtomicUsize::new(0));
10682            handle_completion_request_with_insert_and_replace(
10683                &mut cx,
10684                &run.buffer_marked_text,
10685                vec![(run.completion_label, run.completion_text)],
10686                counter.clone(),
10687            )
10688            .await;
10689            cx.condition(|editor, _| editor.context_menu_visible())
10690                .await;
10691            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10692
10693            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10694                editor
10695                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10696                    .unwrap()
10697            });
10698            cx.assert_editor_state(&expected_text);
10699            handle_resolve_completion_request(&mut cx, None).await;
10700            apply_additional_edits.await.unwrap();
10701        }
10702    }
10703}
10704
10705#[gpui::test]
10706async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10707    init_test(cx, |_| {});
10708    let mut cx = EditorLspTestContext::new_rust(
10709        lsp::ServerCapabilities {
10710            completion_provider: Some(lsp::CompletionOptions {
10711                resolve_provider: Some(true),
10712                ..Default::default()
10713            }),
10714            ..Default::default()
10715        },
10716        cx,
10717    )
10718    .await;
10719
10720    let initial_state = "SubˇError";
10721    let buffer_marked_text = "<Sub|Error>";
10722    let completion_text = "SubscriptionError";
10723    let expected_with_insert_mode = "SubscriptionErrorˇError";
10724    let expected_with_replace_mode = "SubscriptionErrorˇ";
10725
10726    update_test_language_settings(&mut cx, |settings| {
10727        settings.defaults.completions = Some(CompletionSettings {
10728            words: WordsCompletionMode::Disabled,
10729            // set the opposite here to ensure that the action is overriding the default behavior
10730            lsp_insert_mode: LspInsertMode::Insert,
10731            lsp: true,
10732            lsp_fetch_timeout_ms: 0,
10733        });
10734    });
10735
10736    cx.set_state(initial_state);
10737    cx.update_editor(|editor, window, cx| {
10738        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10739    });
10740
10741    let counter = Arc::new(AtomicUsize::new(0));
10742    handle_completion_request_with_insert_and_replace(
10743        &mut cx,
10744        &buffer_marked_text,
10745        vec![(completion_text, completion_text)],
10746        counter.clone(),
10747    )
10748    .await;
10749    cx.condition(|editor, _| editor.context_menu_visible())
10750        .await;
10751    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10752
10753    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10754        editor
10755            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10756            .unwrap()
10757    });
10758    cx.assert_editor_state(&expected_with_replace_mode);
10759    handle_resolve_completion_request(&mut cx, None).await;
10760    apply_additional_edits.await.unwrap();
10761
10762    update_test_language_settings(&mut cx, |settings| {
10763        settings.defaults.completions = Some(CompletionSettings {
10764            words: WordsCompletionMode::Disabled,
10765            // set the opposite here to ensure that the action is overriding the default behavior
10766            lsp_insert_mode: LspInsertMode::Replace,
10767            lsp: true,
10768            lsp_fetch_timeout_ms: 0,
10769        });
10770    });
10771
10772    cx.set_state(initial_state);
10773    cx.update_editor(|editor, window, cx| {
10774        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10775    });
10776    handle_completion_request_with_insert_and_replace(
10777        &mut cx,
10778        &buffer_marked_text,
10779        vec![(completion_text, completion_text)],
10780        counter.clone(),
10781    )
10782    .await;
10783    cx.condition(|editor, _| editor.context_menu_visible())
10784        .await;
10785    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10786
10787    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10788        editor
10789            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10790            .unwrap()
10791    });
10792    cx.assert_editor_state(&expected_with_insert_mode);
10793    handle_resolve_completion_request(&mut cx, None).await;
10794    apply_additional_edits.await.unwrap();
10795}
10796
10797#[gpui::test]
10798async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10799    init_test(cx, |_| {});
10800    let mut cx = EditorLspTestContext::new_rust(
10801        lsp::ServerCapabilities {
10802            completion_provider: Some(lsp::CompletionOptions {
10803                resolve_provider: Some(true),
10804                ..Default::default()
10805            }),
10806            ..Default::default()
10807        },
10808        cx,
10809    )
10810    .await;
10811
10812    // scenario: surrounding text matches completion text
10813    let completion_text = "to_offset";
10814    let initial_state = indoc! {"
10815        1. buf.to_offˇsuffix
10816        2. buf.to_offˇsuf
10817        3. buf.to_offˇfix
10818        4. buf.to_offˇ
10819        5. into_offˇensive
10820        6. ˇsuffix
10821        7. let ˇ //
10822        8. aaˇzz
10823        9. buf.to_off«zzzzzˇ»suffix
10824        10. buf.«ˇzzzzz»suffix
10825        11. to_off«ˇzzzzz»
10826
10827        buf.to_offˇsuffix  // newest cursor
10828    "};
10829    let completion_marked_buffer = indoc! {"
10830        1. buf.to_offsuffix
10831        2. buf.to_offsuf
10832        3. buf.to_offfix
10833        4. buf.to_off
10834        5. into_offensive
10835        6. suffix
10836        7. let  //
10837        8. aazz
10838        9. buf.to_offzzzzzsuffix
10839        10. buf.zzzzzsuffix
10840        11. to_offzzzzz
10841
10842        buf.<to_off|suffix>  // newest cursor
10843    "};
10844    let expected = indoc! {"
10845        1. buf.to_offsetˇ
10846        2. buf.to_offsetˇsuf
10847        3. buf.to_offsetˇfix
10848        4. buf.to_offsetˇ
10849        5. into_offsetˇensive
10850        6. to_offsetˇsuffix
10851        7. let to_offsetˇ //
10852        8. aato_offsetˇzz
10853        9. buf.to_offsetˇ
10854        10. buf.to_offsetˇsuffix
10855        11. to_offsetˇ
10856
10857        buf.to_offsetˇ  // newest cursor
10858    "};
10859    cx.set_state(initial_state);
10860    cx.update_editor(|editor, window, cx| {
10861        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10862    });
10863    handle_completion_request_with_insert_and_replace(
10864        &mut cx,
10865        completion_marked_buffer,
10866        vec![(completion_text, completion_text)],
10867        Arc::new(AtomicUsize::new(0)),
10868    )
10869    .await;
10870    cx.condition(|editor, _| editor.context_menu_visible())
10871        .await;
10872    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10873        editor
10874            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10875            .unwrap()
10876    });
10877    cx.assert_editor_state(expected);
10878    handle_resolve_completion_request(&mut cx, None).await;
10879    apply_additional_edits.await.unwrap();
10880
10881    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10882    let completion_text = "foo_and_bar";
10883    let initial_state = indoc! {"
10884        1. ooanbˇ
10885        2. zooanbˇ
10886        3. ooanbˇz
10887        4. zooanbˇz
10888        5. ooanˇ
10889        6. oanbˇ
10890
10891        ooanbˇ
10892    "};
10893    let completion_marked_buffer = indoc! {"
10894        1. ooanb
10895        2. zooanb
10896        3. ooanbz
10897        4. zooanbz
10898        5. ooan
10899        6. oanb
10900
10901        <ooanb|>
10902    "};
10903    let expected = indoc! {"
10904        1. foo_and_barˇ
10905        2. zfoo_and_barˇ
10906        3. foo_and_barˇz
10907        4. zfoo_and_barˇz
10908        5. ooanfoo_and_barˇ
10909        6. oanbfoo_and_barˇ
10910
10911        foo_and_barˇ
10912    "};
10913    cx.set_state(initial_state);
10914    cx.update_editor(|editor, window, cx| {
10915        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10916    });
10917    handle_completion_request_with_insert_and_replace(
10918        &mut cx,
10919        completion_marked_buffer,
10920        vec![(completion_text, completion_text)],
10921        Arc::new(AtomicUsize::new(0)),
10922    )
10923    .await;
10924    cx.condition(|editor, _| editor.context_menu_visible())
10925        .await;
10926    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10927        editor
10928            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10929            .unwrap()
10930    });
10931    cx.assert_editor_state(expected);
10932    handle_resolve_completion_request(&mut cx, None).await;
10933    apply_additional_edits.await.unwrap();
10934
10935    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10936    // (expects the same as if it was inserted at the end)
10937    let completion_text = "foo_and_bar";
10938    let initial_state = indoc! {"
10939        1. ooˇanb
10940        2. zooˇanb
10941        3. ooˇanbz
10942        4. zooˇanbz
10943
10944        ooˇanb
10945    "};
10946    let completion_marked_buffer = indoc! {"
10947        1. ooanb
10948        2. zooanb
10949        3. ooanbz
10950        4. zooanbz
10951
10952        <oo|anb>
10953    "};
10954    let expected = indoc! {"
10955        1. foo_and_barˇ
10956        2. zfoo_and_barˇ
10957        3. foo_and_barˇz
10958        4. zfoo_and_barˇz
10959
10960        foo_and_barˇ
10961    "};
10962    cx.set_state(initial_state);
10963    cx.update_editor(|editor, window, cx| {
10964        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10965    });
10966    handle_completion_request_with_insert_and_replace(
10967        &mut cx,
10968        completion_marked_buffer,
10969        vec![(completion_text, completion_text)],
10970        Arc::new(AtomicUsize::new(0)),
10971    )
10972    .await;
10973    cx.condition(|editor, _| editor.context_menu_visible())
10974        .await;
10975    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10976        editor
10977            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10978            .unwrap()
10979    });
10980    cx.assert_editor_state(expected);
10981    handle_resolve_completion_request(&mut cx, None).await;
10982    apply_additional_edits.await.unwrap();
10983}
10984
10985// This used to crash
10986#[gpui::test]
10987async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10988    init_test(cx, |_| {});
10989
10990    let buffer_text = indoc! {"
10991        fn main() {
10992            10.satu;
10993
10994            //
10995            // separate cursors so they open in different excerpts (manually reproducible)
10996            //
10997
10998            10.satu20;
10999        }
11000    "};
11001    let multibuffer_text_with_selections = indoc! {"
11002        fn main() {
11003            10.satuˇ;
11004
11005            //
11006
11007            //
11008
11009            10.satuˇ20;
11010        }
11011    "};
11012    let expected_multibuffer = indoc! {"
11013        fn main() {
11014            10.saturating_sub()ˇ;
11015
11016            //
11017
11018            //
11019
11020            10.saturating_sub()ˇ;
11021        }
11022    "};
11023
11024    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
11025    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
11026
11027    let fs = FakeFs::new(cx.executor());
11028    fs.insert_tree(
11029        path!("/a"),
11030        json!({
11031            "main.rs": buffer_text,
11032        }),
11033    )
11034    .await;
11035
11036    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11037    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11038    language_registry.add(rust_lang());
11039    let mut fake_servers = language_registry.register_fake_lsp(
11040        "Rust",
11041        FakeLspAdapter {
11042            capabilities: lsp::ServerCapabilities {
11043                completion_provider: Some(lsp::CompletionOptions {
11044                    resolve_provider: None,
11045                    ..lsp::CompletionOptions::default()
11046                }),
11047                ..lsp::ServerCapabilities::default()
11048            },
11049            ..FakeLspAdapter::default()
11050        },
11051    );
11052    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11053    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11054    let buffer = project
11055        .update(cx, |project, cx| {
11056            project.open_local_buffer(path!("/a/main.rs"), cx)
11057        })
11058        .await
11059        .unwrap();
11060
11061    let multi_buffer = cx.new(|cx| {
11062        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
11063        multi_buffer.push_excerpts(
11064            buffer.clone(),
11065            [ExcerptRange::new(0..first_excerpt_end)],
11066            cx,
11067        );
11068        multi_buffer.push_excerpts(
11069            buffer.clone(),
11070            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
11071            cx,
11072        );
11073        multi_buffer
11074    });
11075
11076    let editor = workspace
11077        .update(cx, |_, window, cx| {
11078            cx.new(|cx| {
11079                Editor::new(
11080                    EditorMode::Full {
11081                        scale_ui_elements_with_buffer_font_size: false,
11082                        show_active_line_background: false,
11083                        sized_by_content: false,
11084                    },
11085                    multi_buffer.clone(),
11086                    Some(project.clone()),
11087                    window,
11088                    cx,
11089                )
11090            })
11091        })
11092        .unwrap();
11093
11094    let pane = workspace
11095        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11096        .unwrap();
11097    pane.update_in(cx, |pane, window, cx| {
11098        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
11099    });
11100
11101    let fake_server = fake_servers.next().await.unwrap();
11102
11103    editor.update_in(cx, |editor, window, cx| {
11104        editor.change_selections(None, window, cx, |s| {
11105            s.select_ranges([
11106                Point::new(1, 11)..Point::new(1, 11),
11107                Point::new(7, 11)..Point::new(7, 11),
11108            ])
11109        });
11110
11111        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
11112    });
11113
11114    editor.update_in(cx, |editor, window, cx| {
11115        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11116    });
11117
11118    fake_server
11119        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11120            let completion_item = lsp::CompletionItem {
11121                label: "saturating_sub()".into(),
11122                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11123                    lsp::InsertReplaceEdit {
11124                        new_text: "saturating_sub()".to_owned(),
11125                        insert: lsp::Range::new(
11126                            lsp::Position::new(7, 7),
11127                            lsp::Position::new(7, 11),
11128                        ),
11129                        replace: lsp::Range::new(
11130                            lsp::Position::new(7, 7),
11131                            lsp::Position::new(7, 13),
11132                        ),
11133                    },
11134                )),
11135                ..lsp::CompletionItem::default()
11136            };
11137
11138            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
11139        })
11140        .next()
11141        .await
11142        .unwrap();
11143
11144    cx.condition(&editor, |editor, _| editor.context_menu_visible())
11145        .await;
11146
11147    editor
11148        .update_in(cx, |editor, window, cx| {
11149            editor
11150                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11151                .unwrap()
11152        })
11153        .await
11154        .unwrap();
11155
11156    editor.update(cx, |editor, cx| {
11157        assert_text_with_selections(editor, expected_multibuffer, cx);
11158    })
11159}
11160
11161#[gpui::test]
11162async fn test_completion(cx: &mut TestAppContext) {
11163    init_test(cx, |_| {});
11164
11165    let mut cx = EditorLspTestContext::new_rust(
11166        lsp::ServerCapabilities {
11167            completion_provider: Some(lsp::CompletionOptions {
11168                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11169                resolve_provider: Some(true),
11170                ..Default::default()
11171            }),
11172            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11173            ..Default::default()
11174        },
11175        cx,
11176    )
11177    .await;
11178    let counter = Arc::new(AtomicUsize::new(0));
11179
11180    cx.set_state(indoc! {"
11181        oneˇ
11182        two
11183        three
11184    "});
11185    cx.simulate_keystroke(".");
11186    handle_completion_request(
11187        &mut cx,
11188        indoc! {"
11189            one.|<>
11190            two
11191            three
11192        "},
11193        vec!["first_completion", "second_completion"],
11194        counter.clone(),
11195    )
11196    .await;
11197    cx.condition(|editor, _| editor.context_menu_visible())
11198        .await;
11199    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11200
11201    let _handler = handle_signature_help_request(
11202        &mut cx,
11203        lsp::SignatureHelp {
11204            signatures: vec![lsp::SignatureInformation {
11205                label: "test signature".to_string(),
11206                documentation: None,
11207                parameters: Some(vec![lsp::ParameterInformation {
11208                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
11209                    documentation: None,
11210                }]),
11211                active_parameter: None,
11212            }],
11213            active_signature: None,
11214            active_parameter: None,
11215        },
11216    );
11217    cx.update_editor(|editor, window, cx| {
11218        assert!(
11219            !editor.signature_help_state.is_shown(),
11220            "No signature help was called for"
11221        );
11222        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11223    });
11224    cx.run_until_parked();
11225    cx.update_editor(|editor, _, _| {
11226        assert!(
11227            !editor.signature_help_state.is_shown(),
11228            "No signature help should be shown when completions menu is open"
11229        );
11230    });
11231
11232    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11233        editor.context_menu_next(&Default::default(), window, cx);
11234        editor
11235            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11236            .unwrap()
11237    });
11238    cx.assert_editor_state(indoc! {"
11239        one.second_completionˇ
11240        two
11241        three
11242    "});
11243
11244    handle_resolve_completion_request(
11245        &mut cx,
11246        Some(vec![
11247            (
11248                //This overlaps with the primary completion edit which is
11249                //misbehavior from the LSP spec, test that we filter it out
11250                indoc! {"
11251                    one.second_ˇcompletion
11252                    two
11253                    threeˇ
11254                "},
11255                "overlapping additional edit",
11256            ),
11257            (
11258                indoc! {"
11259                    one.second_completion
11260                    two
11261                    threeˇ
11262                "},
11263                "\nadditional edit",
11264            ),
11265        ]),
11266    )
11267    .await;
11268    apply_additional_edits.await.unwrap();
11269    cx.assert_editor_state(indoc! {"
11270        one.second_completionˇ
11271        two
11272        three
11273        additional edit
11274    "});
11275
11276    cx.set_state(indoc! {"
11277        one.second_completion
11278        twoˇ
11279        threeˇ
11280        additional edit
11281    "});
11282    cx.simulate_keystroke(" ");
11283    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11284    cx.simulate_keystroke("s");
11285    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11286
11287    cx.assert_editor_state(indoc! {"
11288        one.second_completion
11289        two sˇ
11290        three sˇ
11291        additional edit
11292    "});
11293    handle_completion_request(
11294        &mut cx,
11295        indoc! {"
11296            one.second_completion
11297            two s
11298            three <s|>
11299            additional edit
11300        "},
11301        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11302        counter.clone(),
11303    )
11304    .await;
11305    cx.condition(|editor, _| editor.context_menu_visible())
11306        .await;
11307    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11308
11309    cx.simulate_keystroke("i");
11310
11311    handle_completion_request(
11312        &mut cx,
11313        indoc! {"
11314            one.second_completion
11315            two si
11316            three <si|>
11317            additional edit
11318        "},
11319        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11320        counter.clone(),
11321    )
11322    .await;
11323    cx.condition(|editor, _| editor.context_menu_visible())
11324        .await;
11325    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11326
11327    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11328        editor
11329            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11330            .unwrap()
11331    });
11332    cx.assert_editor_state(indoc! {"
11333        one.second_completion
11334        two sixth_completionˇ
11335        three sixth_completionˇ
11336        additional edit
11337    "});
11338
11339    apply_additional_edits.await.unwrap();
11340
11341    update_test_language_settings(&mut cx, |settings| {
11342        settings.defaults.show_completions_on_input = Some(false);
11343    });
11344    cx.set_state("editorˇ");
11345    cx.simulate_keystroke(".");
11346    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11347    cx.simulate_keystrokes("c l o");
11348    cx.assert_editor_state("editor.cloˇ");
11349    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11350    cx.update_editor(|editor, window, cx| {
11351        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11352    });
11353    handle_completion_request(
11354        &mut cx,
11355        "editor.<clo|>",
11356        vec!["close", "clobber"],
11357        counter.clone(),
11358    )
11359    .await;
11360    cx.condition(|editor, _| editor.context_menu_visible())
11361        .await;
11362    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11363
11364    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11365        editor
11366            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11367            .unwrap()
11368    });
11369    cx.assert_editor_state("editor.closeˇ");
11370    handle_resolve_completion_request(&mut cx, None).await;
11371    apply_additional_edits.await.unwrap();
11372}
11373
11374#[gpui::test]
11375async fn test_word_completion(cx: &mut TestAppContext) {
11376    let lsp_fetch_timeout_ms = 10;
11377    init_test(cx, |language_settings| {
11378        language_settings.defaults.completions = Some(CompletionSettings {
11379            words: WordsCompletionMode::Fallback,
11380            lsp: true,
11381            lsp_fetch_timeout_ms: 10,
11382            lsp_insert_mode: LspInsertMode::Insert,
11383        });
11384    });
11385
11386    let mut cx = EditorLspTestContext::new_rust(
11387        lsp::ServerCapabilities {
11388            completion_provider: Some(lsp::CompletionOptions {
11389                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11390                ..lsp::CompletionOptions::default()
11391            }),
11392            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11393            ..lsp::ServerCapabilities::default()
11394        },
11395        cx,
11396    )
11397    .await;
11398
11399    let throttle_completions = Arc::new(AtomicBool::new(false));
11400
11401    let lsp_throttle_completions = throttle_completions.clone();
11402    let _completion_requests_handler =
11403        cx.lsp
11404            .server
11405            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11406                let lsp_throttle_completions = lsp_throttle_completions.clone();
11407                let cx = cx.clone();
11408                async move {
11409                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11410                        cx.background_executor()
11411                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11412                            .await;
11413                    }
11414                    Ok(Some(lsp::CompletionResponse::Array(vec![
11415                        lsp::CompletionItem {
11416                            label: "first".into(),
11417                            ..lsp::CompletionItem::default()
11418                        },
11419                        lsp::CompletionItem {
11420                            label: "last".into(),
11421                            ..lsp::CompletionItem::default()
11422                        },
11423                    ])))
11424                }
11425            });
11426
11427    cx.set_state(indoc! {"
11428        oneˇ
11429        two
11430        three
11431    "});
11432    cx.simulate_keystroke(".");
11433    cx.executor().run_until_parked();
11434    cx.condition(|editor, _| editor.context_menu_visible())
11435        .await;
11436    cx.update_editor(|editor, window, cx| {
11437        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11438        {
11439            assert_eq!(
11440                completion_menu_entries(&menu),
11441                &["first", "last"],
11442                "When LSP server is fast to reply, no fallback word completions are used"
11443            );
11444        } else {
11445            panic!("expected completion menu to be open");
11446        }
11447        editor.cancel(&Cancel, window, cx);
11448    });
11449    cx.executor().run_until_parked();
11450    cx.condition(|editor, _| !editor.context_menu_visible())
11451        .await;
11452
11453    throttle_completions.store(true, atomic::Ordering::Release);
11454    cx.simulate_keystroke(".");
11455    cx.executor()
11456        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11457    cx.executor().run_until_parked();
11458    cx.condition(|editor, _| editor.context_menu_visible())
11459        .await;
11460    cx.update_editor(|editor, _, _| {
11461        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11462        {
11463            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11464                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11465        } else {
11466            panic!("expected completion menu to be open");
11467        }
11468    });
11469}
11470
11471#[gpui::test]
11472async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11473    init_test(cx, |language_settings| {
11474        language_settings.defaults.completions = Some(CompletionSettings {
11475            words: WordsCompletionMode::Enabled,
11476            lsp: true,
11477            lsp_fetch_timeout_ms: 0,
11478            lsp_insert_mode: LspInsertMode::Insert,
11479        });
11480    });
11481
11482    let mut cx = EditorLspTestContext::new_rust(
11483        lsp::ServerCapabilities {
11484            completion_provider: Some(lsp::CompletionOptions {
11485                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11486                ..lsp::CompletionOptions::default()
11487            }),
11488            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11489            ..lsp::ServerCapabilities::default()
11490        },
11491        cx,
11492    )
11493    .await;
11494
11495    let _completion_requests_handler =
11496        cx.lsp
11497            .server
11498            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11499                Ok(Some(lsp::CompletionResponse::Array(vec![
11500                    lsp::CompletionItem {
11501                        label: "first".into(),
11502                        ..lsp::CompletionItem::default()
11503                    },
11504                    lsp::CompletionItem {
11505                        label: "last".into(),
11506                        ..lsp::CompletionItem::default()
11507                    },
11508                ])))
11509            });
11510
11511    cx.set_state(indoc! {"ˇ
11512        first
11513        last
11514        second
11515    "});
11516    cx.simulate_keystroke(".");
11517    cx.executor().run_until_parked();
11518    cx.condition(|editor, _| editor.context_menu_visible())
11519        .await;
11520    cx.update_editor(|editor, _, _| {
11521        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11522        {
11523            assert_eq!(
11524                completion_menu_entries(&menu),
11525                &["first", "last", "second"],
11526                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11527            );
11528        } else {
11529            panic!("expected completion menu to be open");
11530        }
11531    });
11532}
11533
11534#[gpui::test]
11535async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11536    init_test(cx, |language_settings| {
11537        language_settings.defaults.completions = Some(CompletionSettings {
11538            words: WordsCompletionMode::Disabled,
11539            lsp: true,
11540            lsp_fetch_timeout_ms: 0,
11541            lsp_insert_mode: LspInsertMode::Insert,
11542        });
11543    });
11544
11545    let mut cx = EditorLspTestContext::new_rust(
11546        lsp::ServerCapabilities {
11547            completion_provider: Some(lsp::CompletionOptions {
11548                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11549                ..lsp::CompletionOptions::default()
11550            }),
11551            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11552            ..lsp::ServerCapabilities::default()
11553        },
11554        cx,
11555    )
11556    .await;
11557
11558    let _completion_requests_handler =
11559        cx.lsp
11560            .server
11561            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11562                panic!("LSP completions should not be queried when dealing with word completions")
11563            });
11564
11565    cx.set_state(indoc! {"ˇ
11566        first
11567        last
11568        second
11569    "});
11570    cx.update_editor(|editor, window, cx| {
11571        editor.show_word_completions(&ShowWordCompletions, window, cx);
11572    });
11573    cx.executor().run_until_parked();
11574    cx.condition(|editor, _| editor.context_menu_visible())
11575        .await;
11576    cx.update_editor(|editor, _, _| {
11577        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11578        {
11579            assert_eq!(
11580                completion_menu_entries(&menu),
11581                &["first", "last", "second"],
11582                "`ShowWordCompletions` action should show word completions"
11583            );
11584        } else {
11585            panic!("expected completion menu to be open");
11586        }
11587    });
11588
11589    cx.simulate_keystroke("l");
11590    cx.executor().run_until_parked();
11591    cx.condition(|editor, _| editor.context_menu_visible())
11592        .await;
11593    cx.update_editor(|editor, _, _| {
11594        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11595        {
11596            assert_eq!(
11597                completion_menu_entries(&menu),
11598                &["last"],
11599                "After showing word completions, further editing should filter them and not query the LSP"
11600            );
11601        } else {
11602            panic!("expected completion menu to be open");
11603        }
11604    });
11605}
11606
11607#[gpui::test]
11608async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11609    init_test(cx, |language_settings| {
11610        language_settings.defaults.completions = Some(CompletionSettings {
11611            words: WordsCompletionMode::Fallback,
11612            lsp: false,
11613            lsp_fetch_timeout_ms: 0,
11614            lsp_insert_mode: LspInsertMode::Insert,
11615        });
11616    });
11617
11618    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11619
11620    cx.set_state(indoc! {"ˇ
11621        0_usize
11622        let
11623        33
11624        4.5f32
11625    "});
11626    cx.update_editor(|editor, window, cx| {
11627        editor.show_completions(&ShowCompletions::default(), window, cx);
11628    });
11629    cx.executor().run_until_parked();
11630    cx.condition(|editor, _| editor.context_menu_visible())
11631        .await;
11632    cx.update_editor(|editor, window, cx| {
11633        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11634        {
11635            assert_eq!(
11636                completion_menu_entries(&menu),
11637                &["let"],
11638                "With no digits in the completion query, no digits should be in the word completions"
11639            );
11640        } else {
11641            panic!("expected completion menu to be open");
11642        }
11643        editor.cancel(&Cancel, window, cx);
11644    });
11645
11646    cx.set_state(indoc! {"11647        0_usize
11648        let
11649        3
11650        33.35f32
11651    "});
11652    cx.update_editor(|editor, window, cx| {
11653        editor.show_completions(&ShowCompletions::default(), window, cx);
11654    });
11655    cx.executor().run_until_parked();
11656    cx.condition(|editor, _| editor.context_menu_visible())
11657        .await;
11658    cx.update_editor(|editor, _, _| {
11659        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11660        {
11661            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11662                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11663        } else {
11664            panic!("expected completion menu to be open");
11665        }
11666    });
11667}
11668
11669fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11670    let position = || lsp::Position {
11671        line: params.text_document_position.position.line,
11672        character: params.text_document_position.position.character,
11673    };
11674    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11675        range: lsp::Range {
11676            start: position(),
11677            end: position(),
11678        },
11679        new_text: text.to_string(),
11680    }))
11681}
11682
11683#[gpui::test]
11684async fn test_multiline_completion(cx: &mut TestAppContext) {
11685    init_test(cx, |_| {});
11686
11687    let fs = FakeFs::new(cx.executor());
11688    fs.insert_tree(
11689        path!("/a"),
11690        json!({
11691            "main.ts": "a",
11692        }),
11693    )
11694    .await;
11695
11696    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11697    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11698    let typescript_language = Arc::new(Language::new(
11699        LanguageConfig {
11700            name: "TypeScript".into(),
11701            matcher: LanguageMatcher {
11702                path_suffixes: vec!["ts".to_string()],
11703                ..LanguageMatcher::default()
11704            },
11705            line_comments: vec!["// ".into()],
11706            ..LanguageConfig::default()
11707        },
11708        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11709    ));
11710    language_registry.add(typescript_language.clone());
11711    let mut fake_servers = language_registry.register_fake_lsp(
11712        "TypeScript",
11713        FakeLspAdapter {
11714            capabilities: lsp::ServerCapabilities {
11715                completion_provider: Some(lsp::CompletionOptions {
11716                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11717                    ..lsp::CompletionOptions::default()
11718                }),
11719                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11720                ..lsp::ServerCapabilities::default()
11721            },
11722            // Emulate vtsls label generation
11723            label_for_completion: Some(Box::new(|item, _| {
11724                let text = if let Some(description) = item
11725                    .label_details
11726                    .as_ref()
11727                    .and_then(|label_details| label_details.description.as_ref())
11728                {
11729                    format!("{} {}", item.label, description)
11730                } else if let Some(detail) = &item.detail {
11731                    format!("{} {}", item.label, detail)
11732                } else {
11733                    item.label.clone()
11734                };
11735                let len = text.len();
11736                Some(language::CodeLabel {
11737                    text,
11738                    runs: Vec::new(),
11739                    filter_range: 0..len,
11740                })
11741            })),
11742            ..FakeLspAdapter::default()
11743        },
11744    );
11745    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11746    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11747    let worktree_id = workspace
11748        .update(cx, |workspace, _window, cx| {
11749            workspace.project().update(cx, |project, cx| {
11750                project.worktrees(cx).next().unwrap().read(cx).id()
11751            })
11752        })
11753        .unwrap();
11754    let _buffer = project
11755        .update(cx, |project, cx| {
11756            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11757        })
11758        .await
11759        .unwrap();
11760    let editor = workspace
11761        .update(cx, |workspace, window, cx| {
11762            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11763        })
11764        .unwrap()
11765        .await
11766        .unwrap()
11767        .downcast::<Editor>()
11768        .unwrap();
11769    let fake_server = fake_servers.next().await.unwrap();
11770
11771    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11772    let multiline_label_2 = "a\nb\nc\n";
11773    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11774    let multiline_description = "d\ne\nf\n";
11775    let multiline_detail_2 = "g\nh\ni\n";
11776
11777    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11778        move |params, _| async move {
11779            Ok(Some(lsp::CompletionResponse::Array(vec![
11780                lsp::CompletionItem {
11781                    label: multiline_label.to_string(),
11782                    text_edit: gen_text_edit(&params, "new_text_1"),
11783                    ..lsp::CompletionItem::default()
11784                },
11785                lsp::CompletionItem {
11786                    label: "single line label 1".to_string(),
11787                    detail: Some(multiline_detail.to_string()),
11788                    text_edit: gen_text_edit(&params, "new_text_2"),
11789                    ..lsp::CompletionItem::default()
11790                },
11791                lsp::CompletionItem {
11792                    label: "single line label 2".to_string(),
11793                    label_details: Some(lsp::CompletionItemLabelDetails {
11794                        description: Some(multiline_description.to_string()),
11795                        detail: None,
11796                    }),
11797                    text_edit: gen_text_edit(&params, "new_text_2"),
11798                    ..lsp::CompletionItem::default()
11799                },
11800                lsp::CompletionItem {
11801                    label: multiline_label_2.to_string(),
11802                    detail: Some(multiline_detail_2.to_string()),
11803                    text_edit: gen_text_edit(&params, "new_text_3"),
11804                    ..lsp::CompletionItem::default()
11805                },
11806                lsp::CompletionItem {
11807                    label: "Label with many     spaces and \t but without newlines".to_string(),
11808                    detail: Some(
11809                        "Details with many     spaces and \t but without newlines".to_string(),
11810                    ),
11811                    text_edit: gen_text_edit(&params, "new_text_4"),
11812                    ..lsp::CompletionItem::default()
11813                },
11814            ])))
11815        },
11816    );
11817
11818    editor.update_in(cx, |editor, window, cx| {
11819        cx.focus_self(window);
11820        editor.move_to_end(&MoveToEnd, window, cx);
11821        editor.handle_input(".", window, cx);
11822    });
11823    cx.run_until_parked();
11824    completion_handle.next().await.unwrap();
11825
11826    editor.update(cx, |editor, _| {
11827        assert!(editor.context_menu_visible());
11828        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11829        {
11830            let completion_labels = menu
11831                .completions
11832                .borrow()
11833                .iter()
11834                .map(|c| c.label.text.clone())
11835                .collect::<Vec<_>>();
11836            assert_eq!(
11837                completion_labels,
11838                &[
11839                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11840                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11841                    "single line label 2 d e f ",
11842                    "a b c g h i ",
11843                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11844                ],
11845                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11846            );
11847
11848            for completion in menu
11849                .completions
11850                .borrow()
11851                .iter() {
11852                    assert_eq!(
11853                        completion.label.filter_range,
11854                        0..completion.label.text.len(),
11855                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11856                    );
11857                }
11858        } else {
11859            panic!("expected completion menu to be open");
11860        }
11861    });
11862}
11863
11864#[gpui::test]
11865async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11866    init_test(cx, |_| {});
11867    let mut cx = EditorLspTestContext::new_rust(
11868        lsp::ServerCapabilities {
11869            completion_provider: Some(lsp::CompletionOptions {
11870                trigger_characters: Some(vec![".".to_string()]),
11871                ..Default::default()
11872            }),
11873            ..Default::default()
11874        },
11875        cx,
11876    )
11877    .await;
11878    cx.lsp
11879        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11880            Ok(Some(lsp::CompletionResponse::Array(vec![
11881                lsp::CompletionItem {
11882                    label: "first".into(),
11883                    ..Default::default()
11884                },
11885                lsp::CompletionItem {
11886                    label: "last".into(),
11887                    ..Default::default()
11888                },
11889            ])))
11890        });
11891    cx.set_state("variableˇ");
11892    cx.simulate_keystroke(".");
11893    cx.executor().run_until_parked();
11894
11895    cx.update_editor(|editor, _, _| {
11896        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11897        {
11898            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11899        } else {
11900            panic!("expected completion menu to be open");
11901        }
11902    });
11903
11904    cx.update_editor(|editor, window, cx| {
11905        editor.move_page_down(&MovePageDown::default(), window, cx);
11906        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11907        {
11908            assert!(
11909                menu.selected_item == 1,
11910                "expected PageDown to select the last item from the context menu"
11911            );
11912        } else {
11913            panic!("expected completion menu to stay open after PageDown");
11914        }
11915    });
11916
11917    cx.update_editor(|editor, window, cx| {
11918        editor.move_page_up(&MovePageUp::default(), window, cx);
11919        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11920        {
11921            assert!(
11922                menu.selected_item == 0,
11923                "expected PageUp to select the first item from the context menu"
11924            );
11925        } else {
11926            panic!("expected completion menu to stay open after PageUp");
11927        }
11928    });
11929}
11930
11931#[gpui::test]
11932async fn test_as_is_completions(cx: &mut TestAppContext) {
11933    init_test(cx, |_| {});
11934    let mut cx = EditorLspTestContext::new_rust(
11935        lsp::ServerCapabilities {
11936            completion_provider: Some(lsp::CompletionOptions {
11937                ..Default::default()
11938            }),
11939            ..Default::default()
11940        },
11941        cx,
11942    )
11943    .await;
11944    cx.lsp
11945        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11946            Ok(Some(lsp::CompletionResponse::Array(vec![
11947                lsp::CompletionItem {
11948                    label: "unsafe".into(),
11949                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11950                        range: lsp::Range {
11951                            start: lsp::Position {
11952                                line: 1,
11953                                character: 2,
11954                            },
11955                            end: lsp::Position {
11956                                line: 1,
11957                                character: 3,
11958                            },
11959                        },
11960                        new_text: "unsafe".to_string(),
11961                    })),
11962                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11963                    ..Default::default()
11964                },
11965            ])))
11966        });
11967    cx.set_state("fn a() {}\n");
11968    cx.executor().run_until_parked();
11969    cx.update_editor(|editor, window, cx| {
11970        editor.show_completions(
11971            &ShowCompletions {
11972                trigger: Some("\n".into()),
11973            },
11974            window,
11975            cx,
11976        );
11977    });
11978    cx.executor().run_until_parked();
11979
11980    cx.update_editor(|editor, window, cx| {
11981        editor.confirm_completion(&Default::default(), window, cx)
11982    });
11983    cx.executor().run_until_parked();
11984    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11985}
11986
11987#[gpui::test]
11988async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11989    init_test(cx, |_| {});
11990
11991    let mut cx = EditorLspTestContext::new_rust(
11992        lsp::ServerCapabilities {
11993            completion_provider: Some(lsp::CompletionOptions {
11994                trigger_characters: Some(vec![".".to_string()]),
11995                resolve_provider: Some(true),
11996                ..Default::default()
11997            }),
11998            ..Default::default()
11999        },
12000        cx,
12001    )
12002    .await;
12003
12004    cx.set_state("fn main() { let a = 2ˇ; }");
12005    cx.simulate_keystroke(".");
12006    let completion_item = lsp::CompletionItem {
12007        label: "Some".into(),
12008        kind: Some(lsp::CompletionItemKind::SNIPPET),
12009        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12010        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12011            kind: lsp::MarkupKind::Markdown,
12012            value: "```rust\nSome(2)\n```".to_string(),
12013        })),
12014        deprecated: Some(false),
12015        sort_text: Some("Some".to_string()),
12016        filter_text: Some("Some".to_string()),
12017        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12018        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12019            range: lsp::Range {
12020                start: lsp::Position {
12021                    line: 0,
12022                    character: 22,
12023                },
12024                end: lsp::Position {
12025                    line: 0,
12026                    character: 22,
12027                },
12028            },
12029            new_text: "Some(2)".to_string(),
12030        })),
12031        additional_text_edits: Some(vec![lsp::TextEdit {
12032            range: lsp::Range {
12033                start: lsp::Position {
12034                    line: 0,
12035                    character: 20,
12036                },
12037                end: lsp::Position {
12038                    line: 0,
12039                    character: 22,
12040                },
12041            },
12042            new_text: "".to_string(),
12043        }]),
12044        ..Default::default()
12045    };
12046
12047    let closure_completion_item = completion_item.clone();
12048    let counter = Arc::new(AtomicUsize::new(0));
12049    let counter_clone = counter.clone();
12050    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12051        let task_completion_item = closure_completion_item.clone();
12052        counter_clone.fetch_add(1, atomic::Ordering::Release);
12053        async move {
12054            Ok(Some(lsp::CompletionResponse::Array(vec![
12055                task_completion_item,
12056            ])))
12057        }
12058    });
12059
12060    cx.condition(|editor, _| editor.context_menu_visible())
12061        .await;
12062    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
12063    assert!(request.next().await.is_some());
12064    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
12065
12066    cx.simulate_keystrokes("S o m");
12067    cx.condition(|editor, _| editor.context_menu_visible())
12068        .await;
12069    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
12070    assert!(request.next().await.is_some());
12071    assert!(request.next().await.is_some());
12072    assert!(request.next().await.is_some());
12073    request.close();
12074    assert!(request.next().await.is_none());
12075    assert_eq!(
12076        counter.load(atomic::Ordering::Acquire),
12077        4,
12078        "With the completions menu open, only one LSP request should happen per input"
12079    );
12080}
12081
12082#[gpui::test]
12083async fn test_toggle_comment(cx: &mut TestAppContext) {
12084    init_test(cx, |_| {});
12085    let mut cx = EditorTestContext::new(cx).await;
12086    let language = Arc::new(Language::new(
12087        LanguageConfig {
12088            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12089            ..Default::default()
12090        },
12091        Some(tree_sitter_rust::LANGUAGE.into()),
12092    ));
12093    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12094
12095    // If multiple selections intersect a line, the line is only toggled once.
12096    cx.set_state(indoc! {"
12097        fn a() {
12098            «//b();
12099            ˇ»// «c();
12100            //ˇ»  d();
12101        }
12102    "});
12103
12104    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12105
12106    cx.assert_editor_state(indoc! {"
12107        fn a() {
12108            «b();
12109            c();
12110            ˇ» d();
12111        }
12112    "});
12113
12114    // The comment prefix is inserted at the same column for every line in a
12115    // selection.
12116    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12117
12118    cx.assert_editor_state(indoc! {"
12119        fn a() {
12120            // «b();
12121            // c();
12122            ˇ»//  d();
12123        }
12124    "});
12125
12126    // If a selection ends at the beginning of a line, that line is not toggled.
12127    cx.set_selections_state(indoc! {"
12128        fn a() {
12129            // b();
12130            «// c();
12131        ˇ»    //  d();
12132        }
12133    "});
12134
12135    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12136
12137    cx.assert_editor_state(indoc! {"
12138        fn a() {
12139            // b();
12140            «c();
12141        ˇ»    //  d();
12142        }
12143    "});
12144
12145    // If a selection span a single line and is empty, the line is toggled.
12146    cx.set_state(indoc! {"
12147        fn a() {
12148            a();
12149            b();
12150        ˇ
12151        }
12152    "});
12153
12154    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12155
12156    cx.assert_editor_state(indoc! {"
12157        fn a() {
12158            a();
12159            b();
12160        //•ˇ
12161        }
12162    "});
12163
12164    // If a selection span multiple lines, empty lines are not toggled.
12165    cx.set_state(indoc! {"
12166        fn a() {
12167            «a();
12168
12169            c();ˇ»
12170        }
12171    "});
12172
12173    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12174
12175    cx.assert_editor_state(indoc! {"
12176        fn a() {
12177            // «a();
12178
12179            // c();ˇ»
12180        }
12181    "});
12182
12183    // If a selection includes multiple comment prefixes, all lines are uncommented.
12184    cx.set_state(indoc! {"
12185        fn a() {
12186            «// a();
12187            /// b();
12188            //! c();ˇ»
12189        }
12190    "});
12191
12192    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12193
12194    cx.assert_editor_state(indoc! {"
12195        fn a() {
12196            «a();
12197            b();
12198            c();ˇ»
12199        }
12200    "});
12201}
12202
12203#[gpui::test]
12204async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
12205    init_test(cx, |_| {});
12206    let mut cx = EditorTestContext::new(cx).await;
12207    let language = Arc::new(Language::new(
12208        LanguageConfig {
12209            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12210            ..Default::default()
12211        },
12212        Some(tree_sitter_rust::LANGUAGE.into()),
12213    ));
12214    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12215
12216    let toggle_comments = &ToggleComments {
12217        advance_downwards: false,
12218        ignore_indent: true,
12219    };
12220
12221    // If multiple selections intersect a line, the line is only toggled once.
12222    cx.set_state(indoc! {"
12223        fn a() {
12224        //    «b();
12225        //    c();
12226        //    ˇ» d();
12227        }
12228    "});
12229
12230    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12231
12232    cx.assert_editor_state(indoc! {"
12233        fn a() {
12234            «b();
12235            c();
12236            ˇ» d();
12237        }
12238    "});
12239
12240    // The comment prefix is inserted at the beginning of each line
12241    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12242
12243    cx.assert_editor_state(indoc! {"
12244        fn a() {
12245        //    «b();
12246        //    c();
12247        //    ˇ» d();
12248        }
12249    "});
12250
12251    // If a selection ends at the beginning of a line, that line is not toggled.
12252    cx.set_selections_state(indoc! {"
12253        fn a() {
12254        //    b();
12255        //    «c();
12256        ˇ»//     d();
12257        }
12258    "});
12259
12260    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12261
12262    cx.assert_editor_state(indoc! {"
12263        fn a() {
12264        //    b();
12265            «c();
12266        ˇ»//     d();
12267        }
12268    "});
12269
12270    // If a selection span a single line and is empty, the line is toggled.
12271    cx.set_state(indoc! {"
12272        fn a() {
12273            a();
12274            b();
12275        ˇ
12276        }
12277    "});
12278
12279    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12280
12281    cx.assert_editor_state(indoc! {"
12282        fn a() {
12283            a();
12284            b();
12285        //ˇ
12286        }
12287    "});
12288
12289    // If a selection span multiple lines, empty lines are not toggled.
12290    cx.set_state(indoc! {"
12291        fn a() {
12292            «a();
12293
12294            c();ˇ»
12295        }
12296    "});
12297
12298    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12299
12300    cx.assert_editor_state(indoc! {"
12301        fn a() {
12302        //    «a();
12303
12304        //    c();ˇ»
12305        }
12306    "});
12307
12308    // If a selection includes multiple comment prefixes, all lines are uncommented.
12309    cx.set_state(indoc! {"
12310        fn a() {
12311        //    «a();
12312        ///    b();
12313        //!    c();ˇ»
12314        }
12315    "});
12316
12317    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12318
12319    cx.assert_editor_state(indoc! {"
12320        fn a() {
12321            «a();
12322            b();
12323            c();ˇ»
12324        }
12325    "});
12326}
12327
12328#[gpui::test]
12329async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12330    init_test(cx, |_| {});
12331
12332    let language = Arc::new(Language::new(
12333        LanguageConfig {
12334            line_comments: vec!["// ".into()],
12335            ..Default::default()
12336        },
12337        Some(tree_sitter_rust::LANGUAGE.into()),
12338    ));
12339
12340    let mut cx = EditorTestContext::new(cx).await;
12341
12342    cx.language_registry().add(language.clone());
12343    cx.update_buffer(|buffer, cx| {
12344        buffer.set_language(Some(language), cx);
12345    });
12346
12347    let toggle_comments = &ToggleComments {
12348        advance_downwards: true,
12349        ignore_indent: false,
12350    };
12351
12352    // Single cursor on one line -> advance
12353    // Cursor moves horizontally 3 characters as well on non-blank line
12354    cx.set_state(indoc!(
12355        "fn a() {
12356             ˇdog();
12357             cat();
12358        }"
12359    ));
12360    cx.update_editor(|editor, window, cx| {
12361        editor.toggle_comments(toggle_comments, window, cx);
12362    });
12363    cx.assert_editor_state(indoc!(
12364        "fn a() {
12365             // dog();
12366             catˇ();
12367        }"
12368    ));
12369
12370    // Single selection on one line -> don't advance
12371    cx.set_state(indoc!(
12372        "fn a() {
12373             «dog()ˇ»;
12374             cat();
12375        }"
12376    ));
12377    cx.update_editor(|editor, window, cx| {
12378        editor.toggle_comments(toggle_comments, window, cx);
12379    });
12380    cx.assert_editor_state(indoc!(
12381        "fn a() {
12382             // «dog()ˇ»;
12383             cat();
12384        }"
12385    ));
12386
12387    // Multiple cursors on one line -> advance
12388    cx.set_state(indoc!(
12389        "fn a() {
12390             ˇdˇog();
12391             cat();
12392        }"
12393    ));
12394    cx.update_editor(|editor, window, cx| {
12395        editor.toggle_comments(toggle_comments, window, cx);
12396    });
12397    cx.assert_editor_state(indoc!(
12398        "fn a() {
12399             // dog();
12400             catˇ(ˇ);
12401        }"
12402    ));
12403
12404    // Multiple cursors on one line, with selection -> don't advance
12405    cx.set_state(indoc!(
12406        "fn a() {
12407             ˇdˇog«()ˇ»;
12408             cat();
12409        }"
12410    ));
12411    cx.update_editor(|editor, window, cx| {
12412        editor.toggle_comments(toggle_comments, window, cx);
12413    });
12414    cx.assert_editor_state(indoc!(
12415        "fn a() {
12416             // ˇdˇog«()ˇ»;
12417             cat();
12418        }"
12419    ));
12420
12421    // Single cursor on one line -> advance
12422    // Cursor moves to column 0 on blank line
12423    cx.set_state(indoc!(
12424        "fn a() {
12425             ˇdog();
12426
12427             cat();
12428        }"
12429    ));
12430    cx.update_editor(|editor, window, cx| {
12431        editor.toggle_comments(toggle_comments, window, cx);
12432    });
12433    cx.assert_editor_state(indoc!(
12434        "fn a() {
12435             // dog();
12436        ˇ
12437             cat();
12438        }"
12439    ));
12440
12441    // Single cursor on one line -> advance
12442    // Cursor starts and ends at column 0
12443    cx.set_state(indoc!(
12444        "fn a() {
12445         ˇ    dog();
12446             cat();
12447        }"
12448    ));
12449    cx.update_editor(|editor, window, cx| {
12450        editor.toggle_comments(toggle_comments, window, cx);
12451    });
12452    cx.assert_editor_state(indoc!(
12453        "fn a() {
12454             // dog();
12455         ˇ    cat();
12456        }"
12457    ));
12458}
12459
12460#[gpui::test]
12461async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12462    init_test(cx, |_| {});
12463
12464    let mut cx = EditorTestContext::new(cx).await;
12465
12466    let html_language = Arc::new(
12467        Language::new(
12468            LanguageConfig {
12469                name: "HTML".into(),
12470                block_comment: Some(("<!-- ".into(), " -->".into())),
12471                ..Default::default()
12472            },
12473            Some(tree_sitter_html::LANGUAGE.into()),
12474        )
12475        .with_injection_query(
12476            r#"
12477            (script_element
12478                (raw_text) @injection.content
12479                (#set! injection.language "javascript"))
12480            "#,
12481        )
12482        .unwrap(),
12483    );
12484
12485    let javascript_language = Arc::new(Language::new(
12486        LanguageConfig {
12487            name: "JavaScript".into(),
12488            line_comments: vec!["// ".into()],
12489            ..Default::default()
12490        },
12491        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12492    ));
12493
12494    cx.language_registry().add(html_language.clone());
12495    cx.language_registry().add(javascript_language.clone());
12496    cx.update_buffer(|buffer, cx| {
12497        buffer.set_language(Some(html_language), cx);
12498    });
12499
12500    // Toggle comments for empty selections
12501    cx.set_state(
12502        &r#"
12503            <p>A</p>ˇ
12504            <p>B</p>ˇ
12505            <p>C</p>ˇ
12506        "#
12507        .unindent(),
12508    );
12509    cx.update_editor(|editor, window, cx| {
12510        editor.toggle_comments(&ToggleComments::default(), window, cx)
12511    });
12512    cx.assert_editor_state(
12513        &r#"
12514            <!-- <p>A</p>ˇ -->
12515            <!-- <p>B</p>ˇ -->
12516            <!-- <p>C</p>ˇ -->
12517        "#
12518        .unindent(),
12519    );
12520    cx.update_editor(|editor, window, cx| {
12521        editor.toggle_comments(&ToggleComments::default(), window, cx)
12522    });
12523    cx.assert_editor_state(
12524        &r#"
12525            <p>A</p>ˇ
12526            <p>B</p>ˇ
12527            <p>C</p>ˇ
12528        "#
12529        .unindent(),
12530    );
12531
12532    // Toggle comments for mixture of empty and non-empty selections, where
12533    // multiple selections occupy a given line.
12534    cx.set_state(
12535        &r#"
12536            <p>A«</p>
12537            <p>ˇ»B</p>ˇ
12538            <p>C«</p>
12539            <p>ˇ»D</p>ˇ
12540        "#
12541        .unindent(),
12542    );
12543
12544    cx.update_editor(|editor, window, cx| {
12545        editor.toggle_comments(&ToggleComments::default(), window, cx)
12546    });
12547    cx.assert_editor_state(
12548        &r#"
12549            <!-- <p>A«</p>
12550            <p>ˇ»B</p>ˇ -->
12551            <!-- <p>C«</p>
12552            <p>ˇ»D</p>ˇ -->
12553        "#
12554        .unindent(),
12555    );
12556    cx.update_editor(|editor, window, cx| {
12557        editor.toggle_comments(&ToggleComments::default(), window, cx)
12558    });
12559    cx.assert_editor_state(
12560        &r#"
12561            <p>A«</p>
12562            <p>ˇ»B</p>ˇ
12563            <p>C«</p>
12564            <p>ˇ»D</p>ˇ
12565        "#
12566        .unindent(),
12567    );
12568
12569    // Toggle comments when different languages are active for different
12570    // selections.
12571    cx.set_state(
12572        &r#"
12573            ˇ<script>
12574                ˇvar x = new Y();
12575            ˇ</script>
12576        "#
12577        .unindent(),
12578    );
12579    cx.executor().run_until_parked();
12580    cx.update_editor(|editor, window, cx| {
12581        editor.toggle_comments(&ToggleComments::default(), window, cx)
12582    });
12583    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12584    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12585    cx.assert_editor_state(
12586        &r#"
12587            <!-- ˇ<script> -->
12588                // ˇvar x = new Y();
12589            <!-- ˇ</script> -->
12590        "#
12591        .unindent(),
12592    );
12593}
12594
12595#[gpui::test]
12596fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12597    init_test(cx, |_| {});
12598
12599    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12600    let multibuffer = cx.new(|cx| {
12601        let mut multibuffer = MultiBuffer::new(ReadWrite);
12602        multibuffer.push_excerpts(
12603            buffer.clone(),
12604            [
12605                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12606                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12607            ],
12608            cx,
12609        );
12610        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12611        multibuffer
12612    });
12613
12614    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12615    editor.update_in(cx, |editor, window, cx| {
12616        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12617        editor.change_selections(None, window, cx, |s| {
12618            s.select_ranges([
12619                Point::new(0, 0)..Point::new(0, 0),
12620                Point::new(1, 0)..Point::new(1, 0),
12621            ])
12622        });
12623
12624        editor.handle_input("X", window, cx);
12625        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12626        assert_eq!(
12627            editor.selections.ranges(cx),
12628            [
12629                Point::new(0, 1)..Point::new(0, 1),
12630                Point::new(1, 1)..Point::new(1, 1),
12631            ]
12632        );
12633
12634        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12635        editor.change_selections(None, window, cx, |s| {
12636            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12637        });
12638        editor.backspace(&Default::default(), window, cx);
12639        assert_eq!(editor.text(cx), "Xa\nbbb");
12640        assert_eq!(
12641            editor.selections.ranges(cx),
12642            [Point::new(1, 0)..Point::new(1, 0)]
12643        );
12644
12645        editor.change_selections(None, window, cx, |s| {
12646            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12647        });
12648        editor.backspace(&Default::default(), window, cx);
12649        assert_eq!(editor.text(cx), "X\nbb");
12650        assert_eq!(
12651            editor.selections.ranges(cx),
12652            [Point::new(0, 1)..Point::new(0, 1)]
12653        );
12654    });
12655}
12656
12657#[gpui::test]
12658fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12659    init_test(cx, |_| {});
12660
12661    let markers = vec![('[', ']').into(), ('(', ')').into()];
12662    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12663        indoc! {"
12664            [aaaa
12665            (bbbb]
12666            cccc)",
12667        },
12668        markers.clone(),
12669    );
12670    let excerpt_ranges = markers.into_iter().map(|marker| {
12671        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12672        ExcerptRange::new(context.clone())
12673    });
12674    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12675    let multibuffer = cx.new(|cx| {
12676        let mut multibuffer = MultiBuffer::new(ReadWrite);
12677        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12678        multibuffer
12679    });
12680
12681    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12682    editor.update_in(cx, |editor, window, cx| {
12683        let (expected_text, selection_ranges) = marked_text_ranges(
12684            indoc! {"
12685                aaaa
12686                bˇbbb
12687                bˇbbˇb
12688                cccc"
12689            },
12690            true,
12691        );
12692        assert_eq!(editor.text(cx), expected_text);
12693        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12694
12695        editor.handle_input("X", window, cx);
12696
12697        let (expected_text, expected_selections) = marked_text_ranges(
12698            indoc! {"
12699                aaaa
12700                bXˇbbXb
12701                bXˇbbXˇb
12702                cccc"
12703            },
12704            false,
12705        );
12706        assert_eq!(editor.text(cx), expected_text);
12707        assert_eq!(editor.selections.ranges(cx), expected_selections);
12708
12709        editor.newline(&Newline, window, cx);
12710        let (expected_text, expected_selections) = marked_text_ranges(
12711            indoc! {"
12712                aaaa
12713                bX
12714                ˇbbX
12715                b
12716                bX
12717                ˇbbX
12718                ˇb
12719                cccc"
12720            },
12721            false,
12722        );
12723        assert_eq!(editor.text(cx), expected_text);
12724        assert_eq!(editor.selections.ranges(cx), expected_selections);
12725    });
12726}
12727
12728#[gpui::test]
12729fn test_refresh_selections(cx: &mut TestAppContext) {
12730    init_test(cx, |_| {});
12731
12732    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12733    let mut excerpt1_id = None;
12734    let multibuffer = cx.new(|cx| {
12735        let mut multibuffer = MultiBuffer::new(ReadWrite);
12736        excerpt1_id = multibuffer
12737            .push_excerpts(
12738                buffer.clone(),
12739                [
12740                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12741                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12742                ],
12743                cx,
12744            )
12745            .into_iter()
12746            .next();
12747        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12748        multibuffer
12749    });
12750
12751    let editor = cx.add_window(|window, cx| {
12752        let mut editor = build_editor(multibuffer.clone(), window, cx);
12753        let snapshot = editor.snapshot(window, cx);
12754        editor.change_selections(None, window, cx, |s| {
12755            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12756        });
12757        editor.begin_selection(
12758            Point::new(2, 1).to_display_point(&snapshot),
12759            true,
12760            1,
12761            window,
12762            cx,
12763        );
12764        assert_eq!(
12765            editor.selections.ranges(cx),
12766            [
12767                Point::new(1, 3)..Point::new(1, 3),
12768                Point::new(2, 1)..Point::new(2, 1),
12769            ]
12770        );
12771        editor
12772    });
12773
12774    // Refreshing selections is a no-op when excerpts haven't changed.
12775    _ = editor.update(cx, |editor, window, cx| {
12776        editor.change_selections(None, window, cx, |s| s.refresh());
12777        assert_eq!(
12778            editor.selections.ranges(cx),
12779            [
12780                Point::new(1, 3)..Point::new(1, 3),
12781                Point::new(2, 1)..Point::new(2, 1),
12782            ]
12783        );
12784    });
12785
12786    multibuffer.update(cx, |multibuffer, cx| {
12787        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12788    });
12789    _ = editor.update(cx, |editor, window, cx| {
12790        // Removing an excerpt causes the first selection to become degenerate.
12791        assert_eq!(
12792            editor.selections.ranges(cx),
12793            [
12794                Point::new(0, 0)..Point::new(0, 0),
12795                Point::new(0, 1)..Point::new(0, 1)
12796            ]
12797        );
12798
12799        // Refreshing selections will relocate the first selection to the original buffer
12800        // location.
12801        editor.change_selections(None, window, cx, |s| s.refresh());
12802        assert_eq!(
12803            editor.selections.ranges(cx),
12804            [
12805                Point::new(0, 1)..Point::new(0, 1),
12806                Point::new(0, 3)..Point::new(0, 3)
12807            ]
12808        );
12809        assert!(editor.selections.pending_anchor().is_some());
12810    });
12811}
12812
12813#[gpui::test]
12814fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12815    init_test(cx, |_| {});
12816
12817    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12818    let mut excerpt1_id = None;
12819    let multibuffer = cx.new(|cx| {
12820        let mut multibuffer = MultiBuffer::new(ReadWrite);
12821        excerpt1_id = multibuffer
12822            .push_excerpts(
12823                buffer.clone(),
12824                [
12825                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12826                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12827                ],
12828                cx,
12829            )
12830            .into_iter()
12831            .next();
12832        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12833        multibuffer
12834    });
12835
12836    let editor = cx.add_window(|window, cx| {
12837        let mut editor = build_editor(multibuffer.clone(), window, cx);
12838        let snapshot = editor.snapshot(window, cx);
12839        editor.begin_selection(
12840            Point::new(1, 3).to_display_point(&snapshot),
12841            false,
12842            1,
12843            window,
12844            cx,
12845        );
12846        assert_eq!(
12847            editor.selections.ranges(cx),
12848            [Point::new(1, 3)..Point::new(1, 3)]
12849        );
12850        editor
12851    });
12852
12853    multibuffer.update(cx, |multibuffer, cx| {
12854        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12855    });
12856    _ = editor.update(cx, |editor, window, cx| {
12857        assert_eq!(
12858            editor.selections.ranges(cx),
12859            [Point::new(0, 0)..Point::new(0, 0)]
12860        );
12861
12862        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12863        editor.change_selections(None, window, cx, |s| s.refresh());
12864        assert_eq!(
12865            editor.selections.ranges(cx),
12866            [Point::new(0, 3)..Point::new(0, 3)]
12867        );
12868        assert!(editor.selections.pending_anchor().is_some());
12869    });
12870}
12871
12872#[gpui::test]
12873async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12874    init_test(cx, |_| {});
12875
12876    let language = Arc::new(
12877        Language::new(
12878            LanguageConfig {
12879                brackets: BracketPairConfig {
12880                    pairs: vec![
12881                        BracketPair {
12882                            start: "{".to_string(),
12883                            end: "}".to_string(),
12884                            close: true,
12885                            surround: true,
12886                            newline: true,
12887                        },
12888                        BracketPair {
12889                            start: "/* ".to_string(),
12890                            end: " */".to_string(),
12891                            close: true,
12892                            surround: true,
12893                            newline: true,
12894                        },
12895                    ],
12896                    ..Default::default()
12897                },
12898                ..Default::default()
12899            },
12900            Some(tree_sitter_rust::LANGUAGE.into()),
12901        )
12902        .with_indents_query("")
12903        .unwrap(),
12904    );
12905
12906    let text = concat!(
12907        "{   }\n",     //
12908        "  x\n",       //
12909        "  /*   */\n", //
12910        "x\n",         //
12911        "{{} }\n",     //
12912    );
12913
12914    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12915    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12916    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12917    editor
12918        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12919        .await;
12920
12921    editor.update_in(cx, |editor, window, cx| {
12922        editor.change_selections(None, window, cx, |s| {
12923            s.select_display_ranges([
12924                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12925                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12926                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12927            ])
12928        });
12929        editor.newline(&Newline, window, cx);
12930
12931        assert_eq!(
12932            editor.buffer().read(cx).read(cx).text(),
12933            concat!(
12934                "{ \n",    // Suppress rustfmt
12935                "\n",      //
12936                "}\n",     //
12937                "  x\n",   //
12938                "  /* \n", //
12939                "  \n",    //
12940                "  */\n",  //
12941                "x\n",     //
12942                "{{} \n",  //
12943                "}\n",     //
12944            )
12945        );
12946    });
12947}
12948
12949#[gpui::test]
12950fn test_highlighted_ranges(cx: &mut TestAppContext) {
12951    init_test(cx, |_| {});
12952
12953    let editor = cx.add_window(|window, cx| {
12954        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12955        build_editor(buffer.clone(), window, cx)
12956    });
12957
12958    _ = editor.update(cx, |editor, window, cx| {
12959        struct Type1;
12960        struct Type2;
12961
12962        let buffer = editor.buffer.read(cx).snapshot(cx);
12963
12964        let anchor_range =
12965            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12966
12967        editor.highlight_background::<Type1>(
12968            &[
12969                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12970                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12971                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12972                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12973            ],
12974            |_| Hsla::red(),
12975            cx,
12976        );
12977        editor.highlight_background::<Type2>(
12978            &[
12979                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12980                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12981                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12982                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12983            ],
12984            |_| Hsla::green(),
12985            cx,
12986        );
12987
12988        let snapshot = editor.snapshot(window, cx);
12989        let mut highlighted_ranges = editor.background_highlights_in_range(
12990            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12991            &snapshot,
12992            cx.theme().colors(),
12993        );
12994        // Enforce a consistent ordering based on color without relying on the ordering of the
12995        // highlight's `TypeId` which is non-executor.
12996        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12997        assert_eq!(
12998            highlighted_ranges,
12999            &[
13000                (
13001                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
13002                    Hsla::red(),
13003                ),
13004                (
13005                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
13006                    Hsla::red(),
13007                ),
13008                (
13009                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
13010                    Hsla::green(),
13011                ),
13012                (
13013                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
13014                    Hsla::green(),
13015                ),
13016            ]
13017        );
13018        assert_eq!(
13019            editor.background_highlights_in_range(
13020                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
13021                &snapshot,
13022                cx.theme().colors(),
13023            ),
13024            &[(
13025                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
13026                Hsla::red(),
13027            )]
13028        );
13029    });
13030}
13031
13032#[gpui::test]
13033async fn test_following(cx: &mut TestAppContext) {
13034    init_test(cx, |_| {});
13035
13036    let fs = FakeFs::new(cx.executor());
13037    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13038
13039    let buffer = project.update(cx, |project, cx| {
13040        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
13041        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
13042    });
13043    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
13044    let follower = cx.update(|cx| {
13045        cx.open_window(
13046            WindowOptions {
13047                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
13048                    gpui::Point::new(px(0.), px(0.)),
13049                    gpui::Point::new(px(10.), px(80.)),
13050                ))),
13051                ..Default::default()
13052            },
13053            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
13054        )
13055        .unwrap()
13056    });
13057
13058    let is_still_following = Rc::new(RefCell::new(true));
13059    let follower_edit_event_count = Rc::new(RefCell::new(0));
13060    let pending_update = Rc::new(RefCell::new(None));
13061    let leader_entity = leader.root(cx).unwrap();
13062    let follower_entity = follower.root(cx).unwrap();
13063    _ = follower.update(cx, {
13064        let update = pending_update.clone();
13065        let is_still_following = is_still_following.clone();
13066        let follower_edit_event_count = follower_edit_event_count.clone();
13067        |_, window, cx| {
13068            cx.subscribe_in(
13069                &leader_entity,
13070                window,
13071                move |_, leader, event, window, cx| {
13072                    leader.read(cx).add_event_to_update_proto(
13073                        event,
13074                        &mut update.borrow_mut(),
13075                        window,
13076                        cx,
13077                    );
13078                },
13079            )
13080            .detach();
13081
13082            cx.subscribe_in(
13083                &follower_entity,
13084                window,
13085                move |_, _, event: &EditorEvent, _window, _cx| {
13086                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
13087                        *is_still_following.borrow_mut() = false;
13088                    }
13089
13090                    if let EditorEvent::BufferEdited = event {
13091                        *follower_edit_event_count.borrow_mut() += 1;
13092                    }
13093                },
13094            )
13095            .detach();
13096        }
13097    });
13098
13099    // Update the selections only
13100    _ = leader.update(cx, |leader, window, cx| {
13101        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13102    });
13103    follower
13104        .update(cx, |follower, window, cx| {
13105            follower.apply_update_proto(
13106                &project,
13107                pending_update.borrow_mut().take().unwrap(),
13108                window,
13109                cx,
13110            )
13111        })
13112        .unwrap()
13113        .await
13114        .unwrap();
13115    _ = follower.update(cx, |follower, _, cx| {
13116        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
13117    });
13118    assert!(*is_still_following.borrow());
13119    assert_eq!(*follower_edit_event_count.borrow(), 0);
13120
13121    // Update the scroll position only
13122    _ = leader.update(cx, |leader, window, cx| {
13123        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13124    });
13125    follower
13126        .update(cx, |follower, window, cx| {
13127            follower.apply_update_proto(
13128                &project,
13129                pending_update.borrow_mut().take().unwrap(),
13130                window,
13131                cx,
13132            )
13133        })
13134        .unwrap()
13135        .await
13136        .unwrap();
13137    assert_eq!(
13138        follower
13139            .update(cx, |follower, _, cx| follower.scroll_position(cx))
13140            .unwrap(),
13141        gpui::Point::new(1.5, 3.5)
13142    );
13143    assert!(*is_still_following.borrow());
13144    assert_eq!(*follower_edit_event_count.borrow(), 0);
13145
13146    // Update the selections and scroll position. The follower's scroll position is updated
13147    // via autoscroll, not via the leader's exact scroll position.
13148    _ = leader.update(cx, |leader, window, cx| {
13149        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
13150        leader.request_autoscroll(Autoscroll::newest(), cx);
13151        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13152    });
13153    follower
13154        .update(cx, |follower, window, cx| {
13155            follower.apply_update_proto(
13156                &project,
13157                pending_update.borrow_mut().take().unwrap(),
13158                window,
13159                cx,
13160            )
13161        })
13162        .unwrap()
13163        .await
13164        .unwrap();
13165    _ = follower.update(cx, |follower, _, cx| {
13166        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
13167        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
13168    });
13169    assert!(*is_still_following.borrow());
13170
13171    // Creating a pending selection that precedes another selection
13172    _ = leader.update(cx, |leader, window, cx| {
13173        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13174        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
13175    });
13176    follower
13177        .update(cx, |follower, window, cx| {
13178            follower.apply_update_proto(
13179                &project,
13180                pending_update.borrow_mut().take().unwrap(),
13181                window,
13182                cx,
13183            )
13184        })
13185        .unwrap()
13186        .await
13187        .unwrap();
13188    _ = follower.update(cx, |follower, _, cx| {
13189        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
13190    });
13191    assert!(*is_still_following.borrow());
13192
13193    // Extend the pending selection so that it surrounds another selection
13194    _ = leader.update(cx, |leader, window, cx| {
13195        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
13196    });
13197    follower
13198        .update(cx, |follower, window, cx| {
13199            follower.apply_update_proto(
13200                &project,
13201                pending_update.borrow_mut().take().unwrap(),
13202                window,
13203                cx,
13204            )
13205        })
13206        .unwrap()
13207        .await
13208        .unwrap();
13209    _ = follower.update(cx, |follower, _, cx| {
13210        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
13211    });
13212
13213    // Scrolling locally breaks the follow
13214    _ = follower.update(cx, |follower, window, cx| {
13215        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13216        follower.set_scroll_anchor(
13217            ScrollAnchor {
13218                anchor: top_anchor,
13219                offset: gpui::Point::new(0.0, 0.5),
13220            },
13221            window,
13222            cx,
13223        );
13224    });
13225    assert!(!(*is_still_following.borrow()));
13226}
13227
13228#[gpui::test]
13229async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13230    init_test(cx, |_| {});
13231
13232    let fs = FakeFs::new(cx.executor());
13233    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13234    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13235    let pane = workspace
13236        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13237        .unwrap();
13238
13239    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13240
13241    let leader = pane.update_in(cx, |_, window, cx| {
13242        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13243        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13244    });
13245
13246    // Start following the editor when it has no excerpts.
13247    let mut state_message =
13248        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13249    let workspace_entity = workspace.root(cx).unwrap();
13250    let follower_1 = cx
13251        .update_window(*workspace.deref(), |_, window, cx| {
13252            Editor::from_state_proto(
13253                workspace_entity,
13254                ViewId {
13255                    creator: CollaboratorId::PeerId(PeerId::default()),
13256                    id: 0,
13257                },
13258                &mut state_message,
13259                window,
13260                cx,
13261            )
13262        })
13263        .unwrap()
13264        .unwrap()
13265        .await
13266        .unwrap();
13267
13268    let update_message = Rc::new(RefCell::new(None));
13269    follower_1.update_in(cx, {
13270        let update = update_message.clone();
13271        |_, window, cx| {
13272            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13273                leader.read(cx).add_event_to_update_proto(
13274                    event,
13275                    &mut update.borrow_mut(),
13276                    window,
13277                    cx,
13278                );
13279            })
13280            .detach();
13281        }
13282    });
13283
13284    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13285        (
13286            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13287            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13288        )
13289    });
13290
13291    // Insert some excerpts.
13292    leader.update(cx, |leader, cx| {
13293        leader.buffer.update(cx, |multibuffer, cx| {
13294            multibuffer.set_excerpts_for_path(
13295                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13296                buffer_1.clone(),
13297                vec![
13298                    Point::row_range(0..3),
13299                    Point::row_range(1..6),
13300                    Point::row_range(12..15),
13301                ],
13302                0,
13303                cx,
13304            );
13305            multibuffer.set_excerpts_for_path(
13306                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13307                buffer_2.clone(),
13308                vec![Point::row_range(0..6), Point::row_range(8..12)],
13309                0,
13310                cx,
13311            );
13312        });
13313    });
13314
13315    // Apply the update of adding the excerpts.
13316    follower_1
13317        .update_in(cx, |follower, window, cx| {
13318            follower.apply_update_proto(
13319                &project,
13320                update_message.borrow().clone().unwrap(),
13321                window,
13322                cx,
13323            )
13324        })
13325        .await
13326        .unwrap();
13327    assert_eq!(
13328        follower_1.update(cx, |editor, cx| editor.text(cx)),
13329        leader.update(cx, |editor, cx| editor.text(cx))
13330    );
13331    update_message.borrow_mut().take();
13332
13333    // Start following separately after it already has excerpts.
13334    let mut state_message =
13335        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13336    let workspace_entity = workspace.root(cx).unwrap();
13337    let follower_2 = cx
13338        .update_window(*workspace.deref(), |_, window, cx| {
13339            Editor::from_state_proto(
13340                workspace_entity,
13341                ViewId {
13342                    creator: CollaboratorId::PeerId(PeerId::default()),
13343                    id: 0,
13344                },
13345                &mut state_message,
13346                window,
13347                cx,
13348            )
13349        })
13350        .unwrap()
13351        .unwrap()
13352        .await
13353        .unwrap();
13354    assert_eq!(
13355        follower_2.update(cx, |editor, cx| editor.text(cx)),
13356        leader.update(cx, |editor, cx| editor.text(cx))
13357    );
13358
13359    // Remove some excerpts.
13360    leader.update(cx, |leader, cx| {
13361        leader.buffer.update(cx, |multibuffer, cx| {
13362            let excerpt_ids = multibuffer.excerpt_ids();
13363            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13364            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13365        });
13366    });
13367
13368    // Apply the update of removing the excerpts.
13369    follower_1
13370        .update_in(cx, |follower, window, cx| {
13371            follower.apply_update_proto(
13372                &project,
13373                update_message.borrow().clone().unwrap(),
13374                window,
13375                cx,
13376            )
13377        })
13378        .await
13379        .unwrap();
13380    follower_2
13381        .update_in(cx, |follower, window, cx| {
13382            follower.apply_update_proto(
13383                &project,
13384                update_message.borrow().clone().unwrap(),
13385                window,
13386                cx,
13387            )
13388        })
13389        .await
13390        .unwrap();
13391    update_message.borrow_mut().take();
13392    assert_eq!(
13393        follower_1.update(cx, |editor, cx| editor.text(cx)),
13394        leader.update(cx, |editor, cx| editor.text(cx))
13395    );
13396}
13397
13398#[gpui::test]
13399async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13400    init_test(cx, |_| {});
13401
13402    let mut cx = EditorTestContext::new(cx).await;
13403    let lsp_store =
13404        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13405
13406    cx.set_state(indoc! {"
13407        ˇfn func(abc def: i32) -> u32 {
13408        }
13409    "});
13410
13411    cx.update(|_, cx| {
13412        lsp_store.update(cx, |lsp_store, cx| {
13413            lsp_store
13414                .update_diagnostics(
13415                    LanguageServerId(0),
13416                    lsp::PublishDiagnosticsParams {
13417                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13418                        version: None,
13419                        diagnostics: vec![
13420                            lsp::Diagnostic {
13421                                range: lsp::Range::new(
13422                                    lsp::Position::new(0, 11),
13423                                    lsp::Position::new(0, 12),
13424                                ),
13425                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13426                                ..Default::default()
13427                            },
13428                            lsp::Diagnostic {
13429                                range: lsp::Range::new(
13430                                    lsp::Position::new(0, 12),
13431                                    lsp::Position::new(0, 15),
13432                                ),
13433                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13434                                ..Default::default()
13435                            },
13436                            lsp::Diagnostic {
13437                                range: lsp::Range::new(
13438                                    lsp::Position::new(0, 25),
13439                                    lsp::Position::new(0, 28),
13440                                ),
13441                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13442                                ..Default::default()
13443                            },
13444                        ],
13445                    },
13446                    &[],
13447                    cx,
13448                )
13449                .unwrap()
13450        });
13451    });
13452
13453    executor.run_until_parked();
13454
13455    cx.update_editor(|editor, window, cx| {
13456        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13457    });
13458
13459    cx.assert_editor_state(indoc! {"
13460        fn func(abc def: i32) -> ˇu32 {
13461        }
13462    "});
13463
13464    cx.update_editor(|editor, window, cx| {
13465        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13466    });
13467
13468    cx.assert_editor_state(indoc! {"
13469        fn func(abc ˇdef: i32) -> u32 {
13470        }
13471    "});
13472
13473    cx.update_editor(|editor, window, cx| {
13474        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13475    });
13476
13477    cx.assert_editor_state(indoc! {"
13478        fn func(abcˇ def: i32) -> u32 {
13479        }
13480    "});
13481
13482    cx.update_editor(|editor, window, cx| {
13483        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13484    });
13485
13486    cx.assert_editor_state(indoc! {"
13487        fn func(abc def: i32) -> ˇu32 {
13488        }
13489    "});
13490}
13491
13492#[gpui::test]
13493async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13494    init_test(cx, |_| {});
13495
13496    let mut cx = EditorTestContext::new(cx).await;
13497
13498    let diff_base = r#"
13499        use some::mod;
13500
13501        const A: u32 = 42;
13502
13503        fn main() {
13504            println!("hello");
13505
13506            println!("world");
13507        }
13508        "#
13509    .unindent();
13510
13511    // Edits are modified, removed, modified, added
13512    cx.set_state(
13513        &r#"
13514        use some::modified;
13515
13516        ˇ
13517        fn main() {
13518            println!("hello there");
13519
13520            println!("around the");
13521            println!("world");
13522        }
13523        "#
13524        .unindent(),
13525    );
13526
13527    cx.set_head_text(&diff_base);
13528    executor.run_until_parked();
13529
13530    cx.update_editor(|editor, window, cx| {
13531        //Wrap around the bottom of the buffer
13532        for _ in 0..3 {
13533            editor.go_to_next_hunk(&GoToHunk, window, cx);
13534        }
13535    });
13536
13537    cx.assert_editor_state(
13538        &r#"
13539        ˇuse some::modified;
13540
13541
13542        fn main() {
13543            println!("hello there");
13544
13545            println!("around the");
13546            println!("world");
13547        }
13548        "#
13549        .unindent(),
13550    );
13551
13552    cx.update_editor(|editor, window, cx| {
13553        //Wrap around the top of the buffer
13554        for _ in 0..2 {
13555            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13556        }
13557    });
13558
13559    cx.assert_editor_state(
13560        &r#"
13561        use some::modified;
13562
13563
13564        fn main() {
13565        ˇ    println!("hello there");
13566
13567            println!("around the");
13568            println!("world");
13569        }
13570        "#
13571        .unindent(),
13572    );
13573
13574    cx.update_editor(|editor, window, cx| {
13575        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13576    });
13577
13578    cx.assert_editor_state(
13579        &r#"
13580        use some::modified;
13581
13582        ˇ
13583        fn main() {
13584            println!("hello there");
13585
13586            println!("around the");
13587            println!("world");
13588        }
13589        "#
13590        .unindent(),
13591    );
13592
13593    cx.update_editor(|editor, window, cx| {
13594        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13595    });
13596
13597    cx.assert_editor_state(
13598        &r#"
13599        ˇuse some::modified;
13600
13601
13602        fn main() {
13603            println!("hello there");
13604
13605            println!("around the");
13606            println!("world");
13607        }
13608        "#
13609        .unindent(),
13610    );
13611
13612    cx.update_editor(|editor, window, cx| {
13613        for _ in 0..2 {
13614            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13615        }
13616    });
13617
13618    cx.assert_editor_state(
13619        &r#"
13620        use some::modified;
13621
13622
13623        fn main() {
13624        ˇ    println!("hello there");
13625
13626            println!("around the");
13627            println!("world");
13628        }
13629        "#
13630        .unindent(),
13631    );
13632
13633    cx.update_editor(|editor, window, cx| {
13634        editor.fold(&Fold, window, cx);
13635    });
13636
13637    cx.update_editor(|editor, window, cx| {
13638        editor.go_to_next_hunk(&GoToHunk, window, cx);
13639    });
13640
13641    cx.assert_editor_state(
13642        &r#"
13643        ˇuse some::modified;
13644
13645
13646        fn main() {
13647            println!("hello there");
13648
13649            println!("around the");
13650            println!("world");
13651        }
13652        "#
13653        .unindent(),
13654    );
13655}
13656
13657#[test]
13658fn test_split_words() {
13659    fn split(text: &str) -> Vec<&str> {
13660        split_words(text).collect()
13661    }
13662
13663    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13664    assert_eq!(split("hello_world"), &["hello_", "world"]);
13665    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13666    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13667    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13668    assert_eq!(split("helloworld"), &["helloworld"]);
13669
13670    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13671}
13672
13673#[gpui::test]
13674async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13675    init_test(cx, |_| {});
13676
13677    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13678    let mut assert = |before, after| {
13679        let _state_context = cx.set_state(before);
13680        cx.run_until_parked();
13681        cx.update_editor(|editor, window, cx| {
13682            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13683        });
13684        cx.run_until_parked();
13685        cx.assert_editor_state(after);
13686    };
13687
13688    // Outside bracket jumps to outside of matching bracket
13689    assert("console.logˇ(var);", "console.log(var)ˇ;");
13690    assert("console.log(var)ˇ;", "console.logˇ(var);");
13691
13692    // Inside bracket jumps to inside of matching bracket
13693    assert("console.log(ˇvar);", "console.log(varˇ);");
13694    assert("console.log(varˇ);", "console.log(ˇvar);");
13695
13696    // When outside a bracket and inside, favor jumping to the inside bracket
13697    assert(
13698        "console.log('foo', [1, 2, 3]ˇ);",
13699        "console.log(ˇ'foo', [1, 2, 3]);",
13700    );
13701    assert(
13702        "console.log(ˇ'foo', [1, 2, 3]);",
13703        "console.log('foo', [1, 2, 3]ˇ);",
13704    );
13705
13706    // Bias forward if two options are equally likely
13707    assert(
13708        "let result = curried_fun()ˇ();",
13709        "let result = curried_fun()()ˇ;",
13710    );
13711
13712    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13713    assert(
13714        indoc! {"
13715            function test() {
13716                console.log('test')ˇ
13717            }"},
13718        indoc! {"
13719            function test() {
13720                console.logˇ('test')
13721            }"},
13722    );
13723}
13724
13725#[gpui::test]
13726async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13727    init_test(cx, |_| {});
13728
13729    let fs = FakeFs::new(cx.executor());
13730    fs.insert_tree(
13731        path!("/a"),
13732        json!({
13733            "main.rs": "fn main() { let a = 5; }",
13734            "other.rs": "// Test file",
13735        }),
13736    )
13737    .await;
13738    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13739
13740    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13741    language_registry.add(Arc::new(Language::new(
13742        LanguageConfig {
13743            name: "Rust".into(),
13744            matcher: LanguageMatcher {
13745                path_suffixes: vec!["rs".to_string()],
13746                ..Default::default()
13747            },
13748            brackets: BracketPairConfig {
13749                pairs: vec![BracketPair {
13750                    start: "{".to_string(),
13751                    end: "}".to_string(),
13752                    close: true,
13753                    surround: true,
13754                    newline: true,
13755                }],
13756                disabled_scopes_by_bracket_ix: Vec::new(),
13757            },
13758            ..Default::default()
13759        },
13760        Some(tree_sitter_rust::LANGUAGE.into()),
13761    )));
13762    let mut fake_servers = language_registry.register_fake_lsp(
13763        "Rust",
13764        FakeLspAdapter {
13765            capabilities: lsp::ServerCapabilities {
13766                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13767                    first_trigger_character: "{".to_string(),
13768                    more_trigger_character: None,
13769                }),
13770                ..Default::default()
13771            },
13772            ..Default::default()
13773        },
13774    );
13775
13776    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13777
13778    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13779
13780    let worktree_id = workspace
13781        .update(cx, |workspace, _, cx| {
13782            workspace.project().update(cx, |project, cx| {
13783                project.worktrees(cx).next().unwrap().read(cx).id()
13784            })
13785        })
13786        .unwrap();
13787
13788    let buffer = project
13789        .update(cx, |project, cx| {
13790            project.open_local_buffer(path!("/a/main.rs"), cx)
13791        })
13792        .await
13793        .unwrap();
13794    let editor_handle = workspace
13795        .update(cx, |workspace, window, cx| {
13796            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13797        })
13798        .unwrap()
13799        .await
13800        .unwrap()
13801        .downcast::<Editor>()
13802        .unwrap();
13803
13804    cx.executor().start_waiting();
13805    let fake_server = fake_servers.next().await.unwrap();
13806
13807    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13808        |params, _| async move {
13809            assert_eq!(
13810                params.text_document_position.text_document.uri,
13811                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13812            );
13813            assert_eq!(
13814                params.text_document_position.position,
13815                lsp::Position::new(0, 21),
13816            );
13817
13818            Ok(Some(vec![lsp::TextEdit {
13819                new_text: "]".to_string(),
13820                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13821            }]))
13822        },
13823    );
13824
13825    editor_handle.update_in(cx, |editor, window, cx| {
13826        window.focus(&editor.focus_handle(cx));
13827        editor.change_selections(None, window, cx, |s| {
13828            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13829        });
13830        editor.handle_input("{", window, cx);
13831    });
13832
13833    cx.executor().run_until_parked();
13834
13835    buffer.update(cx, |buffer, _| {
13836        assert_eq!(
13837            buffer.text(),
13838            "fn main() { let a = {5}; }",
13839            "No extra braces from on type formatting should appear in the buffer"
13840        )
13841    });
13842}
13843
13844#[gpui::test]
13845async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13846    init_test(cx, |_| {});
13847
13848    let fs = FakeFs::new(cx.executor());
13849    fs.insert_tree(
13850        path!("/a"),
13851        json!({
13852            "main.rs": "fn main() { let a = 5; }",
13853            "other.rs": "// Test file",
13854        }),
13855    )
13856    .await;
13857
13858    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13859
13860    let server_restarts = Arc::new(AtomicUsize::new(0));
13861    let closure_restarts = Arc::clone(&server_restarts);
13862    let language_server_name = "test language server";
13863    let language_name: LanguageName = "Rust".into();
13864
13865    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13866    language_registry.add(Arc::new(Language::new(
13867        LanguageConfig {
13868            name: language_name.clone(),
13869            matcher: LanguageMatcher {
13870                path_suffixes: vec!["rs".to_string()],
13871                ..Default::default()
13872            },
13873            ..Default::default()
13874        },
13875        Some(tree_sitter_rust::LANGUAGE.into()),
13876    )));
13877    let mut fake_servers = language_registry.register_fake_lsp(
13878        "Rust",
13879        FakeLspAdapter {
13880            name: language_server_name,
13881            initialization_options: Some(json!({
13882                "testOptionValue": true
13883            })),
13884            initializer: Some(Box::new(move |fake_server| {
13885                let task_restarts = Arc::clone(&closure_restarts);
13886                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13887                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13888                    futures::future::ready(Ok(()))
13889                });
13890            })),
13891            ..Default::default()
13892        },
13893    );
13894
13895    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13896    let _buffer = project
13897        .update(cx, |project, cx| {
13898            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13899        })
13900        .await
13901        .unwrap();
13902    let _fake_server = fake_servers.next().await.unwrap();
13903    update_test_language_settings(cx, |language_settings| {
13904        language_settings.languages.insert(
13905            language_name.clone(),
13906            LanguageSettingsContent {
13907                tab_size: NonZeroU32::new(8),
13908                ..Default::default()
13909            },
13910        );
13911    });
13912    cx.executor().run_until_parked();
13913    assert_eq!(
13914        server_restarts.load(atomic::Ordering::Acquire),
13915        0,
13916        "Should not restart LSP server on an unrelated change"
13917    );
13918
13919    update_test_project_settings(cx, |project_settings| {
13920        project_settings.lsp.insert(
13921            "Some other server name".into(),
13922            LspSettings {
13923                binary: None,
13924                settings: None,
13925                initialization_options: Some(json!({
13926                    "some other init value": false
13927                })),
13928                enable_lsp_tasks: false,
13929            },
13930        );
13931    });
13932    cx.executor().run_until_parked();
13933    assert_eq!(
13934        server_restarts.load(atomic::Ordering::Acquire),
13935        0,
13936        "Should not restart LSP server on an unrelated LSP settings change"
13937    );
13938
13939    update_test_project_settings(cx, |project_settings| {
13940        project_settings.lsp.insert(
13941            language_server_name.into(),
13942            LspSettings {
13943                binary: None,
13944                settings: None,
13945                initialization_options: Some(json!({
13946                    "anotherInitValue": false
13947                })),
13948                enable_lsp_tasks: false,
13949            },
13950        );
13951    });
13952    cx.executor().run_until_parked();
13953    assert_eq!(
13954        server_restarts.load(atomic::Ordering::Acquire),
13955        1,
13956        "Should restart LSP server on a related LSP settings change"
13957    );
13958
13959    update_test_project_settings(cx, |project_settings| {
13960        project_settings.lsp.insert(
13961            language_server_name.into(),
13962            LspSettings {
13963                binary: None,
13964                settings: None,
13965                initialization_options: Some(json!({
13966                    "anotherInitValue": false
13967                })),
13968                enable_lsp_tasks: false,
13969            },
13970        );
13971    });
13972    cx.executor().run_until_parked();
13973    assert_eq!(
13974        server_restarts.load(atomic::Ordering::Acquire),
13975        1,
13976        "Should not restart LSP server on a related LSP settings change that is the same"
13977    );
13978
13979    update_test_project_settings(cx, |project_settings| {
13980        project_settings.lsp.insert(
13981            language_server_name.into(),
13982            LspSettings {
13983                binary: None,
13984                settings: None,
13985                initialization_options: None,
13986                enable_lsp_tasks: false,
13987            },
13988        );
13989    });
13990    cx.executor().run_until_parked();
13991    assert_eq!(
13992        server_restarts.load(atomic::Ordering::Acquire),
13993        2,
13994        "Should restart LSP server on another related LSP settings change"
13995    );
13996}
13997
13998#[gpui::test]
13999async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
14000    init_test(cx, |_| {});
14001
14002    let mut cx = EditorLspTestContext::new_rust(
14003        lsp::ServerCapabilities {
14004            completion_provider: Some(lsp::CompletionOptions {
14005                trigger_characters: Some(vec![".".to_string()]),
14006                resolve_provider: Some(true),
14007                ..Default::default()
14008            }),
14009            ..Default::default()
14010        },
14011        cx,
14012    )
14013    .await;
14014
14015    cx.set_state("fn main() { let a = 2ˇ; }");
14016    cx.simulate_keystroke(".");
14017    let completion_item = lsp::CompletionItem {
14018        label: "some".into(),
14019        kind: Some(lsp::CompletionItemKind::SNIPPET),
14020        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
14021        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
14022            kind: lsp::MarkupKind::Markdown,
14023            value: "```rust\nSome(2)\n```".to_string(),
14024        })),
14025        deprecated: Some(false),
14026        sort_text: Some("fffffff2".to_string()),
14027        filter_text: Some("some".to_string()),
14028        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
14029        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14030            range: lsp::Range {
14031                start: lsp::Position {
14032                    line: 0,
14033                    character: 22,
14034                },
14035                end: lsp::Position {
14036                    line: 0,
14037                    character: 22,
14038                },
14039            },
14040            new_text: "Some(2)".to_string(),
14041        })),
14042        additional_text_edits: Some(vec![lsp::TextEdit {
14043            range: lsp::Range {
14044                start: lsp::Position {
14045                    line: 0,
14046                    character: 20,
14047                },
14048                end: lsp::Position {
14049                    line: 0,
14050                    character: 22,
14051                },
14052            },
14053            new_text: "".to_string(),
14054        }]),
14055        ..Default::default()
14056    };
14057
14058    let closure_completion_item = completion_item.clone();
14059    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14060        let task_completion_item = closure_completion_item.clone();
14061        async move {
14062            Ok(Some(lsp::CompletionResponse::Array(vec![
14063                task_completion_item,
14064            ])))
14065        }
14066    });
14067
14068    request.next().await;
14069
14070    cx.condition(|editor, _| editor.context_menu_visible())
14071        .await;
14072    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
14073        editor
14074            .confirm_completion(&ConfirmCompletion::default(), window, cx)
14075            .unwrap()
14076    });
14077    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
14078
14079    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
14080        let task_completion_item = completion_item.clone();
14081        async move { Ok(task_completion_item) }
14082    })
14083    .next()
14084    .await
14085    .unwrap();
14086    apply_additional_edits.await.unwrap();
14087    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
14088}
14089
14090#[gpui::test]
14091async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
14092    init_test(cx, |_| {});
14093
14094    let mut cx = EditorLspTestContext::new_rust(
14095        lsp::ServerCapabilities {
14096            completion_provider: Some(lsp::CompletionOptions {
14097                trigger_characters: Some(vec![".".to_string()]),
14098                resolve_provider: Some(true),
14099                ..Default::default()
14100            }),
14101            ..Default::default()
14102        },
14103        cx,
14104    )
14105    .await;
14106
14107    cx.set_state("fn main() { let a = 2ˇ; }");
14108    cx.simulate_keystroke(".");
14109
14110    let item1 = lsp::CompletionItem {
14111        label: "method id()".to_string(),
14112        filter_text: Some("id".to_string()),
14113        detail: None,
14114        documentation: None,
14115        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14116            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14117            new_text: ".id".to_string(),
14118        })),
14119        ..lsp::CompletionItem::default()
14120    };
14121
14122    let item2 = lsp::CompletionItem {
14123        label: "other".to_string(),
14124        filter_text: Some("other".to_string()),
14125        detail: None,
14126        documentation: None,
14127        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14128            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14129            new_text: ".other".to_string(),
14130        })),
14131        ..lsp::CompletionItem::default()
14132    };
14133
14134    let item1 = item1.clone();
14135    cx.set_request_handler::<lsp::request::Completion, _, _>({
14136        let item1 = item1.clone();
14137        move |_, _, _| {
14138            let item1 = item1.clone();
14139            let item2 = item2.clone();
14140            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
14141        }
14142    })
14143    .next()
14144    .await;
14145
14146    cx.condition(|editor, _| editor.context_menu_visible())
14147        .await;
14148    cx.update_editor(|editor, _, _| {
14149        let context_menu = editor.context_menu.borrow_mut();
14150        let context_menu = context_menu
14151            .as_ref()
14152            .expect("Should have the context menu deployed");
14153        match context_menu {
14154            CodeContextMenu::Completions(completions_menu) => {
14155                let completions = completions_menu.completions.borrow_mut();
14156                assert_eq!(
14157                    completions
14158                        .iter()
14159                        .map(|completion| &completion.label.text)
14160                        .collect::<Vec<_>>(),
14161                    vec!["method id()", "other"]
14162                )
14163            }
14164            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14165        }
14166    });
14167
14168    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
14169        let item1 = item1.clone();
14170        move |_, item_to_resolve, _| {
14171            let item1 = item1.clone();
14172            async move {
14173                if item1 == item_to_resolve {
14174                    Ok(lsp::CompletionItem {
14175                        label: "method id()".to_string(),
14176                        filter_text: Some("id".to_string()),
14177                        detail: Some("Now resolved!".to_string()),
14178                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
14179                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14180                            range: lsp::Range::new(
14181                                lsp::Position::new(0, 22),
14182                                lsp::Position::new(0, 22),
14183                            ),
14184                            new_text: ".id".to_string(),
14185                        })),
14186                        ..lsp::CompletionItem::default()
14187                    })
14188                } else {
14189                    Ok(item_to_resolve)
14190                }
14191            }
14192        }
14193    })
14194    .next()
14195    .await
14196    .unwrap();
14197    cx.run_until_parked();
14198
14199    cx.update_editor(|editor, window, cx| {
14200        editor.context_menu_next(&Default::default(), window, cx);
14201    });
14202
14203    cx.update_editor(|editor, _, _| {
14204        let context_menu = editor.context_menu.borrow_mut();
14205        let context_menu = context_menu
14206            .as_ref()
14207            .expect("Should have the context menu deployed");
14208        match context_menu {
14209            CodeContextMenu::Completions(completions_menu) => {
14210                let completions = completions_menu.completions.borrow_mut();
14211                assert_eq!(
14212                    completions
14213                        .iter()
14214                        .map(|completion| &completion.label.text)
14215                        .collect::<Vec<_>>(),
14216                    vec!["method id() Now resolved!", "other"],
14217                    "Should update first completion label, but not second as the filter text did not match."
14218                );
14219            }
14220            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14221        }
14222    });
14223}
14224
14225#[gpui::test]
14226async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14227    init_test(cx, |_| {});
14228    let mut cx = EditorLspTestContext::new_rust(
14229        lsp::ServerCapabilities {
14230            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14231            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14232            completion_provider: Some(lsp::CompletionOptions {
14233                resolve_provider: Some(true),
14234                ..Default::default()
14235            }),
14236            ..Default::default()
14237        },
14238        cx,
14239    )
14240    .await;
14241    cx.set_state(indoc! {"
14242        struct TestStruct {
14243            field: i32
14244        }
14245
14246        fn mainˇ() {
14247            let unused_var = 42;
14248            let test_struct = TestStruct { field: 42 };
14249        }
14250    "});
14251    let symbol_range = cx.lsp_range(indoc! {"
14252        struct TestStruct {
14253            field: i32
14254        }
14255
14256        «fn main»() {
14257            let unused_var = 42;
14258            let test_struct = TestStruct { field: 42 };
14259        }
14260    "});
14261    let mut hover_requests =
14262        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14263            Ok(Some(lsp::Hover {
14264                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14265                    kind: lsp::MarkupKind::Markdown,
14266                    value: "Function documentation".to_string(),
14267                }),
14268                range: Some(symbol_range),
14269            }))
14270        });
14271
14272    // Case 1: Test that code action menu hide hover popover
14273    cx.dispatch_action(Hover);
14274    hover_requests.next().await;
14275    cx.condition(|editor, _| editor.hover_state.visible()).await;
14276    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14277        move |_, _, _| async move {
14278            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14279                lsp::CodeAction {
14280                    title: "Remove unused variable".to_string(),
14281                    kind: Some(CodeActionKind::QUICKFIX),
14282                    edit: Some(lsp::WorkspaceEdit {
14283                        changes: Some(
14284                            [(
14285                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14286                                vec![lsp::TextEdit {
14287                                    range: lsp::Range::new(
14288                                        lsp::Position::new(5, 4),
14289                                        lsp::Position::new(5, 27),
14290                                    ),
14291                                    new_text: "".to_string(),
14292                                }],
14293                            )]
14294                            .into_iter()
14295                            .collect(),
14296                        ),
14297                        ..Default::default()
14298                    }),
14299                    ..Default::default()
14300                },
14301            )]))
14302        },
14303    );
14304    cx.update_editor(|editor, window, cx| {
14305        editor.toggle_code_actions(
14306            &ToggleCodeActions {
14307                deployed_from: None,
14308                quick_launch: false,
14309            },
14310            window,
14311            cx,
14312        );
14313    });
14314    code_action_requests.next().await;
14315    cx.run_until_parked();
14316    cx.condition(|editor, _| editor.context_menu_visible())
14317        .await;
14318    cx.update_editor(|editor, _, _| {
14319        assert!(
14320            !editor.hover_state.visible(),
14321            "Hover popover should be hidden when code action menu is shown"
14322        );
14323        // Hide code actions
14324        editor.context_menu.take();
14325    });
14326
14327    // Case 2: Test that code completions hide hover popover
14328    cx.dispatch_action(Hover);
14329    hover_requests.next().await;
14330    cx.condition(|editor, _| editor.hover_state.visible()).await;
14331    let counter = Arc::new(AtomicUsize::new(0));
14332    let mut completion_requests =
14333        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14334            let counter = counter.clone();
14335            async move {
14336                counter.fetch_add(1, atomic::Ordering::Release);
14337                Ok(Some(lsp::CompletionResponse::Array(vec![
14338                    lsp::CompletionItem {
14339                        label: "main".into(),
14340                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14341                        detail: Some("() -> ()".to_string()),
14342                        ..Default::default()
14343                    },
14344                    lsp::CompletionItem {
14345                        label: "TestStruct".into(),
14346                        kind: Some(lsp::CompletionItemKind::STRUCT),
14347                        detail: Some("struct TestStruct".to_string()),
14348                        ..Default::default()
14349                    },
14350                ])))
14351            }
14352        });
14353    cx.update_editor(|editor, window, cx| {
14354        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14355    });
14356    completion_requests.next().await;
14357    cx.condition(|editor, _| editor.context_menu_visible())
14358        .await;
14359    cx.update_editor(|editor, _, _| {
14360        assert!(
14361            !editor.hover_state.visible(),
14362            "Hover popover should be hidden when completion menu is shown"
14363        );
14364    });
14365}
14366
14367#[gpui::test]
14368async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14369    init_test(cx, |_| {});
14370
14371    let mut cx = EditorLspTestContext::new_rust(
14372        lsp::ServerCapabilities {
14373            completion_provider: Some(lsp::CompletionOptions {
14374                trigger_characters: Some(vec![".".to_string()]),
14375                resolve_provider: Some(true),
14376                ..Default::default()
14377            }),
14378            ..Default::default()
14379        },
14380        cx,
14381    )
14382    .await;
14383
14384    cx.set_state("fn main() { let a = 2ˇ; }");
14385    cx.simulate_keystroke(".");
14386
14387    let unresolved_item_1 = lsp::CompletionItem {
14388        label: "id".to_string(),
14389        filter_text: Some("id".to_string()),
14390        detail: None,
14391        documentation: None,
14392        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14393            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14394            new_text: ".id".to_string(),
14395        })),
14396        ..lsp::CompletionItem::default()
14397    };
14398    let resolved_item_1 = lsp::CompletionItem {
14399        additional_text_edits: Some(vec![lsp::TextEdit {
14400            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14401            new_text: "!!".to_string(),
14402        }]),
14403        ..unresolved_item_1.clone()
14404    };
14405    let unresolved_item_2 = lsp::CompletionItem {
14406        label: "other".to_string(),
14407        filter_text: Some("other".to_string()),
14408        detail: None,
14409        documentation: None,
14410        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14411            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14412            new_text: ".other".to_string(),
14413        })),
14414        ..lsp::CompletionItem::default()
14415    };
14416    let resolved_item_2 = lsp::CompletionItem {
14417        additional_text_edits: Some(vec![lsp::TextEdit {
14418            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14419            new_text: "??".to_string(),
14420        }]),
14421        ..unresolved_item_2.clone()
14422    };
14423
14424    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14425    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14426    cx.lsp
14427        .server
14428        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14429            let unresolved_item_1 = unresolved_item_1.clone();
14430            let resolved_item_1 = resolved_item_1.clone();
14431            let unresolved_item_2 = unresolved_item_2.clone();
14432            let resolved_item_2 = resolved_item_2.clone();
14433            let resolve_requests_1 = resolve_requests_1.clone();
14434            let resolve_requests_2 = resolve_requests_2.clone();
14435            move |unresolved_request, _| {
14436                let unresolved_item_1 = unresolved_item_1.clone();
14437                let resolved_item_1 = resolved_item_1.clone();
14438                let unresolved_item_2 = unresolved_item_2.clone();
14439                let resolved_item_2 = resolved_item_2.clone();
14440                let resolve_requests_1 = resolve_requests_1.clone();
14441                let resolve_requests_2 = resolve_requests_2.clone();
14442                async move {
14443                    if unresolved_request == unresolved_item_1 {
14444                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14445                        Ok(resolved_item_1.clone())
14446                    } else if unresolved_request == unresolved_item_2 {
14447                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14448                        Ok(resolved_item_2.clone())
14449                    } else {
14450                        panic!("Unexpected completion item {unresolved_request:?}")
14451                    }
14452                }
14453            }
14454        })
14455        .detach();
14456
14457    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14458        let unresolved_item_1 = unresolved_item_1.clone();
14459        let unresolved_item_2 = unresolved_item_2.clone();
14460        async move {
14461            Ok(Some(lsp::CompletionResponse::Array(vec![
14462                unresolved_item_1,
14463                unresolved_item_2,
14464            ])))
14465        }
14466    })
14467    .next()
14468    .await;
14469
14470    cx.condition(|editor, _| editor.context_menu_visible())
14471        .await;
14472    cx.update_editor(|editor, _, _| {
14473        let context_menu = editor.context_menu.borrow_mut();
14474        let context_menu = context_menu
14475            .as_ref()
14476            .expect("Should have the context menu deployed");
14477        match context_menu {
14478            CodeContextMenu::Completions(completions_menu) => {
14479                let completions = completions_menu.completions.borrow_mut();
14480                assert_eq!(
14481                    completions
14482                        .iter()
14483                        .map(|completion| &completion.label.text)
14484                        .collect::<Vec<_>>(),
14485                    vec!["id", "other"]
14486                )
14487            }
14488            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14489        }
14490    });
14491    cx.run_until_parked();
14492
14493    cx.update_editor(|editor, window, cx| {
14494        editor.context_menu_next(&ContextMenuNext, window, cx);
14495    });
14496    cx.run_until_parked();
14497    cx.update_editor(|editor, window, cx| {
14498        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14499    });
14500    cx.run_until_parked();
14501    cx.update_editor(|editor, window, cx| {
14502        editor.context_menu_next(&ContextMenuNext, window, cx);
14503    });
14504    cx.run_until_parked();
14505    cx.update_editor(|editor, window, cx| {
14506        editor
14507            .compose_completion(&ComposeCompletion::default(), window, cx)
14508            .expect("No task returned")
14509    })
14510    .await
14511    .expect("Completion failed");
14512    cx.run_until_parked();
14513
14514    cx.update_editor(|editor, _, cx| {
14515        assert_eq!(
14516            resolve_requests_1.load(atomic::Ordering::Acquire),
14517            1,
14518            "Should always resolve once despite multiple selections"
14519        );
14520        assert_eq!(
14521            resolve_requests_2.load(atomic::Ordering::Acquire),
14522            1,
14523            "Should always resolve once after multiple selections and applying the completion"
14524        );
14525        assert_eq!(
14526            editor.text(cx),
14527            "fn main() { let a = ??.other; }",
14528            "Should use resolved data when applying the completion"
14529        );
14530    });
14531}
14532
14533#[gpui::test]
14534async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14535    init_test(cx, |_| {});
14536
14537    let item_0 = lsp::CompletionItem {
14538        label: "abs".into(),
14539        insert_text: Some("abs".into()),
14540        data: Some(json!({ "very": "special"})),
14541        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14542        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14543            lsp::InsertReplaceEdit {
14544                new_text: "abs".to_string(),
14545                insert: lsp::Range::default(),
14546                replace: lsp::Range::default(),
14547            },
14548        )),
14549        ..lsp::CompletionItem::default()
14550    };
14551    let items = iter::once(item_0.clone())
14552        .chain((11..51).map(|i| lsp::CompletionItem {
14553            label: format!("item_{}", i),
14554            insert_text: Some(format!("item_{}", i)),
14555            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14556            ..lsp::CompletionItem::default()
14557        }))
14558        .collect::<Vec<_>>();
14559
14560    let default_commit_characters = vec!["?".to_string()];
14561    let default_data = json!({ "default": "data"});
14562    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14563    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14564    let default_edit_range = lsp::Range {
14565        start: lsp::Position {
14566            line: 0,
14567            character: 5,
14568        },
14569        end: lsp::Position {
14570            line: 0,
14571            character: 5,
14572        },
14573    };
14574
14575    let mut cx = EditorLspTestContext::new_rust(
14576        lsp::ServerCapabilities {
14577            completion_provider: Some(lsp::CompletionOptions {
14578                trigger_characters: Some(vec![".".to_string()]),
14579                resolve_provider: Some(true),
14580                ..Default::default()
14581            }),
14582            ..Default::default()
14583        },
14584        cx,
14585    )
14586    .await;
14587
14588    cx.set_state("fn main() { let a = 2ˇ; }");
14589    cx.simulate_keystroke(".");
14590
14591    let completion_data = default_data.clone();
14592    let completion_characters = default_commit_characters.clone();
14593    let completion_items = items.clone();
14594    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14595        let default_data = completion_data.clone();
14596        let default_commit_characters = completion_characters.clone();
14597        let items = completion_items.clone();
14598        async move {
14599            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14600                items,
14601                item_defaults: Some(lsp::CompletionListItemDefaults {
14602                    data: Some(default_data.clone()),
14603                    commit_characters: Some(default_commit_characters.clone()),
14604                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14605                        default_edit_range,
14606                    )),
14607                    insert_text_format: Some(default_insert_text_format),
14608                    insert_text_mode: Some(default_insert_text_mode),
14609                }),
14610                ..lsp::CompletionList::default()
14611            })))
14612        }
14613    })
14614    .next()
14615    .await;
14616
14617    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14618    cx.lsp
14619        .server
14620        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14621            let closure_resolved_items = resolved_items.clone();
14622            move |item_to_resolve, _| {
14623                let closure_resolved_items = closure_resolved_items.clone();
14624                async move {
14625                    closure_resolved_items.lock().push(item_to_resolve.clone());
14626                    Ok(item_to_resolve)
14627                }
14628            }
14629        })
14630        .detach();
14631
14632    cx.condition(|editor, _| editor.context_menu_visible())
14633        .await;
14634    cx.run_until_parked();
14635    cx.update_editor(|editor, _, _| {
14636        let menu = editor.context_menu.borrow_mut();
14637        match menu.as_ref().expect("should have the completions menu") {
14638            CodeContextMenu::Completions(completions_menu) => {
14639                assert_eq!(
14640                    completions_menu
14641                        .entries
14642                        .borrow()
14643                        .iter()
14644                        .map(|mat| mat.string.clone())
14645                        .collect::<Vec<String>>(),
14646                    items
14647                        .iter()
14648                        .map(|completion| completion.label.clone())
14649                        .collect::<Vec<String>>()
14650                );
14651            }
14652            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14653        }
14654    });
14655    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14656    // with 4 from the end.
14657    assert_eq!(
14658        *resolved_items.lock(),
14659        [&items[0..16], &items[items.len() - 4..items.len()]]
14660            .concat()
14661            .iter()
14662            .cloned()
14663            .map(|mut item| {
14664                if item.data.is_none() {
14665                    item.data = Some(default_data.clone());
14666                }
14667                item
14668            })
14669            .collect::<Vec<lsp::CompletionItem>>(),
14670        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14671    );
14672    resolved_items.lock().clear();
14673
14674    cx.update_editor(|editor, window, cx| {
14675        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14676    });
14677    cx.run_until_parked();
14678    // Completions that have already been resolved are skipped.
14679    assert_eq!(
14680        *resolved_items.lock(),
14681        items[items.len() - 16..items.len() - 4]
14682            .iter()
14683            .cloned()
14684            .map(|mut item| {
14685                if item.data.is_none() {
14686                    item.data = Some(default_data.clone());
14687                }
14688                item
14689            })
14690            .collect::<Vec<lsp::CompletionItem>>()
14691    );
14692    resolved_items.lock().clear();
14693}
14694
14695#[gpui::test]
14696async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14697    init_test(cx, |_| {});
14698
14699    let mut cx = EditorLspTestContext::new(
14700        Language::new(
14701            LanguageConfig {
14702                matcher: LanguageMatcher {
14703                    path_suffixes: vec!["jsx".into()],
14704                    ..Default::default()
14705                },
14706                overrides: [(
14707                    "element".into(),
14708                    LanguageConfigOverride {
14709                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14710                        ..Default::default()
14711                    },
14712                )]
14713                .into_iter()
14714                .collect(),
14715                ..Default::default()
14716            },
14717            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14718        )
14719        .with_override_query("(jsx_self_closing_element) @element")
14720        .unwrap(),
14721        lsp::ServerCapabilities {
14722            completion_provider: Some(lsp::CompletionOptions {
14723                trigger_characters: Some(vec![":".to_string()]),
14724                ..Default::default()
14725            }),
14726            ..Default::default()
14727        },
14728        cx,
14729    )
14730    .await;
14731
14732    cx.lsp
14733        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14734            Ok(Some(lsp::CompletionResponse::Array(vec![
14735                lsp::CompletionItem {
14736                    label: "bg-blue".into(),
14737                    ..Default::default()
14738                },
14739                lsp::CompletionItem {
14740                    label: "bg-red".into(),
14741                    ..Default::default()
14742                },
14743                lsp::CompletionItem {
14744                    label: "bg-yellow".into(),
14745                    ..Default::default()
14746                },
14747            ])))
14748        });
14749
14750    cx.set_state(r#"<p class="bgˇ" />"#);
14751
14752    // Trigger completion when typing a dash, because the dash is an extra
14753    // word character in the 'element' scope, which contains the cursor.
14754    cx.simulate_keystroke("-");
14755    cx.executor().run_until_parked();
14756    cx.update_editor(|editor, _, _| {
14757        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14758        {
14759            assert_eq!(
14760                completion_menu_entries(&menu),
14761                &["bg-red", "bg-blue", "bg-yellow"]
14762            );
14763        } else {
14764            panic!("expected completion menu to be open");
14765        }
14766    });
14767
14768    cx.simulate_keystroke("l");
14769    cx.executor().run_until_parked();
14770    cx.update_editor(|editor, _, _| {
14771        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14772        {
14773            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14774        } else {
14775            panic!("expected completion menu to be open");
14776        }
14777    });
14778
14779    // When filtering completions, consider the character after the '-' to
14780    // be the start of a subword.
14781    cx.set_state(r#"<p class="yelˇ" />"#);
14782    cx.simulate_keystroke("l");
14783    cx.executor().run_until_parked();
14784    cx.update_editor(|editor, _, _| {
14785        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14786        {
14787            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14788        } else {
14789            panic!("expected completion menu to be open");
14790        }
14791    });
14792}
14793
14794fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14795    let entries = menu.entries.borrow();
14796    entries.iter().map(|mat| mat.string.clone()).collect()
14797}
14798
14799#[gpui::test]
14800async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14801    init_test(cx, |settings| {
14802        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14803            FormatterList(vec![Formatter::Prettier].into()),
14804        ))
14805    });
14806
14807    let fs = FakeFs::new(cx.executor());
14808    fs.insert_file(path!("/file.ts"), Default::default()).await;
14809
14810    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14811    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14812
14813    language_registry.add(Arc::new(Language::new(
14814        LanguageConfig {
14815            name: "TypeScript".into(),
14816            matcher: LanguageMatcher {
14817                path_suffixes: vec!["ts".to_string()],
14818                ..Default::default()
14819            },
14820            ..Default::default()
14821        },
14822        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14823    )));
14824    update_test_language_settings(cx, |settings| {
14825        settings.defaults.prettier = Some(PrettierSettings {
14826            allowed: true,
14827            ..PrettierSettings::default()
14828        });
14829    });
14830
14831    let test_plugin = "test_plugin";
14832    let _ = language_registry.register_fake_lsp(
14833        "TypeScript",
14834        FakeLspAdapter {
14835            prettier_plugins: vec![test_plugin],
14836            ..Default::default()
14837        },
14838    );
14839
14840    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14841    let buffer = project
14842        .update(cx, |project, cx| {
14843            project.open_local_buffer(path!("/file.ts"), cx)
14844        })
14845        .await
14846        .unwrap();
14847
14848    let buffer_text = "one\ntwo\nthree\n";
14849    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14850    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14851    editor.update_in(cx, |editor, window, cx| {
14852        editor.set_text(buffer_text, window, cx)
14853    });
14854
14855    editor
14856        .update_in(cx, |editor, window, cx| {
14857            editor.perform_format(
14858                project.clone(),
14859                FormatTrigger::Manual,
14860                FormatTarget::Buffers,
14861                window,
14862                cx,
14863            )
14864        })
14865        .unwrap()
14866        .await;
14867    assert_eq!(
14868        editor.update(cx, |editor, cx| editor.text(cx)),
14869        buffer_text.to_string() + prettier_format_suffix,
14870        "Test prettier formatting was not applied to the original buffer text",
14871    );
14872
14873    update_test_language_settings(cx, |settings| {
14874        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14875    });
14876    let format = editor.update_in(cx, |editor, window, cx| {
14877        editor.perform_format(
14878            project.clone(),
14879            FormatTrigger::Manual,
14880            FormatTarget::Buffers,
14881            window,
14882            cx,
14883        )
14884    });
14885    format.await.unwrap();
14886    assert_eq!(
14887        editor.update(cx, |editor, cx| editor.text(cx)),
14888        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14889        "Autoformatting (via test prettier) was not applied to the original buffer text",
14890    );
14891}
14892
14893#[gpui::test]
14894async fn test_addition_reverts(cx: &mut TestAppContext) {
14895    init_test(cx, |_| {});
14896    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14897    let base_text = indoc! {r#"
14898        struct Row;
14899        struct Row1;
14900        struct Row2;
14901
14902        struct Row4;
14903        struct Row5;
14904        struct Row6;
14905
14906        struct Row8;
14907        struct Row9;
14908        struct Row10;"#};
14909
14910    // When addition hunks are not adjacent to carets, no hunk revert is performed
14911    assert_hunk_revert(
14912        indoc! {r#"struct Row;
14913                   struct Row1;
14914                   struct Row1.1;
14915                   struct Row1.2;
14916                   struct Row2;ˇ
14917
14918                   struct Row4;
14919                   struct Row5;
14920                   struct Row6;
14921
14922                   struct Row8;
14923                   ˇstruct Row9;
14924                   struct Row9.1;
14925                   struct Row9.2;
14926                   struct Row9.3;
14927                   struct Row10;"#},
14928        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14929        indoc! {r#"struct Row;
14930                   struct Row1;
14931                   struct Row1.1;
14932                   struct Row1.2;
14933                   struct Row2;ˇ
14934
14935                   struct Row4;
14936                   struct Row5;
14937                   struct Row6;
14938
14939                   struct Row8;
14940                   ˇstruct Row9;
14941                   struct Row9.1;
14942                   struct Row9.2;
14943                   struct Row9.3;
14944                   struct Row10;"#},
14945        base_text,
14946        &mut cx,
14947    );
14948    // Same for selections
14949    assert_hunk_revert(
14950        indoc! {r#"struct Row;
14951                   struct Row1;
14952                   struct Row2;
14953                   struct Row2.1;
14954                   struct Row2.2;
14955                   «ˇ
14956                   struct Row4;
14957                   struct» Row5;
14958                   «struct Row6;
14959                   ˇ»
14960                   struct Row9.1;
14961                   struct Row9.2;
14962                   struct Row9.3;
14963                   struct Row8;
14964                   struct Row9;
14965                   struct Row10;"#},
14966        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14967        indoc! {r#"struct Row;
14968                   struct Row1;
14969                   struct Row2;
14970                   struct Row2.1;
14971                   struct Row2.2;
14972                   «ˇ
14973                   struct Row4;
14974                   struct» Row5;
14975                   «struct Row6;
14976                   ˇ»
14977                   struct Row9.1;
14978                   struct Row9.2;
14979                   struct Row9.3;
14980                   struct Row8;
14981                   struct Row9;
14982                   struct Row10;"#},
14983        base_text,
14984        &mut cx,
14985    );
14986
14987    // When carets and selections intersect the addition hunks, those are reverted.
14988    // Adjacent carets got merged.
14989    assert_hunk_revert(
14990        indoc! {r#"struct Row;
14991                   ˇ// something on the top
14992                   struct Row1;
14993                   struct Row2;
14994                   struct Roˇw3.1;
14995                   struct Row2.2;
14996                   struct Row2.3;ˇ
14997
14998                   struct Row4;
14999                   struct ˇRow5.1;
15000                   struct Row5.2;
15001                   struct «Rowˇ»5.3;
15002                   struct Row5;
15003                   struct Row6;
15004                   ˇ
15005                   struct Row9.1;
15006                   struct «Rowˇ»9.2;
15007                   struct «ˇRow»9.3;
15008                   struct Row8;
15009                   struct Row9;
15010                   «ˇ// something on bottom»
15011                   struct Row10;"#},
15012        vec![
15013            DiffHunkStatusKind::Added,
15014            DiffHunkStatusKind::Added,
15015            DiffHunkStatusKind::Added,
15016            DiffHunkStatusKind::Added,
15017            DiffHunkStatusKind::Added,
15018        ],
15019        indoc! {r#"struct Row;
15020                   ˇstruct Row1;
15021                   struct Row2;
15022                   ˇ
15023                   struct Row4;
15024                   ˇstruct Row5;
15025                   struct Row6;
15026                   ˇ
15027                   ˇstruct Row8;
15028                   struct Row9;
15029                   ˇstruct Row10;"#},
15030        base_text,
15031        &mut cx,
15032    );
15033}
15034
15035#[gpui::test]
15036async fn test_modification_reverts(cx: &mut TestAppContext) {
15037    init_test(cx, |_| {});
15038    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15039    let base_text = indoc! {r#"
15040        struct Row;
15041        struct Row1;
15042        struct Row2;
15043
15044        struct Row4;
15045        struct Row5;
15046        struct Row6;
15047
15048        struct Row8;
15049        struct Row9;
15050        struct Row10;"#};
15051
15052    // Modification hunks behave the same as the addition ones.
15053    assert_hunk_revert(
15054        indoc! {r#"struct Row;
15055                   struct Row1;
15056                   struct Row33;
15057                   ˇ
15058                   struct Row4;
15059                   struct Row5;
15060                   struct Row6;
15061                   ˇ
15062                   struct Row99;
15063                   struct Row9;
15064                   struct Row10;"#},
15065        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15066        indoc! {r#"struct Row;
15067                   struct Row1;
15068                   struct Row33;
15069                   ˇ
15070                   struct Row4;
15071                   struct Row5;
15072                   struct Row6;
15073                   ˇ
15074                   struct Row99;
15075                   struct Row9;
15076                   struct Row10;"#},
15077        base_text,
15078        &mut cx,
15079    );
15080    assert_hunk_revert(
15081        indoc! {r#"struct Row;
15082                   struct Row1;
15083                   struct Row33;
15084                   «ˇ
15085                   struct Row4;
15086                   struct» Row5;
15087                   «struct Row6;
15088                   ˇ»
15089                   struct Row99;
15090                   struct Row9;
15091                   struct Row10;"#},
15092        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15093        indoc! {r#"struct Row;
15094                   struct Row1;
15095                   struct Row33;
15096                   «ˇ
15097                   struct Row4;
15098                   struct» Row5;
15099                   «struct Row6;
15100                   ˇ»
15101                   struct Row99;
15102                   struct Row9;
15103                   struct Row10;"#},
15104        base_text,
15105        &mut cx,
15106    );
15107
15108    assert_hunk_revert(
15109        indoc! {r#"ˇstruct Row1.1;
15110                   struct Row1;
15111                   «ˇstr»uct Row22;
15112
15113                   struct ˇRow44;
15114                   struct Row5;
15115                   struct «Rˇ»ow66;ˇ
15116
15117                   «struˇ»ct Row88;
15118                   struct Row9;
15119                   struct Row1011;ˇ"#},
15120        vec![
15121            DiffHunkStatusKind::Modified,
15122            DiffHunkStatusKind::Modified,
15123            DiffHunkStatusKind::Modified,
15124            DiffHunkStatusKind::Modified,
15125            DiffHunkStatusKind::Modified,
15126            DiffHunkStatusKind::Modified,
15127        ],
15128        indoc! {r#"struct Row;
15129                   ˇstruct Row1;
15130                   struct Row2;
15131                   ˇ
15132                   struct Row4;
15133                   ˇstruct Row5;
15134                   struct Row6;
15135                   ˇ
15136                   struct Row8;
15137                   ˇstruct Row9;
15138                   struct Row10;ˇ"#},
15139        base_text,
15140        &mut cx,
15141    );
15142}
15143
15144#[gpui::test]
15145async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
15146    init_test(cx, |_| {});
15147    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15148    let base_text = indoc! {r#"
15149        one
15150
15151        two
15152        three
15153        "#};
15154
15155    cx.set_head_text(base_text);
15156    cx.set_state("\nˇ\n");
15157    cx.executor().run_until_parked();
15158    cx.update_editor(|editor, _window, cx| {
15159        editor.expand_selected_diff_hunks(cx);
15160    });
15161    cx.executor().run_until_parked();
15162    cx.update_editor(|editor, window, cx| {
15163        editor.backspace(&Default::default(), window, cx);
15164    });
15165    cx.run_until_parked();
15166    cx.assert_state_with_diff(
15167        indoc! {r#"
15168
15169        - two
15170        - threeˇ
15171        +
15172        "#}
15173        .to_string(),
15174    );
15175}
15176
15177#[gpui::test]
15178async fn test_deletion_reverts(cx: &mut TestAppContext) {
15179    init_test(cx, |_| {});
15180    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15181    let base_text = indoc! {r#"struct Row;
15182struct Row1;
15183struct Row2;
15184
15185struct Row4;
15186struct Row5;
15187struct Row6;
15188
15189struct Row8;
15190struct Row9;
15191struct Row10;"#};
15192
15193    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
15194    assert_hunk_revert(
15195        indoc! {r#"struct Row;
15196                   struct Row2;
15197
15198                   ˇstruct Row4;
15199                   struct Row5;
15200                   struct Row6;
15201                   ˇ
15202                   struct Row8;
15203                   struct Row10;"#},
15204        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15205        indoc! {r#"struct Row;
15206                   struct Row2;
15207
15208                   ˇstruct Row4;
15209                   struct Row5;
15210                   struct Row6;
15211                   ˇ
15212                   struct Row8;
15213                   struct Row10;"#},
15214        base_text,
15215        &mut cx,
15216    );
15217    assert_hunk_revert(
15218        indoc! {r#"struct Row;
15219                   struct Row2;
15220
15221                   «ˇstruct Row4;
15222                   struct» Row5;
15223                   «struct Row6;
15224                   ˇ»
15225                   struct Row8;
15226                   struct Row10;"#},
15227        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15228        indoc! {r#"struct Row;
15229                   struct Row2;
15230
15231                   «ˇstruct Row4;
15232                   struct» Row5;
15233                   «struct Row6;
15234                   ˇ»
15235                   struct Row8;
15236                   struct Row10;"#},
15237        base_text,
15238        &mut cx,
15239    );
15240
15241    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15242    assert_hunk_revert(
15243        indoc! {r#"struct Row;
15244                   ˇstruct Row2;
15245
15246                   struct Row4;
15247                   struct Row5;
15248                   struct Row6;
15249
15250                   struct Row8;ˇ
15251                   struct Row10;"#},
15252        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15253        indoc! {r#"struct Row;
15254                   struct Row1;
15255                   ˇstruct Row2;
15256
15257                   struct Row4;
15258                   struct Row5;
15259                   struct Row6;
15260
15261                   struct Row8;ˇ
15262                   struct Row9;
15263                   struct Row10;"#},
15264        base_text,
15265        &mut cx,
15266    );
15267    assert_hunk_revert(
15268        indoc! {r#"struct Row;
15269                   struct Row2«ˇ;
15270                   struct Row4;
15271                   struct» Row5;
15272                   «struct Row6;
15273
15274                   struct Row8;ˇ»
15275                   struct Row10;"#},
15276        vec![
15277            DiffHunkStatusKind::Deleted,
15278            DiffHunkStatusKind::Deleted,
15279            DiffHunkStatusKind::Deleted,
15280        ],
15281        indoc! {r#"struct Row;
15282                   struct Row1;
15283                   struct Row2«ˇ;
15284
15285                   struct Row4;
15286                   struct» Row5;
15287                   «struct Row6;
15288
15289                   struct Row8;ˇ»
15290                   struct Row9;
15291                   struct Row10;"#},
15292        base_text,
15293        &mut cx,
15294    );
15295}
15296
15297#[gpui::test]
15298async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15299    init_test(cx, |_| {});
15300
15301    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15302    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15303    let base_text_3 =
15304        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15305
15306    let text_1 = edit_first_char_of_every_line(base_text_1);
15307    let text_2 = edit_first_char_of_every_line(base_text_2);
15308    let text_3 = edit_first_char_of_every_line(base_text_3);
15309
15310    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15311    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15312    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15313
15314    let multibuffer = cx.new(|cx| {
15315        let mut multibuffer = MultiBuffer::new(ReadWrite);
15316        multibuffer.push_excerpts(
15317            buffer_1.clone(),
15318            [
15319                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15320                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15321                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15322            ],
15323            cx,
15324        );
15325        multibuffer.push_excerpts(
15326            buffer_2.clone(),
15327            [
15328                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15329                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15330                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15331            ],
15332            cx,
15333        );
15334        multibuffer.push_excerpts(
15335            buffer_3.clone(),
15336            [
15337                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15338                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15339                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15340            ],
15341            cx,
15342        );
15343        multibuffer
15344    });
15345
15346    let fs = FakeFs::new(cx.executor());
15347    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15348    let (editor, cx) = cx
15349        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15350    editor.update_in(cx, |editor, _window, cx| {
15351        for (buffer, diff_base) in [
15352            (buffer_1.clone(), base_text_1),
15353            (buffer_2.clone(), base_text_2),
15354            (buffer_3.clone(), base_text_3),
15355        ] {
15356            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15357            editor
15358                .buffer
15359                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15360        }
15361    });
15362    cx.executor().run_until_parked();
15363
15364    editor.update_in(cx, |editor, window, cx| {
15365        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}");
15366        editor.select_all(&SelectAll, window, cx);
15367        editor.git_restore(&Default::default(), window, cx);
15368    });
15369    cx.executor().run_until_parked();
15370
15371    // When all ranges are selected, all buffer hunks are reverted.
15372    editor.update(cx, |editor, cx| {
15373        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");
15374    });
15375    buffer_1.update(cx, |buffer, _| {
15376        assert_eq!(buffer.text(), base_text_1);
15377    });
15378    buffer_2.update(cx, |buffer, _| {
15379        assert_eq!(buffer.text(), base_text_2);
15380    });
15381    buffer_3.update(cx, |buffer, _| {
15382        assert_eq!(buffer.text(), base_text_3);
15383    });
15384
15385    editor.update_in(cx, |editor, window, cx| {
15386        editor.undo(&Default::default(), window, cx);
15387    });
15388
15389    editor.update_in(cx, |editor, window, cx| {
15390        editor.change_selections(None, window, cx, |s| {
15391            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15392        });
15393        editor.git_restore(&Default::default(), window, cx);
15394    });
15395
15396    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15397    // but not affect buffer_2 and its related excerpts.
15398    editor.update(cx, |editor, cx| {
15399        assert_eq!(
15400            editor.text(cx),
15401            "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}"
15402        );
15403    });
15404    buffer_1.update(cx, |buffer, _| {
15405        assert_eq!(buffer.text(), base_text_1);
15406    });
15407    buffer_2.update(cx, |buffer, _| {
15408        assert_eq!(
15409            buffer.text(),
15410            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15411        );
15412    });
15413    buffer_3.update(cx, |buffer, _| {
15414        assert_eq!(
15415            buffer.text(),
15416            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15417        );
15418    });
15419
15420    fn edit_first_char_of_every_line(text: &str) -> String {
15421        text.split('\n')
15422            .map(|line| format!("X{}", &line[1..]))
15423            .collect::<Vec<_>>()
15424            .join("\n")
15425    }
15426}
15427
15428#[gpui::test]
15429async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15430    init_test(cx, |_| {});
15431
15432    let cols = 4;
15433    let rows = 10;
15434    let sample_text_1 = sample_text(rows, cols, 'a');
15435    assert_eq!(
15436        sample_text_1,
15437        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15438    );
15439    let sample_text_2 = sample_text(rows, cols, 'l');
15440    assert_eq!(
15441        sample_text_2,
15442        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15443    );
15444    let sample_text_3 = sample_text(rows, cols, 'v');
15445    assert_eq!(
15446        sample_text_3,
15447        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15448    );
15449
15450    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15451    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15452    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15453
15454    let multi_buffer = cx.new(|cx| {
15455        let mut multibuffer = MultiBuffer::new(ReadWrite);
15456        multibuffer.push_excerpts(
15457            buffer_1.clone(),
15458            [
15459                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15460                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15461                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15462            ],
15463            cx,
15464        );
15465        multibuffer.push_excerpts(
15466            buffer_2.clone(),
15467            [
15468                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15469                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15470                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15471            ],
15472            cx,
15473        );
15474        multibuffer.push_excerpts(
15475            buffer_3.clone(),
15476            [
15477                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15478                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15479                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15480            ],
15481            cx,
15482        );
15483        multibuffer
15484    });
15485
15486    let fs = FakeFs::new(cx.executor());
15487    fs.insert_tree(
15488        "/a",
15489        json!({
15490            "main.rs": sample_text_1,
15491            "other.rs": sample_text_2,
15492            "lib.rs": sample_text_3,
15493        }),
15494    )
15495    .await;
15496    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15497    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15498    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15499    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15500        Editor::new(
15501            EditorMode::full(),
15502            multi_buffer,
15503            Some(project.clone()),
15504            window,
15505            cx,
15506        )
15507    });
15508    let multibuffer_item_id = workspace
15509        .update(cx, |workspace, window, cx| {
15510            assert!(
15511                workspace.active_item(cx).is_none(),
15512                "active item should be None before the first item is added"
15513            );
15514            workspace.add_item_to_active_pane(
15515                Box::new(multi_buffer_editor.clone()),
15516                None,
15517                true,
15518                window,
15519                cx,
15520            );
15521            let active_item = workspace
15522                .active_item(cx)
15523                .expect("should have an active item after adding the multi buffer");
15524            assert!(
15525                !active_item.is_singleton(cx),
15526                "A multi buffer was expected to active after adding"
15527            );
15528            active_item.item_id()
15529        })
15530        .unwrap();
15531    cx.executor().run_until_parked();
15532
15533    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15534        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15535            s.select_ranges(Some(1..2))
15536        });
15537        editor.open_excerpts(&OpenExcerpts, window, cx);
15538    });
15539    cx.executor().run_until_parked();
15540    let first_item_id = workspace
15541        .update(cx, |workspace, window, cx| {
15542            let active_item = workspace
15543                .active_item(cx)
15544                .expect("should have an active item after navigating into the 1st buffer");
15545            let first_item_id = active_item.item_id();
15546            assert_ne!(
15547                first_item_id, multibuffer_item_id,
15548                "Should navigate into the 1st buffer and activate it"
15549            );
15550            assert!(
15551                active_item.is_singleton(cx),
15552                "New active item should be a singleton buffer"
15553            );
15554            assert_eq!(
15555                active_item
15556                    .act_as::<Editor>(cx)
15557                    .expect("should have navigated into an editor for the 1st buffer")
15558                    .read(cx)
15559                    .text(cx),
15560                sample_text_1
15561            );
15562
15563            workspace
15564                .go_back(workspace.active_pane().downgrade(), window, cx)
15565                .detach_and_log_err(cx);
15566
15567            first_item_id
15568        })
15569        .unwrap();
15570    cx.executor().run_until_parked();
15571    workspace
15572        .update(cx, |workspace, _, cx| {
15573            let active_item = workspace
15574                .active_item(cx)
15575                .expect("should have an active item after navigating back");
15576            assert_eq!(
15577                active_item.item_id(),
15578                multibuffer_item_id,
15579                "Should navigate back to the multi buffer"
15580            );
15581            assert!(!active_item.is_singleton(cx));
15582        })
15583        .unwrap();
15584
15585    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15586        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15587            s.select_ranges(Some(39..40))
15588        });
15589        editor.open_excerpts(&OpenExcerpts, window, cx);
15590    });
15591    cx.executor().run_until_parked();
15592    let second_item_id = workspace
15593        .update(cx, |workspace, window, cx| {
15594            let active_item = workspace
15595                .active_item(cx)
15596                .expect("should have an active item after navigating into the 2nd buffer");
15597            let second_item_id = active_item.item_id();
15598            assert_ne!(
15599                second_item_id, multibuffer_item_id,
15600                "Should navigate away from the multibuffer"
15601            );
15602            assert_ne!(
15603                second_item_id, first_item_id,
15604                "Should navigate into the 2nd buffer and activate it"
15605            );
15606            assert!(
15607                active_item.is_singleton(cx),
15608                "New active item should be a singleton buffer"
15609            );
15610            assert_eq!(
15611                active_item
15612                    .act_as::<Editor>(cx)
15613                    .expect("should have navigated into an editor")
15614                    .read(cx)
15615                    .text(cx),
15616                sample_text_2
15617            );
15618
15619            workspace
15620                .go_back(workspace.active_pane().downgrade(), window, cx)
15621                .detach_and_log_err(cx);
15622
15623            second_item_id
15624        })
15625        .unwrap();
15626    cx.executor().run_until_parked();
15627    workspace
15628        .update(cx, |workspace, _, cx| {
15629            let active_item = workspace
15630                .active_item(cx)
15631                .expect("should have an active item after navigating back from the 2nd buffer");
15632            assert_eq!(
15633                active_item.item_id(),
15634                multibuffer_item_id,
15635                "Should navigate back from the 2nd buffer to the multi buffer"
15636            );
15637            assert!(!active_item.is_singleton(cx));
15638        })
15639        .unwrap();
15640
15641    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15642        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15643            s.select_ranges(Some(70..70))
15644        });
15645        editor.open_excerpts(&OpenExcerpts, window, cx);
15646    });
15647    cx.executor().run_until_parked();
15648    workspace
15649        .update(cx, |workspace, window, cx| {
15650            let active_item = workspace
15651                .active_item(cx)
15652                .expect("should have an active item after navigating into the 3rd buffer");
15653            let third_item_id = active_item.item_id();
15654            assert_ne!(
15655                third_item_id, multibuffer_item_id,
15656                "Should navigate into the 3rd buffer and activate it"
15657            );
15658            assert_ne!(third_item_id, first_item_id);
15659            assert_ne!(third_item_id, second_item_id);
15660            assert!(
15661                active_item.is_singleton(cx),
15662                "New active item should be a singleton buffer"
15663            );
15664            assert_eq!(
15665                active_item
15666                    .act_as::<Editor>(cx)
15667                    .expect("should have navigated into an editor")
15668                    .read(cx)
15669                    .text(cx),
15670                sample_text_3
15671            );
15672
15673            workspace
15674                .go_back(workspace.active_pane().downgrade(), window, cx)
15675                .detach_and_log_err(cx);
15676        })
15677        .unwrap();
15678    cx.executor().run_until_parked();
15679    workspace
15680        .update(cx, |workspace, _, cx| {
15681            let active_item = workspace
15682                .active_item(cx)
15683                .expect("should have an active item after navigating back from the 3rd buffer");
15684            assert_eq!(
15685                active_item.item_id(),
15686                multibuffer_item_id,
15687                "Should navigate back from the 3rd buffer to the multi buffer"
15688            );
15689            assert!(!active_item.is_singleton(cx));
15690        })
15691        .unwrap();
15692}
15693
15694#[gpui::test]
15695async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15696    init_test(cx, |_| {});
15697
15698    let mut cx = EditorTestContext::new(cx).await;
15699
15700    let diff_base = r#"
15701        use some::mod;
15702
15703        const A: u32 = 42;
15704
15705        fn main() {
15706            println!("hello");
15707
15708            println!("world");
15709        }
15710        "#
15711    .unindent();
15712
15713    cx.set_state(
15714        &r#"
15715        use some::modified;
15716
15717        ˇ
15718        fn main() {
15719            println!("hello there");
15720
15721            println!("around the");
15722            println!("world");
15723        }
15724        "#
15725        .unindent(),
15726    );
15727
15728    cx.set_head_text(&diff_base);
15729    executor.run_until_parked();
15730
15731    cx.update_editor(|editor, window, cx| {
15732        editor.go_to_next_hunk(&GoToHunk, window, cx);
15733        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15734    });
15735    executor.run_until_parked();
15736    cx.assert_state_with_diff(
15737        r#"
15738          use some::modified;
15739
15740
15741          fn main() {
15742        -     println!("hello");
15743        + ˇ    println!("hello there");
15744
15745              println!("around the");
15746              println!("world");
15747          }
15748        "#
15749        .unindent(),
15750    );
15751
15752    cx.update_editor(|editor, window, cx| {
15753        for _ in 0..2 {
15754            editor.go_to_next_hunk(&GoToHunk, window, cx);
15755            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15756        }
15757    });
15758    executor.run_until_parked();
15759    cx.assert_state_with_diff(
15760        r#"
15761        - use some::mod;
15762        + ˇuse some::modified;
15763
15764
15765          fn main() {
15766        -     println!("hello");
15767        +     println!("hello there");
15768
15769        +     println!("around the");
15770              println!("world");
15771          }
15772        "#
15773        .unindent(),
15774    );
15775
15776    cx.update_editor(|editor, window, cx| {
15777        editor.go_to_next_hunk(&GoToHunk, window, cx);
15778        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15779    });
15780    executor.run_until_parked();
15781    cx.assert_state_with_diff(
15782        r#"
15783        - use some::mod;
15784        + use some::modified;
15785
15786        - const A: u32 = 42;
15787          ˇ
15788          fn main() {
15789        -     println!("hello");
15790        +     println!("hello there");
15791
15792        +     println!("around the");
15793              println!("world");
15794          }
15795        "#
15796        .unindent(),
15797    );
15798
15799    cx.update_editor(|editor, window, cx| {
15800        editor.cancel(&Cancel, window, cx);
15801    });
15802
15803    cx.assert_state_with_diff(
15804        r#"
15805          use some::modified;
15806
15807          ˇ
15808          fn main() {
15809              println!("hello there");
15810
15811              println!("around the");
15812              println!("world");
15813          }
15814        "#
15815        .unindent(),
15816    );
15817}
15818
15819#[gpui::test]
15820async fn test_diff_base_change_with_expanded_diff_hunks(
15821    executor: BackgroundExecutor,
15822    cx: &mut TestAppContext,
15823) {
15824    init_test(cx, |_| {});
15825
15826    let mut cx = EditorTestContext::new(cx).await;
15827
15828    let diff_base = r#"
15829        use some::mod1;
15830        use some::mod2;
15831
15832        const A: u32 = 42;
15833        const B: u32 = 42;
15834        const C: u32 = 42;
15835
15836        fn main() {
15837            println!("hello");
15838
15839            println!("world");
15840        }
15841        "#
15842    .unindent();
15843
15844    cx.set_state(
15845        &r#"
15846        use some::mod2;
15847
15848        const A: u32 = 42;
15849        const C: u32 = 42;
15850
15851        fn main(ˇ) {
15852            //println!("hello");
15853
15854            println!("world");
15855            //
15856            //
15857        }
15858        "#
15859        .unindent(),
15860    );
15861
15862    cx.set_head_text(&diff_base);
15863    executor.run_until_parked();
15864
15865    cx.update_editor(|editor, window, cx| {
15866        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15867    });
15868    executor.run_until_parked();
15869    cx.assert_state_with_diff(
15870        r#"
15871        - use some::mod1;
15872          use some::mod2;
15873
15874          const A: u32 = 42;
15875        - const B: u32 = 42;
15876          const C: u32 = 42;
15877
15878          fn main(ˇ) {
15879        -     println!("hello");
15880        +     //println!("hello");
15881
15882              println!("world");
15883        +     //
15884        +     //
15885          }
15886        "#
15887        .unindent(),
15888    );
15889
15890    cx.set_head_text("new diff base!");
15891    executor.run_until_parked();
15892    cx.assert_state_with_diff(
15893        r#"
15894        - new diff base!
15895        + use some::mod2;
15896        +
15897        + const A: u32 = 42;
15898        + const C: u32 = 42;
15899        +
15900        + fn main(ˇ) {
15901        +     //println!("hello");
15902        +
15903        +     println!("world");
15904        +     //
15905        +     //
15906        + }
15907        "#
15908        .unindent(),
15909    );
15910}
15911
15912#[gpui::test]
15913async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15914    init_test(cx, |_| {});
15915
15916    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15917    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15918    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15919    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15920    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15921    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15922
15923    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15924    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15925    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15926
15927    let multi_buffer = cx.new(|cx| {
15928        let mut multibuffer = MultiBuffer::new(ReadWrite);
15929        multibuffer.push_excerpts(
15930            buffer_1.clone(),
15931            [
15932                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15933                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15934                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15935            ],
15936            cx,
15937        );
15938        multibuffer.push_excerpts(
15939            buffer_2.clone(),
15940            [
15941                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15942                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15943                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15944            ],
15945            cx,
15946        );
15947        multibuffer.push_excerpts(
15948            buffer_3.clone(),
15949            [
15950                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15951                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15952                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15953            ],
15954            cx,
15955        );
15956        multibuffer
15957    });
15958
15959    let editor =
15960        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15961    editor
15962        .update(cx, |editor, _window, cx| {
15963            for (buffer, diff_base) in [
15964                (buffer_1.clone(), file_1_old),
15965                (buffer_2.clone(), file_2_old),
15966                (buffer_3.clone(), file_3_old),
15967            ] {
15968                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15969                editor
15970                    .buffer
15971                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15972            }
15973        })
15974        .unwrap();
15975
15976    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15977    cx.run_until_parked();
15978
15979    cx.assert_editor_state(
15980        &"
15981            ˇaaa
15982            ccc
15983            ddd
15984
15985            ggg
15986            hhh
15987
15988
15989            lll
15990            mmm
15991            NNN
15992
15993            qqq
15994            rrr
15995
15996            uuu
15997            111
15998            222
15999            333
16000
16001            666
16002            777
16003
16004            000
16005            !!!"
16006        .unindent(),
16007    );
16008
16009    cx.update_editor(|editor, window, cx| {
16010        editor.select_all(&SelectAll, window, cx);
16011        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
16012    });
16013    cx.executor().run_until_parked();
16014
16015    cx.assert_state_with_diff(
16016        "
16017            «aaa
16018          - bbb
16019            ccc
16020            ddd
16021
16022            ggg
16023            hhh
16024
16025
16026            lll
16027            mmm
16028          - nnn
16029          + NNN
16030
16031            qqq
16032            rrr
16033
16034            uuu
16035            111
16036            222
16037            333
16038
16039          + 666
16040            777
16041
16042            000
16043            !!!ˇ»"
16044            .unindent(),
16045    );
16046}
16047
16048#[gpui::test]
16049async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
16050    init_test(cx, |_| {});
16051
16052    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
16053    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
16054
16055    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
16056    let multi_buffer = cx.new(|cx| {
16057        let mut multibuffer = MultiBuffer::new(ReadWrite);
16058        multibuffer.push_excerpts(
16059            buffer.clone(),
16060            [
16061                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
16062                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
16063                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
16064            ],
16065            cx,
16066        );
16067        multibuffer
16068    });
16069
16070    let editor =
16071        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
16072    editor
16073        .update(cx, |editor, _window, cx| {
16074            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
16075            editor
16076                .buffer
16077                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
16078        })
16079        .unwrap();
16080
16081    let mut cx = EditorTestContext::for_editor(editor, cx).await;
16082    cx.run_until_parked();
16083
16084    cx.update_editor(|editor, window, cx| {
16085        editor.expand_all_diff_hunks(&Default::default(), window, cx)
16086    });
16087    cx.executor().run_until_parked();
16088
16089    // When the start of a hunk coincides with the start of its excerpt,
16090    // the hunk is expanded. When the start of a a hunk is earlier than
16091    // the start of its excerpt, the hunk is not expanded.
16092    cx.assert_state_with_diff(
16093        "
16094            ˇaaa
16095          - bbb
16096          + BBB
16097
16098          - ddd
16099          - eee
16100          + DDD
16101          + EEE
16102            fff
16103
16104            iii
16105        "
16106        .unindent(),
16107    );
16108}
16109
16110#[gpui::test]
16111async fn test_edits_around_expanded_insertion_hunks(
16112    executor: BackgroundExecutor,
16113    cx: &mut TestAppContext,
16114) {
16115    init_test(cx, |_| {});
16116
16117    let mut cx = EditorTestContext::new(cx).await;
16118
16119    let diff_base = r#"
16120        use some::mod1;
16121        use some::mod2;
16122
16123        const A: u32 = 42;
16124
16125        fn main() {
16126            println!("hello");
16127
16128            println!("world");
16129        }
16130        "#
16131    .unindent();
16132    executor.run_until_parked();
16133    cx.set_state(
16134        &r#"
16135        use some::mod1;
16136        use some::mod2;
16137
16138        const A: u32 = 42;
16139        const B: u32 = 42;
16140        const C: u32 = 42;
16141        ˇ
16142
16143        fn main() {
16144            println!("hello");
16145
16146            println!("world");
16147        }
16148        "#
16149        .unindent(),
16150    );
16151
16152    cx.set_head_text(&diff_base);
16153    executor.run_until_parked();
16154
16155    cx.update_editor(|editor, window, cx| {
16156        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16157    });
16158    executor.run_until_parked();
16159
16160    cx.assert_state_with_diff(
16161        r#"
16162        use some::mod1;
16163        use some::mod2;
16164
16165        const A: u32 = 42;
16166      + const B: u32 = 42;
16167      + const C: u32 = 42;
16168      + ˇ
16169
16170        fn main() {
16171            println!("hello");
16172
16173            println!("world");
16174        }
16175      "#
16176        .unindent(),
16177    );
16178
16179    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
16180    executor.run_until_parked();
16181
16182    cx.assert_state_with_diff(
16183        r#"
16184        use some::mod1;
16185        use some::mod2;
16186
16187        const A: u32 = 42;
16188      + const B: u32 = 42;
16189      + const C: u32 = 42;
16190      + const D: u32 = 42;
16191      + ˇ
16192
16193        fn main() {
16194            println!("hello");
16195
16196            println!("world");
16197        }
16198      "#
16199        .unindent(),
16200    );
16201
16202    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
16203    executor.run_until_parked();
16204
16205    cx.assert_state_with_diff(
16206        r#"
16207        use some::mod1;
16208        use some::mod2;
16209
16210        const A: u32 = 42;
16211      + const B: u32 = 42;
16212      + const C: u32 = 42;
16213      + const D: u32 = 42;
16214      + const E: u32 = 42;
16215      + ˇ
16216
16217        fn main() {
16218            println!("hello");
16219
16220            println!("world");
16221        }
16222      "#
16223        .unindent(),
16224    );
16225
16226    cx.update_editor(|editor, window, cx| {
16227        editor.delete_line(&DeleteLine, window, cx);
16228    });
16229    executor.run_until_parked();
16230
16231    cx.assert_state_with_diff(
16232        r#"
16233        use some::mod1;
16234        use some::mod2;
16235
16236        const A: u32 = 42;
16237      + const B: u32 = 42;
16238      + const C: u32 = 42;
16239      + const D: u32 = 42;
16240      + const E: u32 = 42;
16241        ˇ
16242        fn main() {
16243            println!("hello");
16244
16245            println!("world");
16246        }
16247      "#
16248        .unindent(),
16249    );
16250
16251    cx.update_editor(|editor, window, cx| {
16252        editor.move_up(&MoveUp, window, cx);
16253        editor.delete_line(&DeleteLine, window, cx);
16254        editor.move_up(&MoveUp, window, cx);
16255        editor.delete_line(&DeleteLine, window, cx);
16256        editor.move_up(&MoveUp, window, cx);
16257        editor.delete_line(&DeleteLine, window, cx);
16258    });
16259    executor.run_until_parked();
16260    cx.assert_state_with_diff(
16261        r#"
16262        use some::mod1;
16263        use some::mod2;
16264
16265        const A: u32 = 42;
16266      + const B: u32 = 42;
16267        ˇ
16268        fn main() {
16269            println!("hello");
16270
16271            println!("world");
16272        }
16273      "#
16274        .unindent(),
16275    );
16276
16277    cx.update_editor(|editor, window, cx| {
16278        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16279        editor.delete_line(&DeleteLine, window, cx);
16280    });
16281    executor.run_until_parked();
16282    cx.assert_state_with_diff(
16283        r#"
16284        ˇ
16285        fn main() {
16286            println!("hello");
16287
16288            println!("world");
16289        }
16290      "#
16291        .unindent(),
16292    );
16293}
16294
16295#[gpui::test]
16296async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16297    init_test(cx, |_| {});
16298
16299    let mut cx = EditorTestContext::new(cx).await;
16300    cx.set_head_text(indoc! { "
16301        one
16302        two
16303        three
16304        four
16305        five
16306        "
16307    });
16308    cx.set_state(indoc! { "
16309        one
16310        ˇthree
16311        five
16312    "});
16313    cx.run_until_parked();
16314    cx.update_editor(|editor, window, cx| {
16315        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16316    });
16317    cx.assert_state_with_diff(
16318        indoc! { "
16319        one
16320      - two
16321        ˇthree
16322      - four
16323        five
16324    "}
16325        .to_string(),
16326    );
16327    cx.update_editor(|editor, window, cx| {
16328        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16329    });
16330
16331    cx.assert_state_with_diff(
16332        indoc! { "
16333        one
16334        ˇthree
16335        five
16336    "}
16337        .to_string(),
16338    );
16339
16340    cx.set_state(indoc! { "
16341        one
16342        ˇTWO
16343        three
16344        four
16345        five
16346    "});
16347    cx.run_until_parked();
16348    cx.update_editor(|editor, window, cx| {
16349        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16350    });
16351
16352    cx.assert_state_with_diff(
16353        indoc! { "
16354            one
16355          - two
16356          + ˇTWO
16357            three
16358            four
16359            five
16360        "}
16361        .to_string(),
16362    );
16363    cx.update_editor(|editor, window, cx| {
16364        editor.move_up(&Default::default(), window, cx);
16365        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16366    });
16367    cx.assert_state_with_diff(
16368        indoc! { "
16369            one
16370            ˇTWO
16371            three
16372            four
16373            five
16374        "}
16375        .to_string(),
16376    );
16377}
16378
16379#[gpui::test]
16380async fn test_edits_around_expanded_deletion_hunks(
16381    executor: BackgroundExecutor,
16382    cx: &mut TestAppContext,
16383) {
16384    init_test(cx, |_| {});
16385
16386    let mut cx = EditorTestContext::new(cx).await;
16387
16388    let diff_base = r#"
16389        use some::mod1;
16390        use some::mod2;
16391
16392        const A: u32 = 42;
16393        const B: u32 = 42;
16394        const C: u32 = 42;
16395
16396
16397        fn main() {
16398            println!("hello");
16399
16400            println!("world");
16401        }
16402    "#
16403    .unindent();
16404    executor.run_until_parked();
16405    cx.set_state(
16406        &r#"
16407        use some::mod1;
16408        use some::mod2;
16409
16410        ˇconst B: u32 = 42;
16411        const C: u32 = 42;
16412
16413
16414        fn main() {
16415            println!("hello");
16416
16417            println!("world");
16418        }
16419        "#
16420        .unindent(),
16421    );
16422
16423    cx.set_head_text(&diff_base);
16424    executor.run_until_parked();
16425
16426    cx.update_editor(|editor, window, cx| {
16427        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16428    });
16429    executor.run_until_parked();
16430
16431    cx.assert_state_with_diff(
16432        r#"
16433        use some::mod1;
16434        use some::mod2;
16435
16436      - const A: u32 = 42;
16437        ˇconst B: u32 = 42;
16438        const C: u32 = 42;
16439
16440
16441        fn main() {
16442            println!("hello");
16443
16444            println!("world");
16445        }
16446      "#
16447        .unindent(),
16448    );
16449
16450    cx.update_editor(|editor, window, cx| {
16451        editor.delete_line(&DeleteLine, window, cx);
16452    });
16453    executor.run_until_parked();
16454    cx.assert_state_with_diff(
16455        r#"
16456        use some::mod1;
16457        use some::mod2;
16458
16459      - const A: u32 = 42;
16460      - const B: u32 = 42;
16461        ˇconst C: u32 = 42;
16462
16463
16464        fn main() {
16465            println!("hello");
16466
16467            println!("world");
16468        }
16469      "#
16470        .unindent(),
16471    );
16472
16473    cx.update_editor(|editor, window, cx| {
16474        editor.delete_line(&DeleteLine, window, cx);
16475    });
16476    executor.run_until_parked();
16477    cx.assert_state_with_diff(
16478        r#"
16479        use some::mod1;
16480        use some::mod2;
16481
16482      - const A: u32 = 42;
16483      - const B: u32 = 42;
16484      - const C: u32 = 42;
16485        ˇ
16486
16487        fn main() {
16488            println!("hello");
16489
16490            println!("world");
16491        }
16492      "#
16493        .unindent(),
16494    );
16495
16496    cx.update_editor(|editor, window, cx| {
16497        editor.handle_input("replacement", window, cx);
16498    });
16499    executor.run_until_parked();
16500    cx.assert_state_with_diff(
16501        r#"
16502        use some::mod1;
16503        use some::mod2;
16504
16505      - const A: u32 = 42;
16506      - const B: u32 = 42;
16507      - const C: u32 = 42;
16508      -
16509      + replacementˇ
16510
16511        fn main() {
16512            println!("hello");
16513
16514            println!("world");
16515        }
16516      "#
16517        .unindent(),
16518    );
16519}
16520
16521#[gpui::test]
16522async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16523    init_test(cx, |_| {});
16524
16525    let mut cx = EditorTestContext::new(cx).await;
16526
16527    let base_text = r#"
16528        one
16529        two
16530        three
16531        four
16532        five
16533    "#
16534    .unindent();
16535    executor.run_until_parked();
16536    cx.set_state(
16537        &r#"
16538        one
16539        two
16540        fˇour
16541        five
16542        "#
16543        .unindent(),
16544    );
16545
16546    cx.set_head_text(&base_text);
16547    executor.run_until_parked();
16548
16549    cx.update_editor(|editor, window, cx| {
16550        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16551    });
16552    executor.run_until_parked();
16553
16554    cx.assert_state_with_diff(
16555        r#"
16556          one
16557          two
16558        - three
16559          fˇour
16560          five
16561        "#
16562        .unindent(),
16563    );
16564
16565    cx.update_editor(|editor, window, cx| {
16566        editor.backspace(&Backspace, window, cx);
16567        editor.backspace(&Backspace, window, cx);
16568    });
16569    executor.run_until_parked();
16570    cx.assert_state_with_diff(
16571        r#"
16572          one
16573          two
16574        - threeˇ
16575        - four
16576        + our
16577          five
16578        "#
16579        .unindent(),
16580    );
16581}
16582
16583#[gpui::test]
16584async fn test_edit_after_expanded_modification_hunk(
16585    executor: BackgroundExecutor,
16586    cx: &mut TestAppContext,
16587) {
16588    init_test(cx, |_| {});
16589
16590    let mut cx = EditorTestContext::new(cx).await;
16591
16592    let diff_base = r#"
16593        use some::mod1;
16594        use some::mod2;
16595
16596        const A: u32 = 42;
16597        const B: u32 = 42;
16598        const C: u32 = 42;
16599        const D: u32 = 42;
16600
16601
16602        fn main() {
16603            println!("hello");
16604
16605            println!("world");
16606        }"#
16607    .unindent();
16608
16609    cx.set_state(
16610        &r#"
16611        use some::mod1;
16612        use some::mod2;
16613
16614        const A: u32 = 42;
16615        const B: u32 = 42;
16616        const C: u32 = 43ˇ
16617        const D: u32 = 42;
16618
16619
16620        fn main() {
16621            println!("hello");
16622
16623            println!("world");
16624        }"#
16625        .unindent(),
16626    );
16627
16628    cx.set_head_text(&diff_base);
16629    executor.run_until_parked();
16630    cx.update_editor(|editor, window, cx| {
16631        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16632    });
16633    executor.run_until_parked();
16634
16635    cx.assert_state_with_diff(
16636        r#"
16637        use some::mod1;
16638        use some::mod2;
16639
16640        const A: u32 = 42;
16641        const B: u32 = 42;
16642      - const C: u32 = 42;
16643      + const C: u32 = 43ˇ
16644        const D: u32 = 42;
16645
16646
16647        fn main() {
16648            println!("hello");
16649
16650            println!("world");
16651        }"#
16652        .unindent(),
16653    );
16654
16655    cx.update_editor(|editor, window, cx| {
16656        editor.handle_input("\nnew_line\n", window, cx);
16657    });
16658    executor.run_until_parked();
16659
16660    cx.assert_state_with_diff(
16661        r#"
16662        use some::mod1;
16663        use some::mod2;
16664
16665        const A: u32 = 42;
16666        const B: u32 = 42;
16667      - const C: u32 = 42;
16668      + const C: u32 = 43
16669      + new_line
16670      + ˇ
16671        const D: u32 = 42;
16672
16673
16674        fn main() {
16675            println!("hello");
16676
16677            println!("world");
16678        }"#
16679        .unindent(),
16680    );
16681}
16682
16683#[gpui::test]
16684async fn test_stage_and_unstage_added_file_hunk(
16685    executor: BackgroundExecutor,
16686    cx: &mut TestAppContext,
16687) {
16688    init_test(cx, |_| {});
16689
16690    let mut cx = EditorTestContext::new(cx).await;
16691    cx.update_editor(|editor, _, cx| {
16692        editor.set_expand_all_diff_hunks(cx);
16693    });
16694
16695    let working_copy = r#"
16696            ˇfn main() {
16697                println!("hello, world!");
16698            }
16699        "#
16700    .unindent();
16701
16702    cx.set_state(&working_copy);
16703    executor.run_until_parked();
16704
16705    cx.assert_state_with_diff(
16706        r#"
16707            + ˇfn main() {
16708            +     println!("hello, world!");
16709            + }
16710        "#
16711        .unindent(),
16712    );
16713    cx.assert_index_text(None);
16714
16715    cx.update_editor(|editor, window, cx| {
16716        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16717    });
16718    executor.run_until_parked();
16719    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16720    cx.assert_state_with_diff(
16721        r#"
16722            + ˇfn main() {
16723            +     println!("hello, world!");
16724            + }
16725        "#
16726        .unindent(),
16727    );
16728
16729    cx.update_editor(|editor, window, cx| {
16730        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16731    });
16732    executor.run_until_parked();
16733    cx.assert_index_text(None);
16734}
16735
16736async fn setup_indent_guides_editor(
16737    text: &str,
16738    cx: &mut TestAppContext,
16739) -> (BufferId, EditorTestContext) {
16740    init_test(cx, |_| {});
16741
16742    let mut cx = EditorTestContext::new(cx).await;
16743
16744    let buffer_id = cx.update_editor(|editor, window, cx| {
16745        editor.set_text(text, window, cx);
16746        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16747
16748        buffer_ids[0]
16749    });
16750
16751    (buffer_id, cx)
16752}
16753
16754fn assert_indent_guides(
16755    range: Range<u32>,
16756    expected: Vec<IndentGuide>,
16757    active_indices: Option<Vec<usize>>,
16758    cx: &mut EditorTestContext,
16759) {
16760    let indent_guides = cx.update_editor(|editor, window, cx| {
16761        let snapshot = editor.snapshot(window, cx).display_snapshot;
16762        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16763            editor,
16764            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16765            true,
16766            &snapshot,
16767            cx,
16768        );
16769
16770        indent_guides.sort_by(|a, b| {
16771            a.depth.cmp(&b.depth).then(
16772                a.start_row
16773                    .cmp(&b.start_row)
16774                    .then(a.end_row.cmp(&b.end_row)),
16775            )
16776        });
16777        indent_guides
16778    });
16779
16780    if let Some(expected) = active_indices {
16781        let active_indices = cx.update_editor(|editor, window, cx| {
16782            let snapshot = editor.snapshot(window, cx).display_snapshot;
16783            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16784        });
16785
16786        assert_eq!(
16787            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16788            expected,
16789            "Active indent guide indices do not match"
16790        );
16791    }
16792
16793    assert_eq!(indent_guides, expected, "Indent guides do not match");
16794}
16795
16796fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16797    IndentGuide {
16798        buffer_id,
16799        start_row: MultiBufferRow(start_row),
16800        end_row: MultiBufferRow(end_row),
16801        depth,
16802        tab_size: 4,
16803        settings: IndentGuideSettings {
16804            enabled: true,
16805            line_width: 1,
16806            active_line_width: 1,
16807            ..Default::default()
16808        },
16809    }
16810}
16811
16812#[gpui::test]
16813async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16814    let (buffer_id, mut cx) = setup_indent_guides_editor(
16815        &"
16816        fn main() {
16817            let a = 1;
16818        }"
16819        .unindent(),
16820        cx,
16821    )
16822    .await;
16823
16824    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16825}
16826
16827#[gpui::test]
16828async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16829    let (buffer_id, mut cx) = setup_indent_guides_editor(
16830        &"
16831        fn main() {
16832            let a = 1;
16833            let b = 2;
16834        }"
16835        .unindent(),
16836        cx,
16837    )
16838    .await;
16839
16840    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16841}
16842
16843#[gpui::test]
16844async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16845    let (buffer_id, mut cx) = setup_indent_guides_editor(
16846        &"
16847        fn main() {
16848            let a = 1;
16849            if a == 3 {
16850                let b = 2;
16851            } else {
16852                let c = 3;
16853            }
16854        }"
16855        .unindent(),
16856        cx,
16857    )
16858    .await;
16859
16860    assert_indent_guides(
16861        0..8,
16862        vec![
16863            indent_guide(buffer_id, 1, 6, 0),
16864            indent_guide(buffer_id, 3, 3, 1),
16865            indent_guide(buffer_id, 5, 5, 1),
16866        ],
16867        None,
16868        &mut cx,
16869    );
16870}
16871
16872#[gpui::test]
16873async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16874    let (buffer_id, mut cx) = setup_indent_guides_editor(
16875        &"
16876        fn main() {
16877            let a = 1;
16878                let b = 2;
16879            let c = 3;
16880        }"
16881        .unindent(),
16882        cx,
16883    )
16884    .await;
16885
16886    assert_indent_guides(
16887        0..5,
16888        vec![
16889            indent_guide(buffer_id, 1, 3, 0),
16890            indent_guide(buffer_id, 2, 2, 1),
16891        ],
16892        None,
16893        &mut cx,
16894    );
16895}
16896
16897#[gpui::test]
16898async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16899    let (buffer_id, mut cx) = setup_indent_guides_editor(
16900        &"
16901        fn main() {
16902            let a = 1;
16903
16904            let c = 3;
16905        }"
16906        .unindent(),
16907        cx,
16908    )
16909    .await;
16910
16911    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16912}
16913
16914#[gpui::test]
16915async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16916    let (buffer_id, mut cx) = setup_indent_guides_editor(
16917        &"
16918        fn main() {
16919            let a = 1;
16920
16921            let c = 3;
16922
16923            if a == 3 {
16924                let b = 2;
16925            } else {
16926                let c = 3;
16927            }
16928        }"
16929        .unindent(),
16930        cx,
16931    )
16932    .await;
16933
16934    assert_indent_guides(
16935        0..11,
16936        vec![
16937            indent_guide(buffer_id, 1, 9, 0),
16938            indent_guide(buffer_id, 6, 6, 1),
16939            indent_guide(buffer_id, 8, 8, 1),
16940        ],
16941        None,
16942        &mut cx,
16943    );
16944}
16945
16946#[gpui::test]
16947async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16948    let (buffer_id, mut cx) = setup_indent_guides_editor(
16949        &"
16950        fn main() {
16951            let a = 1;
16952
16953            let c = 3;
16954
16955            if a == 3 {
16956                let b = 2;
16957            } else {
16958                let c = 3;
16959            }
16960        }"
16961        .unindent(),
16962        cx,
16963    )
16964    .await;
16965
16966    assert_indent_guides(
16967        1..11,
16968        vec![
16969            indent_guide(buffer_id, 1, 9, 0),
16970            indent_guide(buffer_id, 6, 6, 1),
16971            indent_guide(buffer_id, 8, 8, 1),
16972        ],
16973        None,
16974        &mut cx,
16975    );
16976}
16977
16978#[gpui::test]
16979async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16980    let (buffer_id, mut cx) = setup_indent_guides_editor(
16981        &"
16982        fn main() {
16983            let a = 1;
16984
16985            let c = 3;
16986
16987            if a == 3 {
16988                let b = 2;
16989            } else {
16990                let c = 3;
16991            }
16992        }"
16993        .unindent(),
16994        cx,
16995    )
16996    .await;
16997
16998    assert_indent_guides(
16999        1..10,
17000        vec![
17001            indent_guide(buffer_id, 1, 9, 0),
17002            indent_guide(buffer_id, 6, 6, 1),
17003            indent_guide(buffer_id, 8, 8, 1),
17004        ],
17005        None,
17006        &mut cx,
17007    );
17008}
17009
17010#[gpui::test]
17011async fn test_indent_guide_with_folds(cx: &mut TestAppContext) {
17012    let (buffer_id, mut cx) = setup_indent_guides_editor(
17013        &"
17014        fn main() {
17015            if a {
17016                b(
17017                    c,
17018                    d,
17019                )
17020            } else {
17021                e(
17022                    f
17023                )
17024            }
17025        }"
17026        .unindent(),
17027        cx,
17028    )
17029    .await;
17030
17031    assert_indent_guides(
17032        0..11,
17033        vec![
17034            indent_guide(buffer_id, 1, 10, 0),
17035            indent_guide(buffer_id, 2, 5, 1),
17036            indent_guide(buffer_id, 7, 9, 1),
17037            indent_guide(buffer_id, 3, 4, 2),
17038            indent_guide(buffer_id, 8, 8, 2),
17039        ],
17040        None,
17041        &mut cx,
17042    );
17043
17044    cx.update_editor(|editor, window, cx| {
17045        editor.fold_at(MultiBufferRow(2), window, cx);
17046        assert_eq!(
17047            editor.display_text(cx),
17048            "
17049            fn main() {
17050                if a {
17051                    b(⋯
17052                    )
17053                } else {
17054                    e(
17055                        f
17056                    )
17057                }
17058            }"
17059            .unindent()
17060        );
17061    });
17062
17063    assert_indent_guides(
17064        0..11,
17065        vec![
17066            indent_guide(buffer_id, 1, 10, 0),
17067            indent_guide(buffer_id, 2, 5, 1),
17068            indent_guide(buffer_id, 7, 9, 1),
17069            indent_guide(buffer_id, 8, 8, 2),
17070        ],
17071        None,
17072        &mut cx,
17073    );
17074}
17075
17076#[gpui::test]
17077async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
17078    let (buffer_id, mut cx) = setup_indent_guides_editor(
17079        &"
17080        block1
17081            block2
17082                block3
17083                    block4
17084            block2
17085        block1
17086        block1"
17087            .unindent(),
17088        cx,
17089    )
17090    .await;
17091
17092    assert_indent_guides(
17093        1..10,
17094        vec![
17095            indent_guide(buffer_id, 1, 4, 0),
17096            indent_guide(buffer_id, 2, 3, 1),
17097            indent_guide(buffer_id, 3, 3, 2),
17098        ],
17099        None,
17100        &mut cx,
17101    );
17102}
17103
17104#[gpui::test]
17105async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
17106    let (buffer_id, mut cx) = setup_indent_guides_editor(
17107        &"
17108        block1
17109            block2
17110                block3
17111
17112        block1
17113        block1"
17114            .unindent(),
17115        cx,
17116    )
17117    .await;
17118
17119    assert_indent_guides(
17120        0..6,
17121        vec![
17122            indent_guide(buffer_id, 1, 2, 0),
17123            indent_guide(buffer_id, 2, 2, 1),
17124        ],
17125        None,
17126        &mut cx,
17127    );
17128}
17129
17130#[gpui::test]
17131async fn test_indent_guide_ignored_only_whitespace_lines(cx: &mut TestAppContext) {
17132    let (buffer_id, mut cx) = setup_indent_guides_editor(
17133        &"
17134        function component() {
17135        \treturn (
17136        \t\t\t
17137        \t\t<div>
17138        \t\t\t<abc></abc>
17139        \t\t</div>
17140        \t)
17141        }"
17142        .unindent(),
17143        cx,
17144    )
17145    .await;
17146
17147    assert_indent_guides(
17148        0..8,
17149        vec![
17150            indent_guide(buffer_id, 1, 6, 0),
17151            indent_guide(buffer_id, 2, 5, 1),
17152            indent_guide(buffer_id, 4, 4, 2),
17153        ],
17154        None,
17155        &mut cx,
17156    );
17157}
17158
17159#[gpui::test]
17160async fn test_indent_guide_fallback_to_next_non_entirely_whitespace_line(cx: &mut TestAppContext) {
17161    let (buffer_id, mut cx) = setup_indent_guides_editor(
17162        &"
17163        function component() {
17164        \treturn (
17165        \t
17166        \t\t<div>
17167        \t\t\t<abc></abc>
17168        \t\t</div>
17169        \t)
17170        }"
17171        .unindent(),
17172        cx,
17173    )
17174    .await;
17175
17176    assert_indent_guides(
17177        0..8,
17178        vec![
17179            indent_guide(buffer_id, 1, 6, 0),
17180            indent_guide(buffer_id, 2, 5, 1),
17181            indent_guide(buffer_id, 4, 4, 2),
17182        ],
17183        None,
17184        &mut cx,
17185    );
17186}
17187
17188#[gpui::test]
17189async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
17190    let (buffer_id, mut cx) = setup_indent_guides_editor(
17191        &"
17192        block1
17193
17194
17195
17196            block2
17197        "
17198        .unindent(),
17199        cx,
17200    )
17201    .await;
17202
17203    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
17204}
17205
17206#[gpui::test]
17207async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
17208    let (buffer_id, mut cx) = setup_indent_guides_editor(
17209        &"
17210        def a:
17211        \tb = 3
17212        \tif True:
17213        \t\tc = 4
17214        \t\td = 5
17215        \tprint(b)
17216        "
17217        .unindent(),
17218        cx,
17219    )
17220    .await;
17221
17222    assert_indent_guides(
17223        0..6,
17224        vec![
17225            indent_guide(buffer_id, 1, 5, 0),
17226            indent_guide(buffer_id, 3, 4, 1),
17227        ],
17228        None,
17229        &mut cx,
17230    );
17231}
17232
17233#[gpui::test]
17234async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
17235    let (buffer_id, mut cx) = setup_indent_guides_editor(
17236        &"
17237    fn main() {
17238        let a = 1;
17239    }"
17240        .unindent(),
17241        cx,
17242    )
17243    .await;
17244
17245    cx.update_editor(|editor, window, cx| {
17246        editor.change_selections(None, window, cx, |s| {
17247            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17248        });
17249    });
17250
17251    assert_indent_guides(
17252        0..3,
17253        vec![indent_guide(buffer_id, 1, 1, 0)],
17254        Some(vec![0]),
17255        &mut cx,
17256    );
17257}
17258
17259#[gpui::test]
17260async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
17261    let (buffer_id, mut cx) = setup_indent_guides_editor(
17262        &"
17263    fn main() {
17264        if 1 == 2 {
17265            let a = 1;
17266        }
17267    }"
17268        .unindent(),
17269        cx,
17270    )
17271    .await;
17272
17273    cx.update_editor(|editor, window, cx| {
17274        editor.change_selections(None, window, cx, |s| {
17275            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17276        });
17277    });
17278
17279    assert_indent_guides(
17280        0..4,
17281        vec![
17282            indent_guide(buffer_id, 1, 3, 0),
17283            indent_guide(buffer_id, 2, 2, 1),
17284        ],
17285        Some(vec![1]),
17286        &mut cx,
17287    );
17288
17289    cx.update_editor(|editor, window, cx| {
17290        editor.change_selections(None, window, cx, |s| {
17291            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17292        });
17293    });
17294
17295    assert_indent_guides(
17296        0..4,
17297        vec![
17298            indent_guide(buffer_id, 1, 3, 0),
17299            indent_guide(buffer_id, 2, 2, 1),
17300        ],
17301        Some(vec![1]),
17302        &mut cx,
17303    );
17304
17305    cx.update_editor(|editor, window, cx| {
17306        editor.change_selections(None, window, cx, |s| {
17307            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
17308        });
17309    });
17310
17311    assert_indent_guides(
17312        0..4,
17313        vec![
17314            indent_guide(buffer_id, 1, 3, 0),
17315            indent_guide(buffer_id, 2, 2, 1),
17316        ],
17317        Some(vec![0]),
17318        &mut cx,
17319    );
17320}
17321
17322#[gpui::test]
17323async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
17324    let (buffer_id, mut cx) = setup_indent_guides_editor(
17325        &"
17326    fn main() {
17327        let a = 1;
17328
17329        let b = 2;
17330    }"
17331        .unindent(),
17332        cx,
17333    )
17334    .await;
17335
17336    cx.update_editor(|editor, window, cx| {
17337        editor.change_selections(None, window, cx, |s| {
17338            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17339        });
17340    });
17341
17342    assert_indent_guides(
17343        0..5,
17344        vec![indent_guide(buffer_id, 1, 3, 0)],
17345        Some(vec![0]),
17346        &mut cx,
17347    );
17348}
17349
17350#[gpui::test]
17351async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17352    let (buffer_id, mut cx) = setup_indent_guides_editor(
17353        &"
17354    def m:
17355        a = 1
17356        pass"
17357            .unindent(),
17358        cx,
17359    )
17360    .await;
17361
17362    cx.update_editor(|editor, window, cx| {
17363        editor.change_selections(None, window, cx, |s| {
17364            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17365        });
17366    });
17367
17368    assert_indent_guides(
17369        0..3,
17370        vec![indent_guide(buffer_id, 1, 2, 0)],
17371        Some(vec![0]),
17372        &mut cx,
17373    );
17374}
17375
17376#[gpui::test]
17377async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17378    init_test(cx, |_| {});
17379    let mut cx = EditorTestContext::new(cx).await;
17380    let text = indoc! {
17381        "
17382        impl A {
17383            fn b() {
17384                0;
17385                3;
17386                5;
17387                6;
17388                7;
17389            }
17390        }
17391        "
17392    };
17393    let base_text = indoc! {
17394        "
17395        impl A {
17396            fn b() {
17397                0;
17398                1;
17399                2;
17400                3;
17401                4;
17402            }
17403            fn c() {
17404                5;
17405                6;
17406                7;
17407            }
17408        }
17409        "
17410    };
17411
17412    cx.update_editor(|editor, window, cx| {
17413        editor.set_text(text, window, cx);
17414
17415        editor.buffer().update(cx, |multibuffer, cx| {
17416            let buffer = multibuffer.as_singleton().unwrap();
17417            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17418
17419            multibuffer.set_all_diff_hunks_expanded(cx);
17420            multibuffer.add_diff(diff, cx);
17421
17422            buffer.read(cx).remote_id()
17423        })
17424    });
17425    cx.run_until_parked();
17426
17427    cx.assert_state_with_diff(
17428        indoc! { "
17429          impl A {
17430              fn b() {
17431                  0;
17432        -         1;
17433        -         2;
17434                  3;
17435        -         4;
17436        -     }
17437        -     fn c() {
17438                  5;
17439                  6;
17440                  7;
17441              }
17442          }
17443          ˇ"
17444        }
17445        .to_string(),
17446    );
17447
17448    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17449        editor
17450            .snapshot(window, cx)
17451            .buffer_snapshot
17452            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17453            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17454            .collect::<Vec<_>>()
17455    });
17456    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17457    assert_eq!(
17458        actual_guides,
17459        vec![
17460            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17461            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17462            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17463        ]
17464    );
17465}
17466
17467#[gpui::test]
17468async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17469    init_test(cx, |_| {});
17470    let mut cx = EditorTestContext::new(cx).await;
17471
17472    let diff_base = r#"
17473        a
17474        b
17475        c
17476        "#
17477    .unindent();
17478
17479    cx.set_state(
17480        &r#"
17481        ˇA
17482        b
17483        C
17484        "#
17485        .unindent(),
17486    );
17487    cx.set_head_text(&diff_base);
17488    cx.update_editor(|editor, window, cx| {
17489        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17490    });
17491    executor.run_until_parked();
17492
17493    let both_hunks_expanded = r#"
17494        - a
17495        + ˇA
17496          b
17497        - c
17498        + C
17499        "#
17500    .unindent();
17501
17502    cx.assert_state_with_diff(both_hunks_expanded.clone());
17503
17504    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17505        let snapshot = editor.snapshot(window, cx);
17506        let hunks = editor
17507            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17508            .collect::<Vec<_>>();
17509        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17510        let buffer_id = hunks[0].buffer_id;
17511        hunks
17512            .into_iter()
17513            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17514            .collect::<Vec<_>>()
17515    });
17516    assert_eq!(hunk_ranges.len(), 2);
17517
17518    cx.update_editor(|editor, _, cx| {
17519        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17520    });
17521    executor.run_until_parked();
17522
17523    let second_hunk_expanded = r#"
17524          ˇA
17525          b
17526        - c
17527        + C
17528        "#
17529    .unindent();
17530
17531    cx.assert_state_with_diff(second_hunk_expanded);
17532
17533    cx.update_editor(|editor, _, cx| {
17534        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17535    });
17536    executor.run_until_parked();
17537
17538    cx.assert_state_with_diff(both_hunks_expanded.clone());
17539
17540    cx.update_editor(|editor, _, cx| {
17541        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17542    });
17543    executor.run_until_parked();
17544
17545    let first_hunk_expanded = r#"
17546        - a
17547        + ˇA
17548          b
17549          C
17550        "#
17551    .unindent();
17552
17553    cx.assert_state_with_diff(first_hunk_expanded);
17554
17555    cx.update_editor(|editor, _, cx| {
17556        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17557    });
17558    executor.run_until_parked();
17559
17560    cx.assert_state_with_diff(both_hunks_expanded);
17561
17562    cx.set_state(
17563        &r#"
17564        ˇA
17565        b
17566        "#
17567        .unindent(),
17568    );
17569    cx.run_until_parked();
17570
17571    // TODO this cursor position seems bad
17572    cx.assert_state_with_diff(
17573        r#"
17574        - ˇa
17575        + A
17576          b
17577        "#
17578        .unindent(),
17579    );
17580
17581    cx.update_editor(|editor, window, cx| {
17582        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17583    });
17584
17585    cx.assert_state_with_diff(
17586        r#"
17587            - ˇa
17588            + A
17589              b
17590            - c
17591            "#
17592        .unindent(),
17593    );
17594
17595    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17596        let snapshot = editor.snapshot(window, cx);
17597        let hunks = editor
17598            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17599            .collect::<Vec<_>>();
17600        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17601        let buffer_id = hunks[0].buffer_id;
17602        hunks
17603            .into_iter()
17604            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17605            .collect::<Vec<_>>()
17606    });
17607    assert_eq!(hunk_ranges.len(), 2);
17608
17609    cx.update_editor(|editor, _, cx| {
17610        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17611    });
17612    executor.run_until_parked();
17613
17614    cx.assert_state_with_diff(
17615        r#"
17616        - ˇa
17617        + A
17618          b
17619        "#
17620        .unindent(),
17621    );
17622}
17623
17624#[gpui::test]
17625async fn test_toggle_deletion_hunk_at_start_of_file(
17626    executor: BackgroundExecutor,
17627    cx: &mut TestAppContext,
17628) {
17629    init_test(cx, |_| {});
17630    let mut cx = EditorTestContext::new(cx).await;
17631
17632    let diff_base = r#"
17633        a
17634        b
17635        c
17636        "#
17637    .unindent();
17638
17639    cx.set_state(
17640        &r#"
17641        ˇb
17642        c
17643        "#
17644        .unindent(),
17645    );
17646    cx.set_head_text(&diff_base);
17647    cx.update_editor(|editor, window, cx| {
17648        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17649    });
17650    executor.run_until_parked();
17651
17652    let hunk_expanded = r#"
17653        - a
17654          ˇb
17655          c
17656        "#
17657    .unindent();
17658
17659    cx.assert_state_with_diff(hunk_expanded.clone());
17660
17661    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17662        let snapshot = editor.snapshot(window, cx);
17663        let hunks = editor
17664            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17665            .collect::<Vec<_>>();
17666        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17667        let buffer_id = hunks[0].buffer_id;
17668        hunks
17669            .into_iter()
17670            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17671            .collect::<Vec<_>>()
17672    });
17673    assert_eq!(hunk_ranges.len(), 1);
17674
17675    cx.update_editor(|editor, _, cx| {
17676        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17677    });
17678    executor.run_until_parked();
17679
17680    let hunk_collapsed = r#"
17681          ˇb
17682          c
17683        "#
17684    .unindent();
17685
17686    cx.assert_state_with_diff(hunk_collapsed);
17687
17688    cx.update_editor(|editor, _, cx| {
17689        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17690    });
17691    executor.run_until_parked();
17692
17693    cx.assert_state_with_diff(hunk_expanded.clone());
17694}
17695
17696#[gpui::test]
17697async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17698    init_test(cx, |_| {});
17699
17700    let fs = FakeFs::new(cx.executor());
17701    fs.insert_tree(
17702        path!("/test"),
17703        json!({
17704            ".git": {},
17705            "file-1": "ONE\n",
17706            "file-2": "TWO\n",
17707            "file-3": "THREE\n",
17708        }),
17709    )
17710    .await;
17711
17712    fs.set_head_for_repo(
17713        path!("/test/.git").as_ref(),
17714        &[
17715            ("file-1".into(), "one\n".into()),
17716            ("file-2".into(), "two\n".into()),
17717            ("file-3".into(), "three\n".into()),
17718        ],
17719    );
17720
17721    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17722    let mut buffers = vec![];
17723    for i in 1..=3 {
17724        let buffer = project
17725            .update(cx, |project, cx| {
17726                let path = format!(path!("/test/file-{}"), i);
17727                project.open_local_buffer(path, cx)
17728            })
17729            .await
17730            .unwrap();
17731        buffers.push(buffer);
17732    }
17733
17734    let multibuffer = cx.new(|cx| {
17735        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17736        multibuffer.set_all_diff_hunks_expanded(cx);
17737        for buffer in &buffers {
17738            let snapshot = buffer.read(cx).snapshot();
17739            multibuffer.set_excerpts_for_path(
17740                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17741                buffer.clone(),
17742                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17743                DEFAULT_MULTIBUFFER_CONTEXT,
17744                cx,
17745            );
17746        }
17747        multibuffer
17748    });
17749
17750    let editor = cx.add_window(|window, cx| {
17751        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17752    });
17753    cx.run_until_parked();
17754
17755    let snapshot = editor
17756        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17757        .unwrap();
17758    let hunks = snapshot
17759        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17760        .map(|hunk| match hunk {
17761            DisplayDiffHunk::Unfolded {
17762                display_row_range, ..
17763            } => display_row_range,
17764            DisplayDiffHunk::Folded { .. } => unreachable!(),
17765        })
17766        .collect::<Vec<_>>();
17767    assert_eq!(
17768        hunks,
17769        [
17770            DisplayRow(2)..DisplayRow(4),
17771            DisplayRow(7)..DisplayRow(9),
17772            DisplayRow(12)..DisplayRow(14),
17773        ]
17774    );
17775}
17776
17777#[gpui::test]
17778async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17779    init_test(cx, |_| {});
17780
17781    let mut cx = EditorTestContext::new(cx).await;
17782    cx.set_head_text(indoc! { "
17783        one
17784        two
17785        three
17786        four
17787        five
17788        "
17789    });
17790    cx.set_index_text(indoc! { "
17791        one
17792        two
17793        three
17794        four
17795        five
17796        "
17797    });
17798    cx.set_state(indoc! {"
17799        one
17800        TWO
17801        ˇTHREE
17802        FOUR
17803        five
17804    "});
17805    cx.run_until_parked();
17806    cx.update_editor(|editor, window, cx| {
17807        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17808    });
17809    cx.run_until_parked();
17810    cx.assert_index_text(Some(indoc! {"
17811        one
17812        TWO
17813        THREE
17814        FOUR
17815        five
17816    "}));
17817    cx.set_state(indoc! { "
17818        one
17819        TWO
17820        ˇTHREE-HUNDRED
17821        FOUR
17822        five
17823    "});
17824    cx.run_until_parked();
17825    cx.update_editor(|editor, window, cx| {
17826        let snapshot = editor.snapshot(window, cx);
17827        let hunks = editor
17828            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17829            .collect::<Vec<_>>();
17830        assert_eq!(hunks.len(), 1);
17831        assert_eq!(
17832            hunks[0].status(),
17833            DiffHunkStatus {
17834                kind: DiffHunkStatusKind::Modified,
17835                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17836            }
17837        );
17838
17839        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17840    });
17841    cx.run_until_parked();
17842    cx.assert_index_text(Some(indoc! {"
17843        one
17844        TWO
17845        THREE-HUNDRED
17846        FOUR
17847        five
17848    "}));
17849}
17850
17851#[gpui::test]
17852fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17853    init_test(cx, |_| {});
17854
17855    let editor = cx.add_window(|window, cx| {
17856        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17857        build_editor(buffer, window, cx)
17858    });
17859
17860    let render_args = Arc::new(Mutex::new(None));
17861    let snapshot = editor
17862        .update(cx, |editor, window, cx| {
17863            let snapshot = editor.buffer().read(cx).snapshot(cx);
17864            let range =
17865                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17866
17867            struct RenderArgs {
17868                row: MultiBufferRow,
17869                folded: bool,
17870                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17871            }
17872
17873            let crease = Crease::inline(
17874                range,
17875                FoldPlaceholder::test(),
17876                {
17877                    let toggle_callback = render_args.clone();
17878                    move |row, folded, callback, _window, _cx| {
17879                        *toggle_callback.lock() = Some(RenderArgs {
17880                            row,
17881                            folded,
17882                            callback,
17883                        });
17884                        div()
17885                    }
17886                },
17887                |_row, _folded, _window, _cx| div(),
17888            );
17889
17890            editor.insert_creases(Some(crease), cx);
17891            let snapshot = editor.snapshot(window, cx);
17892            let _div = snapshot.render_crease_toggle(
17893                MultiBufferRow(1),
17894                false,
17895                cx.entity().clone(),
17896                window,
17897                cx,
17898            );
17899            snapshot
17900        })
17901        .unwrap();
17902
17903    let render_args = render_args.lock().take().unwrap();
17904    assert_eq!(render_args.row, MultiBufferRow(1));
17905    assert!(!render_args.folded);
17906    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17907
17908    cx.update_window(*editor, |_, window, cx| {
17909        (render_args.callback)(true, window, cx)
17910    })
17911    .unwrap();
17912    let snapshot = editor
17913        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17914        .unwrap();
17915    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17916
17917    cx.update_window(*editor, |_, window, cx| {
17918        (render_args.callback)(false, window, cx)
17919    })
17920    .unwrap();
17921    let snapshot = editor
17922        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17923        .unwrap();
17924    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17925}
17926
17927#[gpui::test]
17928async fn test_input_text(cx: &mut TestAppContext) {
17929    init_test(cx, |_| {});
17930    let mut cx = EditorTestContext::new(cx).await;
17931
17932    cx.set_state(
17933        &r#"ˇone
17934        two
17935
17936        three
17937        fourˇ
17938        five
17939
17940        siˇx"#
17941            .unindent(),
17942    );
17943
17944    cx.dispatch_action(HandleInput(String::new()));
17945    cx.assert_editor_state(
17946        &r#"ˇone
17947        two
17948
17949        three
17950        fourˇ
17951        five
17952
17953        siˇx"#
17954            .unindent(),
17955    );
17956
17957    cx.dispatch_action(HandleInput("AAAA".to_string()));
17958    cx.assert_editor_state(
17959        &r#"AAAAˇone
17960        two
17961
17962        three
17963        fourAAAAˇ
17964        five
17965
17966        siAAAAˇx"#
17967            .unindent(),
17968    );
17969}
17970
17971#[gpui::test]
17972async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17973    init_test(cx, |_| {});
17974
17975    let mut cx = EditorTestContext::new(cx).await;
17976    cx.set_state(
17977        r#"let foo = 1;
17978let foo = 2;
17979let foo = 3;
17980let fooˇ = 4;
17981let foo = 5;
17982let foo = 6;
17983let foo = 7;
17984let foo = 8;
17985let foo = 9;
17986let foo = 10;
17987let foo = 11;
17988let foo = 12;
17989let foo = 13;
17990let foo = 14;
17991let foo = 15;"#,
17992    );
17993
17994    cx.update_editor(|e, window, cx| {
17995        assert_eq!(
17996            e.next_scroll_position,
17997            NextScrollCursorCenterTopBottom::Center,
17998            "Default next scroll direction is center",
17999        );
18000
18001        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18002        assert_eq!(
18003            e.next_scroll_position,
18004            NextScrollCursorCenterTopBottom::Top,
18005            "After center, next scroll direction should be top",
18006        );
18007
18008        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18009        assert_eq!(
18010            e.next_scroll_position,
18011            NextScrollCursorCenterTopBottom::Bottom,
18012            "After top, next scroll direction should be bottom",
18013        );
18014
18015        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18016        assert_eq!(
18017            e.next_scroll_position,
18018            NextScrollCursorCenterTopBottom::Center,
18019            "After bottom, scrolling should start over",
18020        );
18021
18022        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
18023        assert_eq!(
18024            e.next_scroll_position,
18025            NextScrollCursorCenterTopBottom::Top,
18026            "Scrolling continues if retriggered fast enough"
18027        );
18028    });
18029
18030    cx.executor()
18031        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
18032    cx.executor().run_until_parked();
18033    cx.update_editor(|e, _, _| {
18034        assert_eq!(
18035            e.next_scroll_position,
18036            NextScrollCursorCenterTopBottom::Center,
18037            "If scrolling is not triggered fast enough, it should reset"
18038        );
18039    });
18040}
18041
18042#[gpui::test]
18043async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
18044    init_test(cx, |_| {});
18045    let mut cx = EditorLspTestContext::new_rust(
18046        lsp::ServerCapabilities {
18047            definition_provider: Some(lsp::OneOf::Left(true)),
18048            references_provider: Some(lsp::OneOf::Left(true)),
18049            ..lsp::ServerCapabilities::default()
18050        },
18051        cx,
18052    )
18053    .await;
18054
18055    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
18056        let go_to_definition = cx
18057            .lsp
18058            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
18059                move |params, _| async move {
18060                    if empty_go_to_definition {
18061                        Ok(None)
18062                    } else {
18063                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
18064                            uri: params.text_document_position_params.text_document.uri,
18065                            range: lsp::Range::new(
18066                                lsp::Position::new(4, 3),
18067                                lsp::Position::new(4, 6),
18068                            ),
18069                        })))
18070                    }
18071                },
18072            );
18073        let references = cx
18074            .lsp
18075            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
18076                Ok(Some(vec![lsp::Location {
18077                    uri: params.text_document_position.text_document.uri,
18078                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
18079                }]))
18080            });
18081        (go_to_definition, references)
18082    };
18083
18084    cx.set_state(
18085        &r#"fn one() {
18086            let mut a = ˇtwo();
18087        }
18088
18089        fn two() {}"#
18090            .unindent(),
18091    );
18092    set_up_lsp_handlers(false, &mut cx);
18093    let navigated = cx
18094        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18095        .await
18096        .expect("Failed to navigate to definition");
18097    assert_eq!(
18098        navigated,
18099        Navigated::Yes,
18100        "Should have navigated to definition from the GetDefinition response"
18101    );
18102    cx.assert_editor_state(
18103        &r#"fn one() {
18104            let mut a = two();
18105        }
18106
18107        fn «twoˇ»() {}"#
18108            .unindent(),
18109    );
18110
18111    let editors = cx.update_workspace(|workspace, _, cx| {
18112        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18113    });
18114    cx.update_editor(|_, _, test_editor_cx| {
18115        assert_eq!(
18116            editors.len(),
18117            1,
18118            "Initially, only one, test, editor should be open in the workspace"
18119        );
18120        assert_eq!(
18121            test_editor_cx.entity(),
18122            editors.last().expect("Asserted len is 1").clone()
18123        );
18124    });
18125
18126    set_up_lsp_handlers(true, &mut cx);
18127    let navigated = cx
18128        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18129        .await
18130        .expect("Failed to navigate to lookup references");
18131    assert_eq!(
18132        navigated,
18133        Navigated::Yes,
18134        "Should have navigated to references as a fallback after empty GoToDefinition response"
18135    );
18136    // We should not change the selections in the existing file,
18137    // if opening another milti buffer with the references
18138    cx.assert_editor_state(
18139        &r#"fn one() {
18140            let mut a = two();
18141        }
18142
18143        fn «twoˇ»() {}"#
18144            .unindent(),
18145    );
18146    let editors = cx.update_workspace(|workspace, _, cx| {
18147        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18148    });
18149    cx.update_editor(|_, _, test_editor_cx| {
18150        assert_eq!(
18151            editors.len(),
18152            2,
18153            "After falling back to references search, we open a new editor with the results"
18154        );
18155        let references_fallback_text = editors
18156            .into_iter()
18157            .find(|new_editor| *new_editor != test_editor_cx.entity())
18158            .expect("Should have one non-test editor now")
18159            .read(test_editor_cx)
18160            .text(test_editor_cx);
18161        assert_eq!(
18162            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
18163            "Should use the range from the references response and not the GoToDefinition one"
18164        );
18165    });
18166}
18167
18168#[gpui::test]
18169async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
18170    init_test(cx, |_| {});
18171    cx.update(|cx| {
18172        let mut editor_settings = EditorSettings::get_global(cx).clone();
18173        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
18174        EditorSettings::override_global(editor_settings, cx);
18175    });
18176    let mut cx = EditorLspTestContext::new_rust(
18177        lsp::ServerCapabilities {
18178            definition_provider: Some(lsp::OneOf::Left(true)),
18179            references_provider: Some(lsp::OneOf::Left(true)),
18180            ..lsp::ServerCapabilities::default()
18181        },
18182        cx,
18183    )
18184    .await;
18185    let original_state = r#"fn one() {
18186        let mut a = ˇtwo();
18187    }
18188
18189    fn two() {}"#
18190        .unindent();
18191    cx.set_state(&original_state);
18192
18193    let mut go_to_definition = cx
18194        .lsp
18195        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
18196            move |_, _| async move { Ok(None) },
18197        );
18198    let _references = cx
18199        .lsp
18200        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
18201            panic!("Should not call for references with no go to definition fallback")
18202        });
18203
18204    let navigated = cx
18205        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
18206        .await
18207        .expect("Failed to navigate to lookup references");
18208    go_to_definition
18209        .next()
18210        .await
18211        .expect("Should have called the go_to_definition handler");
18212
18213    assert_eq!(
18214        navigated,
18215        Navigated::No,
18216        "Should have navigated to references as a fallback after empty GoToDefinition response"
18217    );
18218    cx.assert_editor_state(&original_state);
18219    let editors = cx.update_workspace(|workspace, _, cx| {
18220        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18221    });
18222    cx.update_editor(|_, _, _| {
18223        assert_eq!(
18224            editors.len(),
18225            1,
18226            "After unsuccessful fallback, no other editor should have been opened"
18227        );
18228    });
18229}
18230
18231#[gpui::test]
18232async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
18233    init_test(cx, |_| {});
18234
18235    let language = Arc::new(Language::new(
18236        LanguageConfig::default(),
18237        Some(tree_sitter_rust::LANGUAGE.into()),
18238    ));
18239
18240    let text = r#"
18241        #[cfg(test)]
18242        mod tests() {
18243            #[test]
18244            fn runnable_1() {
18245                let a = 1;
18246            }
18247
18248            #[test]
18249            fn runnable_2() {
18250                let a = 1;
18251                let b = 2;
18252            }
18253        }
18254    "#
18255    .unindent();
18256
18257    let fs = FakeFs::new(cx.executor());
18258    fs.insert_file("/file.rs", Default::default()).await;
18259
18260    let project = Project::test(fs, ["/a".as_ref()], cx).await;
18261    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18262    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18263    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
18264    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
18265
18266    let editor = cx.new_window_entity(|window, cx| {
18267        Editor::new(
18268            EditorMode::full(),
18269            multi_buffer,
18270            Some(project.clone()),
18271            window,
18272            cx,
18273        )
18274    });
18275
18276    editor.update_in(cx, |editor, window, cx| {
18277        let snapshot = editor.buffer().read(cx).snapshot(cx);
18278        editor.tasks.insert(
18279            (buffer.read(cx).remote_id(), 3),
18280            RunnableTasks {
18281                templates: vec![],
18282                offset: snapshot.anchor_before(43),
18283                column: 0,
18284                extra_variables: HashMap::default(),
18285                context_range: BufferOffset(43)..BufferOffset(85),
18286            },
18287        );
18288        editor.tasks.insert(
18289            (buffer.read(cx).remote_id(), 8),
18290            RunnableTasks {
18291                templates: vec![],
18292                offset: snapshot.anchor_before(86),
18293                column: 0,
18294                extra_variables: HashMap::default(),
18295                context_range: BufferOffset(86)..BufferOffset(191),
18296            },
18297        );
18298
18299        // Test finding task when cursor is inside function body
18300        editor.change_selections(None, window, cx, |s| {
18301            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
18302        });
18303        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18304        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
18305
18306        // Test finding task when cursor is on function name
18307        editor.change_selections(None, window, cx, |s| {
18308            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
18309        });
18310        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18311        assert_eq!(row, 8, "Should find task when cursor is on function name");
18312    });
18313}
18314
18315#[gpui::test]
18316async fn test_folding_buffers(cx: &mut TestAppContext) {
18317    init_test(cx, |_| {});
18318
18319    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18320    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
18321    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
18322
18323    let fs = FakeFs::new(cx.executor());
18324    fs.insert_tree(
18325        path!("/a"),
18326        json!({
18327            "first.rs": sample_text_1,
18328            "second.rs": sample_text_2,
18329            "third.rs": sample_text_3,
18330        }),
18331    )
18332    .await;
18333    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18334    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18335    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18336    let worktree = project.update(cx, |project, cx| {
18337        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18338        assert_eq!(worktrees.len(), 1);
18339        worktrees.pop().unwrap()
18340    });
18341    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18342
18343    let buffer_1 = project
18344        .update(cx, |project, cx| {
18345            project.open_buffer((worktree_id, "first.rs"), cx)
18346        })
18347        .await
18348        .unwrap();
18349    let buffer_2 = project
18350        .update(cx, |project, cx| {
18351            project.open_buffer((worktree_id, "second.rs"), cx)
18352        })
18353        .await
18354        .unwrap();
18355    let buffer_3 = project
18356        .update(cx, |project, cx| {
18357            project.open_buffer((worktree_id, "third.rs"), cx)
18358        })
18359        .await
18360        .unwrap();
18361
18362    let multi_buffer = cx.new(|cx| {
18363        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18364        multi_buffer.push_excerpts(
18365            buffer_1.clone(),
18366            [
18367                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18368                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18369                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18370            ],
18371            cx,
18372        );
18373        multi_buffer.push_excerpts(
18374            buffer_2.clone(),
18375            [
18376                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18377                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18378                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18379            ],
18380            cx,
18381        );
18382        multi_buffer.push_excerpts(
18383            buffer_3.clone(),
18384            [
18385                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18386                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18387                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18388            ],
18389            cx,
18390        );
18391        multi_buffer
18392    });
18393    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18394        Editor::new(
18395            EditorMode::full(),
18396            multi_buffer.clone(),
18397            Some(project.clone()),
18398            window,
18399            cx,
18400        )
18401    });
18402
18403    assert_eq!(
18404        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18405        "\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",
18406    );
18407
18408    multi_buffer_editor.update(cx, |editor, cx| {
18409        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18410    });
18411    assert_eq!(
18412        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18413        "\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",
18414        "After folding the first buffer, its text should not be displayed"
18415    );
18416
18417    multi_buffer_editor.update(cx, |editor, cx| {
18418        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18419    });
18420    assert_eq!(
18421        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18422        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18423        "After folding the second buffer, its text should not be displayed"
18424    );
18425
18426    multi_buffer_editor.update(cx, |editor, cx| {
18427        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18428    });
18429    assert_eq!(
18430        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18431        "\n\n\n\n\n",
18432        "After folding the third buffer, its text should not be displayed"
18433    );
18434
18435    // Emulate selection inside the fold logic, that should work
18436    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18437        editor
18438            .snapshot(window, cx)
18439            .next_line_boundary(Point::new(0, 4));
18440    });
18441
18442    multi_buffer_editor.update(cx, |editor, cx| {
18443        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18444    });
18445    assert_eq!(
18446        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18447        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18448        "After unfolding the second buffer, its text should be displayed"
18449    );
18450
18451    // Typing inside of buffer 1 causes that buffer to be unfolded.
18452    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18453        assert_eq!(
18454            multi_buffer
18455                .read(cx)
18456                .snapshot(cx)
18457                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18458                .collect::<String>(),
18459            "bbbb"
18460        );
18461        editor.change_selections(None, window, cx, |selections| {
18462            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18463        });
18464        editor.handle_input("B", window, cx);
18465    });
18466
18467    assert_eq!(
18468        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18469        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18470        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18471    );
18472
18473    multi_buffer_editor.update(cx, |editor, cx| {
18474        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18475    });
18476    assert_eq!(
18477        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18478        "\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",
18479        "After unfolding the all buffers, all original text should be displayed"
18480    );
18481}
18482
18483#[gpui::test]
18484async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18485    init_test(cx, |_| {});
18486
18487    let sample_text_1 = "1111\n2222\n3333".to_string();
18488    let sample_text_2 = "4444\n5555\n6666".to_string();
18489    let sample_text_3 = "7777\n8888\n9999".to_string();
18490
18491    let fs = FakeFs::new(cx.executor());
18492    fs.insert_tree(
18493        path!("/a"),
18494        json!({
18495            "first.rs": sample_text_1,
18496            "second.rs": sample_text_2,
18497            "third.rs": sample_text_3,
18498        }),
18499    )
18500    .await;
18501    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18502    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18503    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18504    let worktree = project.update(cx, |project, cx| {
18505        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18506        assert_eq!(worktrees.len(), 1);
18507        worktrees.pop().unwrap()
18508    });
18509    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18510
18511    let buffer_1 = project
18512        .update(cx, |project, cx| {
18513            project.open_buffer((worktree_id, "first.rs"), cx)
18514        })
18515        .await
18516        .unwrap();
18517    let buffer_2 = project
18518        .update(cx, |project, cx| {
18519            project.open_buffer((worktree_id, "second.rs"), cx)
18520        })
18521        .await
18522        .unwrap();
18523    let buffer_3 = project
18524        .update(cx, |project, cx| {
18525            project.open_buffer((worktree_id, "third.rs"), cx)
18526        })
18527        .await
18528        .unwrap();
18529
18530    let multi_buffer = cx.new(|cx| {
18531        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18532        multi_buffer.push_excerpts(
18533            buffer_1.clone(),
18534            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18535            cx,
18536        );
18537        multi_buffer.push_excerpts(
18538            buffer_2.clone(),
18539            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18540            cx,
18541        );
18542        multi_buffer.push_excerpts(
18543            buffer_3.clone(),
18544            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18545            cx,
18546        );
18547        multi_buffer
18548    });
18549
18550    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18551        Editor::new(
18552            EditorMode::full(),
18553            multi_buffer,
18554            Some(project.clone()),
18555            window,
18556            cx,
18557        )
18558    });
18559
18560    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18561    assert_eq!(
18562        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18563        full_text,
18564    );
18565
18566    multi_buffer_editor.update(cx, |editor, cx| {
18567        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18568    });
18569    assert_eq!(
18570        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18571        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18572        "After folding the first buffer, its text should not be displayed"
18573    );
18574
18575    multi_buffer_editor.update(cx, |editor, cx| {
18576        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18577    });
18578
18579    assert_eq!(
18580        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18581        "\n\n\n\n\n\n7777\n8888\n9999",
18582        "After folding the second buffer, its text should not be displayed"
18583    );
18584
18585    multi_buffer_editor.update(cx, |editor, cx| {
18586        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18587    });
18588    assert_eq!(
18589        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18590        "\n\n\n\n\n",
18591        "After folding the third buffer, its text should not be displayed"
18592    );
18593
18594    multi_buffer_editor.update(cx, |editor, cx| {
18595        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18596    });
18597    assert_eq!(
18598        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18599        "\n\n\n\n4444\n5555\n6666\n\n",
18600        "After unfolding the second buffer, its text should be displayed"
18601    );
18602
18603    multi_buffer_editor.update(cx, |editor, cx| {
18604        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18605    });
18606    assert_eq!(
18607        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18608        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18609        "After unfolding the first buffer, its text should be displayed"
18610    );
18611
18612    multi_buffer_editor.update(cx, |editor, cx| {
18613        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18614    });
18615    assert_eq!(
18616        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18617        full_text,
18618        "After unfolding all buffers, all original text should be displayed"
18619    );
18620}
18621
18622#[gpui::test]
18623async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18624    init_test(cx, |_| {});
18625
18626    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18627
18628    let fs = FakeFs::new(cx.executor());
18629    fs.insert_tree(
18630        path!("/a"),
18631        json!({
18632            "main.rs": sample_text,
18633        }),
18634    )
18635    .await;
18636    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18637    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18638    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18639    let worktree = project.update(cx, |project, cx| {
18640        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18641        assert_eq!(worktrees.len(), 1);
18642        worktrees.pop().unwrap()
18643    });
18644    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18645
18646    let buffer_1 = project
18647        .update(cx, |project, cx| {
18648            project.open_buffer((worktree_id, "main.rs"), cx)
18649        })
18650        .await
18651        .unwrap();
18652
18653    let multi_buffer = cx.new(|cx| {
18654        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18655        multi_buffer.push_excerpts(
18656            buffer_1.clone(),
18657            [ExcerptRange::new(
18658                Point::new(0, 0)
18659                    ..Point::new(
18660                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18661                        0,
18662                    ),
18663            )],
18664            cx,
18665        );
18666        multi_buffer
18667    });
18668    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18669        Editor::new(
18670            EditorMode::full(),
18671            multi_buffer,
18672            Some(project.clone()),
18673            window,
18674            cx,
18675        )
18676    });
18677
18678    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18679    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18680        enum TestHighlight {}
18681        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18682        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18683        editor.highlight_text::<TestHighlight>(
18684            vec![highlight_range.clone()],
18685            HighlightStyle::color(Hsla::green()),
18686            cx,
18687        );
18688        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18689    });
18690
18691    let full_text = format!("\n\n{sample_text}");
18692    assert_eq!(
18693        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18694        full_text,
18695    );
18696}
18697
18698#[gpui::test]
18699async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18700    init_test(cx, |_| {});
18701    cx.update(|cx| {
18702        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18703            "keymaps/default-linux.json",
18704            cx,
18705        )
18706        .unwrap();
18707        cx.bind_keys(default_key_bindings);
18708    });
18709
18710    let (editor, cx) = cx.add_window_view(|window, cx| {
18711        let multi_buffer = MultiBuffer::build_multi(
18712            [
18713                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18714                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18715                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18716                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18717            ],
18718            cx,
18719        );
18720        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18721
18722        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18723        // fold all but the second buffer, so that we test navigating between two
18724        // adjacent folded buffers, as well as folded buffers at the start and
18725        // end the multibuffer
18726        editor.fold_buffer(buffer_ids[0], cx);
18727        editor.fold_buffer(buffer_ids[2], cx);
18728        editor.fold_buffer(buffer_ids[3], cx);
18729
18730        editor
18731    });
18732    cx.simulate_resize(size(px(1000.), px(1000.)));
18733
18734    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18735    cx.assert_excerpts_with_selections(indoc! {"
18736        [EXCERPT]
18737        ˇ[FOLDED]
18738        [EXCERPT]
18739        a1
18740        b1
18741        [EXCERPT]
18742        [FOLDED]
18743        [EXCERPT]
18744        [FOLDED]
18745        "
18746    });
18747    cx.simulate_keystroke("down");
18748    cx.assert_excerpts_with_selections(indoc! {"
18749        [EXCERPT]
18750        [FOLDED]
18751        [EXCERPT]
18752        ˇa1
18753        b1
18754        [EXCERPT]
18755        [FOLDED]
18756        [EXCERPT]
18757        [FOLDED]
18758        "
18759    });
18760    cx.simulate_keystroke("down");
18761    cx.assert_excerpts_with_selections(indoc! {"
18762        [EXCERPT]
18763        [FOLDED]
18764        [EXCERPT]
18765        a1
18766        ˇb1
18767        [EXCERPT]
18768        [FOLDED]
18769        [EXCERPT]
18770        [FOLDED]
18771        "
18772    });
18773    cx.simulate_keystroke("down");
18774    cx.assert_excerpts_with_selections(indoc! {"
18775        [EXCERPT]
18776        [FOLDED]
18777        [EXCERPT]
18778        a1
18779        b1
18780        ˇ[EXCERPT]
18781        [FOLDED]
18782        [EXCERPT]
18783        [FOLDED]
18784        "
18785    });
18786    cx.simulate_keystroke("down");
18787    cx.assert_excerpts_with_selections(indoc! {"
18788        [EXCERPT]
18789        [FOLDED]
18790        [EXCERPT]
18791        a1
18792        b1
18793        [EXCERPT]
18794        ˇ[FOLDED]
18795        [EXCERPT]
18796        [FOLDED]
18797        "
18798    });
18799    for _ in 0..5 {
18800        cx.simulate_keystroke("down");
18801        cx.assert_excerpts_with_selections(indoc! {"
18802            [EXCERPT]
18803            [FOLDED]
18804            [EXCERPT]
18805            a1
18806            b1
18807            [EXCERPT]
18808            [FOLDED]
18809            [EXCERPT]
18810            ˇ[FOLDED]
18811            "
18812        });
18813    }
18814
18815    cx.simulate_keystroke("up");
18816    cx.assert_excerpts_with_selections(indoc! {"
18817        [EXCERPT]
18818        [FOLDED]
18819        [EXCERPT]
18820        a1
18821        b1
18822        [EXCERPT]
18823        ˇ[FOLDED]
18824        [EXCERPT]
18825        [FOLDED]
18826        "
18827    });
18828    cx.simulate_keystroke("up");
18829    cx.assert_excerpts_with_selections(indoc! {"
18830        [EXCERPT]
18831        [FOLDED]
18832        [EXCERPT]
18833        a1
18834        b1
18835        ˇ[EXCERPT]
18836        [FOLDED]
18837        [EXCERPT]
18838        [FOLDED]
18839        "
18840    });
18841    cx.simulate_keystroke("up");
18842    cx.assert_excerpts_with_selections(indoc! {"
18843        [EXCERPT]
18844        [FOLDED]
18845        [EXCERPT]
18846        a1
18847        ˇb1
18848        [EXCERPT]
18849        [FOLDED]
18850        [EXCERPT]
18851        [FOLDED]
18852        "
18853    });
18854    cx.simulate_keystroke("up");
18855    cx.assert_excerpts_with_selections(indoc! {"
18856        [EXCERPT]
18857        [FOLDED]
18858        [EXCERPT]
18859        ˇa1
18860        b1
18861        [EXCERPT]
18862        [FOLDED]
18863        [EXCERPT]
18864        [FOLDED]
18865        "
18866    });
18867    for _ in 0..5 {
18868        cx.simulate_keystroke("up");
18869        cx.assert_excerpts_with_selections(indoc! {"
18870            [EXCERPT]
18871            ˇ[FOLDED]
18872            [EXCERPT]
18873            a1
18874            b1
18875            [EXCERPT]
18876            [FOLDED]
18877            [EXCERPT]
18878            [FOLDED]
18879            "
18880        });
18881    }
18882}
18883
18884#[gpui::test]
18885async fn test_inline_completion_text(cx: &mut TestAppContext) {
18886    init_test(cx, |_| {});
18887
18888    // Simple insertion
18889    assert_highlighted_edits(
18890        "Hello, world!",
18891        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18892        true,
18893        cx,
18894        |highlighted_edits, cx| {
18895            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18896            assert_eq!(highlighted_edits.highlights.len(), 1);
18897            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18898            assert_eq!(
18899                highlighted_edits.highlights[0].1.background_color,
18900                Some(cx.theme().status().created_background)
18901            );
18902        },
18903    )
18904    .await;
18905
18906    // Replacement
18907    assert_highlighted_edits(
18908        "This is a test.",
18909        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18910        false,
18911        cx,
18912        |highlighted_edits, cx| {
18913            assert_eq!(highlighted_edits.text, "That is a test.");
18914            assert_eq!(highlighted_edits.highlights.len(), 1);
18915            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18916            assert_eq!(
18917                highlighted_edits.highlights[0].1.background_color,
18918                Some(cx.theme().status().created_background)
18919            );
18920        },
18921    )
18922    .await;
18923
18924    // Multiple edits
18925    assert_highlighted_edits(
18926        "Hello, world!",
18927        vec![
18928            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18929            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18930        ],
18931        false,
18932        cx,
18933        |highlighted_edits, cx| {
18934            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18935            assert_eq!(highlighted_edits.highlights.len(), 2);
18936            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18937            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18938            assert_eq!(
18939                highlighted_edits.highlights[0].1.background_color,
18940                Some(cx.theme().status().created_background)
18941            );
18942            assert_eq!(
18943                highlighted_edits.highlights[1].1.background_color,
18944                Some(cx.theme().status().created_background)
18945            );
18946        },
18947    )
18948    .await;
18949
18950    // Multiple lines with edits
18951    assert_highlighted_edits(
18952        "First line\nSecond line\nThird line\nFourth line",
18953        vec![
18954            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18955            (
18956                Point::new(2, 0)..Point::new(2, 10),
18957                "New third line".to_string(),
18958            ),
18959            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18960        ],
18961        false,
18962        cx,
18963        |highlighted_edits, cx| {
18964            assert_eq!(
18965                highlighted_edits.text,
18966                "Second modified\nNew third line\nFourth updated line"
18967            );
18968            assert_eq!(highlighted_edits.highlights.len(), 3);
18969            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18970            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18971            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18972            for highlight in &highlighted_edits.highlights {
18973                assert_eq!(
18974                    highlight.1.background_color,
18975                    Some(cx.theme().status().created_background)
18976                );
18977            }
18978        },
18979    )
18980    .await;
18981}
18982
18983#[gpui::test]
18984async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18985    init_test(cx, |_| {});
18986
18987    // Deletion
18988    assert_highlighted_edits(
18989        "Hello, world!",
18990        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18991        true,
18992        cx,
18993        |highlighted_edits, cx| {
18994            assert_eq!(highlighted_edits.text, "Hello, world!");
18995            assert_eq!(highlighted_edits.highlights.len(), 1);
18996            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18997            assert_eq!(
18998                highlighted_edits.highlights[0].1.background_color,
18999                Some(cx.theme().status().deleted_background)
19000            );
19001        },
19002    )
19003    .await;
19004
19005    // Insertion
19006    assert_highlighted_edits(
19007        "Hello, world!",
19008        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
19009        true,
19010        cx,
19011        |highlighted_edits, cx| {
19012            assert_eq!(highlighted_edits.highlights.len(), 1);
19013            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
19014            assert_eq!(
19015                highlighted_edits.highlights[0].1.background_color,
19016                Some(cx.theme().status().created_background)
19017            );
19018        },
19019    )
19020    .await;
19021}
19022
19023async fn assert_highlighted_edits(
19024    text: &str,
19025    edits: Vec<(Range<Point>, String)>,
19026    include_deletions: bool,
19027    cx: &mut TestAppContext,
19028    assertion_fn: impl Fn(HighlightedText, &App),
19029) {
19030    let window = cx.add_window(|window, cx| {
19031        let buffer = MultiBuffer::build_simple(text, cx);
19032        Editor::new(EditorMode::full(), buffer, None, window, cx)
19033    });
19034    let cx = &mut VisualTestContext::from_window(*window, cx);
19035
19036    let (buffer, snapshot) = window
19037        .update(cx, |editor, _window, cx| {
19038            (
19039                editor.buffer().clone(),
19040                editor.buffer().read(cx).snapshot(cx),
19041            )
19042        })
19043        .unwrap();
19044
19045    let edits = edits
19046        .into_iter()
19047        .map(|(range, edit)| {
19048            (
19049                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
19050                edit,
19051            )
19052        })
19053        .collect::<Vec<_>>();
19054
19055    let text_anchor_edits = edits
19056        .clone()
19057        .into_iter()
19058        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
19059        .collect::<Vec<_>>();
19060
19061    let edit_preview = window
19062        .update(cx, |_, _window, cx| {
19063            buffer
19064                .read(cx)
19065                .as_singleton()
19066                .unwrap()
19067                .read(cx)
19068                .preview_edits(text_anchor_edits.into(), cx)
19069        })
19070        .unwrap()
19071        .await;
19072
19073    cx.update(|_window, cx| {
19074        let highlighted_edits = inline_completion_edit_text(
19075            &snapshot.as_singleton().unwrap().2,
19076            &edits,
19077            &edit_preview,
19078            include_deletions,
19079            cx,
19080        );
19081        assertion_fn(highlighted_edits, cx)
19082    });
19083}
19084
19085#[track_caller]
19086fn assert_breakpoint(
19087    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
19088    path: &Arc<Path>,
19089    expected: Vec<(u32, Breakpoint)>,
19090) {
19091    if expected.len() == 0usize {
19092        assert!(!breakpoints.contains_key(path), "{}", path.display());
19093    } else {
19094        let mut breakpoint = breakpoints
19095            .get(path)
19096            .unwrap()
19097            .into_iter()
19098            .map(|breakpoint| {
19099                (
19100                    breakpoint.row,
19101                    Breakpoint {
19102                        message: breakpoint.message.clone(),
19103                        state: breakpoint.state,
19104                        condition: breakpoint.condition.clone(),
19105                        hit_condition: breakpoint.hit_condition.clone(),
19106                    },
19107                )
19108            })
19109            .collect::<Vec<_>>();
19110
19111        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
19112
19113        assert_eq!(expected, breakpoint);
19114    }
19115}
19116
19117fn add_log_breakpoint_at_cursor(
19118    editor: &mut Editor,
19119    log_message: &str,
19120    window: &mut Window,
19121    cx: &mut Context<Editor>,
19122) {
19123    let (anchor, bp) = editor
19124        .breakpoints_at_cursors(window, cx)
19125        .first()
19126        .and_then(|(anchor, bp)| {
19127            if let Some(bp) = bp {
19128                Some((*anchor, bp.clone()))
19129            } else {
19130                None
19131            }
19132        })
19133        .unwrap_or_else(|| {
19134            let cursor_position: Point = editor.selections.newest(cx).head();
19135
19136            let breakpoint_position = editor
19137                .snapshot(window, cx)
19138                .display_snapshot
19139                .buffer_snapshot
19140                .anchor_before(Point::new(cursor_position.row, 0));
19141
19142            (breakpoint_position, Breakpoint::new_log(&log_message))
19143        });
19144
19145    editor.edit_breakpoint_at_anchor(
19146        anchor,
19147        bp,
19148        BreakpointEditAction::EditLogMessage(log_message.into()),
19149        cx,
19150    );
19151}
19152
19153#[gpui::test]
19154async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
19155    init_test(cx, |_| {});
19156
19157    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19158    let fs = FakeFs::new(cx.executor());
19159    fs.insert_tree(
19160        path!("/a"),
19161        json!({
19162            "main.rs": sample_text,
19163        }),
19164    )
19165    .await;
19166    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19167    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19168    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19169
19170    let fs = FakeFs::new(cx.executor());
19171    fs.insert_tree(
19172        path!("/a"),
19173        json!({
19174            "main.rs": sample_text,
19175        }),
19176    )
19177    .await;
19178    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19179    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19180    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19181    let worktree_id = workspace
19182        .update(cx, |workspace, _window, cx| {
19183            workspace.project().update(cx, |project, cx| {
19184                project.worktrees(cx).next().unwrap().read(cx).id()
19185            })
19186        })
19187        .unwrap();
19188
19189    let buffer = project
19190        .update(cx, |project, cx| {
19191            project.open_buffer((worktree_id, "main.rs"), cx)
19192        })
19193        .await
19194        .unwrap();
19195
19196    let (editor, cx) = cx.add_window_view(|window, cx| {
19197        Editor::new(
19198            EditorMode::full(),
19199            MultiBuffer::build_from_buffer(buffer, cx),
19200            Some(project.clone()),
19201            window,
19202            cx,
19203        )
19204    });
19205
19206    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19207    let abs_path = project.read_with(cx, |project, cx| {
19208        project
19209            .absolute_path(&project_path, cx)
19210            .map(|path_buf| Arc::from(path_buf.to_owned()))
19211            .unwrap()
19212    });
19213
19214    // assert we can add breakpoint on the first line
19215    editor.update_in(cx, |editor, window, cx| {
19216        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19217        editor.move_to_end(&MoveToEnd, window, cx);
19218        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19219    });
19220
19221    let breakpoints = editor.update(cx, |editor, cx| {
19222        editor
19223            .breakpoint_store()
19224            .as_ref()
19225            .unwrap()
19226            .read(cx)
19227            .all_source_breakpoints(cx)
19228            .clone()
19229    });
19230
19231    assert_eq!(1, breakpoints.len());
19232    assert_breakpoint(
19233        &breakpoints,
19234        &abs_path,
19235        vec![
19236            (0, Breakpoint::new_standard()),
19237            (3, Breakpoint::new_standard()),
19238        ],
19239    );
19240
19241    editor.update_in(cx, |editor, window, cx| {
19242        editor.move_to_beginning(&MoveToBeginning, window, cx);
19243        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19244    });
19245
19246    let breakpoints = editor.update(cx, |editor, cx| {
19247        editor
19248            .breakpoint_store()
19249            .as_ref()
19250            .unwrap()
19251            .read(cx)
19252            .all_source_breakpoints(cx)
19253            .clone()
19254    });
19255
19256    assert_eq!(1, breakpoints.len());
19257    assert_breakpoint(
19258        &breakpoints,
19259        &abs_path,
19260        vec![(3, Breakpoint::new_standard())],
19261    );
19262
19263    editor.update_in(cx, |editor, window, cx| {
19264        editor.move_to_end(&MoveToEnd, window, cx);
19265        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19266    });
19267
19268    let breakpoints = editor.update(cx, |editor, cx| {
19269        editor
19270            .breakpoint_store()
19271            .as_ref()
19272            .unwrap()
19273            .read(cx)
19274            .all_source_breakpoints(cx)
19275            .clone()
19276    });
19277
19278    assert_eq!(0, breakpoints.len());
19279    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19280}
19281
19282#[gpui::test]
19283async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
19284    init_test(cx, |_| {});
19285
19286    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19287
19288    let fs = FakeFs::new(cx.executor());
19289    fs.insert_tree(
19290        path!("/a"),
19291        json!({
19292            "main.rs": sample_text,
19293        }),
19294    )
19295    .await;
19296    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19297    let (workspace, cx) =
19298        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19299
19300    let worktree_id = workspace.update(cx, |workspace, cx| {
19301        workspace.project().update(cx, |project, cx| {
19302            project.worktrees(cx).next().unwrap().read(cx).id()
19303        })
19304    });
19305
19306    let buffer = project
19307        .update(cx, |project, cx| {
19308            project.open_buffer((worktree_id, "main.rs"), cx)
19309        })
19310        .await
19311        .unwrap();
19312
19313    let (editor, cx) = cx.add_window_view(|window, cx| {
19314        Editor::new(
19315            EditorMode::full(),
19316            MultiBuffer::build_from_buffer(buffer, cx),
19317            Some(project.clone()),
19318            window,
19319            cx,
19320        )
19321    });
19322
19323    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19324    let abs_path = project.read_with(cx, |project, cx| {
19325        project
19326            .absolute_path(&project_path, cx)
19327            .map(|path_buf| Arc::from(path_buf.to_owned()))
19328            .unwrap()
19329    });
19330
19331    editor.update_in(cx, |editor, window, cx| {
19332        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19333    });
19334
19335    let breakpoints = editor.update(cx, |editor, cx| {
19336        editor
19337            .breakpoint_store()
19338            .as_ref()
19339            .unwrap()
19340            .read(cx)
19341            .all_source_breakpoints(cx)
19342            .clone()
19343    });
19344
19345    assert_breakpoint(
19346        &breakpoints,
19347        &abs_path,
19348        vec![(0, Breakpoint::new_log("hello world"))],
19349    );
19350
19351    // Removing a log message from a log breakpoint should remove it
19352    editor.update_in(cx, |editor, window, cx| {
19353        add_log_breakpoint_at_cursor(editor, "", window, cx);
19354    });
19355
19356    let breakpoints = editor.update(cx, |editor, cx| {
19357        editor
19358            .breakpoint_store()
19359            .as_ref()
19360            .unwrap()
19361            .read(cx)
19362            .all_source_breakpoints(cx)
19363            .clone()
19364    });
19365
19366    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19367
19368    editor.update_in(cx, |editor, window, cx| {
19369        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19370        editor.move_to_end(&MoveToEnd, window, cx);
19371        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19372        // Not adding a log message to a standard breakpoint shouldn't remove it
19373        add_log_breakpoint_at_cursor(editor, "", window, cx);
19374    });
19375
19376    let breakpoints = editor.update(cx, |editor, cx| {
19377        editor
19378            .breakpoint_store()
19379            .as_ref()
19380            .unwrap()
19381            .read(cx)
19382            .all_source_breakpoints(cx)
19383            .clone()
19384    });
19385
19386    assert_breakpoint(
19387        &breakpoints,
19388        &abs_path,
19389        vec![
19390            (0, Breakpoint::new_standard()),
19391            (3, Breakpoint::new_standard()),
19392        ],
19393    );
19394
19395    editor.update_in(cx, |editor, window, cx| {
19396        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19397    });
19398
19399    let breakpoints = editor.update(cx, |editor, cx| {
19400        editor
19401            .breakpoint_store()
19402            .as_ref()
19403            .unwrap()
19404            .read(cx)
19405            .all_source_breakpoints(cx)
19406            .clone()
19407    });
19408
19409    assert_breakpoint(
19410        &breakpoints,
19411        &abs_path,
19412        vec![
19413            (0, Breakpoint::new_standard()),
19414            (3, Breakpoint::new_log("hello world")),
19415        ],
19416    );
19417
19418    editor.update_in(cx, |editor, window, cx| {
19419        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19420    });
19421
19422    let breakpoints = editor.update(cx, |editor, cx| {
19423        editor
19424            .breakpoint_store()
19425            .as_ref()
19426            .unwrap()
19427            .read(cx)
19428            .all_source_breakpoints(cx)
19429            .clone()
19430    });
19431
19432    assert_breakpoint(
19433        &breakpoints,
19434        &abs_path,
19435        vec![
19436            (0, Breakpoint::new_standard()),
19437            (3, Breakpoint::new_log("hello Earth!!")),
19438        ],
19439    );
19440}
19441
19442/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19443/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19444/// or when breakpoints were placed out of order. This tests for a regression too
19445#[gpui::test]
19446async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19447    init_test(cx, |_| {});
19448
19449    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19450    let fs = FakeFs::new(cx.executor());
19451    fs.insert_tree(
19452        path!("/a"),
19453        json!({
19454            "main.rs": sample_text,
19455        }),
19456    )
19457    .await;
19458    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19459    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19460    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19461
19462    let fs = FakeFs::new(cx.executor());
19463    fs.insert_tree(
19464        path!("/a"),
19465        json!({
19466            "main.rs": sample_text,
19467        }),
19468    )
19469    .await;
19470    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19471    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19472    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19473    let worktree_id = workspace
19474        .update(cx, |workspace, _window, cx| {
19475            workspace.project().update(cx, |project, cx| {
19476                project.worktrees(cx).next().unwrap().read(cx).id()
19477            })
19478        })
19479        .unwrap();
19480
19481    let buffer = project
19482        .update(cx, |project, cx| {
19483            project.open_buffer((worktree_id, "main.rs"), cx)
19484        })
19485        .await
19486        .unwrap();
19487
19488    let (editor, cx) = cx.add_window_view(|window, cx| {
19489        Editor::new(
19490            EditorMode::full(),
19491            MultiBuffer::build_from_buffer(buffer, cx),
19492            Some(project.clone()),
19493            window,
19494            cx,
19495        )
19496    });
19497
19498    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19499    let abs_path = project.read_with(cx, |project, cx| {
19500        project
19501            .absolute_path(&project_path, cx)
19502            .map(|path_buf| Arc::from(path_buf.to_owned()))
19503            .unwrap()
19504    });
19505
19506    // assert we can add breakpoint on the first line
19507    editor.update_in(cx, |editor, window, cx| {
19508        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19509        editor.move_to_end(&MoveToEnd, window, cx);
19510        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19511        editor.move_up(&MoveUp, window, cx);
19512        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19513    });
19514
19515    let breakpoints = editor.update(cx, |editor, cx| {
19516        editor
19517            .breakpoint_store()
19518            .as_ref()
19519            .unwrap()
19520            .read(cx)
19521            .all_source_breakpoints(cx)
19522            .clone()
19523    });
19524
19525    assert_eq!(1, breakpoints.len());
19526    assert_breakpoint(
19527        &breakpoints,
19528        &abs_path,
19529        vec![
19530            (0, Breakpoint::new_standard()),
19531            (2, Breakpoint::new_standard()),
19532            (3, Breakpoint::new_standard()),
19533        ],
19534    );
19535
19536    editor.update_in(cx, |editor, window, cx| {
19537        editor.move_to_beginning(&MoveToBeginning, window, cx);
19538        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19539        editor.move_to_end(&MoveToEnd, window, cx);
19540        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19541        // Disabling a breakpoint that doesn't exist should do nothing
19542        editor.move_up(&MoveUp, window, cx);
19543        editor.move_up(&MoveUp, window, cx);
19544        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19545    });
19546
19547    let breakpoints = editor.update(cx, |editor, cx| {
19548        editor
19549            .breakpoint_store()
19550            .as_ref()
19551            .unwrap()
19552            .read(cx)
19553            .all_source_breakpoints(cx)
19554            .clone()
19555    });
19556
19557    let disable_breakpoint = {
19558        let mut bp = Breakpoint::new_standard();
19559        bp.state = BreakpointState::Disabled;
19560        bp
19561    };
19562
19563    assert_eq!(1, breakpoints.len());
19564    assert_breakpoint(
19565        &breakpoints,
19566        &abs_path,
19567        vec![
19568            (0, disable_breakpoint.clone()),
19569            (2, Breakpoint::new_standard()),
19570            (3, disable_breakpoint.clone()),
19571        ],
19572    );
19573
19574    editor.update_in(cx, |editor, window, cx| {
19575        editor.move_to_beginning(&MoveToBeginning, window, cx);
19576        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19577        editor.move_to_end(&MoveToEnd, window, cx);
19578        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19579        editor.move_up(&MoveUp, window, cx);
19580        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19581    });
19582
19583    let breakpoints = editor.update(cx, |editor, cx| {
19584        editor
19585            .breakpoint_store()
19586            .as_ref()
19587            .unwrap()
19588            .read(cx)
19589            .all_source_breakpoints(cx)
19590            .clone()
19591    });
19592
19593    assert_eq!(1, breakpoints.len());
19594    assert_breakpoint(
19595        &breakpoints,
19596        &abs_path,
19597        vec![
19598            (0, Breakpoint::new_standard()),
19599            (2, disable_breakpoint),
19600            (3, Breakpoint::new_standard()),
19601        ],
19602    );
19603}
19604
19605#[gpui::test]
19606async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19607    init_test(cx, |_| {});
19608    let capabilities = lsp::ServerCapabilities {
19609        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19610            prepare_provider: Some(true),
19611            work_done_progress_options: Default::default(),
19612        })),
19613        ..Default::default()
19614    };
19615    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19616
19617    cx.set_state(indoc! {"
19618        struct Fˇoo {}
19619    "});
19620
19621    cx.update_editor(|editor, _, cx| {
19622        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19623        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19624        editor.highlight_background::<DocumentHighlightRead>(
19625            &[highlight_range],
19626            |c| c.editor_document_highlight_read_background,
19627            cx,
19628        );
19629    });
19630
19631    let mut prepare_rename_handler = cx
19632        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19633            move |_, _, _| async move {
19634                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19635                    start: lsp::Position {
19636                        line: 0,
19637                        character: 7,
19638                    },
19639                    end: lsp::Position {
19640                        line: 0,
19641                        character: 10,
19642                    },
19643                })))
19644            },
19645        );
19646    let prepare_rename_task = cx
19647        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19648        .expect("Prepare rename was not started");
19649    prepare_rename_handler.next().await.unwrap();
19650    prepare_rename_task.await.expect("Prepare rename failed");
19651
19652    let mut rename_handler =
19653        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19654            let edit = lsp::TextEdit {
19655                range: lsp::Range {
19656                    start: lsp::Position {
19657                        line: 0,
19658                        character: 7,
19659                    },
19660                    end: lsp::Position {
19661                        line: 0,
19662                        character: 10,
19663                    },
19664                },
19665                new_text: "FooRenamed".to_string(),
19666            };
19667            Ok(Some(lsp::WorkspaceEdit::new(
19668                // Specify the same edit twice
19669                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19670            )))
19671        });
19672    let rename_task = cx
19673        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19674        .expect("Confirm rename was not started");
19675    rename_handler.next().await.unwrap();
19676    rename_task.await.expect("Confirm rename failed");
19677    cx.run_until_parked();
19678
19679    // Despite two edits, only one is actually applied as those are identical
19680    cx.assert_editor_state(indoc! {"
19681        struct FooRenamedˇ {}
19682    "});
19683}
19684
19685#[gpui::test]
19686async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19687    init_test(cx, |_| {});
19688    // These capabilities indicate that the server does not support prepare rename.
19689    let capabilities = lsp::ServerCapabilities {
19690        rename_provider: Some(lsp::OneOf::Left(true)),
19691        ..Default::default()
19692    };
19693    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19694
19695    cx.set_state(indoc! {"
19696        struct Fˇoo {}
19697    "});
19698
19699    cx.update_editor(|editor, _window, cx| {
19700        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19701        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19702        editor.highlight_background::<DocumentHighlightRead>(
19703            &[highlight_range],
19704            |c| c.editor_document_highlight_read_background,
19705            cx,
19706        );
19707    });
19708
19709    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19710        .expect("Prepare rename was not started")
19711        .await
19712        .expect("Prepare rename failed");
19713
19714    let mut rename_handler =
19715        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19716            let edit = lsp::TextEdit {
19717                range: lsp::Range {
19718                    start: lsp::Position {
19719                        line: 0,
19720                        character: 7,
19721                    },
19722                    end: lsp::Position {
19723                        line: 0,
19724                        character: 10,
19725                    },
19726                },
19727                new_text: "FooRenamed".to_string(),
19728            };
19729            Ok(Some(lsp::WorkspaceEdit::new(
19730                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19731            )))
19732        });
19733    let rename_task = cx
19734        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19735        .expect("Confirm rename was not started");
19736    rename_handler.next().await.unwrap();
19737    rename_task.await.expect("Confirm rename failed");
19738    cx.run_until_parked();
19739
19740    // Correct range is renamed, as `surrounding_word` is used to find it.
19741    cx.assert_editor_state(indoc! {"
19742        struct FooRenamedˇ {}
19743    "});
19744}
19745
19746#[gpui::test]
19747async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19748    init_test(cx, |_| {});
19749    let mut cx = EditorTestContext::new(cx).await;
19750
19751    let language = Arc::new(
19752        Language::new(
19753            LanguageConfig::default(),
19754            Some(tree_sitter_html::LANGUAGE.into()),
19755        )
19756        .with_brackets_query(
19757            r#"
19758            ("<" @open "/>" @close)
19759            ("</" @open ">" @close)
19760            ("<" @open ">" @close)
19761            ("\"" @open "\"" @close)
19762            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19763        "#,
19764        )
19765        .unwrap(),
19766    );
19767    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19768
19769    cx.set_state(indoc! {"
19770        <span>ˇ</span>
19771    "});
19772    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19773    cx.assert_editor_state(indoc! {"
19774        <span>
19775        ˇ
19776        </span>
19777    "});
19778
19779    cx.set_state(indoc! {"
19780        <span><span></span>ˇ</span>
19781    "});
19782    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19783    cx.assert_editor_state(indoc! {"
19784        <span><span></span>
19785        ˇ</span>
19786    "});
19787
19788    cx.set_state(indoc! {"
19789        <span>ˇ
19790        </span>
19791    "});
19792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19793    cx.assert_editor_state(indoc! {"
19794        <span>
19795        ˇ
19796        </span>
19797    "});
19798}
19799
19800#[gpui::test(iterations = 10)]
19801async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19802    init_test(cx, |_| {});
19803
19804    let fs = FakeFs::new(cx.executor());
19805    fs.insert_tree(
19806        path!("/dir"),
19807        json!({
19808            "a.ts": "a",
19809        }),
19810    )
19811    .await;
19812
19813    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19814    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19815    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19816
19817    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19818    language_registry.add(Arc::new(Language::new(
19819        LanguageConfig {
19820            name: "TypeScript".into(),
19821            matcher: LanguageMatcher {
19822                path_suffixes: vec!["ts".to_string()],
19823                ..Default::default()
19824            },
19825            ..Default::default()
19826        },
19827        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19828    )));
19829    let mut fake_language_servers = language_registry.register_fake_lsp(
19830        "TypeScript",
19831        FakeLspAdapter {
19832            capabilities: lsp::ServerCapabilities {
19833                code_lens_provider: Some(lsp::CodeLensOptions {
19834                    resolve_provider: Some(true),
19835                }),
19836                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19837                    commands: vec!["_the/command".to_string()],
19838                    ..lsp::ExecuteCommandOptions::default()
19839                }),
19840                ..lsp::ServerCapabilities::default()
19841            },
19842            ..FakeLspAdapter::default()
19843        },
19844    );
19845
19846    let (buffer, _handle) = project
19847        .update(cx, |p, cx| {
19848            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19849        })
19850        .await
19851        .unwrap();
19852    cx.executor().run_until_parked();
19853
19854    let fake_server = fake_language_servers.next().await.unwrap();
19855
19856    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19857    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19858    drop(buffer_snapshot);
19859    let actions = cx
19860        .update_window(*workspace, |_, window, cx| {
19861            project.code_actions(&buffer, anchor..anchor, window, cx)
19862        })
19863        .unwrap();
19864
19865    fake_server
19866        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19867            Ok(Some(vec![
19868                lsp::CodeLens {
19869                    range: lsp::Range::default(),
19870                    command: Some(lsp::Command {
19871                        title: "Code lens command".to_owned(),
19872                        command: "_the/command".to_owned(),
19873                        arguments: None,
19874                    }),
19875                    data: None,
19876                },
19877                lsp::CodeLens {
19878                    range: lsp::Range::default(),
19879                    command: Some(lsp::Command {
19880                        title: "Command not in capabilities".to_owned(),
19881                        command: "not in capabilities".to_owned(),
19882                        arguments: None,
19883                    }),
19884                    data: None,
19885                },
19886                lsp::CodeLens {
19887                    range: lsp::Range {
19888                        start: lsp::Position {
19889                            line: 1,
19890                            character: 1,
19891                        },
19892                        end: lsp::Position {
19893                            line: 1,
19894                            character: 1,
19895                        },
19896                    },
19897                    command: Some(lsp::Command {
19898                        title: "Command not in range".to_owned(),
19899                        command: "_the/command".to_owned(),
19900                        arguments: None,
19901                    }),
19902                    data: None,
19903                },
19904            ]))
19905        })
19906        .next()
19907        .await;
19908
19909    let actions = actions.await.unwrap();
19910    assert_eq!(
19911        actions.len(),
19912        1,
19913        "Should have only one valid action for the 0..0 range"
19914    );
19915    let action = actions[0].clone();
19916    let apply = project.update(cx, |project, cx| {
19917        project.apply_code_action(buffer.clone(), action, true, cx)
19918    });
19919
19920    // Resolving the code action does not populate its edits. In absence of
19921    // edits, we must execute the given command.
19922    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19923        |mut lens, _| async move {
19924            let lens_command = lens.command.as_mut().expect("should have a command");
19925            assert_eq!(lens_command.title, "Code lens command");
19926            lens_command.arguments = Some(vec![json!("the-argument")]);
19927            Ok(lens)
19928        },
19929    );
19930
19931    // While executing the command, the language server sends the editor
19932    // a `workspaceEdit` request.
19933    fake_server
19934        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19935            let fake = fake_server.clone();
19936            move |params, _| {
19937                assert_eq!(params.command, "_the/command");
19938                let fake = fake.clone();
19939                async move {
19940                    fake.server
19941                        .request::<lsp::request::ApplyWorkspaceEdit>(
19942                            lsp::ApplyWorkspaceEditParams {
19943                                label: None,
19944                                edit: lsp::WorkspaceEdit {
19945                                    changes: Some(
19946                                        [(
19947                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19948                                            vec![lsp::TextEdit {
19949                                                range: lsp::Range::new(
19950                                                    lsp::Position::new(0, 0),
19951                                                    lsp::Position::new(0, 0),
19952                                                ),
19953                                                new_text: "X".into(),
19954                                            }],
19955                                        )]
19956                                        .into_iter()
19957                                        .collect(),
19958                                    ),
19959                                    ..Default::default()
19960                                },
19961                            },
19962                        )
19963                        .await
19964                        .into_response()
19965                        .unwrap();
19966                    Ok(Some(json!(null)))
19967                }
19968            }
19969        })
19970        .next()
19971        .await;
19972
19973    // Applying the code lens command returns a project transaction containing the edits
19974    // sent by the language server in its `workspaceEdit` request.
19975    let transaction = apply.await.unwrap();
19976    assert!(transaction.0.contains_key(&buffer));
19977    buffer.update(cx, |buffer, cx| {
19978        assert_eq!(buffer.text(), "Xa");
19979        buffer.undo(cx);
19980        assert_eq!(buffer.text(), "a");
19981    });
19982}
19983
19984#[gpui::test]
19985async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19986    init_test(cx, |_| {});
19987
19988    let fs = FakeFs::new(cx.executor());
19989    let main_text = r#"fn main() {
19990println!("1");
19991println!("2");
19992println!("3");
19993println!("4");
19994println!("5");
19995}"#;
19996    let lib_text = "mod foo {}";
19997    fs.insert_tree(
19998        path!("/a"),
19999        json!({
20000            "lib.rs": lib_text,
20001            "main.rs": main_text,
20002        }),
20003    )
20004    .await;
20005
20006    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20007    let (workspace, cx) =
20008        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20009    let worktree_id = workspace.update(cx, |workspace, cx| {
20010        workspace.project().update(cx, |project, cx| {
20011            project.worktrees(cx).next().unwrap().read(cx).id()
20012        })
20013    });
20014
20015    let expected_ranges = vec![
20016        Point::new(0, 0)..Point::new(0, 0),
20017        Point::new(1, 0)..Point::new(1, 1),
20018        Point::new(2, 0)..Point::new(2, 2),
20019        Point::new(3, 0)..Point::new(3, 3),
20020    ];
20021
20022    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20023    let editor_1 = workspace
20024        .update_in(cx, |workspace, window, cx| {
20025            workspace.open_path(
20026                (worktree_id, "main.rs"),
20027                Some(pane_1.downgrade()),
20028                true,
20029                window,
20030                cx,
20031            )
20032        })
20033        .unwrap()
20034        .await
20035        .downcast::<Editor>()
20036        .unwrap();
20037    pane_1.update(cx, |pane, cx| {
20038        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20039        open_editor.update(cx, |editor, cx| {
20040            assert_eq!(
20041                editor.display_text(cx),
20042                main_text,
20043                "Original main.rs text on initial open",
20044            );
20045            assert_eq!(
20046                editor
20047                    .selections
20048                    .all::<Point>(cx)
20049                    .into_iter()
20050                    .map(|s| s.range())
20051                    .collect::<Vec<_>>(),
20052                vec![Point::zero()..Point::zero()],
20053                "Default selections on initial open",
20054            );
20055        })
20056    });
20057    editor_1.update_in(cx, |editor, window, cx| {
20058        editor.change_selections(None, window, cx, |s| {
20059            s.select_ranges(expected_ranges.clone());
20060        });
20061    });
20062
20063    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
20064        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
20065    });
20066    let editor_2 = workspace
20067        .update_in(cx, |workspace, window, cx| {
20068            workspace.open_path(
20069                (worktree_id, "main.rs"),
20070                Some(pane_2.downgrade()),
20071                true,
20072                window,
20073                cx,
20074            )
20075        })
20076        .unwrap()
20077        .await
20078        .downcast::<Editor>()
20079        .unwrap();
20080    pane_2.update(cx, |pane, cx| {
20081        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20082        open_editor.update(cx, |editor, cx| {
20083            assert_eq!(
20084                editor.display_text(cx),
20085                main_text,
20086                "Original main.rs text on initial open in another panel",
20087            );
20088            assert_eq!(
20089                editor
20090                    .selections
20091                    .all::<Point>(cx)
20092                    .into_iter()
20093                    .map(|s| s.range())
20094                    .collect::<Vec<_>>(),
20095                vec![Point::zero()..Point::zero()],
20096                "Default selections on initial open in another panel",
20097            );
20098        })
20099    });
20100
20101    editor_2.update_in(cx, |editor, window, cx| {
20102        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
20103    });
20104
20105    let _other_editor_1 = workspace
20106        .update_in(cx, |workspace, window, cx| {
20107            workspace.open_path(
20108                (worktree_id, "lib.rs"),
20109                Some(pane_1.downgrade()),
20110                true,
20111                window,
20112                cx,
20113            )
20114        })
20115        .unwrap()
20116        .await
20117        .downcast::<Editor>()
20118        .unwrap();
20119    pane_1
20120        .update_in(cx, |pane, window, cx| {
20121            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
20122        })
20123        .await
20124        .unwrap();
20125    drop(editor_1);
20126    pane_1.update(cx, |pane, cx| {
20127        pane.active_item()
20128            .unwrap()
20129            .downcast::<Editor>()
20130            .unwrap()
20131            .update(cx, |editor, cx| {
20132                assert_eq!(
20133                    editor.display_text(cx),
20134                    lib_text,
20135                    "Other file should be open and active",
20136                );
20137            });
20138        assert_eq!(pane.items().count(), 1, "No other editors should be open");
20139    });
20140
20141    let _other_editor_2 = workspace
20142        .update_in(cx, |workspace, window, cx| {
20143            workspace.open_path(
20144                (worktree_id, "lib.rs"),
20145                Some(pane_2.downgrade()),
20146                true,
20147                window,
20148                cx,
20149            )
20150        })
20151        .unwrap()
20152        .await
20153        .downcast::<Editor>()
20154        .unwrap();
20155    pane_2
20156        .update_in(cx, |pane, window, cx| {
20157            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
20158        })
20159        .await
20160        .unwrap();
20161    drop(editor_2);
20162    pane_2.update(cx, |pane, cx| {
20163        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20164        open_editor.update(cx, |editor, cx| {
20165            assert_eq!(
20166                editor.display_text(cx),
20167                lib_text,
20168                "Other file should be open and active in another panel too",
20169            );
20170        });
20171        assert_eq!(
20172            pane.items().count(),
20173            1,
20174            "No other editors should be open in another pane",
20175        );
20176    });
20177
20178    let _editor_1_reopened = workspace
20179        .update_in(cx, |workspace, window, cx| {
20180            workspace.open_path(
20181                (worktree_id, "main.rs"),
20182                Some(pane_1.downgrade()),
20183                true,
20184                window,
20185                cx,
20186            )
20187        })
20188        .unwrap()
20189        .await
20190        .downcast::<Editor>()
20191        .unwrap();
20192    let _editor_2_reopened = workspace
20193        .update_in(cx, |workspace, window, cx| {
20194            workspace.open_path(
20195                (worktree_id, "main.rs"),
20196                Some(pane_2.downgrade()),
20197                true,
20198                window,
20199                cx,
20200            )
20201        })
20202        .unwrap()
20203        .await
20204        .downcast::<Editor>()
20205        .unwrap();
20206    pane_1.update(cx, |pane, cx| {
20207        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20208        open_editor.update(cx, |editor, cx| {
20209            assert_eq!(
20210                editor.display_text(cx),
20211                main_text,
20212                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
20213            );
20214            assert_eq!(
20215                editor
20216                    .selections
20217                    .all::<Point>(cx)
20218                    .into_iter()
20219                    .map(|s| s.range())
20220                    .collect::<Vec<_>>(),
20221                expected_ranges,
20222                "Previous editor in the 1st panel had selections and should get them restored on reopen",
20223            );
20224        })
20225    });
20226    pane_2.update(cx, |pane, cx| {
20227        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20228        open_editor.update(cx, |editor, cx| {
20229            assert_eq!(
20230                editor.display_text(cx),
20231                r#"fn main() {
20232⋯rintln!("1");
20233⋯intln!("2");
20234⋯ntln!("3");
20235println!("4");
20236println!("5");
20237}"#,
20238                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
20239            );
20240            assert_eq!(
20241                editor
20242                    .selections
20243                    .all::<Point>(cx)
20244                    .into_iter()
20245                    .map(|s| s.range())
20246                    .collect::<Vec<_>>(),
20247                vec![Point::zero()..Point::zero()],
20248                "Previous editor in the 2nd pane had no selections changed hence should restore none",
20249            );
20250        })
20251    });
20252}
20253
20254#[gpui::test]
20255async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
20256    init_test(cx, |_| {});
20257
20258    let fs = FakeFs::new(cx.executor());
20259    let main_text = r#"fn main() {
20260println!("1");
20261println!("2");
20262println!("3");
20263println!("4");
20264println!("5");
20265}"#;
20266    let lib_text = "mod foo {}";
20267    fs.insert_tree(
20268        path!("/a"),
20269        json!({
20270            "lib.rs": lib_text,
20271            "main.rs": main_text,
20272        }),
20273    )
20274    .await;
20275
20276    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20277    let (workspace, cx) =
20278        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20279    let worktree_id = workspace.update(cx, |workspace, cx| {
20280        workspace.project().update(cx, |project, cx| {
20281            project.worktrees(cx).next().unwrap().read(cx).id()
20282        })
20283    });
20284
20285    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20286    let editor = workspace
20287        .update_in(cx, |workspace, window, cx| {
20288            workspace.open_path(
20289                (worktree_id, "main.rs"),
20290                Some(pane.downgrade()),
20291                true,
20292                window,
20293                cx,
20294            )
20295        })
20296        .unwrap()
20297        .await
20298        .downcast::<Editor>()
20299        .unwrap();
20300    pane.update(cx, |pane, cx| {
20301        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20302        open_editor.update(cx, |editor, cx| {
20303            assert_eq!(
20304                editor.display_text(cx),
20305                main_text,
20306                "Original main.rs text on initial open",
20307            );
20308        })
20309    });
20310    editor.update_in(cx, |editor, window, cx| {
20311        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
20312    });
20313
20314    cx.update_global(|store: &mut SettingsStore, cx| {
20315        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20316            s.restore_on_file_reopen = Some(false);
20317        });
20318    });
20319    editor.update_in(cx, |editor, window, cx| {
20320        editor.fold_ranges(
20321            vec![
20322                Point::new(1, 0)..Point::new(1, 1),
20323                Point::new(2, 0)..Point::new(2, 2),
20324                Point::new(3, 0)..Point::new(3, 3),
20325            ],
20326            false,
20327            window,
20328            cx,
20329        );
20330    });
20331    pane.update_in(cx, |pane, window, cx| {
20332        pane.close_all_items(&CloseAllItems::default(), window, cx)
20333    })
20334    .await
20335    .unwrap();
20336    pane.update(cx, |pane, _| {
20337        assert!(pane.active_item().is_none());
20338    });
20339    cx.update_global(|store: &mut SettingsStore, cx| {
20340        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20341            s.restore_on_file_reopen = Some(true);
20342        });
20343    });
20344
20345    let _editor_reopened = workspace
20346        .update_in(cx, |workspace, window, cx| {
20347            workspace.open_path(
20348                (worktree_id, "main.rs"),
20349                Some(pane.downgrade()),
20350                true,
20351                window,
20352                cx,
20353            )
20354        })
20355        .unwrap()
20356        .await
20357        .downcast::<Editor>()
20358        .unwrap();
20359    pane.update(cx, |pane, cx| {
20360        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20361        open_editor.update(cx, |editor, cx| {
20362            assert_eq!(
20363                editor.display_text(cx),
20364                main_text,
20365                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20366            );
20367        })
20368    });
20369}
20370
20371#[gpui::test]
20372async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20373    struct EmptyModalView {
20374        focus_handle: gpui::FocusHandle,
20375    }
20376    impl EventEmitter<DismissEvent> for EmptyModalView {}
20377    impl Render for EmptyModalView {
20378        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20379            div()
20380        }
20381    }
20382    impl Focusable for EmptyModalView {
20383        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20384            self.focus_handle.clone()
20385        }
20386    }
20387    impl workspace::ModalView for EmptyModalView {}
20388    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20389        EmptyModalView {
20390            focus_handle: cx.focus_handle(),
20391        }
20392    }
20393
20394    init_test(cx, |_| {});
20395
20396    let fs = FakeFs::new(cx.executor());
20397    let project = Project::test(fs, [], cx).await;
20398    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20399    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20400    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20401    let editor = cx.new_window_entity(|window, cx| {
20402        Editor::new(
20403            EditorMode::full(),
20404            buffer,
20405            Some(project.clone()),
20406            window,
20407            cx,
20408        )
20409    });
20410    workspace
20411        .update(cx, |workspace, window, cx| {
20412            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20413        })
20414        .unwrap();
20415    editor.update_in(cx, |editor, window, cx| {
20416        editor.open_context_menu(&OpenContextMenu, window, cx);
20417        assert!(editor.mouse_context_menu.is_some());
20418    });
20419    workspace
20420        .update(cx, |workspace, window, cx| {
20421            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20422        })
20423        .unwrap();
20424    cx.read(|cx| {
20425        assert!(editor.read(cx).mouse_context_menu.is_none());
20426    });
20427}
20428
20429#[gpui::test]
20430async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20431    init_test(cx, |_| {});
20432
20433    let fs = FakeFs::new(cx.executor());
20434    fs.insert_file(path!("/file.html"), Default::default())
20435        .await;
20436
20437    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20438
20439    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20440    let html_language = Arc::new(Language::new(
20441        LanguageConfig {
20442            name: "HTML".into(),
20443            matcher: LanguageMatcher {
20444                path_suffixes: vec!["html".to_string()],
20445                ..LanguageMatcher::default()
20446            },
20447            brackets: BracketPairConfig {
20448                pairs: vec![BracketPair {
20449                    start: "<".into(),
20450                    end: ">".into(),
20451                    close: true,
20452                    ..Default::default()
20453                }],
20454                ..Default::default()
20455            },
20456            ..Default::default()
20457        },
20458        Some(tree_sitter_html::LANGUAGE.into()),
20459    ));
20460    language_registry.add(html_language);
20461    let mut fake_servers = language_registry.register_fake_lsp(
20462        "HTML",
20463        FakeLspAdapter {
20464            capabilities: lsp::ServerCapabilities {
20465                completion_provider: Some(lsp::CompletionOptions {
20466                    resolve_provider: Some(true),
20467                    ..Default::default()
20468                }),
20469                ..Default::default()
20470            },
20471            ..Default::default()
20472        },
20473    );
20474
20475    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20476    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20477
20478    let worktree_id = workspace
20479        .update(cx, |workspace, _window, cx| {
20480            workspace.project().update(cx, |project, cx| {
20481                project.worktrees(cx).next().unwrap().read(cx).id()
20482            })
20483        })
20484        .unwrap();
20485    project
20486        .update(cx, |project, cx| {
20487            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20488        })
20489        .await
20490        .unwrap();
20491    let editor = workspace
20492        .update(cx, |workspace, window, cx| {
20493            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20494        })
20495        .unwrap()
20496        .await
20497        .unwrap()
20498        .downcast::<Editor>()
20499        .unwrap();
20500
20501    let fake_server = fake_servers.next().await.unwrap();
20502    editor.update_in(cx, |editor, window, cx| {
20503        editor.set_text("<ad></ad>", window, cx);
20504        editor.change_selections(None, window, cx, |selections| {
20505            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20506        });
20507        let Some((buffer, _)) = editor
20508            .buffer
20509            .read(cx)
20510            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20511        else {
20512            panic!("Failed to get buffer for selection position");
20513        };
20514        let buffer = buffer.read(cx);
20515        let buffer_id = buffer.remote_id();
20516        let opening_range =
20517            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20518        let closing_range =
20519            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20520        let mut linked_ranges = HashMap::default();
20521        linked_ranges.insert(
20522            buffer_id,
20523            vec![(opening_range.clone(), vec![closing_range.clone()])],
20524        );
20525        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20526    });
20527    let mut completion_handle =
20528        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20529            Ok(Some(lsp::CompletionResponse::Array(vec![
20530                lsp::CompletionItem {
20531                    label: "head".to_string(),
20532                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20533                        lsp::InsertReplaceEdit {
20534                            new_text: "head".to_string(),
20535                            insert: lsp::Range::new(
20536                                lsp::Position::new(0, 1),
20537                                lsp::Position::new(0, 3),
20538                            ),
20539                            replace: lsp::Range::new(
20540                                lsp::Position::new(0, 1),
20541                                lsp::Position::new(0, 3),
20542                            ),
20543                        },
20544                    )),
20545                    ..Default::default()
20546                },
20547            ])))
20548        });
20549    editor.update_in(cx, |editor, window, cx| {
20550        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20551    });
20552    cx.run_until_parked();
20553    completion_handle.next().await.unwrap();
20554    editor.update(cx, |editor, _| {
20555        assert!(
20556            editor.context_menu_visible(),
20557            "Completion menu should be visible"
20558        );
20559    });
20560    editor.update_in(cx, |editor, window, cx| {
20561        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20562    });
20563    cx.executor().run_until_parked();
20564    editor.update(cx, |editor, cx| {
20565        assert_eq!(editor.text(cx), "<head></head>");
20566    });
20567}
20568
20569#[gpui::test]
20570async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20571    init_test(cx, |_| {});
20572
20573    let fs = FakeFs::new(cx.executor());
20574    fs.insert_tree(
20575        path!("/root"),
20576        json!({
20577            "a": {
20578                "main.rs": "fn main() {}",
20579            },
20580            "foo": {
20581                "bar": {
20582                    "external_file.rs": "pub mod external {}",
20583                }
20584            }
20585        }),
20586    )
20587    .await;
20588
20589    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20590    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20591    language_registry.add(rust_lang());
20592    let _fake_servers = language_registry.register_fake_lsp(
20593        "Rust",
20594        FakeLspAdapter {
20595            ..FakeLspAdapter::default()
20596        },
20597    );
20598    let (workspace, cx) =
20599        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20600    let worktree_id = workspace.update(cx, |workspace, cx| {
20601        workspace.project().update(cx, |project, cx| {
20602            project.worktrees(cx).next().unwrap().read(cx).id()
20603        })
20604    });
20605
20606    let assert_language_servers_count =
20607        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20608            project.update(cx, |project, cx| {
20609                let current = project
20610                    .lsp_store()
20611                    .read(cx)
20612                    .as_local()
20613                    .unwrap()
20614                    .language_servers
20615                    .len();
20616                assert_eq!(expected, current, "{context}");
20617            });
20618        };
20619
20620    assert_language_servers_count(
20621        0,
20622        "No servers should be running before any file is open",
20623        cx,
20624    );
20625    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20626    let main_editor = workspace
20627        .update_in(cx, |workspace, window, cx| {
20628            workspace.open_path(
20629                (worktree_id, "main.rs"),
20630                Some(pane.downgrade()),
20631                true,
20632                window,
20633                cx,
20634            )
20635        })
20636        .unwrap()
20637        .await
20638        .downcast::<Editor>()
20639        .unwrap();
20640    pane.update(cx, |pane, cx| {
20641        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20642        open_editor.update(cx, |editor, cx| {
20643            assert_eq!(
20644                editor.display_text(cx),
20645                "fn main() {}",
20646                "Original main.rs text on initial open",
20647            );
20648        });
20649        assert_eq!(open_editor, main_editor);
20650    });
20651    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20652
20653    let external_editor = workspace
20654        .update_in(cx, |workspace, window, cx| {
20655            workspace.open_abs_path(
20656                PathBuf::from("/root/foo/bar/external_file.rs"),
20657                OpenOptions::default(),
20658                window,
20659                cx,
20660            )
20661        })
20662        .await
20663        .expect("opening external file")
20664        .downcast::<Editor>()
20665        .expect("downcasted external file's open element to editor");
20666    pane.update(cx, |pane, cx| {
20667        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20668        open_editor.update(cx, |editor, cx| {
20669            assert_eq!(
20670                editor.display_text(cx),
20671                "pub mod external {}",
20672                "External file is open now",
20673            );
20674        });
20675        assert_eq!(open_editor, external_editor);
20676    });
20677    assert_language_servers_count(
20678        1,
20679        "Second, external, *.rs file should join the existing server",
20680        cx,
20681    );
20682
20683    pane.update_in(cx, |pane, window, cx| {
20684        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20685    })
20686    .await
20687    .unwrap();
20688    pane.update_in(cx, |pane, window, cx| {
20689        pane.navigate_backward(window, cx);
20690    });
20691    cx.run_until_parked();
20692    pane.update(cx, |pane, cx| {
20693        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20694        open_editor.update(cx, |editor, cx| {
20695            assert_eq!(
20696                editor.display_text(cx),
20697                "pub mod external {}",
20698                "External file is open now",
20699            );
20700        });
20701    });
20702    assert_language_servers_count(
20703        1,
20704        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20705        cx,
20706    );
20707
20708    cx.update(|_, cx| {
20709        workspace::reload(&workspace::Reload::default(), cx);
20710    });
20711    assert_language_servers_count(
20712        1,
20713        "After reloading the worktree with local and external files opened, only one project should be started",
20714        cx,
20715    );
20716}
20717
20718#[gpui::test]
20719async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20720    init_test(cx, |_| {});
20721
20722    let mut cx = EditorTestContext::new(cx).await;
20723    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20724    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20725
20726    // test cursor move to start of each line on tab
20727    // for `if`, `elif`, `else`, `while`, `with` and `for`
20728    cx.set_state(indoc! {"
20729        def main():
20730        ˇ    for item in items:
20731        ˇ        while item.active:
20732        ˇ            if item.value > 10:
20733        ˇ                continue
20734        ˇ            elif item.value < 0:
20735        ˇ                break
20736        ˇ            else:
20737        ˇ                with item.context() as ctx:
20738        ˇ                    yield count
20739        ˇ        else:
20740        ˇ            log('while else')
20741        ˇ    else:
20742        ˇ        log('for else')
20743    "});
20744    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20745    cx.assert_editor_state(indoc! {"
20746        def main():
20747            ˇfor item in items:
20748                ˇwhile item.active:
20749                    ˇif item.value > 10:
20750                        ˇcontinue
20751                    ˇelif item.value < 0:
20752                        ˇbreak
20753                    ˇelse:
20754                        ˇwith item.context() as ctx:
20755                            ˇyield count
20756                ˇelse:
20757                    ˇlog('while else')
20758            ˇelse:
20759                ˇlog('for else')
20760    "});
20761    // test relative indent is preserved when tab
20762    // for `if`, `elif`, `else`, `while`, `with` and `for`
20763    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20764    cx.assert_editor_state(indoc! {"
20765        def main():
20766                ˇfor item in items:
20767                    ˇwhile item.active:
20768                        ˇif item.value > 10:
20769                            ˇcontinue
20770                        ˇelif item.value < 0:
20771                            ˇbreak
20772                        ˇelse:
20773                            ˇwith item.context() as ctx:
20774                                ˇyield count
20775                    ˇelse:
20776                        ˇlog('while else')
20777                ˇelse:
20778                    ˇlog('for else')
20779    "});
20780
20781    // test cursor move to start of each line on tab
20782    // for `try`, `except`, `else`, `finally`, `match` and `def`
20783    cx.set_state(indoc! {"
20784        def main():
20785        ˇ    try:
20786        ˇ       fetch()
20787        ˇ    except ValueError:
20788        ˇ       handle_error()
20789        ˇ    else:
20790        ˇ        match value:
20791        ˇ            case _:
20792        ˇ    finally:
20793        ˇ        def status():
20794        ˇ            return 0
20795    "});
20796    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20797    cx.assert_editor_state(indoc! {"
20798        def main():
20799            ˇtry:
20800                ˇfetch()
20801            ˇexcept ValueError:
20802                ˇhandle_error()
20803            ˇelse:
20804                ˇmatch value:
20805                    ˇcase _:
20806            ˇfinally:
20807                ˇdef status():
20808                    ˇreturn 0
20809    "});
20810    // test relative indent is preserved when tab
20811    // for `try`, `except`, `else`, `finally`, `match` and `def`
20812    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20813    cx.assert_editor_state(indoc! {"
20814        def main():
20815                ˇtry:
20816                    ˇfetch()
20817                ˇexcept ValueError:
20818                    ˇhandle_error()
20819                ˇelse:
20820                    ˇmatch value:
20821                        ˇcase _:
20822                ˇfinally:
20823                    ˇdef status():
20824                        ˇreturn 0
20825    "});
20826}
20827
20828#[gpui::test]
20829async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20830    init_test(cx, |_| {});
20831
20832    let mut cx = EditorTestContext::new(cx).await;
20833    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20834    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20835
20836    // test `else` auto outdents when typed inside `if` block
20837    cx.set_state(indoc! {"
20838        def main():
20839            if i == 2:
20840                return
20841                ˇ
20842    "});
20843    cx.update_editor(|editor, window, cx| {
20844        editor.handle_input("else:", window, cx);
20845    });
20846    cx.assert_editor_state(indoc! {"
20847        def main():
20848            if i == 2:
20849                return
20850            else:ˇ
20851    "});
20852
20853    // test `except` auto outdents when typed inside `try` block
20854    cx.set_state(indoc! {"
20855        def main():
20856            try:
20857                i = 2
20858                ˇ
20859    "});
20860    cx.update_editor(|editor, window, cx| {
20861        editor.handle_input("except:", window, cx);
20862    });
20863    cx.assert_editor_state(indoc! {"
20864        def main():
20865            try:
20866                i = 2
20867            except:ˇ
20868    "});
20869
20870    // test `else` auto outdents when typed inside `except` block
20871    cx.set_state(indoc! {"
20872        def main():
20873            try:
20874                i = 2
20875            except:
20876                j = 2
20877                ˇ
20878    "});
20879    cx.update_editor(|editor, window, cx| {
20880        editor.handle_input("else:", window, cx);
20881    });
20882    cx.assert_editor_state(indoc! {"
20883        def main():
20884            try:
20885                i = 2
20886            except:
20887                j = 2
20888            else:ˇ
20889    "});
20890
20891    // test `finally` auto outdents when typed inside `else` block
20892    cx.set_state(indoc! {"
20893        def main():
20894            try:
20895                i = 2
20896            except:
20897                j = 2
20898            else:
20899                k = 2
20900                ˇ
20901    "});
20902    cx.update_editor(|editor, window, cx| {
20903        editor.handle_input("finally:", window, cx);
20904    });
20905    cx.assert_editor_state(indoc! {"
20906        def main():
20907            try:
20908                i = 2
20909            except:
20910                j = 2
20911            else:
20912                k = 2
20913            finally:ˇ
20914    "});
20915
20916    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20917    // cx.set_state(indoc! {"
20918    //     def main():
20919    //         try:
20920    //             for i in range(n):
20921    //                 pass
20922    //             ˇ
20923    // "});
20924    // cx.update_editor(|editor, window, cx| {
20925    //     editor.handle_input("except:", window, cx);
20926    // });
20927    // cx.assert_editor_state(indoc! {"
20928    //     def main():
20929    //         try:
20930    //             for i in range(n):
20931    //                 pass
20932    //         except:ˇ
20933    // "});
20934
20935    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20936    // cx.set_state(indoc! {"
20937    //     def main():
20938    //         try:
20939    //             i = 2
20940    //         except:
20941    //             for i in range(n):
20942    //                 pass
20943    //             ˇ
20944    // "});
20945    // cx.update_editor(|editor, window, cx| {
20946    //     editor.handle_input("else:", window, cx);
20947    // });
20948    // cx.assert_editor_state(indoc! {"
20949    //     def main():
20950    //         try:
20951    //             i = 2
20952    //         except:
20953    //             for i in range(n):
20954    //                 pass
20955    //         else:ˇ
20956    // "});
20957
20958    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20959    // cx.set_state(indoc! {"
20960    //     def main():
20961    //         try:
20962    //             i = 2
20963    //         except:
20964    //             j = 2
20965    //         else:
20966    //             for i in range(n):
20967    //                 pass
20968    //             ˇ
20969    // "});
20970    // cx.update_editor(|editor, window, cx| {
20971    //     editor.handle_input("finally:", window, cx);
20972    // });
20973    // cx.assert_editor_state(indoc! {"
20974    //     def main():
20975    //         try:
20976    //             i = 2
20977    //         except:
20978    //             j = 2
20979    //         else:
20980    //             for i in range(n):
20981    //                 pass
20982    //         finally:ˇ
20983    // "});
20984
20985    // test `else` stays at correct indent when typed after `for` block
20986    cx.set_state(indoc! {"
20987        def main():
20988            for i in range(10):
20989                if i == 3:
20990                    break
20991            ˇ
20992    "});
20993    cx.update_editor(|editor, window, cx| {
20994        editor.handle_input("else:", window, cx);
20995    });
20996    cx.assert_editor_state(indoc! {"
20997        def main():
20998            for i in range(10):
20999                if i == 3:
21000                    break
21001            else:ˇ
21002    "});
21003
21004    // test does not outdent on typing after line with square brackets
21005    cx.set_state(indoc! {"
21006        def f() -> list[str]:
21007            ˇ
21008    "});
21009    cx.update_editor(|editor, window, cx| {
21010        editor.handle_input("a", window, cx);
21011    });
21012    cx.assert_editor_state(indoc! {"
21013        def f() -> list[str]:
2101421015    "});
21016}
21017
21018#[gpui::test]
21019async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
21020    init_test(cx, |_| {});
21021    update_test_language_settings(cx, |settings| {
21022        settings.defaults.extend_comment_on_newline = Some(false);
21023    });
21024    let mut cx = EditorTestContext::new(cx).await;
21025    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
21026    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
21027
21028    // test correct indent after newline on comment
21029    cx.set_state(indoc! {"
21030        # COMMENT:ˇ
21031    "});
21032    cx.update_editor(|editor, window, cx| {
21033        editor.newline(&Newline, window, cx);
21034    });
21035    cx.assert_editor_state(indoc! {"
21036        # COMMENT:
21037        ˇ
21038    "});
21039
21040    // test correct indent after newline in brackets
21041    cx.set_state(indoc! {"
21042        {ˇ}
21043    "});
21044    cx.update_editor(|editor, window, cx| {
21045        editor.newline(&Newline, window, cx);
21046    });
21047    cx.run_until_parked();
21048    cx.assert_editor_state(indoc! {"
21049        {
21050            ˇ
21051        }
21052    "});
21053
21054    cx.set_state(indoc! {"
21055        (ˇ)
21056    "});
21057    cx.update_editor(|editor, window, cx| {
21058        editor.newline(&Newline, window, cx);
21059    });
21060    cx.run_until_parked();
21061    cx.assert_editor_state(indoc! {"
21062        (
21063            ˇ
21064        )
21065    "});
21066
21067    // do not indent after empty lists or dictionaries
21068    cx.set_state(indoc! {"
21069        a = []ˇ
21070    "});
21071    cx.update_editor(|editor, window, cx| {
21072        editor.newline(&Newline, window, cx);
21073    });
21074    cx.run_until_parked();
21075    cx.assert_editor_state(indoc! {"
21076        a = []
21077        ˇ
21078    "});
21079}
21080
21081fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
21082    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
21083    point..point
21084}
21085
21086fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
21087    let (text, ranges) = marked_text_ranges(marked_text, true);
21088    assert_eq!(editor.text(cx), text);
21089    assert_eq!(
21090        editor.selections.ranges(cx),
21091        ranges,
21092        "Assert selections are {}",
21093        marked_text
21094    );
21095}
21096
21097pub fn handle_signature_help_request(
21098    cx: &mut EditorLspTestContext,
21099    mocked_response: lsp::SignatureHelp,
21100) -> impl Future<Output = ()> + use<> {
21101    let mut request =
21102        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
21103            let mocked_response = mocked_response.clone();
21104            async move { Ok(Some(mocked_response)) }
21105        });
21106
21107    async move {
21108        request.next().await;
21109    }
21110}
21111
21112/// Handle completion request passing a marked string specifying where the completion
21113/// should be triggered from using '|' character, what range should be replaced, and what completions
21114/// should be returned using '<' and '>' to delimit the range.
21115///
21116/// Also see `handle_completion_request_with_insert_and_replace`.
21117#[track_caller]
21118pub fn handle_completion_request(
21119    cx: &mut EditorLspTestContext,
21120    marked_string: &str,
21121    completions: Vec<&'static str>,
21122    counter: Arc<AtomicUsize>,
21123) -> impl Future<Output = ()> {
21124    let complete_from_marker: TextRangeMarker = '|'.into();
21125    let replace_range_marker: TextRangeMarker = ('<', '>').into();
21126    let (_, mut marked_ranges) = marked_text_ranges_by(
21127        marked_string,
21128        vec![complete_from_marker.clone(), replace_range_marker.clone()],
21129    );
21130
21131    let complete_from_position =
21132        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
21133    let replace_range =
21134        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
21135
21136    let mut request =
21137        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
21138            let completions = completions.clone();
21139            counter.fetch_add(1, atomic::Ordering::Release);
21140            async move {
21141                assert_eq!(params.text_document_position.text_document.uri, url.clone());
21142                assert_eq!(
21143                    params.text_document_position.position,
21144                    complete_from_position
21145                );
21146                Ok(Some(lsp::CompletionResponse::Array(
21147                    completions
21148                        .iter()
21149                        .map(|completion_text| lsp::CompletionItem {
21150                            label: completion_text.to_string(),
21151                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
21152                                range: replace_range,
21153                                new_text: completion_text.to_string(),
21154                            })),
21155                            ..Default::default()
21156                        })
21157                        .collect(),
21158                )))
21159            }
21160        });
21161
21162    async move {
21163        request.next().await;
21164    }
21165}
21166
21167/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
21168/// given instead, which also contains an `insert` range.
21169///
21170/// This function uses markers to define ranges:
21171/// - `|` marks the cursor position
21172/// - `<>` marks the replace range
21173/// - `[]` marks the insert range (optional, defaults to `replace_range.start..cursor_pos`which is what Rust-Analyzer provides)
21174pub fn handle_completion_request_with_insert_and_replace(
21175    cx: &mut EditorLspTestContext,
21176    marked_string: &str,
21177    completions: Vec<(&'static str, &'static str)>, // (label, new_text)
21178    counter: Arc<AtomicUsize>,
21179) -> impl Future<Output = ()> {
21180    let complete_from_marker: TextRangeMarker = '|'.into();
21181    let replace_range_marker: TextRangeMarker = ('<', '>').into();
21182    let insert_range_marker: TextRangeMarker = ('{', '}').into();
21183
21184    let (_, mut marked_ranges) = marked_text_ranges_by(
21185        marked_string,
21186        vec![
21187            complete_from_marker.clone(),
21188            replace_range_marker.clone(),
21189            insert_range_marker.clone(),
21190        ],
21191    );
21192
21193    let complete_from_position =
21194        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
21195    let replace_range =
21196        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
21197
21198    let insert_range = match marked_ranges.remove(&insert_range_marker) {
21199        Some(ranges) if !ranges.is_empty() => cx.to_lsp_range(ranges[0].clone()),
21200        _ => lsp::Range {
21201            start: replace_range.start,
21202            end: complete_from_position,
21203        },
21204    };
21205
21206    let mut request =
21207        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
21208            let completions = completions.clone();
21209            counter.fetch_add(1, atomic::Ordering::Release);
21210            async move {
21211                assert_eq!(params.text_document_position.text_document.uri, url.clone());
21212                assert_eq!(
21213                    params.text_document_position.position, complete_from_position,
21214                    "marker `|` position doesn't match",
21215                );
21216                Ok(Some(lsp::CompletionResponse::Array(
21217                    completions
21218                        .iter()
21219                        .map(|(label, new_text)| lsp::CompletionItem {
21220                            label: label.to_string(),
21221                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21222                                lsp::InsertReplaceEdit {
21223                                    insert: insert_range,
21224                                    replace: replace_range,
21225                                    new_text: new_text.to_string(),
21226                                },
21227                            )),
21228                            ..Default::default()
21229                        })
21230                        .collect(),
21231                )))
21232            }
21233        });
21234
21235    async move {
21236        request.next().await;
21237    }
21238}
21239
21240fn handle_resolve_completion_request(
21241    cx: &mut EditorLspTestContext,
21242    edits: Option<Vec<(&'static str, &'static str)>>,
21243) -> impl Future<Output = ()> {
21244    let edits = edits.map(|edits| {
21245        edits
21246            .iter()
21247            .map(|(marked_string, new_text)| {
21248                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
21249                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
21250                lsp::TextEdit::new(replace_range, new_text.to_string())
21251            })
21252            .collect::<Vec<_>>()
21253    });
21254
21255    let mut request =
21256        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
21257            let edits = edits.clone();
21258            async move {
21259                Ok(lsp::CompletionItem {
21260                    additional_text_edits: edits,
21261                    ..Default::default()
21262                })
21263            }
21264        });
21265
21266    async move {
21267        request.next().await;
21268    }
21269}
21270
21271pub(crate) fn update_test_language_settings(
21272    cx: &mut TestAppContext,
21273    f: impl Fn(&mut AllLanguageSettingsContent),
21274) {
21275    cx.update(|cx| {
21276        SettingsStore::update_global(cx, |store, cx| {
21277            store.update_user_settings::<AllLanguageSettings>(cx, f);
21278        });
21279    });
21280}
21281
21282pub(crate) fn update_test_project_settings(
21283    cx: &mut TestAppContext,
21284    f: impl Fn(&mut ProjectSettings),
21285) {
21286    cx.update(|cx| {
21287        SettingsStore::update_global(cx, |store, cx| {
21288            store.update_user_settings::<ProjectSettings>(cx, f);
21289        });
21290    });
21291}
21292
21293pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
21294    cx.update(|cx| {
21295        assets::Assets.load_test_fonts(cx);
21296        let store = SettingsStore::test(cx);
21297        cx.set_global(store);
21298        theme::init(theme::LoadThemes::JustBase, cx);
21299        release_channel::init(SemanticVersion::default(), cx);
21300        client::init_settings(cx);
21301        language::init(cx);
21302        Project::init_settings(cx);
21303        workspace::init_settings(cx);
21304        crate::init(cx);
21305    });
21306
21307    update_test_language_settings(cx, f);
21308}
21309
21310#[track_caller]
21311fn assert_hunk_revert(
21312    not_reverted_text_with_selections: &str,
21313    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
21314    expected_reverted_text_with_selections: &str,
21315    base_text: &str,
21316    cx: &mut EditorLspTestContext,
21317) {
21318    cx.set_state(not_reverted_text_with_selections);
21319    cx.set_head_text(base_text);
21320    cx.executor().run_until_parked();
21321
21322    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
21323        let snapshot = editor.snapshot(window, cx);
21324        let reverted_hunk_statuses = snapshot
21325            .buffer_snapshot
21326            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
21327            .map(|hunk| hunk.status().kind)
21328            .collect::<Vec<_>>();
21329
21330        editor.git_restore(&Default::default(), window, cx);
21331        reverted_hunk_statuses
21332    });
21333    cx.executor().run_until_parked();
21334    cx.assert_editor_state(expected_reverted_text_with_selections);
21335    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
21336}