editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    inline_completion_tests::FakeInlineCompletionProvider,
    5    linked_editing_ranges::LinkedEditingRanges,
    6    scroll::scroll_amount::ScrollAmount,
    7    test::{
    8        assert_text_with_selections, build_editor,
    9        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
   10        editor_test_context::EditorTestContext,
   11        select_ranges,
   12    },
   13};
   14use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   15use futures::StreamExt;
   16use gpui::{
   17    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   18    VisualTestContext, WindowBounds, WindowOptions, div,
   19};
   20use indoc::indoc;
   21use language::{
   22    BracketPairConfig,
   23    Capability::ReadWrite,
   24    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   25    Override, Point,
   26    language_settings::{
   27        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   28        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   29    },
   30    tree_sitter_python,
   31};
   32use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   33use lsp::CompletionParams;
   34use multi_buffer::{IndentGuide, PathKey};
   35use parking_lot::Mutex;
   36use pretty_assertions::{assert_eq, assert_ne};
   37use project::{
   38    FakeFs,
   39    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   40    project_settings::{LspSettings, ProjectSettings},
   41};
   42use serde_json::{self, json};
   43use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   44use std::{
   45    iter,
   46    sync::atomic::{self, AtomicUsize},
   47};
   48use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   49use text::ToPoint as _;
   50use unindent::Unindent;
   51use util::{
   52    assert_set_eq, path,
   53    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   54    uri,
   55};
   56use workspace::{
   57    CloseActiveItem, CloseAllItems, CloseInactiveItems, NavigationEntry, OpenOptions, ViewId,
   58    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   59};
   60
   61#[gpui::test]
   62fn test_edit_events(cx: &mut TestAppContext) {
   63    init_test(cx, |_| {});
   64
   65    let buffer = cx.new(|cx| {
   66        let mut buffer = language::Buffer::local("123456", cx);
   67        buffer.set_group_interval(Duration::from_secs(1));
   68        buffer
   69    });
   70
   71    let events = Rc::new(RefCell::new(Vec::new()));
   72    let editor1 = cx.add_window({
   73        let events = events.clone();
   74        |window, cx| {
   75            let entity = cx.entity().clone();
   76            cx.subscribe_in(
   77                &entity,
   78                window,
   79                move |_, _, event: &EditorEvent, _, _| match event {
   80                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   81                    EditorEvent::BufferEdited => {
   82                        events.borrow_mut().push(("editor1", "buffer edited"))
   83                    }
   84                    _ => {}
   85                },
   86            )
   87            .detach();
   88            Editor::for_buffer(buffer.clone(), None, window, cx)
   89        }
   90    });
   91
   92    let editor2 = cx.add_window({
   93        let events = events.clone();
   94        |window, cx| {
   95            cx.subscribe_in(
   96                &cx.entity().clone(),
   97                window,
   98                move |_, _, event: &EditorEvent, _, _| match event {
   99                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
  100                    EditorEvent::BufferEdited => {
  101                        events.borrow_mut().push(("editor2", "buffer edited"))
  102                    }
  103                    _ => {}
  104                },
  105            )
  106            .detach();
  107            Editor::for_buffer(buffer.clone(), None, window, cx)
  108        }
  109    });
  110
  111    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  112
  113    // Mutating editor 1 will emit an `Edited` event only for that editor.
  114    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  115    assert_eq!(
  116        mem::take(&mut *events.borrow_mut()),
  117        [
  118            ("editor1", "edited"),
  119            ("editor1", "buffer edited"),
  120            ("editor2", "buffer edited"),
  121        ]
  122    );
  123
  124    // Mutating editor 2 will emit an `Edited` event only for that editor.
  125    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  126    assert_eq!(
  127        mem::take(&mut *events.borrow_mut()),
  128        [
  129            ("editor2", "edited"),
  130            ("editor1", "buffer edited"),
  131            ("editor2", "buffer edited"),
  132        ]
  133    );
  134
  135    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  136    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  137    assert_eq!(
  138        mem::take(&mut *events.borrow_mut()),
  139        [
  140            ("editor1", "edited"),
  141            ("editor1", "buffer edited"),
  142            ("editor2", "buffer edited"),
  143        ]
  144    );
  145
  146    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  147    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  148    assert_eq!(
  149        mem::take(&mut *events.borrow_mut()),
  150        [
  151            ("editor1", "edited"),
  152            ("editor1", "buffer edited"),
  153            ("editor2", "buffer edited"),
  154        ]
  155    );
  156
  157    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  158    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  159    assert_eq!(
  160        mem::take(&mut *events.borrow_mut()),
  161        [
  162            ("editor2", "edited"),
  163            ("editor1", "buffer edited"),
  164            ("editor2", "buffer edited"),
  165        ]
  166    );
  167
  168    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  169    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  170    assert_eq!(
  171        mem::take(&mut *events.borrow_mut()),
  172        [
  173            ("editor2", "edited"),
  174            ("editor1", "buffer edited"),
  175            ("editor2", "buffer edited"),
  176        ]
  177    );
  178
  179    // No event is emitted when the mutation is a no-op.
  180    _ = editor2.update(cx, |editor, window, cx| {
  181        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  182
  183        editor.backspace(&Backspace, window, cx);
  184    });
  185    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  186}
  187
  188#[gpui::test]
  189fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  190    init_test(cx, |_| {});
  191
  192    let mut now = Instant::now();
  193    let group_interval = Duration::from_millis(1);
  194    let buffer = cx.new(|cx| {
  195        let mut buf = language::Buffer::local("123456", cx);
  196        buf.set_group_interval(group_interval);
  197        buf
  198    });
  199    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  200    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  201
  202    _ = editor.update(cx, |editor, window, cx| {
  203        editor.start_transaction_at(now, window, cx);
  204        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  205
  206        editor.insert("cd", window, cx);
  207        editor.end_transaction_at(now, cx);
  208        assert_eq!(editor.text(cx), "12cd56");
  209        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  210
  211        editor.start_transaction_at(now, window, cx);
  212        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  213        editor.insert("e", window, cx);
  214        editor.end_transaction_at(now, cx);
  215        assert_eq!(editor.text(cx), "12cde6");
  216        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  217
  218        now += group_interval + Duration::from_millis(1);
  219        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  220
  221        // Simulate an edit in another editor
  222        buffer.update(cx, |buffer, cx| {
  223            buffer.start_transaction_at(now, cx);
  224            buffer.edit([(0..1, "a")], None, cx);
  225            buffer.edit([(1..1, "b")], None, cx);
  226            buffer.end_transaction_at(now, cx);
  227        });
  228
  229        assert_eq!(editor.text(cx), "ab2cde6");
  230        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  231
  232        // Last transaction happened past the group interval in a different editor.
  233        // Undo it individually and don't restore selections.
  234        editor.undo(&Undo, window, cx);
  235        assert_eq!(editor.text(cx), "12cde6");
  236        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  237
  238        // First two transactions happened within the group interval in this editor.
  239        // Undo them together and restore selections.
  240        editor.undo(&Undo, window, cx);
  241        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  242        assert_eq!(editor.text(cx), "123456");
  243        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  244
  245        // Redo the first two transactions together.
  246        editor.redo(&Redo, window, cx);
  247        assert_eq!(editor.text(cx), "12cde6");
  248        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  249
  250        // Redo the last transaction on its own.
  251        editor.redo(&Redo, window, cx);
  252        assert_eq!(editor.text(cx), "ab2cde6");
  253        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  254
  255        // Test empty transactions.
  256        editor.start_transaction_at(now, window, cx);
  257        editor.end_transaction_at(now, cx);
  258        editor.undo(&Undo, window, cx);
  259        assert_eq!(editor.text(cx), "12cde6");
  260    });
  261}
  262
  263#[gpui::test]
  264fn test_ime_composition(cx: &mut TestAppContext) {
  265    init_test(cx, |_| {});
  266
  267    let buffer = cx.new(|cx| {
  268        let mut buffer = language::Buffer::local("abcde", cx);
  269        // Ensure automatic grouping doesn't occur.
  270        buffer.set_group_interval(Duration::ZERO);
  271        buffer
  272    });
  273
  274    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  275    cx.add_window(|window, cx| {
  276        let mut editor = build_editor(buffer.clone(), window, cx);
  277
  278        // Start a new IME composition.
  279        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  280        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  281        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  282        assert_eq!(editor.text(cx), "äbcde");
  283        assert_eq!(
  284            editor.marked_text_ranges(cx),
  285            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  286        );
  287
  288        // Finalize IME composition.
  289        editor.replace_text_in_range(None, "ā", window, cx);
  290        assert_eq!(editor.text(cx), "ābcde");
  291        assert_eq!(editor.marked_text_ranges(cx), None);
  292
  293        // IME composition edits are grouped and are undone/redone at once.
  294        editor.undo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "abcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297        editor.redo(&Default::default(), window, cx);
  298        assert_eq!(editor.text(cx), "ābcde");
  299        assert_eq!(editor.marked_text_ranges(cx), None);
  300
  301        // Start a new IME composition.
  302        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  303        assert_eq!(
  304            editor.marked_text_ranges(cx),
  305            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  306        );
  307
  308        // Undoing during an IME composition cancels it.
  309        editor.undo(&Default::default(), window, cx);
  310        assert_eq!(editor.text(cx), "ābcde");
  311        assert_eq!(editor.marked_text_ranges(cx), None);
  312
  313        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  314        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  315        assert_eq!(editor.text(cx), "ābcdè");
  316        assert_eq!(
  317            editor.marked_text_ranges(cx),
  318            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  319        );
  320
  321        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  322        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  323        assert_eq!(editor.text(cx), "ābcdę");
  324        assert_eq!(editor.marked_text_ranges(cx), None);
  325
  326        // Start a new IME composition with multiple cursors.
  327        editor.change_selections(None, window, cx, |s| {
  328            s.select_ranges([
  329                OffsetUtf16(1)..OffsetUtf16(1),
  330                OffsetUtf16(3)..OffsetUtf16(3),
  331                OffsetUtf16(5)..OffsetUtf16(5),
  332            ])
  333        });
  334        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  335        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  336        assert_eq!(
  337            editor.marked_text_ranges(cx),
  338            Some(vec![
  339                OffsetUtf16(0)..OffsetUtf16(3),
  340                OffsetUtf16(4)..OffsetUtf16(7),
  341                OffsetUtf16(8)..OffsetUtf16(11)
  342            ])
  343        );
  344
  345        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  346        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  347        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  348        assert_eq!(
  349            editor.marked_text_ranges(cx),
  350            Some(vec![
  351                OffsetUtf16(1)..OffsetUtf16(2),
  352                OffsetUtf16(5)..OffsetUtf16(6),
  353                OffsetUtf16(9)..OffsetUtf16(10)
  354            ])
  355        );
  356
  357        // Finalize IME composition with multiple cursors.
  358        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  359        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  360        assert_eq!(editor.marked_text_ranges(cx), None);
  361
  362        editor
  363    });
  364}
  365
  366#[gpui::test]
  367fn test_selection_with_mouse(cx: &mut TestAppContext) {
  368    init_test(cx, |_| {});
  369
  370    let editor = cx.add_window(|window, cx| {
  371        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  372        build_editor(buffer, window, cx)
  373    });
  374
  375    _ = editor.update(cx, |editor, window, cx| {
  376        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  377    });
  378    assert_eq!(
  379        editor
  380            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  381            .unwrap(),
  382        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  383    );
  384
  385    _ = editor.update(cx, |editor, window, cx| {
  386        editor.update_selection(
  387            DisplayPoint::new(DisplayRow(3), 3),
  388            0,
  389            gpui::Point::<f32>::default(),
  390            window,
  391            cx,
  392        );
  393    });
  394
  395    assert_eq!(
  396        editor
  397            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  398            .unwrap(),
  399        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  400    );
  401
  402    _ = editor.update(cx, |editor, window, cx| {
  403        editor.update_selection(
  404            DisplayPoint::new(DisplayRow(1), 1),
  405            0,
  406            gpui::Point::<f32>::default(),
  407            window,
  408            cx,
  409        );
  410    });
  411
  412    assert_eq!(
  413        editor
  414            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  415            .unwrap(),
  416        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  417    );
  418
  419    _ = editor.update(cx, |editor, window, cx| {
  420        editor.end_selection(window, cx);
  421        editor.update_selection(
  422            DisplayPoint::new(DisplayRow(3), 3),
  423            0,
  424            gpui::Point::<f32>::default(),
  425            window,
  426            cx,
  427        );
  428    });
  429
  430    assert_eq!(
  431        editor
  432            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  433            .unwrap(),
  434        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  435    );
  436
  437    _ = editor.update(cx, |editor, window, cx| {
  438        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  439        editor.update_selection(
  440            DisplayPoint::new(DisplayRow(0), 0),
  441            0,
  442            gpui::Point::<f32>::default(),
  443            window,
  444            cx,
  445        );
  446    });
  447
  448    assert_eq!(
  449        editor
  450            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  451            .unwrap(),
  452        [
  453            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  454            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  455        ]
  456    );
  457
  458    _ = editor.update(cx, |editor, window, cx| {
  459        editor.end_selection(window, cx);
  460    });
  461
  462    assert_eq!(
  463        editor
  464            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  465            .unwrap(),
  466        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  467    );
  468}
  469
  470#[gpui::test]
  471fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  472    init_test(cx, |_| {});
  473
  474    let editor = cx.add_window(|window, cx| {
  475        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  476        build_editor(buffer, window, cx)
  477    });
  478
  479    _ = editor.update(cx, |editor, window, cx| {
  480        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  481    });
  482
  483    _ = editor.update(cx, |editor, window, cx| {
  484        editor.end_selection(window, cx);
  485    });
  486
  487    _ = editor.update(cx, |editor, window, cx| {
  488        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  489    });
  490
  491    _ = editor.update(cx, |editor, window, cx| {
  492        editor.end_selection(window, cx);
  493    });
  494
  495    assert_eq!(
  496        editor
  497            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  498            .unwrap(),
  499        [
  500            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  501            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  502        ]
  503    );
  504
  505    _ = editor.update(cx, |editor, window, cx| {
  506        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  507    });
  508
  509    _ = editor.update(cx, |editor, window, cx| {
  510        editor.end_selection(window, cx);
  511    });
  512
  513    assert_eq!(
  514        editor
  515            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  516            .unwrap(),
  517        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  518    );
  519}
  520
  521#[gpui::test]
  522fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  523    init_test(cx, |_| {});
  524
  525    let editor = cx.add_window(|window, cx| {
  526        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  527        build_editor(buffer, window, cx)
  528    });
  529
  530    _ = editor.update(cx, |editor, window, cx| {
  531        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  532        assert_eq!(
  533            editor.selections.display_ranges(cx),
  534            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  535        );
  536    });
  537
  538    _ = editor.update(cx, |editor, window, cx| {
  539        editor.update_selection(
  540            DisplayPoint::new(DisplayRow(3), 3),
  541            0,
  542            gpui::Point::<f32>::default(),
  543            window,
  544            cx,
  545        );
  546        assert_eq!(
  547            editor.selections.display_ranges(cx),
  548            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  549        );
  550    });
  551
  552    _ = editor.update(cx, |editor, window, cx| {
  553        editor.cancel(&Cancel, window, cx);
  554        editor.update_selection(
  555            DisplayPoint::new(DisplayRow(1), 1),
  556            0,
  557            gpui::Point::<f32>::default(),
  558            window,
  559            cx,
  560        );
  561        assert_eq!(
  562            editor.selections.display_ranges(cx),
  563            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  564        );
  565    });
  566}
  567
  568#[gpui::test]
  569fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  570    init_test(cx, |_| {});
  571
  572    let editor = cx.add_window(|window, cx| {
  573        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  574        build_editor(buffer, window, cx)
  575    });
  576
  577    _ = editor.update(cx, |editor, window, cx| {
  578        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  579        assert_eq!(
  580            editor.selections.display_ranges(cx),
  581            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  582        );
  583
  584        editor.move_down(&Default::default(), window, cx);
  585        assert_eq!(
  586            editor.selections.display_ranges(cx),
  587            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  588        );
  589
  590        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  591        assert_eq!(
  592            editor.selections.display_ranges(cx),
  593            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  594        );
  595
  596        editor.move_up(&Default::default(), window, cx);
  597        assert_eq!(
  598            editor.selections.display_ranges(cx),
  599            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  600        );
  601    });
  602}
  603
  604#[gpui::test]
  605fn test_clone(cx: &mut TestAppContext) {
  606    init_test(cx, |_| {});
  607
  608    let (text, selection_ranges) = marked_text_ranges(
  609        indoc! {"
  610            one
  611            two
  612            threeˇ
  613            four
  614            fiveˇ
  615        "},
  616        true,
  617    );
  618
  619    let editor = cx.add_window(|window, cx| {
  620        let buffer = MultiBuffer::build_simple(&text, cx);
  621        build_editor(buffer, window, cx)
  622    });
  623
  624    _ = editor.update(cx, |editor, window, cx| {
  625        editor.change_selections(None, window, cx, |s| {
  626            s.select_ranges(selection_ranges.clone())
  627        });
  628        editor.fold_creases(
  629            vec![
  630                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  631                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  632            ],
  633            true,
  634            window,
  635            cx,
  636        );
  637    });
  638
  639    let cloned_editor = editor
  640        .update(cx, |editor, _, cx| {
  641            cx.open_window(Default::default(), |window, cx| {
  642                cx.new(|cx| editor.clone(window, cx))
  643            })
  644        })
  645        .unwrap()
  646        .unwrap();
  647
  648    let snapshot = editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651    let cloned_snapshot = cloned_editor
  652        .update(cx, |e, window, cx| e.snapshot(window, cx))
  653        .unwrap();
  654
  655    assert_eq!(
  656        cloned_editor
  657            .update(cx, |e, _, cx| e.display_text(cx))
  658            .unwrap(),
  659        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  660    );
  661    assert_eq!(
  662        cloned_snapshot
  663            .folds_in_range(0..text.len())
  664            .collect::<Vec<_>>(),
  665        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  666    );
  667    assert_set_eq!(
  668        cloned_editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  670            .unwrap(),
  671        editor
  672            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  673            .unwrap()
  674    );
  675    assert_set_eq!(
  676        cloned_editor
  677            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  678            .unwrap(),
  679        editor
  680            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  681            .unwrap()
  682    );
  683}
  684
  685#[gpui::test]
  686async fn test_navigation_history(cx: &mut TestAppContext) {
  687    init_test(cx, |_| {});
  688
  689    use workspace::item::Item;
  690
  691    let fs = FakeFs::new(cx.executor());
  692    let project = Project::test(fs, [], cx).await;
  693    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  694    let pane = workspace
  695        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  696        .unwrap();
  697
  698    _ = workspace.update(cx, |_v, window, cx| {
  699        cx.new(|cx| {
  700            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  701            let mut editor = build_editor(buffer.clone(), window, cx);
  702            let handle = cx.entity();
  703            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  704
  705            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  706                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  707            }
  708
  709            // Move the cursor a small distance.
  710            // Nothing is added to the navigation history.
  711            editor.change_selections(None, window, cx, |s| {
  712                s.select_display_ranges([
  713                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  714                ])
  715            });
  716            editor.change_selections(None, window, cx, |s| {
  717                s.select_display_ranges([
  718                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  719                ])
  720            });
  721            assert!(pop_history(&mut editor, cx).is_none());
  722
  723            // Move the cursor a large distance.
  724            // The history can jump back to the previous position.
  725            editor.change_selections(None, window, cx, |s| {
  726                s.select_display_ranges([
  727                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  728                ])
  729            });
  730            let nav_entry = pop_history(&mut editor, cx).unwrap();
  731            editor.navigate(nav_entry.data.unwrap(), window, cx);
  732            assert_eq!(nav_entry.item.id(), cx.entity_id());
  733            assert_eq!(
  734                editor.selections.display_ranges(cx),
  735                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  736            );
  737            assert!(pop_history(&mut editor, cx).is_none());
  738
  739            // Move the cursor a small distance via the mouse.
  740            // Nothing is added to the navigation history.
  741            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  742            editor.end_selection(window, cx);
  743            assert_eq!(
  744                editor.selections.display_ranges(cx),
  745                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  746            );
  747            assert!(pop_history(&mut editor, cx).is_none());
  748
  749            // Move the cursor a large distance via the mouse.
  750            // The history can jump back to the previous position.
  751            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  752            editor.end_selection(window, cx);
  753            assert_eq!(
  754                editor.selections.display_ranges(cx),
  755                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  756            );
  757            let nav_entry = pop_history(&mut editor, cx).unwrap();
  758            editor.navigate(nav_entry.data.unwrap(), window, cx);
  759            assert_eq!(nav_entry.item.id(), cx.entity_id());
  760            assert_eq!(
  761                editor.selections.display_ranges(cx),
  762                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  763            );
  764            assert!(pop_history(&mut editor, cx).is_none());
  765
  766            // Set scroll position to check later
  767            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  768            let original_scroll_position = editor.scroll_manager.anchor();
  769
  770            // Jump to the end of the document and adjust scroll
  771            editor.move_to_end(&MoveToEnd, window, cx);
  772            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  773            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  774
  775            let nav_entry = pop_history(&mut editor, cx).unwrap();
  776            editor.navigate(nav_entry.data.unwrap(), window, cx);
  777            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  778
  779            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  780            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  781            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  782            let invalid_point = Point::new(9999, 0);
  783            editor.navigate(
  784                Box::new(NavigationData {
  785                    cursor_anchor: invalid_anchor,
  786                    cursor_position: invalid_point,
  787                    scroll_anchor: ScrollAnchor {
  788                        anchor: invalid_anchor,
  789                        offset: Default::default(),
  790                    },
  791                    scroll_top_row: invalid_point.row,
  792                }),
  793                window,
  794                cx,
  795            );
  796            assert_eq!(
  797                editor.selections.display_ranges(cx),
  798                &[editor.max_point(cx)..editor.max_point(cx)]
  799            );
  800            assert_eq!(
  801                editor.scroll_position(cx),
  802                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  803            );
  804
  805            editor
  806        })
  807    });
  808}
  809
  810#[gpui::test]
  811fn test_cancel(cx: &mut TestAppContext) {
  812    init_test(cx, |_| {});
  813
  814    let editor = cx.add_window(|window, cx| {
  815        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  816        build_editor(buffer, window, cx)
  817    });
  818
  819    _ = editor.update(cx, |editor, window, cx| {
  820        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  821        editor.update_selection(
  822            DisplayPoint::new(DisplayRow(1), 1),
  823            0,
  824            gpui::Point::<f32>::default(),
  825            window,
  826            cx,
  827        );
  828        editor.end_selection(window, cx);
  829
  830        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  831        editor.update_selection(
  832            DisplayPoint::new(DisplayRow(0), 3),
  833            0,
  834            gpui::Point::<f32>::default(),
  835            window,
  836            cx,
  837        );
  838        editor.end_selection(window, cx);
  839        assert_eq!(
  840            editor.selections.display_ranges(cx),
  841            [
  842                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  843                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  844            ]
  845        );
  846    });
  847
  848    _ = editor.update(cx, |editor, window, cx| {
  849        editor.cancel(&Cancel, window, cx);
  850        assert_eq!(
  851            editor.selections.display_ranges(cx),
  852            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  853        );
  854    });
  855
  856    _ = editor.update(cx, |editor, window, cx| {
  857        editor.cancel(&Cancel, window, cx);
  858        assert_eq!(
  859            editor.selections.display_ranges(cx),
  860            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  861        );
  862    });
  863}
  864
  865#[gpui::test]
  866fn test_fold_action(cx: &mut TestAppContext) {
  867    init_test(cx, |_| {});
  868
  869    let editor = cx.add_window(|window, cx| {
  870        let buffer = MultiBuffer::build_simple(
  871            &"
  872                impl Foo {
  873                    // Hello!
  874
  875                    fn a() {
  876                        1
  877                    }
  878
  879                    fn b() {
  880                        2
  881                    }
  882
  883                    fn c() {
  884                        3
  885                    }
  886                }
  887            "
  888            .unindent(),
  889            cx,
  890        );
  891        build_editor(buffer.clone(), window, cx)
  892    });
  893
  894    _ = editor.update(cx, |editor, window, cx| {
  895        editor.change_selections(None, window, cx, |s| {
  896            s.select_display_ranges([
  897                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  898            ]);
  899        });
  900        editor.fold(&Fold, window, cx);
  901        assert_eq!(
  902            editor.display_text(cx),
  903            "
  904                impl Foo {
  905                    // Hello!
  906
  907                    fn a() {
  908                        1
  909                    }
  910
  911                    fn b() {⋯
  912                    }
  913
  914                    fn c() {⋯
  915                    }
  916                }
  917            "
  918            .unindent(),
  919        );
  920
  921        editor.fold(&Fold, window, cx);
  922        assert_eq!(
  923            editor.display_text(cx),
  924            "
  925                impl Foo {⋯
  926                }
  927            "
  928            .unindent(),
  929        );
  930
  931        editor.unfold_lines(&UnfoldLines, window, cx);
  932        assert_eq!(
  933            editor.display_text(cx),
  934            "
  935                impl Foo {
  936                    // Hello!
  937
  938                    fn a() {
  939                        1
  940                    }
  941
  942                    fn b() {⋯
  943                    }
  944
  945                    fn c() {⋯
  946                    }
  947                }
  948            "
  949            .unindent(),
  950        );
  951
  952        editor.unfold_lines(&UnfoldLines, window, cx);
  953        assert_eq!(
  954            editor.display_text(cx),
  955            editor.buffer.read(cx).read(cx).text()
  956        );
  957    });
  958}
  959
  960#[gpui::test]
  961fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  962    init_test(cx, |_| {});
  963
  964    let editor = cx.add_window(|window, cx| {
  965        let buffer = MultiBuffer::build_simple(
  966            &"
  967                class Foo:
  968                    # Hello!
  969
  970                    def a():
  971                        print(1)
  972
  973                    def b():
  974                        print(2)
  975
  976                    def c():
  977                        print(3)
  978            "
  979            .unindent(),
  980            cx,
  981        );
  982        build_editor(buffer.clone(), window, cx)
  983    });
  984
  985    _ = editor.update(cx, |editor, window, cx| {
  986        editor.change_selections(None, window, cx, |s| {
  987            s.select_display_ranges([
  988                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  989            ]);
  990        });
  991        editor.fold(&Fold, window, cx);
  992        assert_eq!(
  993            editor.display_text(cx),
  994            "
  995                class Foo:
  996                    # Hello!
  997
  998                    def a():
  999                        print(1)
 1000
 1001                    def b():⋯
 1002
 1003                    def c():⋯
 1004            "
 1005            .unindent(),
 1006        );
 1007
 1008        editor.fold(&Fold, window, cx);
 1009        assert_eq!(
 1010            editor.display_text(cx),
 1011            "
 1012                class Foo:⋯
 1013            "
 1014            .unindent(),
 1015        );
 1016
 1017        editor.unfold_lines(&UnfoldLines, window, cx);
 1018        assert_eq!(
 1019            editor.display_text(cx),
 1020            "
 1021                class Foo:
 1022                    # Hello!
 1023
 1024                    def a():
 1025                        print(1)
 1026
 1027                    def b():⋯
 1028
 1029                    def c():⋯
 1030            "
 1031            .unindent(),
 1032        );
 1033
 1034        editor.unfold_lines(&UnfoldLines, window, cx);
 1035        assert_eq!(
 1036            editor.display_text(cx),
 1037            editor.buffer.read(cx).read(cx).text()
 1038        );
 1039    });
 1040}
 1041
 1042#[gpui::test]
 1043fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1044    init_test(cx, |_| {});
 1045
 1046    let editor = cx.add_window(|window, cx| {
 1047        let buffer = MultiBuffer::build_simple(
 1048            &"
 1049                class Foo:
 1050                    # Hello!
 1051
 1052                    def a():
 1053                        print(1)
 1054
 1055                    def b():
 1056                        print(2)
 1057
 1058
 1059                    def c():
 1060                        print(3)
 1061
 1062
 1063            "
 1064            .unindent(),
 1065            cx,
 1066        );
 1067        build_editor(buffer.clone(), window, cx)
 1068    });
 1069
 1070    _ = editor.update(cx, |editor, window, cx| {
 1071        editor.change_selections(None, window, cx, |s| {
 1072            s.select_display_ranges([
 1073                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1074            ]);
 1075        });
 1076        editor.fold(&Fold, window, cx);
 1077        assert_eq!(
 1078            editor.display_text(cx),
 1079            "
 1080                class Foo:
 1081                    # Hello!
 1082
 1083                    def a():
 1084                        print(1)
 1085
 1086                    def b():⋯
 1087
 1088
 1089                    def c():⋯
 1090
 1091
 1092            "
 1093            .unindent(),
 1094        );
 1095
 1096        editor.fold(&Fold, window, cx);
 1097        assert_eq!(
 1098            editor.display_text(cx),
 1099            "
 1100                class Foo:⋯
 1101
 1102
 1103            "
 1104            .unindent(),
 1105        );
 1106
 1107        editor.unfold_lines(&UnfoldLines, window, cx);
 1108        assert_eq!(
 1109            editor.display_text(cx),
 1110            "
 1111                class Foo:
 1112                    # Hello!
 1113
 1114                    def a():
 1115                        print(1)
 1116
 1117                    def b():⋯
 1118
 1119
 1120                    def c():⋯
 1121
 1122
 1123            "
 1124            .unindent(),
 1125        );
 1126
 1127        editor.unfold_lines(&UnfoldLines, window, cx);
 1128        assert_eq!(
 1129            editor.display_text(cx),
 1130            editor.buffer.read(cx).read(cx).text()
 1131        );
 1132    });
 1133}
 1134
 1135#[gpui::test]
 1136fn test_fold_at_level(cx: &mut TestAppContext) {
 1137    init_test(cx, |_| {});
 1138
 1139    let editor = cx.add_window(|window, cx| {
 1140        let buffer = MultiBuffer::build_simple(
 1141            &"
 1142                class Foo:
 1143                    # Hello!
 1144
 1145                    def a():
 1146                        print(1)
 1147
 1148                    def b():
 1149                        print(2)
 1150
 1151
 1152                class Bar:
 1153                    # World!
 1154
 1155                    def a():
 1156                        print(1)
 1157
 1158                    def b():
 1159                        print(2)
 1160
 1161
 1162            "
 1163            .unindent(),
 1164            cx,
 1165        );
 1166        build_editor(buffer.clone(), window, cx)
 1167    });
 1168
 1169    _ = editor.update(cx, |editor, window, cx| {
 1170        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1171        assert_eq!(
 1172            editor.display_text(cx),
 1173            "
 1174                class Foo:
 1175                    # Hello!
 1176
 1177                    def a():⋯
 1178
 1179                    def b():⋯
 1180
 1181
 1182                class Bar:
 1183                    # World!
 1184
 1185                    def a():⋯
 1186
 1187                    def b():⋯
 1188
 1189
 1190            "
 1191            .unindent(),
 1192        );
 1193
 1194        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1195        assert_eq!(
 1196            editor.display_text(cx),
 1197            "
 1198                class Foo:⋯
 1199
 1200
 1201                class Bar:⋯
 1202
 1203
 1204            "
 1205            .unindent(),
 1206        );
 1207
 1208        editor.unfold_all(&UnfoldAll, window, cx);
 1209        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1210        assert_eq!(
 1211            editor.display_text(cx),
 1212            "
 1213                class Foo:
 1214                    # Hello!
 1215
 1216                    def a():
 1217                        print(1)
 1218
 1219                    def b():
 1220                        print(2)
 1221
 1222
 1223                class Bar:
 1224                    # World!
 1225
 1226                    def a():
 1227                        print(1)
 1228
 1229                    def b():
 1230                        print(2)
 1231
 1232
 1233            "
 1234            .unindent(),
 1235        );
 1236
 1237        assert_eq!(
 1238            editor.display_text(cx),
 1239            editor.buffer.read(cx).read(cx).text()
 1240        );
 1241    });
 1242}
 1243
 1244#[gpui::test]
 1245fn test_move_cursor(cx: &mut TestAppContext) {
 1246    init_test(cx, |_| {});
 1247
 1248    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1249    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1250
 1251    buffer.update(cx, |buffer, cx| {
 1252        buffer.edit(
 1253            vec![
 1254                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1255                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1256            ],
 1257            None,
 1258            cx,
 1259        );
 1260    });
 1261    _ = editor.update(cx, |editor, window, cx| {
 1262        assert_eq!(
 1263            editor.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1265        );
 1266
 1267        editor.move_down(&MoveDown, window, cx);
 1268        assert_eq!(
 1269            editor.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1271        );
 1272
 1273        editor.move_right(&MoveRight, window, cx);
 1274        assert_eq!(
 1275            editor.selections.display_ranges(cx),
 1276            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1277        );
 1278
 1279        editor.move_left(&MoveLeft, window, cx);
 1280        assert_eq!(
 1281            editor.selections.display_ranges(cx),
 1282            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1283        );
 1284
 1285        editor.move_up(&MoveUp, window, cx);
 1286        assert_eq!(
 1287            editor.selections.display_ranges(cx),
 1288            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1289        );
 1290
 1291        editor.move_to_end(&MoveToEnd, window, cx);
 1292        assert_eq!(
 1293            editor.selections.display_ranges(cx),
 1294            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1295        );
 1296
 1297        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1298        assert_eq!(
 1299            editor.selections.display_ranges(cx),
 1300            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1301        );
 1302
 1303        editor.change_selections(None, window, cx, |s| {
 1304            s.select_display_ranges([
 1305                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1306            ]);
 1307        });
 1308        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1309        assert_eq!(
 1310            editor.selections.display_ranges(cx),
 1311            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1312        );
 1313
 1314        editor.select_to_end(&SelectToEnd, window, cx);
 1315        assert_eq!(
 1316            editor.selections.display_ranges(cx),
 1317            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1318        );
 1319    });
 1320}
 1321
 1322#[gpui::test]
 1323fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1324    init_test(cx, |_| {});
 1325
 1326    let editor = cx.add_window(|window, cx| {
 1327        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1328        build_editor(buffer.clone(), window, cx)
 1329    });
 1330
 1331    assert_eq!('🟥'.len_utf8(), 4);
 1332    assert_eq!('α'.len_utf8(), 2);
 1333
 1334    _ = editor.update(cx, |editor, window, cx| {
 1335        editor.fold_creases(
 1336            vec![
 1337                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1339                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1340            ],
 1341            true,
 1342            window,
 1343            cx,
 1344        );
 1345        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1346
 1347        editor.move_right(&MoveRight, window, cx);
 1348        assert_eq!(
 1349            editor.selections.display_ranges(cx),
 1350            &[empty_range(0, "🟥".len())]
 1351        );
 1352        editor.move_right(&MoveRight, window, cx);
 1353        assert_eq!(
 1354            editor.selections.display_ranges(cx),
 1355            &[empty_range(0, "🟥🟧".len())]
 1356        );
 1357        editor.move_right(&MoveRight, window, cx);
 1358        assert_eq!(
 1359            editor.selections.display_ranges(cx),
 1360            &[empty_range(0, "🟥🟧⋯".len())]
 1361        );
 1362
 1363        editor.move_down(&MoveDown, window, cx);
 1364        assert_eq!(
 1365            editor.selections.display_ranges(cx),
 1366            &[empty_range(1, "ab⋯e".len())]
 1367        );
 1368        editor.move_left(&MoveLeft, window, cx);
 1369        assert_eq!(
 1370            editor.selections.display_ranges(cx),
 1371            &[empty_range(1, "ab⋯".len())]
 1372        );
 1373        editor.move_left(&MoveLeft, window, cx);
 1374        assert_eq!(
 1375            editor.selections.display_ranges(cx),
 1376            &[empty_range(1, "ab".len())]
 1377        );
 1378        editor.move_left(&MoveLeft, window, cx);
 1379        assert_eq!(
 1380            editor.selections.display_ranges(cx),
 1381            &[empty_range(1, "a".len())]
 1382        );
 1383
 1384        editor.move_down(&MoveDown, window, cx);
 1385        assert_eq!(
 1386            editor.selections.display_ranges(cx),
 1387            &[empty_range(2, "α".len())]
 1388        );
 1389        editor.move_right(&MoveRight, window, cx);
 1390        assert_eq!(
 1391            editor.selections.display_ranges(cx),
 1392            &[empty_range(2, "αβ".len())]
 1393        );
 1394        editor.move_right(&MoveRight, window, cx);
 1395        assert_eq!(
 1396            editor.selections.display_ranges(cx),
 1397            &[empty_range(2, "αβ⋯".len())]
 1398        );
 1399        editor.move_right(&MoveRight, window, cx);
 1400        assert_eq!(
 1401            editor.selections.display_ranges(cx),
 1402            &[empty_range(2, "αβ⋯ε".len())]
 1403        );
 1404
 1405        editor.move_up(&MoveUp, window, cx);
 1406        assert_eq!(
 1407            editor.selections.display_ranges(cx),
 1408            &[empty_range(1, "ab⋯e".len())]
 1409        );
 1410        editor.move_down(&MoveDown, window, cx);
 1411        assert_eq!(
 1412            editor.selections.display_ranges(cx),
 1413            &[empty_range(2, "αβ⋯ε".len())]
 1414        );
 1415        editor.move_up(&MoveUp, window, cx);
 1416        assert_eq!(
 1417            editor.selections.display_ranges(cx),
 1418            &[empty_range(1, "ab⋯e".len())]
 1419        );
 1420
 1421        editor.move_up(&MoveUp, window, cx);
 1422        assert_eq!(
 1423            editor.selections.display_ranges(cx),
 1424            &[empty_range(0, "🟥🟧".len())]
 1425        );
 1426        editor.move_left(&MoveLeft, window, cx);
 1427        assert_eq!(
 1428            editor.selections.display_ranges(cx),
 1429            &[empty_range(0, "🟥".len())]
 1430        );
 1431        editor.move_left(&MoveLeft, window, cx);
 1432        assert_eq!(
 1433            editor.selections.display_ranges(cx),
 1434            &[empty_range(0, "".len())]
 1435        );
 1436    });
 1437}
 1438
 1439#[gpui::test]
 1440fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1441    init_test(cx, |_| {});
 1442
 1443    let editor = cx.add_window(|window, cx| {
 1444        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1445        build_editor(buffer.clone(), window, cx)
 1446    });
 1447    _ = editor.update(cx, |editor, window, cx| {
 1448        editor.change_selections(None, window, cx, |s| {
 1449            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1450        });
 1451
 1452        // moving above start of document should move selection to start of document,
 1453        // but the next move down should still be at the original goal_x
 1454        editor.move_up(&MoveUp, window, cx);
 1455        assert_eq!(
 1456            editor.selections.display_ranges(cx),
 1457            &[empty_range(0, "".len())]
 1458        );
 1459
 1460        editor.move_down(&MoveDown, window, cx);
 1461        assert_eq!(
 1462            editor.selections.display_ranges(cx),
 1463            &[empty_range(1, "abcd".len())]
 1464        );
 1465
 1466        editor.move_down(&MoveDown, window, cx);
 1467        assert_eq!(
 1468            editor.selections.display_ranges(cx),
 1469            &[empty_range(2, "αβγ".len())]
 1470        );
 1471
 1472        editor.move_down(&MoveDown, window, cx);
 1473        assert_eq!(
 1474            editor.selections.display_ranges(cx),
 1475            &[empty_range(3, "abcd".len())]
 1476        );
 1477
 1478        editor.move_down(&MoveDown, window, cx);
 1479        assert_eq!(
 1480            editor.selections.display_ranges(cx),
 1481            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1482        );
 1483
 1484        // moving past end of document should not change goal_x
 1485        editor.move_down(&MoveDown, window, cx);
 1486        assert_eq!(
 1487            editor.selections.display_ranges(cx),
 1488            &[empty_range(5, "".len())]
 1489        );
 1490
 1491        editor.move_down(&MoveDown, window, cx);
 1492        assert_eq!(
 1493            editor.selections.display_ranges(cx),
 1494            &[empty_range(5, "".len())]
 1495        );
 1496
 1497        editor.move_up(&MoveUp, window, cx);
 1498        assert_eq!(
 1499            editor.selections.display_ranges(cx),
 1500            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1501        );
 1502
 1503        editor.move_up(&MoveUp, window, cx);
 1504        assert_eq!(
 1505            editor.selections.display_ranges(cx),
 1506            &[empty_range(3, "abcd".len())]
 1507        );
 1508
 1509        editor.move_up(&MoveUp, window, cx);
 1510        assert_eq!(
 1511            editor.selections.display_ranges(cx),
 1512            &[empty_range(2, "αβγ".len())]
 1513        );
 1514    });
 1515}
 1516
 1517#[gpui::test]
 1518fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1519    init_test(cx, |_| {});
 1520    let move_to_beg = MoveToBeginningOfLine {
 1521        stop_at_soft_wraps: true,
 1522        stop_at_indent: true,
 1523    };
 1524
 1525    let delete_to_beg = DeleteToBeginningOfLine {
 1526        stop_at_indent: false,
 1527    };
 1528
 1529    let move_to_end = MoveToEndOfLine {
 1530        stop_at_soft_wraps: true,
 1531    };
 1532
 1533    let editor = cx.add_window(|window, cx| {
 1534        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1535        build_editor(buffer, window, cx)
 1536    });
 1537    _ = editor.update(cx, |editor, window, cx| {
 1538        editor.change_selections(None, window, cx, |s| {
 1539            s.select_display_ranges([
 1540                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1541                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1542            ]);
 1543        });
 1544    });
 1545
 1546    _ = editor.update(cx, |editor, window, cx| {
 1547        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1548        assert_eq!(
 1549            editor.selections.display_ranges(cx),
 1550            &[
 1551                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1552                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1553            ]
 1554        );
 1555    });
 1556
 1557    _ = editor.update(cx, |editor, window, cx| {
 1558        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1559        assert_eq!(
 1560            editor.selections.display_ranges(cx),
 1561            &[
 1562                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1563                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1564            ]
 1565        );
 1566    });
 1567
 1568    _ = editor.update(cx, |editor, window, cx| {
 1569        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1570        assert_eq!(
 1571            editor.selections.display_ranges(cx),
 1572            &[
 1573                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1574                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1575            ]
 1576        );
 1577    });
 1578
 1579    _ = editor.update(cx, |editor, window, cx| {
 1580        editor.move_to_end_of_line(&move_to_end, window, cx);
 1581        assert_eq!(
 1582            editor.selections.display_ranges(cx),
 1583            &[
 1584                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1585                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1586            ]
 1587        );
 1588    });
 1589
 1590    // Moving to the end of line again is a no-op.
 1591    _ = editor.update(cx, |editor, window, cx| {
 1592        editor.move_to_end_of_line(&move_to_end, window, cx);
 1593        assert_eq!(
 1594            editor.selections.display_ranges(cx),
 1595            &[
 1596                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1597                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1598            ]
 1599        );
 1600    });
 1601
 1602    _ = editor.update(cx, |editor, window, cx| {
 1603        editor.move_left(&MoveLeft, window, cx);
 1604        editor.select_to_beginning_of_line(
 1605            &SelectToBeginningOfLine {
 1606                stop_at_soft_wraps: true,
 1607                stop_at_indent: true,
 1608            },
 1609            window,
 1610            cx,
 1611        );
 1612        assert_eq!(
 1613            editor.selections.display_ranges(cx),
 1614            &[
 1615                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1616                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1617            ]
 1618        );
 1619    });
 1620
 1621    _ = editor.update(cx, |editor, window, cx| {
 1622        editor.select_to_beginning_of_line(
 1623            &SelectToBeginningOfLine {
 1624                stop_at_soft_wraps: true,
 1625                stop_at_indent: true,
 1626            },
 1627            window,
 1628            cx,
 1629        );
 1630        assert_eq!(
 1631            editor.selections.display_ranges(cx),
 1632            &[
 1633                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1634                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1635            ]
 1636        );
 1637    });
 1638
 1639    _ = editor.update(cx, |editor, window, cx| {
 1640        editor.select_to_beginning_of_line(
 1641            &SelectToBeginningOfLine {
 1642                stop_at_soft_wraps: true,
 1643                stop_at_indent: true,
 1644            },
 1645            window,
 1646            cx,
 1647        );
 1648        assert_eq!(
 1649            editor.selections.display_ranges(cx),
 1650            &[
 1651                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1652                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1653            ]
 1654        );
 1655    });
 1656
 1657    _ = editor.update(cx, |editor, window, cx| {
 1658        editor.select_to_end_of_line(
 1659            &SelectToEndOfLine {
 1660                stop_at_soft_wraps: true,
 1661            },
 1662            window,
 1663            cx,
 1664        );
 1665        assert_eq!(
 1666            editor.selections.display_ranges(cx),
 1667            &[
 1668                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1669                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1670            ]
 1671        );
 1672    });
 1673
 1674    _ = editor.update(cx, |editor, window, cx| {
 1675        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1676        assert_eq!(editor.display_text(cx), "ab\n  de");
 1677        assert_eq!(
 1678            editor.selections.display_ranges(cx),
 1679            &[
 1680                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1681                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1682            ]
 1683        );
 1684    });
 1685
 1686    _ = editor.update(cx, |editor, window, cx| {
 1687        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1688        assert_eq!(editor.display_text(cx), "\n");
 1689        assert_eq!(
 1690            editor.selections.display_ranges(cx),
 1691            &[
 1692                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1693                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1694            ]
 1695        );
 1696    });
 1697}
 1698
 1699#[gpui::test]
 1700fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1701    init_test(cx, |_| {});
 1702    let move_to_beg = MoveToBeginningOfLine {
 1703        stop_at_soft_wraps: false,
 1704        stop_at_indent: false,
 1705    };
 1706
 1707    let move_to_end = MoveToEndOfLine {
 1708        stop_at_soft_wraps: false,
 1709    };
 1710
 1711    let editor = cx.add_window(|window, cx| {
 1712        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1713        build_editor(buffer, window, cx)
 1714    });
 1715
 1716    _ = editor.update(cx, |editor, window, cx| {
 1717        editor.set_wrap_width(Some(140.0.into()), cx);
 1718
 1719        // We expect the following lines after wrapping
 1720        // ```
 1721        // thequickbrownfox
 1722        // jumpedoverthelazydo
 1723        // gs
 1724        // ```
 1725        // The final `gs` was soft-wrapped onto a new line.
 1726        assert_eq!(
 1727            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1728            editor.display_text(cx),
 1729        );
 1730
 1731        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1732        // Start the cursor at the `k` on the first line
 1733        editor.change_selections(None, window, cx, |s| {
 1734            s.select_display_ranges([
 1735                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1736            ]);
 1737        });
 1738
 1739        // Moving to the beginning of the line should put us at the beginning of the line.
 1740        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1741        assert_eq!(
 1742            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1743            editor.selections.display_ranges(cx)
 1744        );
 1745
 1746        // Moving to the end of the line should put us at the end of the line.
 1747        editor.move_to_end_of_line(&move_to_end, window, cx);
 1748        assert_eq!(
 1749            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1750            editor.selections.display_ranges(cx)
 1751        );
 1752
 1753        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1754        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1755        editor.change_selections(None, window, cx, |s| {
 1756            s.select_display_ranges([
 1757                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1758            ]);
 1759        });
 1760
 1761        // Moving to the beginning of the line should put us at the start of the second line of
 1762        // display text, i.e., the `j`.
 1763        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1764        assert_eq!(
 1765            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1766            editor.selections.display_ranges(cx)
 1767        );
 1768
 1769        // Moving to the beginning of the line again should be a no-op.
 1770        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1771        assert_eq!(
 1772            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1773            editor.selections.display_ranges(cx)
 1774        );
 1775
 1776        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1777        // next display line.
 1778        editor.move_to_end_of_line(&move_to_end, window, cx);
 1779        assert_eq!(
 1780            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1781            editor.selections.display_ranges(cx)
 1782        );
 1783
 1784        // Moving to the end of the line again should be a no-op.
 1785        editor.move_to_end_of_line(&move_to_end, window, cx);
 1786        assert_eq!(
 1787            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1788            editor.selections.display_ranges(cx)
 1789        );
 1790    });
 1791}
 1792
 1793#[gpui::test]
 1794fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1795    init_test(cx, |_| {});
 1796
 1797    let move_to_beg = MoveToBeginningOfLine {
 1798        stop_at_soft_wraps: true,
 1799        stop_at_indent: true,
 1800    };
 1801
 1802    let select_to_beg = SelectToBeginningOfLine {
 1803        stop_at_soft_wraps: true,
 1804        stop_at_indent: true,
 1805    };
 1806
 1807    let delete_to_beg = DeleteToBeginningOfLine {
 1808        stop_at_indent: true,
 1809    };
 1810
 1811    let move_to_end = MoveToEndOfLine {
 1812        stop_at_soft_wraps: false,
 1813    };
 1814
 1815    let editor = cx.add_window(|window, cx| {
 1816        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1817        build_editor(buffer, window, cx)
 1818    });
 1819
 1820    _ = editor.update(cx, |editor, window, cx| {
 1821        editor.change_selections(None, window, cx, |s| {
 1822            s.select_display_ranges([
 1823                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1824                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1825            ]);
 1826        });
 1827
 1828        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1829        // and the second cursor at the first non-whitespace character in the line.
 1830        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1831        assert_eq!(
 1832            editor.selections.display_ranges(cx),
 1833            &[
 1834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1835                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1836            ]
 1837        );
 1838
 1839        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1840        // and should move the second cursor to the beginning of the line.
 1841        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1842        assert_eq!(
 1843            editor.selections.display_ranges(cx),
 1844            &[
 1845                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1846                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1847            ]
 1848        );
 1849
 1850        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1851        // and should move the second cursor back to the first non-whitespace character in the line.
 1852        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1853        assert_eq!(
 1854            editor.selections.display_ranges(cx),
 1855            &[
 1856                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1857                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1858            ]
 1859        );
 1860
 1861        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1862        // and to the first non-whitespace character in the line for the second cursor.
 1863        editor.move_to_end_of_line(&move_to_end, window, cx);
 1864        editor.move_left(&MoveLeft, window, cx);
 1865        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1866        assert_eq!(
 1867            editor.selections.display_ranges(cx),
 1868            &[
 1869                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1870                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1871            ]
 1872        );
 1873
 1874        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1875        // and should select to the beginning of the line for the second cursor.
 1876        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1877        assert_eq!(
 1878            editor.selections.display_ranges(cx),
 1879            &[
 1880                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1881                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1882            ]
 1883        );
 1884
 1885        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1886        // and should delete to the first non-whitespace character in the line for the second cursor.
 1887        editor.move_to_end_of_line(&move_to_end, window, cx);
 1888        editor.move_left(&MoveLeft, window, cx);
 1889        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1890        assert_eq!(editor.text(cx), "c\n  f");
 1891    });
 1892}
 1893
 1894#[gpui::test]
 1895fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1896    init_test(cx, |_| {});
 1897
 1898    let editor = cx.add_window(|window, cx| {
 1899        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1900        build_editor(buffer, window, cx)
 1901    });
 1902    _ = editor.update(cx, |editor, window, cx| {
 1903        editor.change_selections(None, window, cx, |s| {
 1904            s.select_display_ranges([
 1905                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1906                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1907            ])
 1908        });
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1923        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1930
 1931        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1932        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1933
 1934        editor.move_right(&MoveRight, window, cx);
 1935        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1936        assert_selection_ranges(
 1937            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1938            editor,
 1939            cx,
 1940        );
 1941
 1942        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1943        assert_selection_ranges(
 1944            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1945            editor,
 1946            cx,
 1947        );
 1948
 1949        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1950        assert_selection_ranges(
 1951            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1952            editor,
 1953            cx,
 1954        );
 1955    });
 1956}
 1957
 1958#[gpui::test]
 1959fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1960    init_test(cx, |_| {});
 1961
 1962    let editor = cx.add_window(|window, cx| {
 1963        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1964        build_editor(buffer, window, cx)
 1965    });
 1966
 1967    _ = editor.update(cx, |editor, window, cx| {
 1968        editor.set_wrap_width(Some(140.0.into()), cx);
 1969        assert_eq!(
 1970            editor.display_text(cx),
 1971            "use one::{\n    two::three::\n    four::five\n};"
 1972        );
 1973
 1974        editor.change_selections(None, window, cx, |s| {
 1975            s.select_display_ranges([
 1976                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1977            ]);
 1978        });
 1979
 1980        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1981        assert_eq!(
 1982            editor.selections.display_ranges(cx),
 1983            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1984        );
 1985
 1986        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1987        assert_eq!(
 1988            editor.selections.display_ranges(cx),
 1989            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1990        );
 1991
 1992        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1993        assert_eq!(
 1994            editor.selections.display_ranges(cx),
 1995            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1996        );
 1997
 1998        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1999        assert_eq!(
 2000            editor.selections.display_ranges(cx),
 2001            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2002        );
 2003
 2004        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2005        assert_eq!(
 2006            editor.selections.display_ranges(cx),
 2007            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2008        );
 2009
 2010        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2011        assert_eq!(
 2012            editor.selections.display_ranges(cx),
 2013            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2014        );
 2015    });
 2016}
 2017
 2018#[gpui::test]
 2019async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2020    init_test(cx, |_| {});
 2021    let mut cx = EditorTestContext::new(cx).await;
 2022
 2023    let line_height = cx.editor(|editor, window, _| {
 2024        editor
 2025            .style()
 2026            .unwrap()
 2027            .text
 2028            .line_height_in_pixels(window.rem_size())
 2029    });
 2030    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2031
 2032    cx.set_state(
 2033        &r#"ˇone
 2034        two
 2035
 2036        three
 2037        fourˇ
 2038        five
 2039
 2040        six"#
 2041            .unindent(),
 2042    );
 2043
 2044    cx.update_editor(|editor, window, cx| {
 2045        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2046    });
 2047    cx.assert_editor_state(
 2048        &r#"one
 2049        two
 2050        ˇ
 2051        three
 2052        four
 2053        five
 2054        ˇ
 2055        six"#
 2056            .unindent(),
 2057    );
 2058
 2059    cx.update_editor(|editor, window, cx| {
 2060        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2061    });
 2062    cx.assert_editor_state(
 2063        &r#"one
 2064        two
 2065
 2066        three
 2067        four
 2068        five
 2069        ˇ
 2070        sixˇ"#
 2071            .unindent(),
 2072    );
 2073
 2074    cx.update_editor(|editor, window, cx| {
 2075        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2076    });
 2077    cx.assert_editor_state(
 2078        &r#"one
 2079        two
 2080
 2081        three
 2082        four
 2083        five
 2084
 2085        sixˇ"#
 2086            .unindent(),
 2087    );
 2088
 2089    cx.update_editor(|editor, window, cx| {
 2090        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2091    });
 2092    cx.assert_editor_state(
 2093        &r#"one
 2094        two
 2095
 2096        three
 2097        four
 2098        five
 2099        ˇ
 2100        six"#
 2101            .unindent(),
 2102    );
 2103
 2104    cx.update_editor(|editor, window, cx| {
 2105        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2106    });
 2107    cx.assert_editor_state(
 2108        &r#"one
 2109        two
 2110        ˇ
 2111        three
 2112        four
 2113        five
 2114
 2115        six"#
 2116            .unindent(),
 2117    );
 2118
 2119    cx.update_editor(|editor, window, cx| {
 2120        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2121    });
 2122    cx.assert_editor_state(
 2123        &r#"ˇone
 2124        two
 2125
 2126        three
 2127        four
 2128        five
 2129
 2130        six"#
 2131            .unindent(),
 2132    );
 2133}
 2134
 2135#[gpui::test]
 2136async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2137    init_test(cx, |_| {});
 2138    let mut cx = EditorTestContext::new(cx).await;
 2139    let line_height = cx.editor(|editor, window, _| {
 2140        editor
 2141            .style()
 2142            .unwrap()
 2143            .text
 2144            .line_height_in_pixels(window.rem_size())
 2145    });
 2146    let window = cx.window;
 2147    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2148
 2149    cx.set_state(
 2150        r#"ˇone
 2151        two
 2152        three
 2153        four
 2154        five
 2155        six
 2156        seven
 2157        eight
 2158        nine
 2159        ten
 2160        "#,
 2161    );
 2162
 2163    cx.update_editor(|editor, window, cx| {
 2164        assert_eq!(
 2165            editor.snapshot(window, cx).scroll_position(),
 2166            gpui::Point::new(0., 0.)
 2167        );
 2168        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2169        assert_eq!(
 2170            editor.snapshot(window, cx).scroll_position(),
 2171            gpui::Point::new(0., 3.)
 2172        );
 2173        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2174        assert_eq!(
 2175            editor.snapshot(window, cx).scroll_position(),
 2176            gpui::Point::new(0., 6.)
 2177        );
 2178        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2179        assert_eq!(
 2180            editor.snapshot(window, cx).scroll_position(),
 2181            gpui::Point::new(0., 3.)
 2182        );
 2183
 2184        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2185        assert_eq!(
 2186            editor.snapshot(window, cx).scroll_position(),
 2187            gpui::Point::new(0., 1.)
 2188        );
 2189        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2190        assert_eq!(
 2191            editor.snapshot(window, cx).scroll_position(),
 2192            gpui::Point::new(0., 3.)
 2193        );
 2194    });
 2195}
 2196
 2197#[gpui::test]
 2198async fn test_autoscroll(cx: &mut TestAppContext) {
 2199    init_test(cx, |_| {});
 2200    let mut cx = EditorTestContext::new(cx).await;
 2201
 2202    let line_height = cx.update_editor(|editor, window, cx| {
 2203        editor.set_vertical_scroll_margin(2, cx);
 2204        editor
 2205            .style()
 2206            .unwrap()
 2207            .text
 2208            .line_height_in_pixels(window.rem_size())
 2209    });
 2210    let window = cx.window;
 2211    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2212
 2213    cx.set_state(
 2214        r#"ˇone
 2215            two
 2216            three
 2217            four
 2218            five
 2219            six
 2220            seven
 2221            eight
 2222            nine
 2223            ten
 2224        "#,
 2225    );
 2226    cx.update_editor(|editor, window, cx| {
 2227        assert_eq!(
 2228            editor.snapshot(window, cx).scroll_position(),
 2229            gpui::Point::new(0., 0.0)
 2230        );
 2231    });
 2232
 2233    // Add a cursor below the visible area. Since both cursors cannot fit
 2234    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2235    // allows the vertical scroll margin below that cursor.
 2236    cx.update_editor(|editor, window, cx| {
 2237        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2238            selections.select_ranges([
 2239                Point::new(0, 0)..Point::new(0, 0),
 2240                Point::new(6, 0)..Point::new(6, 0),
 2241            ]);
 2242        })
 2243    });
 2244    cx.update_editor(|editor, window, cx| {
 2245        assert_eq!(
 2246            editor.snapshot(window, cx).scroll_position(),
 2247            gpui::Point::new(0., 3.0)
 2248        );
 2249    });
 2250
 2251    // Move down. The editor cursor scrolls down to track the newest cursor.
 2252    cx.update_editor(|editor, window, cx| {
 2253        editor.move_down(&Default::default(), window, cx);
 2254    });
 2255    cx.update_editor(|editor, window, cx| {
 2256        assert_eq!(
 2257            editor.snapshot(window, cx).scroll_position(),
 2258            gpui::Point::new(0., 4.0)
 2259        );
 2260    });
 2261
 2262    // Add a cursor above the visible area. Since both cursors fit on screen,
 2263    // the editor scrolls to show both.
 2264    cx.update_editor(|editor, window, cx| {
 2265        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2266            selections.select_ranges([
 2267                Point::new(1, 0)..Point::new(1, 0),
 2268                Point::new(6, 0)..Point::new(6, 0),
 2269            ]);
 2270        })
 2271    });
 2272    cx.update_editor(|editor, window, cx| {
 2273        assert_eq!(
 2274            editor.snapshot(window, cx).scroll_position(),
 2275            gpui::Point::new(0., 1.0)
 2276        );
 2277    });
 2278}
 2279
 2280#[gpui::test]
 2281async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2282    init_test(cx, |_| {});
 2283    let mut cx = EditorTestContext::new(cx).await;
 2284
 2285    let line_height = cx.editor(|editor, window, _cx| {
 2286        editor
 2287            .style()
 2288            .unwrap()
 2289            .text
 2290            .line_height_in_pixels(window.rem_size())
 2291    });
 2292    let window = cx.window;
 2293    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2294    cx.set_state(
 2295        &r#"
 2296        ˇone
 2297        two
 2298        threeˇ
 2299        four
 2300        five
 2301        six
 2302        seven
 2303        eight
 2304        nine
 2305        ten
 2306        "#
 2307        .unindent(),
 2308    );
 2309
 2310    cx.update_editor(|editor, window, cx| {
 2311        editor.move_page_down(&MovePageDown::default(), window, cx)
 2312    });
 2313    cx.assert_editor_state(
 2314        &r#"
 2315        one
 2316        two
 2317        three
 2318        ˇfour
 2319        five
 2320        sixˇ
 2321        seven
 2322        eight
 2323        nine
 2324        ten
 2325        "#
 2326        .unindent(),
 2327    );
 2328
 2329    cx.update_editor(|editor, window, cx| {
 2330        editor.move_page_down(&MovePageDown::default(), window, cx)
 2331    });
 2332    cx.assert_editor_state(
 2333        &r#"
 2334        one
 2335        two
 2336        three
 2337        four
 2338        five
 2339        six
 2340        ˇseven
 2341        eight
 2342        nineˇ
 2343        ten
 2344        "#
 2345        .unindent(),
 2346    );
 2347
 2348    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2349    cx.assert_editor_state(
 2350        &r#"
 2351        one
 2352        two
 2353        three
 2354        ˇfour
 2355        five
 2356        sixˇ
 2357        seven
 2358        eight
 2359        nine
 2360        ten
 2361        "#
 2362        .unindent(),
 2363    );
 2364
 2365    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2366    cx.assert_editor_state(
 2367        &r#"
 2368        ˇone
 2369        two
 2370        threeˇ
 2371        four
 2372        five
 2373        six
 2374        seven
 2375        eight
 2376        nine
 2377        ten
 2378        "#
 2379        .unindent(),
 2380    );
 2381
 2382    // Test select collapsing
 2383    cx.update_editor(|editor, window, cx| {
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386        editor.move_page_down(&MovePageDown::default(), window, cx);
 2387    });
 2388    cx.assert_editor_state(
 2389        &r#"
 2390        one
 2391        two
 2392        three
 2393        four
 2394        five
 2395        six
 2396        seven
 2397        eight
 2398        nine
 2399        ˇten
 2400        ˇ"#
 2401        .unindent(),
 2402    );
 2403}
 2404
 2405#[gpui::test]
 2406async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2407    init_test(cx, |_| {});
 2408    let mut cx = EditorTestContext::new(cx).await;
 2409    cx.set_state("one «two threeˇ» four");
 2410    cx.update_editor(|editor, window, cx| {
 2411        editor.delete_to_beginning_of_line(
 2412            &DeleteToBeginningOfLine {
 2413                stop_at_indent: false,
 2414            },
 2415            window,
 2416            cx,
 2417        );
 2418        assert_eq!(editor.text(cx), " four");
 2419    });
 2420}
 2421
 2422#[gpui::test]
 2423fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2424    init_test(cx, |_| {});
 2425
 2426    let editor = cx.add_window(|window, cx| {
 2427        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2428        build_editor(buffer.clone(), window, cx)
 2429    });
 2430
 2431    _ = editor.update(cx, |editor, window, cx| {
 2432        editor.change_selections(None, window, cx, |s| {
 2433            s.select_display_ranges([
 2434                // an empty selection - the preceding word fragment is deleted
 2435                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2436                // characters selected - they are deleted
 2437                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2438            ])
 2439        });
 2440        editor.delete_to_previous_word_start(
 2441            &DeleteToPreviousWordStart {
 2442                ignore_newlines: false,
 2443            },
 2444            window,
 2445            cx,
 2446        );
 2447        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2448    });
 2449
 2450    _ = editor.update(cx, |editor, window, cx| {
 2451        editor.change_selections(None, window, cx, |s| {
 2452            s.select_display_ranges([
 2453                // an empty selection - the following word fragment is deleted
 2454                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2455                // characters selected - they are deleted
 2456                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2457            ])
 2458        });
 2459        editor.delete_to_next_word_end(
 2460            &DeleteToNextWordEnd {
 2461                ignore_newlines: false,
 2462            },
 2463            window,
 2464            cx,
 2465        );
 2466        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2467    });
 2468}
 2469
 2470#[gpui::test]
 2471fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2472    init_test(cx, |_| {});
 2473
 2474    let editor = cx.add_window(|window, cx| {
 2475        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2476        build_editor(buffer.clone(), window, cx)
 2477    });
 2478    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2479        ignore_newlines: false,
 2480    };
 2481    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2482        ignore_newlines: true,
 2483    };
 2484
 2485    _ = editor.update(cx, |editor, window, cx| {
 2486        editor.change_selections(None, window, cx, |s| {
 2487            s.select_display_ranges([
 2488                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2489            ])
 2490        });
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2501        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2502        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2503    });
 2504}
 2505
 2506#[gpui::test]
 2507fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2508    init_test(cx, |_| {});
 2509
 2510    let editor = cx.add_window(|window, cx| {
 2511        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2512        build_editor(buffer.clone(), window, cx)
 2513    });
 2514    let del_to_next_word_end = DeleteToNextWordEnd {
 2515        ignore_newlines: false,
 2516    };
 2517    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2518        ignore_newlines: true,
 2519    };
 2520
 2521    _ = editor.update(cx, |editor, window, cx| {
 2522        editor.change_selections(None, window, cx, |s| {
 2523            s.select_display_ranges([
 2524                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2525            ])
 2526        });
 2527        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2528        assert_eq!(
 2529            editor.buffer.read(cx).read(cx).text(),
 2530            "one\n   two\nthree\n   four"
 2531        );
 2532        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2533        assert_eq!(
 2534            editor.buffer.read(cx).read(cx).text(),
 2535            "\n   two\nthree\n   four"
 2536        );
 2537        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2538        assert_eq!(
 2539            editor.buffer.read(cx).read(cx).text(),
 2540            "two\nthree\n   four"
 2541        );
 2542        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2546        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2547        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2548    });
 2549}
 2550
 2551#[gpui::test]
 2552fn test_newline(cx: &mut TestAppContext) {
 2553    init_test(cx, |_| {});
 2554
 2555    let editor = cx.add_window(|window, cx| {
 2556        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2557        build_editor(buffer.clone(), window, cx)
 2558    });
 2559
 2560    _ = editor.update(cx, |editor, window, cx| {
 2561        editor.change_selections(None, window, cx, |s| {
 2562            s.select_display_ranges([
 2563                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2564                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2565                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2566            ])
 2567        });
 2568
 2569        editor.newline(&Newline, window, cx);
 2570        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2571    });
 2572}
 2573
 2574#[gpui::test]
 2575fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2576    init_test(cx, |_| {});
 2577
 2578    let editor = cx.add_window(|window, cx| {
 2579        let buffer = MultiBuffer::build_simple(
 2580            "
 2581                a
 2582                b(
 2583                    X
 2584                )
 2585                c(
 2586                    X
 2587                )
 2588            "
 2589            .unindent()
 2590            .as_str(),
 2591            cx,
 2592        );
 2593        let mut editor = build_editor(buffer.clone(), window, cx);
 2594        editor.change_selections(None, window, cx, |s| {
 2595            s.select_ranges([
 2596                Point::new(2, 4)..Point::new(2, 5),
 2597                Point::new(5, 4)..Point::new(5, 5),
 2598            ])
 2599        });
 2600        editor
 2601    });
 2602
 2603    _ = editor.update(cx, |editor, window, cx| {
 2604        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2605        editor.buffer.update(cx, |buffer, cx| {
 2606            buffer.edit(
 2607                [
 2608                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2609                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2610                ],
 2611                None,
 2612                cx,
 2613            );
 2614            assert_eq!(
 2615                buffer.read(cx).text(),
 2616                "
 2617                    a
 2618                    b()
 2619                    c()
 2620                "
 2621                .unindent()
 2622            );
 2623        });
 2624        assert_eq!(
 2625            editor.selections.ranges(cx),
 2626            &[
 2627                Point::new(1, 2)..Point::new(1, 2),
 2628                Point::new(2, 2)..Point::new(2, 2),
 2629            ],
 2630        );
 2631
 2632        editor.newline(&Newline, window, cx);
 2633        assert_eq!(
 2634            editor.text(cx),
 2635            "
 2636                a
 2637                b(
 2638                )
 2639                c(
 2640                )
 2641            "
 2642            .unindent()
 2643        );
 2644
 2645        // The selections are moved after the inserted newlines
 2646        assert_eq!(
 2647            editor.selections.ranges(cx),
 2648            &[
 2649                Point::new(2, 0)..Point::new(2, 0),
 2650                Point::new(4, 0)..Point::new(4, 0),
 2651            ],
 2652        );
 2653    });
 2654}
 2655
 2656#[gpui::test]
 2657async fn test_newline_above(cx: &mut TestAppContext) {
 2658    init_test(cx, |settings| {
 2659        settings.defaults.tab_size = NonZeroU32::new(4)
 2660    });
 2661
 2662    let language = Arc::new(
 2663        Language::new(
 2664            LanguageConfig::default(),
 2665            Some(tree_sitter_rust::LANGUAGE.into()),
 2666        )
 2667        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2668        .unwrap(),
 2669    );
 2670
 2671    let mut cx = EditorTestContext::new(cx).await;
 2672    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2673    cx.set_state(indoc! {"
 2674        const a: ˇA = (
 2675 2676                «const_functionˇ»(ˇ),
 2677                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2678 2679        ˇ);ˇ
 2680    "});
 2681
 2682    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2683    cx.assert_editor_state(indoc! {"
 2684        ˇ
 2685        const a: A = (
 2686            ˇ
 2687            (
 2688                ˇ
 2689                ˇ
 2690                const_function(),
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                ˇ
 2695                something_else,
 2696                ˇ
 2697            )
 2698            ˇ
 2699            ˇ
 2700        );
 2701    "});
 2702}
 2703
 2704#[gpui::test]
 2705async fn test_newline_below(cx: &mut TestAppContext) {
 2706    init_test(cx, |settings| {
 2707        settings.defaults.tab_size = NonZeroU32::new(4)
 2708    });
 2709
 2710    let language = Arc::new(
 2711        Language::new(
 2712            LanguageConfig::default(),
 2713            Some(tree_sitter_rust::LANGUAGE.into()),
 2714        )
 2715        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2716        .unwrap(),
 2717    );
 2718
 2719    let mut cx = EditorTestContext::new(cx).await;
 2720    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2721    cx.set_state(indoc! {"
 2722        const a: ˇA = (
 2723 2724                «const_functionˇ»(ˇ),
 2725                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2726 2727        ˇ);ˇ
 2728    "});
 2729
 2730    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2731    cx.assert_editor_state(indoc! {"
 2732        const a: A = (
 2733            ˇ
 2734            (
 2735                ˇ
 2736                const_function(),
 2737                ˇ
 2738                ˇ
 2739                something_else,
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743                ˇ
 2744            )
 2745            ˇ
 2746        );
 2747        ˇ
 2748        ˇ
 2749    "});
 2750}
 2751
 2752#[gpui::test]
 2753async fn test_newline_comments(cx: &mut TestAppContext) {
 2754    init_test(cx, |settings| {
 2755        settings.defaults.tab_size = NonZeroU32::new(4)
 2756    });
 2757
 2758    let language = Arc::new(Language::new(
 2759        LanguageConfig {
 2760            line_comments: vec!["// ".into()],
 2761            ..LanguageConfig::default()
 2762        },
 2763        None,
 2764    ));
 2765    {
 2766        let mut cx = EditorTestContext::new(cx).await;
 2767        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2768        cx.set_state(indoc! {"
 2769        // Fooˇ
 2770    "});
 2771
 2772        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2773        cx.assert_editor_state(indoc! {"
 2774        // Foo
 2775        // ˇ
 2776    "});
 2777        // Ensure that we add comment prefix when existing line contains space
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(
 2780            indoc! {"
 2781        // Foo
 2782        //s
 2783        // ˇ
 2784    "}
 2785            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2786            .as_str(),
 2787        );
 2788        // Ensure that we add comment prefix when existing line does not contain space
 2789        cx.set_state(indoc! {"
 2790        // Foo
 2791        //ˇ
 2792    "});
 2793        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794        cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        //
 2797        // ˇ
 2798    "});
 2799        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2800        cx.set_state(indoc! {"
 2801        ˇ// Foo
 2802    "});
 2803        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2804        cx.assert_editor_state(indoc! {"
 2805
 2806        ˇ// Foo
 2807    "});
 2808    }
 2809    // Ensure that comment continuations can be disabled.
 2810    update_test_language_settings(cx, |settings| {
 2811        settings.defaults.extend_comment_on_newline = Some(false);
 2812    });
 2813    let mut cx = EditorTestContext::new(cx).await;
 2814    cx.set_state(indoc! {"
 2815        // Fooˇ
 2816    "});
 2817    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2818    cx.assert_editor_state(indoc! {"
 2819        // Foo
 2820        ˇ
 2821    "});
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.tab_size = NonZeroU32::new(4)
 2828    });
 2829
 2830    let language = Arc::new(Language::new(
 2831        LanguageConfig {
 2832            line_comments: vec!["// ".into(), "/// ".into()],
 2833            ..LanguageConfig::default()
 2834        },
 2835        None,
 2836    ));
 2837    {
 2838        let mut cx = EditorTestContext::new(cx).await;
 2839        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2840        cx.set_state(indoc! {"
 2841        //ˇ
 2842    "});
 2843        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2844        cx.assert_editor_state(indoc! {"
 2845        //
 2846        // ˇ
 2847    "});
 2848
 2849        cx.set_state(indoc! {"
 2850        ///ˇ
 2851    "});
 2852        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2853        cx.assert_editor_state(indoc! {"
 2854        ///
 2855        /// ˇ
 2856    "});
 2857    }
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 2862    init_test(cx, |settings| {
 2863        settings.defaults.tab_size = NonZeroU32::new(4)
 2864    });
 2865
 2866    let language = Arc::new(Language::new(
 2867        LanguageConfig {
 2868            documentation: Some(language::DocumentationConfig {
 2869                start: "/**".into(),
 2870                end: "*/".into(),
 2871                prefix: "* ".into(),
 2872                tab_size: NonZeroU32::new(1).unwrap(),
 2873            }),
 2874            ..LanguageConfig::default()
 2875        },
 2876        None,
 2877    ));
 2878    {
 2879        let mut cx = EditorTestContext::new(cx).await;
 2880        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2881        cx.set_state(indoc! {"
 2882        /**ˇ
 2883    "});
 2884
 2885        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2886        cx.assert_editor_state(indoc! {"
 2887        /**
 2888         * ˇ
 2889    "});
 2890        // Ensure that if cursor is before the comment start,
 2891        // we do not actually insert a comment prefix.
 2892        cx.set_state(indoc! {"
 2893        ˇ/**
 2894    "});
 2895        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2896        cx.assert_editor_state(indoc! {"
 2897
 2898        ˇ/**
 2899    "});
 2900        // Ensure that if cursor is between it doesn't add comment prefix.
 2901        cx.set_state(indoc! {"
 2902        /*ˇ*
 2903    "});
 2904        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2905        cx.assert_editor_state(indoc! {"
 2906        /*
 2907        ˇ*
 2908    "});
 2909        // Ensure that if suffix exists on same line after cursor it adds new line.
 2910        cx.set_state(indoc! {"
 2911        /**ˇ*/
 2912    "});
 2913        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2914        cx.assert_editor_state(indoc! {"
 2915        /**
 2916         * ˇ
 2917         */
 2918    "});
 2919        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2920        cx.set_state(indoc! {"
 2921        /**ˇ */
 2922    "});
 2923        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2924        cx.assert_editor_state(indoc! {"
 2925        /**
 2926         * ˇ
 2927         */
 2928    "});
 2929        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2930        cx.set_state(indoc! {"
 2931        /** ˇ*/
 2932    "});
 2933        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2934        cx.assert_editor_state(
 2935            indoc! {"
 2936        /**s
 2937         * ˇ
 2938         */
 2939    "}
 2940            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2941            .as_str(),
 2942        );
 2943        // Ensure that delimiter space is preserved when newline on already
 2944        // spaced delimiter.
 2945        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2946        cx.assert_editor_state(
 2947            indoc! {"
 2948        /**s
 2949         *s
 2950         * ˇ
 2951         */
 2952    "}
 2953            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2954            .as_str(),
 2955        );
 2956        // Ensure that delimiter space is preserved when space is not
 2957        // on existing delimiter.
 2958        cx.set_state(indoc! {"
 2959        /**
 2960 2961         */
 2962    "});
 2963        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2964        cx.assert_editor_state(indoc! {"
 2965        /**
 2966         *
 2967         * ˇ
 2968         */
 2969    "});
 2970        // Ensure that if suffix exists on same line after cursor it
 2971        // doesn't add extra new line if prefix is not on same line.
 2972        cx.set_state(indoc! {"
 2973        /**
 2974        ˇ*/
 2975    "});
 2976        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2977        cx.assert_editor_state(indoc! {"
 2978        /**
 2979
 2980        ˇ*/
 2981    "});
 2982        // Ensure that it detects suffix after existing prefix.
 2983        cx.set_state(indoc! {"
 2984        /**ˇ/
 2985    "});
 2986        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2987        cx.assert_editor_state(indoc! {"
 2988        /**
 2989        ˇ/
 2990    "});
 2991        // Ensure that if suffix exists on same line before
 2992        // cursor it does not add comment prefix.
 2993        cx.set_state(indoc! {"
 2994        /** */ˇ
 2995    "});
 2996        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2997        cx.assert_editor_state(indoc! {"
 2998        /** */
 2999        ˇ
 3000    "});
 3001        // Ensure that if suffix exists on same line before
 3002        // cursor it does not add comment prefix.
 3003        cx.set_state(indoc! {"
 3004        /**
 3005         *
 3006         */ˇ
 3007    "});
 3008        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3009        cx.assert_editor_state(indoc! {"
 3010        /**
 3011         *
 3012         */
 3013         ˇ
 3014    "});
 3015    }
 3016    // Ensure that comment continuations can be disabled.
 3017    update_test_language_settings(cx, |settings| {
 3018        settings.defaults.extend_comment_on_newline = Some(false);
 3019    });
 3020    let mut cx = EditorTestContext::new(cx).await;
 3021    cx.set_state(indoc! {"
 3022        /**ˇ
 3023    "});
 3024    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3025    cx.assert_editor_state(indoc! {"
 3026        /**
 3027        ˇ
 3028    "});
 3029}
 3030
 3031#[gpui::test]
 3032fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 3033    init_test(cx, |_| {});
 3034
 3035    let editor = cx.add_window(|window, cx| {
 3036        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3037        let mut editor = build_editor(buffer.clone(), window, cx);
 3038        editor.change_selections(None, window, cx, |s| {
 3039            s.select_ranges([3..4, 11..12, 19..20])
 3040        });
 3041        editor
 3042    });
 3043
 3044    _ = editor.update(cx, |editor, window, cx| {
 3045        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3046        editor.buffer.update(cx, |buffer, cx| {
 3047            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3048            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3049        });
 3050        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3051
 3052        editor.insert("Z", window, cx);
 3053        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3054
 3055        // The selections are moved after the inserted characters
 3056        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3057    });
 3058}
 3059
 3060#[gpui::test]
 3061async fn test_tab(cx: &mut TestAppContext) {
 3062    init_test(cx, |settings| {
 3063        settings.defaults.tab_size = NonZeroU32::new(3)
 3064    });
 3065
 3066    let mut cx = EditorTestContext::new(cx).await;
 3067    cx.set_state(indoc! {"
 3068        ˇabˇc
 3069        ˇ🏀ˇ🏀ˇefg
 3070 3071    "});
 3072    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3073    cx.assert_editor_state(indoc! {"
 3074           ˇab ˇc
 3075           ˇ🏀  ˇ🏀  ˇefg
 3076        d  ˇ
 3077    "});
 3078
 3079    cx.set_state(indoc! {"
 3080        a
 3081        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3082    "});
 3083    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3084    cx.assert_editor_state(indoc! {"
 3085        a
 3086           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3087    "});
 3088}
 3089
 3090#[gpui::test]
 3091async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3092    init_test(cx, |_| {});
 3093
 3094    let mut cx = EditorTestContext::new(cx).await;
 3095    let language = Arc::new(
 3096        Language::new(
 3097            LanguageConfig::default(),
 3098            Some(tree_sitter_rust::LANGUAGE.into()),
 3099        )
 3100        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3101        .unwrap(),
 3102    );
 3103    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3104
 3105    // test when all cursors are not at suggested indent
 3106    // then simply move to their suggested indent location
 3107    cx.set_state(indoc! {"
 3108        const a: B = (
 3109            c(
 3110        ˇ
 3111        ˇ    )
 3112        );
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        const a: B = (
 3117            c(
 3118                ˇ
 3119            ˇ)
 3120        );
 3121    "});
 3122
 3123    // test cursor already at suggested indent not moving when
 3124    // other cursors are yet to reach their suggested indents
 3125    cx.set_state(indoc! {"
 3126        ˇ
 3127        const a: B = (
 3128            c(
 3129                d(
 3130        ˇ
 3131                )
 3132        ˇ
 3133        ˇ    )
 3134        );
 3135    "});
 3136    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3137    cx.assert_editor_state(indoc! {"
 3138        ˇ
 3139        const a: B = (
 3140            c(
 3141                d(
 3142                    ˇ
 3143                )
 3144                ˇ
 3145            ˇ)
 3146        );
 3147    "});
 3148    // test when all cursors are at suggested indent then tab is inserted
 3149    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3150    cx.assert_editor_state(indoc! {"
 3151            ˇ
 3152        const a: B = (
 3153            c(
 3154                d(
 3155                        ˇ
 3156                )
 3157                    ˇ
 3158                ˇ)
 3159        );
 3160    "});
 3161
 3162    // test when current indent is less than suggested indent,
 3163    // we adjust line to match suggested indent and move cursor to it
 3164    //
 3165    // when no other cursor is at word boundary, all of them should move
 3166    cx.set_state(indoc! {"
 3167        const a: B = (
 3168            c(
 3169                d(
 3170        ˇ
 3171        ˇ   )
 3172        ˇ   )
 3173        );
 3174    "});
 3175    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3176    cx.assert_editor_state(indoc! {"
 3177        const a: B = (
 3178            c(
 3179                d(
 3180                    ˇ
 3181                ˇ)
 3182            ˇ)
 3183        );
 3184    "});
 3185
 3186    // test when current indent is less than suggested indent,
 3187    // we adjust line to match suggested indent and move cursor to it
 3188    //
 3189    // when some other cursor is at word boundary, it should not move
 3190    cx.set_state(indoc! {"
 3191        const a: B = (
 3192            c(
 3193                d(
 3194        ˇ
 3195        ˇ   )
 3196           ˇ)
 3197        );
 3198    "});
 3199    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3200    cx.assert_editor_state(indoc! {"
 3201        const a: B = (
 3202            c(
 3203                d(
 3204                    ˇ
 3205                ˇ)
 3206            ˇ)
 3207        );
 3208    "});
 3209
 3210    // test when current indent is more than suggested indent,
 3211    // we just move cursor to current indent instead of suggested indent
 3212    //
 3213    // when no other cursor is at word boundary, all of them should move
 3214    cx.set_state(indoc! {"
 3215        const a: B = (
 3216            c(
 3217                d(
 3218        ˇ
 3219        ˇ                )
 3220        ˇ   )
 3221        );
 3222    "});
 3223    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3224    cx.assert_editor_state(indoc! {"
 3225        const a: B = (
 3226            c(
 3227                d(
 3228                    ˇ
 3229                        ˇ)
 3230            ˇ)
 3231        );
 3232    "});
 3233    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3234    cx.assert_editor_state(indoc! {"
 3235        const a: B = (
 3236            c(
 3237                d(
 3238                        ˇ
 3239                            ˇ)
 3240                ˇ)
 3241        );
 3242    "});
 3243
 3244    // test when current indent is more than suggested indent,
 3245    // we just move cursor to current indent instead of suggested indent
 3246    //
 3247    // when some other cursor is at word boundary, it doesn't move
 3248    cx.set_state(indoc! {"
 3249        const a: B = (
 3250            c(
 3251                d(
 3252        ˇ
 3253        ˇ                )
 3254            ˇ)
 3255        );
 3256    "});
 3257    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3258    cx.assert_editor_state(indoc! {"
 3259        const a: B = (
 3260            c(
 3261                d(
 3262                    ˇ
 3263                        ˇ)
 3264            ˇ)
 3265        );
 3266    "});
 3267
 3268    // handle auto-indent when there are multiple cursors on the same line
 3269    cx.set_state(indoc! {"
 3270        const a: B = (
 3271            c(
 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                ˇ
 3281            ˇ)
 3282        );
 3283    "});
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3288    init_test(cx, |settings| {
 3289        settings.defaults.tab_size = NonZeroU32::new(3)
 3290    });
 3291
 3292    let mut cx = EditorTestContext::new(cx).await;
 3293    cx.set_state(indoc! {"
 3294         ˇ
 3295        \t ˇ
 3296        \t  ˇ
 3297        \t   ˇ
 3298         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3299    "});
 3300
 3301    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3302    cx.assert_editor_state(indoc! {"
 3303           ˇ
 3304        \t   ˇ
 3305        \t   ˇ
 3306        \t      ˇ
 3307         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3308    "});
 3309}
 3310
 3311#[gpui::test]
 3312async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3313    init_test(cx, |settings| {
 3314        settings.defaults.tab_size = NonZeroU32::new(4)
 3315    });
 3316
 3317    let language = Arc::new(
 3318        Language::new(
 3319            LanguageConfig::default(),
 3320            Some(tree_sitter_rust::LANGUAGE.into()),
 3321        )
 3322        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3323        .unwrap(),
 3324    );
 3325
 3326    let mut cx = EditorTestContext::new(cx).await;
 3327    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3328    cx.set_state(indoc! {"
 3329        fn a() {
 3330            if b {
 3331        \t ˇc
 3332            }
 3333        }
 3334    "});
 3335
 3336    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3337    cx.assert_editor_state(indoc! {"
 3338        fn a() {
 3339            if b {
 3340                ˇc
 3341            }
 3342        }
 3343    "});
 3344}
 3345
 3346#[gpui::test]
 3347async fn test_indent_outdent(cx: &mut TestAppContext) {
 3348    init_test(cx, |settings| {
 3349        settings.defaults.tab_size = NonZeroU32::new(4);
 3350    });
 3351
 3352    let mut cx = EditorTestContext::new(cx).await;
 3353
 3354    cx.set_state(indoc! {"
 3355          «oneˇ» «twoˇ»
 3356        three
 3357         four
 3358    "});
 3359    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3360    cx.assert_editor_state(indoc! {"
 3361            «oneˇ» «twoˇ»
 3362        three
 3363         four
 3364    "});
 3365
 3366    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3367    cx.assert_editor_state(indoc! {"
 3368        «oneˇ» «twoˇ»
 3369        three
 3370         four
 3371    "});
 3372
 3373    // select across line ending
 3374    cx.set_state(indoc! {"
 3375        one two
 3376        t«hree
 3377        ˇ» four
 3378    "});
 3379    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3380    cx.assert_editor_state(indoc! {"
 3381        one two
 3382            t«hree
 3383        ˇ» four
 3384    "});
 3385
 3386    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3387    cx.assert_editor_state(indoc! {"
 3388        one two
 3389        t«hree
 3390        ˇ» four
 3391    "});
 3392
 3393    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3394    cx.set_state(indoc! {"
 3395        one two
 3396        ˇthree
 3397            four
 3398    "});
 3399    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3400    cx.assert_editor_state(indoc! {"
 3401        one two
 3402            ˇthree
 3403            four
 3404    "});
 3405
 3406    cx.set_state(indoc! {"
 3407        one two
 3408        ˇ    three
 3409            four
 3410    "});
 3411    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3412    cx.assert_editor_state(indoc! {"
 3413        one two
 3414        ˇthree
 3415            four
 3416    "});
 3417}
 3418
 3419#[gpui::test]
 3420async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3421    init_test(cx, |settings| {
 3422        settings.defaults.hard_tabs = Some(true);
 3423    });
 3424
 3425    let mut cx = EditorTestContext::new(cx).await;
 3426
 3427    // select two ranges on one line
 3428    cx.set_state(indoc! {"
 3429        «oneˇ» «twoˇ»
 3430        three
 3431        four
 3432    "});
 3433    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3434    cx.assert_editor_state(indoc! {"
 3435        \t«oneˇ» «twoˇ»
 3436        three
 3437        four
 3438    "});
 3439    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        \t\t«oneˇ» «twoˇ»
 3442        three
 3443        four
 3444    "});
 3445    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3446    cx.assert_editor_state(indoc! {"
 3447        \t«oneˇ» «twoˇ»
 3448        three
 3449        four
 3450    "});
 3451    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3452    cx.assert_editor_state(indoc! {"
 3453        «oneˇ» «twoˇ»
 3454        three
 3455        four
 3456    "});
 3457
 3458    // select across a line ending
 3459    cx.set_state(indoc! {"
 3460        one two
 3461        t«hree
 3462        ˇ»four
 3463    "});
 3464    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3465    cx.assert_editor_state(indoc! {"
 3466        one two
 3467        \tt«hree
 3468        ˇ»four
 3469    "});
 3470    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3471    cx.assert_editor_state(indoc! {"
 3472        one two
 3473        \t\tt«hree
 3474        ˇ»four
 3475    "});
 3476    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3477    cx.assert_editor_state(indoc! {"
 3478        one two
 3479        \tt«hree
 3480        ˇ»four
 3481    "});
 3482    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3483    cx.assert_editor_state(indoc! {"
 3484        one two
 3485        t«hree
 3486        ˇ»four
 3487    "});
 3488
 3489    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3490    cx.set_state(indoc! {"
 3491        one two
 3492        ˇthree
 3493        four
 3494    "});
 3495    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3496    cx.assert_editor_state(indoc! {"
 3497        one two
 3498        ˇthree
 3499        four
 3500    "});
 3501    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3502    cx.assert_editor_state(indoc! {"
 3503        one two
 3504        \tˇthree
 3505        four
 3506    "});
 3507    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3508    cx.assert_editor_state(indoc! {"
 3509        one two
 3510        ˇthree
 3511        four
 3512    "});
 3513}
 3514
 3515#[gpui::test]
 3516fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3517    init_test(cx, |settings| {
 3518        settings.languages.extend([
 3519            (
 3520                "TOML".into(),
 3521                LanguageSettingsContent {
 3522                    tab_size: NonZeroU32::new(2),
 3523                    ..Default::default()
 3524                },
 3525            ),
 3526            (
 3527                "Rust".into(),
 3528                LanguageSettingsContent {
 3529                    tab_size: NonZeroU32::new(4),
 3530                    ..Default::default()
 3531                },
 3532            ),
 3533        ]);
 3534    });
 3535
 3536    let toml_language = Arc::new(Language::new(
 3537        LanguageConfig {
 3538            name: "TOML".into(),
 3539            ..Default::default()
 3540        },
 3541        None,
 3542    ));
 3543    let rust_language = Arc::new(Language::new(
 3544        LanguageConfig {
 3545            name: "Rust".into(),
 3546            ..Default::default()
 3547        },
 3548        None,
 3549    ));
 3550
 3551    let toml_buffer =
 3552        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3553    let rust_buffer =
 3554        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3555    let multibuffer = cx.new(|cx| {
 3556        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3557        multibuffer.push_excerpts(
 3558            toml_buffer.clone(),
 3559            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3560            cx,
 3561        );
 3562        multibuffer.push_excerpts(
 3563            rust_buffer.clone(),
 3564            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3565            cx,
 3566        );
 3567        multibuffer
 3568    });
 3569
 3570    cx.add_window(|window, cx| {
 3571        let mut editor = build_editor(multibuffer, window, cx);
 3572
 3573        assert_eq!(
 3574            editor.text(cx),
 3575            indoc! {"
 3576                a = 1
 3577                b = 2
 3578
 3579                const c: usize = 3;
 3580            "}
 3581        );
 3582
 3583        select_ranges(
 3584            &mut editor,
 3585            indoc! {"
 3586                «aˇ» = 1
 3587                b = 2
 3588
 3589                «const c:ˇ» usize = 3;
 3590            "},
 3591            window,
 3592            cx,
 3593        );
 3594
 3595        editor.tab(&Tab, window, cx);
 3596        assert_text_with_selections(
 3597            &mut editor,
 3598            indoc! {"
 3599                  «aˇ» = 1
 3600                b = 2
 3601
 3602                    «const c:ˇ» usize = 3;
 3603            "},
 3604            cx,
 3605        );
 3606        editor.backtab(&Backtab, window, cx);
 3607        assert_text_with_selections(
 3608            &mut editor,
 3609            indoc! {"
 3610                «aˇ» = 1
 3611                b = 2
 3612
 3613                «const c:ˇ» usize = 3;
 3614            "},
 3615            cx,
 3616        );
 3617
 3618        editor
 3619    });
 3620}
 3621
 3622#[gpui::test]
 3623async fn test_backspace(cx: &mut TestAppContext) {
 3624    init_test(cx, |_| {});
 3625
 3626    let mut cx = EditorTestContext::new(cx).await;
 3627
 3628    // Basic backspace
 3629    cx.set_state(indoc! {"
 3630        onˇe two three
 3631        fou«rˇ» five six
 3632        seven «ˇeight nine
 3633        »ten
 3634    "});
 3635    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3636    cx.assert_editor_state(indoc! {"
 3637        oˇe two three
 3638        fouˇ five six
 3639        seven ˇten
 3640    "});
 3641
 3642    // Test backspace inside and around indents
 3643    cx.set_state(indoc! {"
 3644        zero
 3645            ˇone
 3646                ˇtwo
 3647            ˇ ˇ ˇ  three
 3648        ˇ  ˇ  four
 3649    "});
 3650    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3651    cx.assert_editor_state(indoc! {"
 3652        zero
 3653        ˇone
 3654            ˇtwo
 3655        ˇ  threeˇ  four
 3656    "});
 3657}
 3658
 3659#[gpui::test]
 3660async fn test_delete(cx: &mut TestAppContext) {
 3661    init_test(cx, |_| {});
 3662
 3663    let mut cx = EditorTestContext::new(cx).await;
 3664    cx.set_state(indoc! {"
 3665        onˇe two three
 3666        fou«rˇ» five six
 3667        seven «ˇeight nine
 3668        »ten
 3669    "});
 3670    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3671    cx.assert_editor_state(indoc! {"
 3672        onˇ two three
 3673        fouˇ five six
 3674        seven ˇten
 3675    "});
 3676}
 3677
 3678#[gpui::test]
 3679fn test_delete_line(cx: &mut TestAppContext) {
 3680    init_test(cx, |_| {});
 3681
 3682    let editor = cx.add_window(|window, cx| {
 3683        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3684        build_editor(buffer, window, cx)
 3685    });
 3686    _ = editor.update(cx, |editor, window, cx| {
 3687        editor.change_selections(None, window, cx, |s| {
 3688            s.select_display_ranges([
 3689                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3691                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3692            ])
 3693        });
 3694        editor.delete_line(&DeleteLine, window, cx);
 3695        assert_eq!(editor.display_text(cx), "ghi");
 3696        assert_eq!(
 3697            editor.selections.display_ranges(cx),
 3698            vec![
 3699                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3700                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3701            ]
 3702        );
 3703    });
 3704
 3705    let editor = cx.add_window(|window, cx| {
 3706        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3707        build_editor(buffer, window, cx)
 3708    });
 3709    _ = editor.update(cx, |editor, window, cx| {
 3710        editor.change_selections(None, window, cx, |s| {
 3711            s.select_display_ranges([
 3712                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3713            ])
 3714        });
 3715        editor.delete_line(&DeleteLine, window, cx);
 3716        assert_eq!(editor.display_text(cx), "ghi\n");
 3717        assert_eq!(
 3718            editor.selections.display_ranges(cx),
 3719            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3720        );
 3721    });
 3722}
 3723
 3724#[gpui::test]
 3725fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3726    init_test(cx, |_| {});
 3727
 3728    cx.add_window(|window, cx| {
 3729        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3730        let mut editor = build_editor(buffer.clone(), window, cx);
 3731        let buffer = buffer.read(cx).as_singleton().unwrap();
 3732
 3733        assert_eq!(
 3734            editor.selections.ranges::<Point>(cx),
 3735            &[Point::new(0, 0)..Point::new(0, 0)]
 3736        );
 3737
 3738        // When on single line, replace newline at end by space
 3739        editor.join_lines(&JoinLines, window, cx);
 3740        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3741        assert_eq!(
 3742            editor.selections.ranges::<Point>(cx),
 3743            &[Point::new(0, 3)..Point::new(0, 3)]
 3744        );
 3745
 3746        // When multiple lines are selected, remove newlines that are spanned by the selection
 3747        editor.change_selections(None, window, cx, |s| {
 3748            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3749        });
 3750        editor.join_lines(&JoinLines, window, cx);
 3751        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3752        assert_eq!(
 3753            editor.selections.ranges::<Point>(cx),
 3754            &[Point::new(0, 11)..Point::new(0, 11)]
 3755        );
 3756
 3757        // Undo should be transactional
 3758        editor.undo(&Undo, window, cx);
 3759        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3760        assert_eq!(
 3761            editor.selections.ranges::<Point>(cx),
 3762            &[Point::new(0, 5)..Point::new(2, 2)]
 3763        );
 3764
 3765        // When joining an empty line don't insert a space
 3766        editor.change_selections(None, window, cx, |s| {
 3767            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3768        });
 3769        editor.join_lines(&JoinLines, window, cx);
 3770        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3771        assert_eq!(
 3772            editor.selections.ranges::<Point>(cx),
 3773            [Point::new(2, 3)..Point::new(2, 3)]
 3774        );
 3775
 3776        // We can remove trailing newlines
 3777        editor.join_lines(&JoinLines, window, cx);
 3778        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3779        assert_eq!(
 3780            editor.selections.ranges::<Point>(cx),
 3781            [Point::new(2, 3)..Point::new(2, 3)]
 3782        );
 3783
 3784        // We don't blow up on the last line
 3785        editor.join_lines(&JoinLines, window, cx);
 3786        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3787        assert_eq!(
 3788            editor.selections.ranges::<Point>(cx),
 3789            [Point::new(2, 3)..Point::new(2, 3)]
 3790        );
 3791
 3792        // reset to test indentation
 3793        editor.buffer.update(cx, |buffer, cx| {
 3794            buffer.edit(
 3795                [
 3796                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3797                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3798                ],
 3799                None,
 3800                cx,
 3801            )
 3802        });
 3803
 3804        // We remove any leading spaces
 3805        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3806        editor.change_selections(None, window, cx, |s| {
 3807            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3808        });
 3809        editor.join_lines(&JoinLines, window, cx);
 3810        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3811
 3812        // We don't insert a space for a line containing only spaces
 3813        editor.join_lines(&JoinLines, window, cx);
 3814        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3815
 3816        // We ignore any leading tabs
 3817        editor.join_lines(&JoinLines, window, cx);
 3818        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3819
 3820        editor
 3821    });
 3822}
 3823
 3824#[gpui::test]
 3825fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3826    init_test(cx, |_| {});
 3827
 3828    cx.add_window(|window, cx| {
 3829        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3830        let mut editor = build_editor(buffer.clone(), window, cx);
 3831        let buffer = buffer.read(cx).as_singleton().unwrap();
 3832
 3833        editor.change_selections(None, window, cx, |s| {
 3834            s.select_ranges([
 3835                Point::new(0, 2)..Point::new(1, 1),
 3836                Point::new(1, 2)..Point::new(1, 2),
 3837                Point::new(3, 1)..Point::new(3, 2),
 3838            ])
 3839        });
 3840
 3841        editor.join_lines(&JoinLines, window, cx);
 3842        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3843
 3844        assert_eq!(
 3845            editor.selections.ranges::<Point>(cx),
 3846            [
 3847                Point::new(0, 7)..Point::new(0, 7),
 3848                Point::new(1, 3)..Point::new(1, 3)
 3849            ]
 3850        );
 3851        editor
 3852    });
 3853}
 3854
 3855#[gpui::test]
 3856async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3857    init_test(cx, |_| {});
 3858
 3859    let mut cx = EditorTestContext::new(cx).await;
 3860
 3861    let diff_base = r#"
 3862        Line 0
 3863        Line 1
 3864        Line 2
 3865        Line 3
 3866        "#
 3867    .unindent();
 3868
 3869    cx.set_state(
 3870        &r#"
 3871        ˇLine 0
 3872        Line 1
 3873        Line 2
 3874        Line 3
 3875        "#
 3876        .unindent(),
 3877    );
 3878
 3879    cx.set_head_text(&diff_base);
 3880    executor.run_until_parked();
 3881
 3882    // Join lines
 3883    cx.update_editor(|editor, window, cx| {
 3884        editor.join_lines(&JoinLines, window, cx);
 3885    });
 3886    executor.run_until_parked();
 3887
 3888    cx.assert_editor_state(
 3889        &r#"
 3890        Line 0ˇ Line 1
 3891        Line 2
 3892        Line 3
 3893        "#
 3894        .unindent(),
 3895    );
 3896    // Join again
 3897    cx.update_editor(|editor, window, cx| {
 3898        editor.join_lines(&JoinLines, window, cx);
 3899    });
 3900    executor.run_until_parked();
 3901
 3902    cx.assert_editor_state(
 3903        &r#"
 3904        Line 0 Line 1ˇ Line 2
 3905        Line 3
 3906        "#
 3907        .unindent(),
 3908    );
 3909}
 3910
 3911#[gpui::test]
 3912async fn test_custom_newlines_cause_no_false_positive_diffs(
 3913    executor: BackgroundExecutor,
 3914    cx: &mut TestAppContext,
 3915) {
 3916    init_test(cx, |_| {});
 3917    let mut cx = EditorTestContext::new(cx).await;
 3918    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3919    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3920    executor.run_until_parked();
 3921
 3922    cx.update_editor(|editor, window, cx| {
 3923        let snapshot = editor.snapshot(window, cx);
 3924        assert_eq!(
 3925            snapshot
 3926                .buffer_snapshot
 3927                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3928                .collect::<Vec<_>>(),
 3929            Vec::new(),
 3930            "Should not have any diffs for files with custom newlines"
 3931        );
 3932    });
 3933}
 3934
 3935#[gpui::test]
 3936async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3937    init_test(cx, |_| {});
 3938
 3939    let mut cx = EditorTestContext::new(cx).await;
 3940
 3941    // Test sort_lines_case_insensitive()
 3942    cx.set_state(indoc! {"
 3943        «z
 3944        y
 3945        x
 3946        Z
 3947        Y
 3948        Xˇ»
 3949    "});
 3950    cx.update_editor(|e, window, cx| {
 3951        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3952    });
 3953    cx.assert_editor_state(indoc! {"
 3954        «x
 3955        X
 3956        y
 3957        Y
 3958        z
 3959        Zˇ»
 3960    "});
 3961
 3962    // Test reverse_lines()
 3963    cx.set_state(indoc! {"
 3964        «5
 3965        4
 3966        3
 3967        2
 3968        1ˇ»
 3969    "});
 3970    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        «1
 3973        2
 3974        3
 3975        4
 3976        5ˇ»
 3977    "});
 3978
 3979    // Skip testing shuffle_line()
 3980
 3981    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3982    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3983
 3984    // Don't manipulate when cursor is on single line, but expand the selection
 3985    cx.set_state(indoc! {"
 3986        ddˇdd
 3987        ccc
 3988        bb
 3989        a
 3990    "});
 3991    cx.update_editor(|e, window, cx| {
 3992        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3993    });
 3994    cx.assert_editor_state(indoc! {"
 3995        «ddddˇ»
 3996        ccc
 3997        bb
 3998        a
 3999    "});
 4000
 4001    // Basic manipulate case
 4002    // Start selection moves to column 0
 4003    // End of selection shrinks to fit shorter line
 4004    cx.set_state(indoc! {"
 4005        dd«d
 4006        ccc
 4007        bb
 4008        aaaaaˇ»
 4009    "});
 4010    cx.update_editor(|e, window, cx| {
 4011        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4012    });
 4013    cx.assert_editor_state(indoc! {"
 4014        «aaaaa
 4015        bb
 4016        ccc
 4017        dddˇ»
 4018    "});
 4019
 4020    // Manipulate case with newlines
 4021    cx.set_state(indoc! {"
 4022        dd«d
 4023        ccc
 4024
 4025        bb
 4026        aaaaa
 4027
 4028        ˇ»
 4029    "});
 4030    cx.update_editor(|e, window, cx| {
 4031        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4032    });
 4033    cx.assert_editor_state(indoc! {"
 4034        «
 4035
 4036        aaaaa
 4037        bb
 4038        ccc
 4039        dddˇ»
 4040
 4041    "});
 4042
 4043    // Adding new line
 4044    cx.set_state(indoc! {"
 4045        aa«a
 4046        bbˇ»b
 4047    "});
 4048    cx.update_editor(|e, window, cx| {
 4049        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4050    });
 4051    cx.assert_editor_state(indoc! {"
 4052        «aaa
 4053        bbb
 4054        added_lineˇ»
 4055    "});
 4056
 4057    // Removing line
 4058    cx.set_state(indoc! {"
 4059        aa«a
 4060        bbbˇ»
 4061    "});
 4062    cx.update_editor(|e, window, cx| {
 4063        e.manipulate_lines(window, cx, |lines| {
 4064            lines.pop();
 4065        })
 4066    });
 4067    cx.assert_editor_state(indoc! {"
 4068        «aaaˇ»
 4069    "});
 4070
 4071    // Removing all lines
 4072    cx.set_state(indoc! {"
 4073        aa«a
 4074        bbbˇ»
 4075    "});
 4076    cx.update_editor(|e, window, cx| {
 4077        e.manipulate_lines(window, cx, |lines| {
 4078            lines.drain(..);
 4079        })
 4080    });
 4081    cx.assert_editor_state(indoc! {"
 4082        ˇ
 4083    "});
 4084}
 4085
 4086#[gpui::test]
 4087async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4088    init_test(cx, |_| {});
 4089
 4090    let mut cx = EditorTestContext::new(cx).await;
 4091
 4092    // Consider continuous selection as single selection
 4093    cx.set_state(indoc! {"
 4094        Aaa«aa
 4095        cˇ»c«c
 4096        bb
 4097        aaaˇ»aa
 4098    "});
 4099    cx.update_editor(|e, window, cx| {
 4100        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4101    });
 4102    cx.assert_editor_state(indoc! {"
 4103        «Aaaaa
 4104        ccc
 4105        bb
 4106        aaaaaˇ»
 4107    "});
 4108
 4109    cx.set_state(indoc! {"
 4110        Aaa«aa
 4111        cˇ»c«c
 4112        bb
 4113        aaaˇ»aa
 4114    "});
 4115    cx.update_editor(|e, window, cx| {
 4116        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4117    });
 4118    cx.assert_editor_state(indoc! {"
 4119        «Aaaaa
 4120        ccc
 4121        bbˇ»
 4122    "});
 4123
 4124    // Consider non continuous selection as distinct dedup operations
 4125    cx.set_state(indoc! {"
 4126        «aaaaa
 4127        bb
 4128        aaaaa
 4129        aaaaaˇ»
 4130
 4131        aaa«aaˇ»
 4132    "});
 4133    cx.update_editor(|e, window, cx| {
 4134        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4135    });
 4136    cx.assert_editor_state(indoc! {"
 4137        «aaaaa
 4138        bbˇ»
 4139
 4140        «aaaaaˇ»
 4141    "});
 4142}
 4143
 4144#[gpui::test]
 4145async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4146    init_test(cx, |_| {});
 4147
 4148    let mut cx = EditorTestContext::new(cx).await;
 4149
 4150    cx.set_state(indoc! {"
 4151        «Aaa
 4152        aAa
 4153        Aaaˇ»
 4154    "});
 4155    cx.update_editor(|e, window, cx| {
 4156        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4157    });
 4158    cx.assert_editor_state(indoc! {"
 4159        «Aaa
 4160        aAaˇ»
 4161    "});
 4162
 4163    cx.set_state(indoc! {"
 4164        «Aaa
 4165        aAa
 4166        aaAˇ»
 4167    "});
 4168    cx.update_editor(|e, window, cx| {
 4169        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4170    });
 4171    cx.assert_editor_state(indoc! {"
 4172        «Aaaˇ»
 4173    "});
 4174}
 4175
 4176#[gpui::test]
 4177async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4178    init_test(cx, |_| {});
 4179
 4180    let mut cx = EditorTestContext::new(cx).await;
 4181
 4182    // Manipulate with multiple selections on a single line
 4183    cx.set_state(indoc! {"
 4184        dd«dd
 4185        cˇ»c«c
 4186        bb
 4187        aaaˇ»aa
 4188    "});
 4189    cx.update_editor(|e, window, cx| {
 4190        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4191    });
 4192    cx.assert_editor_state(indoc! {"
 4193        «aaaaa
 4194        bb
 4195        ccc
 4196        ddddˇ»
 4197    "});
 4198
 4199    // Manipulate with multiple disjoin selections
 4200    cx.set_state(indoc! {"
 4201 4202        4
 4203        3
 4204        2
 4205        1ˇ»
 4206
 4207        dd«dd
 4208        ccc
 4209        bb
 4210        aaaˇ»aa
 4211    "});
 4212    cx.update_editor(|e, window, cx| {
 4213        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4214    });
 4215    cx.assert_editor_state(indoc! {"
 4216        «1
 4217        2
 4218        3
 4219        4
 4220        5ˇ»
 4221
 4222        «aaaaa
 4223        bb
 4224        ccc
 4225        ddddˇ»
 4226    "});
 4227
 4228    // Adding lines on each selection
 4229    cx.set_state(indoc! {"
 4230 4231        1ˇ»
 4232
 4233        bb«bb
 4234        aaaˇ»aa
 4235    "});
 4236    cx.update_editor(|e, window, cx| {
 4237        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4238    });
 4239    cx.assert_editor_state(indoc! {"
 4240        «2
 4241        1
 4242        added lineˇ»
 4243
 4244        «bbbb
 4245        aaaaa
 4246        added lineˇ»
 4247    "});
 4248
 4249    // Removing lines on each selection
 4250    cx.set_state(indoc! {"
 4251 4252        1ˇ»
 4253
 4254        bb«bb
 4255        aaaˇ»aa
 4256    "});
 4257    cx.update_editor(|e, window, cx| {
 4258        e.manipulate_lines(window, cx, |lines| {
 4259            lines.pop();
 4260        })
 4261    });
 4262    cx.assert_editor_state(indoc! {"
 4263        «2ˇ»
 4264
 4265        «bbbbˇ»
 4266    "});
 4267}
 4268
 4269#[gpui::test]
 4270async fn test_toggle_case(cx: &mut TestAppContext) {
 4271    init_test(cx, |_| {});
 4272
 4273    let mut cx = EditorTestContext::new(cx).await;
 4274
 4275    // If all lower case -> upper case
 4276    cx.set_state(indoc! {"
 4277        «hello worldˇ»
 4278    "});
 4279    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4280    cx.assert_editor_state(indoc! {"
 4281        «HELLO WORLDˇ»
 4282    "});
 4283
 4284    // If all upper case -> lower case
 4285    cx.set_state(indoc! {"
 4286        «HELLO WORLDˇ»
 4287    "});
 4288    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4289    cx.assert_editor_state(indoc! {"
 4290        «hello worldˇ»
 4291    "});
 4292
 4293    // If any upper case characters are identified -> lower case
 4294    // This matches JetBrains IDEs
 4295    cx.set_state(indoc! {"
 4296        «hEllo worldˇ»
 4297    "});
 4298    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4299    cx.assert_editor_state(indoc! {"
 4300        «hello worldˇ»
 4301    "});
 4302}
 4303
 4304#[gpui::test]
 4305async fn test_manipulate_text(cx: &mut TestAppContext) {
 4306    init_test(cx, |_| {});
 4307
 4308    let mut cx = EditorTestContext::new(cx).await;
 4309
 4310    // Test convert_to_upper_case()
 4311    cx.set_state(indoc! {"
 4312        «hello worldˇ»
 4313    "});
 4314    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4315    cx.assert_editor_state(indoc! {"
 4316        «HELLO WORLDˇ»
 4317    "});
 4318
 4319    // Test convert_to_lower_case()
 4320    cx.set_state(indoc! {"
 4321        «HELLO WORLDˇ»
 4322    "});
 4323    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4324    cx.assert_editor_state(indoc! {"
 4325        «hello worldˇ»
 4326    "});
 4327
 4328    // Test multiple line, single selection case
 4329    cx.set_state(indoc! {"
 4330        «The quick brown
 4331        fox jumps over
 4332        the lazy dogˇ»
 4333    "});
 4334    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4335    cx.assert_editor_state(indoc! {"
 4336        «The Quick Brown
 4337        Fox Jumps Over
 4338        The Lazy Dogˇ»
 4339    "});
 4340
 4341    // Test multiple line, single selection case
 4342    cx.set_state(indoc! {"
 4343        «The quick brown
 4344        fox jumps over
 4345        the lazy dogˇ»
 4346    "});
 4347    cx.update_editor(|e, window, cx| {
 4348        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4349    });
 4350    cx.assert_editor_state(indoc! {"
 4351        «TheQuickBrown
 4352        FoxJumpsOver
 4353        TheLazyDogˇ»
 4354    "});
 4355
 4356    // From here on out, test more complex cases of manipulate_text()
 4357
 4358    // Test no selection case - should affect words cursors are in
 4359    // Cursor at beginning, middle, and end of word
 4360    cx.set_state(indoc! {"
 4361        ˇhello big beauˇtiful worldˇ
 4362    "});
 4363    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4364    cx.assert_editor_state(indoc! {"
 4365        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4366    "});
 4367
 4368    // Test multiple selections on a single line and across multiple lines
 4369    cx.set_state(indoc! {"
 4370        «Theˇ» quick «brown
 4371        foxˇ» jumps «overˇ»
 4372        the «lazyˇ» dog
 4373    "});
 4374    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4375    cx.assert_editor_state(indoc! {"
 4376        «THEˇ» quick «BROWN
 4377        FOXˇ» jumps «OVERˇ»
 4378        the «LAZYˇ» dog
 4379    "});
 4380
 4381    // Test case where text length grows
 4382    cx.set_state(indoc! {"
 4383        «tschüߡ»
 4384    "});
 4385    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4386    cx.assert_editor_state(indoc! {"
 4387        «TSCHÜSSˇ»
 4388    "});
 4389
 4390    // Test to make sure we don't crash when text shrinks
 4391    cx.set_state(indoc! {"
 4392        aaa_bbbˇ
 4393    "});
 4394    cx.update_editor(|e, window, cx| {
 4395        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4396    });
 4397    cx.assert_editor_state(indoc! {"
 4398        «aaaBbbˇ»
 4399    "});
 4400
 4401    // Test to make sure we all aware of the fact that each word can grow and shrink
 4402    // Final selections should be aware of this fact
 4403    cx.set_state(indoc! {"
 4404        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4405    "});
 4406    cx.update_editor(|e, window, cx| {
 4407        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4408    });
 4409    cx.assert_editor_state(indoc! {"
 4410        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4411    "});
 4412
 4413    cx.set_state(indoc! {"
 4414        «hElLo, WoRld!ˇ»
 4415    "});
 4416    cx.update_editor(|e, window, cx| {
 4417        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4418    });
 4419    cx.assert_editor_state(indoc! {"
 4420        «HeLlO, wOrLD!ˇ»
 4421    "});
 4422}
 4423
 4424#[gpui::test]
 4425fn test_duplicate_line(cx: &mut TestAppContext) {
 4426    init_test(cx, |_| {});
 4427
 4428    let editor = cx.add_window(|window, cx| {
 4429        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4430        build_editor(buffer, window, cx)
 4431    });
 4432    _ = editor.update(cx, |editor, window, cx| {
 4433        editor.change_selections(None, window, cx, |s| {
 4434            s.select_display_ranges([
 4435                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4436                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4437                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4438                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4439            ])
 4440        });
 4441        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4442        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4443        assert_eq!(
 4444            editor.selections.display_ranges(cx),
 4445            vec![
 4446                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4447                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4448                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4449                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4450            ]
 4451        );
 4452    });
 4453
 4454    let editor = cx.add_window(|window, cx| {
 4455        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4456        build_editor(buffer, window, cx)
 4457    });
 4458    _ = editor.update(cx, |editor, window, cx| {
 4459        editor.change_selections(None, window, cx, |s| {
 4460            s.select_display_ranges([
 4461                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4462                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4463            ])
 4464        });
 4465        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4466        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4467        assert_eq!(
 4468            editor.selections.display_ranges(cx),
 4469            vec![
 4470                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4471                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4472            ]
 4473        );
 4474    });
 4475
 4476    // With `move_upwards` the selections stay in place, except for
 4477    // the lines inserted above them
 4478    let editor = cx.add_window(|window, cx| {
 4479        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4480        build_editor(buffer, window, cx)
 4481    });
 4482    _ = editor.update(cx, |editor, window, cx| {
 4483        editor.change_selections(None, window, cx, |s| {
 4484            s.select_display_ranges([
 4485                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4486                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4487                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4488                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4489            ])
 4490        });
 4491        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4492        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4493        assert_eq!(
 4494            editor.selections.display_ranges(cx),
 4495            vec![
 4496                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4497                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4498                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4499                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4500            ]
 4501        );
 4502    });
 4503
 4504    let editor = cx.add_window(|window, cx| {
 4505        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4506        build_editor(buffer, window, cx)
 4507    });
 4508    _ = editor.update(cx, |editor, window, cx| {
 4509        editor.change_selections(None, window, cx, |s| {
 4510            s.select_display_ranges([
 4511                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4512                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4513            ])
 4514        });
 4515        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4516        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4517        assert_eq!(
 4518            editor.selections.display_ranges(cx),
 4519            vec![
 4520                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4521                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4522            ]
 4523        );
 4524    });
 4525
 4526    let editor = cx.add_window(|window, cx| {
 4527        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4528        build_editor(buffer, window, cx)
 4529    });
 4530    _ = editor.update(cx, |editor, window, cx| {
 4531        editor.change_selections(None, window, cx, |s| {
 4532            s.select_display_ranges([
 4533                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4534                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4535            ])
 4536        });
 4537        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4538        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4539        assert_eq!(
 4540            editor.selections.display_ranges(cx),
 4541            vec![
 4542                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4543                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4544            ]
 4545        );
 4546    });
 4547}
 4548
 4549#[gpui::test]
 4550fn test_move_line_up_down(cx: &mut TestAppContext) {
 4551    init_test(cx, |_| {});
 4552
 4553    let editor = cx.add_window(|window, cx| {
 4554        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4555        build_editor(buffer, window, cx)
 4556    });
 4557    _ = editor.update(cx, |editor, window, cx| {
 4558        editor.fold_creases(
 4559            vec![
 4560                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4561                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4562                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4563            ],
 4564            true,
 4565            window,
 4566            cx,
 4567        );
 4568        editor.change_selections(None, window, cx, |s| {
 4569            s.select_display_ranges([
 4570                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4571                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4572                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4573                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4574            ])
 4575        });
 4576        assert_eq!(
 4577            editor.display_text(cx),
 4578            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4579        );
 4580
 4581        editor.move_line_up(&MoveLineUp, window, cx);
 4582        assert_eq!(
 4583            editor.display_text(cx),
 4584            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4585        );
 4586        assert_eq!(
 4587            editor.selections.display_ranges(cx),
 4588            vec![
 4589                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4590                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4591                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4592                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4593            ]
 4594        );
 4595    });
 4596
 4597    _ = editor.update(cx, |editor, window, cx| {
 4598        editor.move_line_down(&MoveLineDown, window, cx);
 4599        assert_eq!(
 4600            editor.display_text(cx),
 4601            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4602        );
 4603        assert_eq!(
 4604            editor.selections.display_ranges(cx),
 4605            vec![
 4606                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4607                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4608                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4609                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4610            ]
 4611        );
 4612    });
 4613
 4614    _ = editor.update(cx, |editor, window, cx| {
 4615        editor.move_line_down(&MoveLineDown, window, cx);
 4616        assert_eq!(
 4617            editor.display_text(cx),
 4618            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4619        );
 4620        assert_eq!(
 4621            editor.selections.display_ranges(cx),
 4622            vec![
 4623                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4624                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4625                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4626                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4627            ]
 4628        );
 4629    });
 4630
 4631    _ = editor.update(cx, |editor, window, cx| {
 4632        editor.move_line_up(&MoveLineUp, window, cx);
 4633        assert_eq!(
 4634            editor.display_text(cx),
 4635            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4636        );
 4637        assert_eq!(
 4638            editor.selections.display_ranges(cx),
 4639            vec![
 4640                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4641                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4642                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4643                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4644            ]
 4645        );
 4646    });
 4647}
 4648
 4649#[gpui::test]
 4650fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4651    init_test(cx, |_| {});
 4652
 4653    let editor = cx.add_window(|window, cx| {
 4654        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4655        build_editor(buffer, window, cx)
 4656    });
 4657    _ = editor.update(cx, |editor, window, cx| {
 4658        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4659        editor.insert_blocks(
 4660            [BlockProperties {
 4661                style: BlockStyle::Fixed,
 4662                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4663                height: Some(1),
 4664                render: Arc::new(|_| div().into_any()),
 4665                priority: 0,
 4666                render_in_minimap: true,
 4667            }],
 4668            Some(Autoscroll::fit()),
 4669            cx,
 4670        );
 4671        editor.change_selections(None, window, cx, |s| {
 4672            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4673        });
 4674        editor.move_line_down(&MoveLineDown, window, cx);
 4675    });
 4676}
 4677
 4678#[gpui::test]
 4679async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4680    init_test(cx, |_| {});
 4681
 4682    let mut cx = EditorTestContext::new(cx).await;
 4683    cx.set_state(
 4684        &"
 4685            ˇzero
 4686            one
 4687            two
 4688            three
 4689            four
 4690            five
 4691        "
 4692        .unindent(),
 4693    );
 4694
 4695    // Create a four-line block that replaces three lines of text.
 4696    cx.update_editor(|editor, window, cx| {
 4697        let snapshot = editor.snapshot(window, cx);
 4698        let snapshot = &snapshot.buffer_snapshot;
 4699        let placement = BlockPlacement::Replace(
 4700            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4701        );
 4702        editor.insert_blocks(
 4703            [BlockProperties {
 4704                placement,
 4705                height: Some(4),
 4706                style: BlockStyle::Sticky,
 4707                render: Arc::new(|_| gpui::div().into_any_element()),
 4708                priority: 0,
 4709                render_in_minimap: true,
 4710            }],
 4711            None,
 4712            cx,
 4713        );
 4714    });
 4715
 4716    // Move down so that the cursor touches the block.
 4717    cx.update_editor(|editor, window, cx| {
 4718        editor.move_down(&Default::default(), window, cx);
 4719    });
 4720    cx.assert_editor_state(
 4721        &"
 4722            zero
 4723            «one
 4724            two
 4725            threeˇ»
 4726            four
 4727            five
 4728        "
 4729        .unindent(),
 4730    );
 4731
 4732    // Move down past the block.
 4733    cx.update_editor(|editor, window, cx| {
 4734        editor.move_down(&Default::default(), window, cx);
 4735    });
 4736    cx.assert_editor_state(
 4737        &"
 4738            zero
 4739            one
 4740            two
 4741            three
 4742            ˇfour
 4743            five
 4744        "
 4745        .unindent(),
 4746    );
 4747}
 4748
 4749#[gpui::test]
 4750fn test_transpose(cx: &mut TestAppContext) {
 4751    init_test(cx, |_| {});
 4752
 4753    _ = cx.add_window(|window, cx| {
 4754        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4755        editor.set_style(EditorStyle::default(), window, cx);
 4756        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4757        editor.transpose(&Default::default(), window, cx);
 4758        assert_eq!(editor.text(cx), "bac");
 4759        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4760
 4761        editor.transpose(&Default::default(), window, cx);
 4762        assert_eq!(editor.text(cx), "bca");
 4763        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4764
 4765        editor.transpose(&Default::default(), window, cx);
 4766        assert_eq!(editor.text(cx), "bac");
 4767        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4768
 4769        editor
 4770    });
 4771
 4772    _ = cx.add_window(|window, cx| {
 4773        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4774        editor.set_style(EditorStyle::default(), window, cx);
 4775        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4776        editor.transpose(&Default::default(), window, cx);
 4777        assert_eq!(editor.text(cx), "acb\nde");
 4778        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4779
 4780        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4781        editor.transpose(&Default::default(), window, cx);
 4782        assert_eq!(editor.text(cx), "acbd\ne");
 4783        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4784
 4785        editor.transpose(&Default::default(), window, cx);
 4786        assert_eq!(editor.text(cx), "acbde\n");
 4787        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4788
 4789        editor.transpose(&Default::default(), window, cx);
 4790        assert_eq!(editor.text(cx), "acbd\ne");
 4791        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4792
 4793        editor
 4794    });
 4795
 4796    _ = cx.add_window(|window, cx| {
 4797        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4798        editor.set_style(EditorStyle::default(), window, cx);
 4799        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4800        editor.transpose(&Default::default(), window, cx);
 4801        assert_eq!(editor.text(cx), "bacd\ne");
 4802        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4803
 4804        editor.transpose(&Default::default(), window, cx);
 4805        assert_eq!(editor.text(cx), "bcade\n");
 4806        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4807
 4808        editor.transpose(&Default::default(), window, cx);
 4809        assert_eq!(editor.text(cx), "bcda\ne");
 4810        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4811
 4812        editor.transpose(&Default::default(), window, cx);
 4813        assert_eq!(editor.text(cx), "bcade\n");
 4814        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4815
 4816        editor.transpose(&Default::default(), window, cx);
 4817        assert_eq!(editor.text(cx), "bcaed\n");
 4818        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4819
 4820        editor
 4821    });
 4822
 4823    _ = cx.add_window(|window, cx| {
 4824        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4825        editor.set_style(EditorStyle::default(), window, cx);
 4826        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4827        editor.transpose(&Default::default(), window, cx);
 4828        assert_eq!(editor.text(cx), "🏀🍐✋");
 4829        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4830
 4831        editor.transpose(&Default::default(), window, cx);
 4832        assert_eq!(editor.text(cx), "🏀✋🍐");
 4833        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4834
 4835        editor.transpose(&Default::default(), window, cx);
 4836        assert_eq!(editor.text(cx), "🏀🍐✋");
 4837        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4838
 4839        editor
 4840    });
 4841}
 4842
 4843#[gpui::test]
 4844async fn test_rewrap(cx: &mut TestAppContext) {
 4845    init_test(cx, |settings| {
 4846        settings.languages.extend([
 4847            (
 4848                "Markdown".into(),
 4849                LanguageSettingsContent {
 4850                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4851                    ..Default::default()
 4852                },
 4853            ),
 4854            (
 4855                "Plain Text".into(),
 4856                LanguageSettingsContent {
 4857                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4858                    ..Default::default()
 4859                },
 4860            ),
 4861        ])
 4862    });
 4863
 4864    let mut cx = EditorTestContext::new(cx).await;
 4865
 4866    let language_with_c_comments = Arc::new(Language::new(
 4867        LanguageConfig {
 4868            line_comments: vec!["// ".into()],
 4869            ..LanguageConfig::default()
 4870        },
 4871        None,
 4872    ));
 4873    let language_with_pound_comments = Arc::new(Language::new(
 4874        LanguageConfig {
 4875            line_comments: vec!["# ".into()],
 4876            ..LanguageConfig::default()
 4877        },
 4878        None,
 4879    ));
 4880    let markdown_language = Arc::new(Language::new(
 4881        LanguageConfig {
 4882            name: "Markdown".into(),
 4883            ..LanguageConfig::default()
 4884        },
 4885        None,
 4886    ));
 4887    let language_with_doc_comments = Arc::new(Language::new(
 4888        LanguageConfig {
 4889            line_comments: vec!["// ".into(), "/// ".into()],
 4890            ..LanguageConfig::default()
 4891        },
 4892        Some(tree_sitter_rust::LANGUAGE.into()),
 4893    ));
 4894
 4895    let plaintext_language = Arc::new(Language::new(
 4896        LanguageConfig {
 4897            name: "Plain Text".into(),
 4898            ..LanguageConfig::default()
 4899        },
 4900        None,
 4901    ));
 4902
 4903    assert_rewrap(
 4904        indoc! {"
 4905            // ˇ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.
 4906        "},
 4907        indoc! {"
 4908            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4909            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4910            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4911            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4912            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4913            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4914            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4915            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4916            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4917            // porttitor id. Aliquam id accumsan eros.
 4918        "},
 4919        language_with_c_comments.clone(),
 4920        &mut cx,
 4921    );
 4922
 4923    // Test that rewrapping works inside of a selection
 4924    assert_rewrap(
 4925        indoc! {"
 4926            «// 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.ˇ»
 4927        "},
 4928        indoc! {"
 4929            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4930            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4931            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4932            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4933            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4934            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4935            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4936            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4937            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4938            // porttitor id. Aliquam id accumsan eros.ˇ»
 4939        "},
 4940        language_with_c_comments.clone(),
 4941        &mut cx,
 4942    );
 4943
 4944    // Test that cursors that expand to the same region are collapsed.
 4945    assert_rewrap(
 4946        indoc! {"
 4947            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4948            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4949            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4950            // ˇ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.
 4951        "},
 4952        indoc! {"
 4953            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4954            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4955            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4956            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4957            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4958            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4959            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4960            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4961            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4962            // porttitor id. Aliquam id accumsan eros.
 4963        "},
 4964        language_with_c_comments.clone(),
 4965        &mut cx,
 4966    );
 4967
 4968    // Test that non-contiguous selections are treated separately.
 4969    assert_rewrap(
 4970        indoc! {"
 4971            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4972            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4973            //
 4974            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4975            // ˇ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.
 4976        "},
 4977        indoc! {"
 4978            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4979            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4980            // auctor, eu lacinia sapien scelerisque.
 4981            //
 4982            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4983            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4984            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4985            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4986            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4987            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4988            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4989        "},
 4990        language_with_c_comments.clone(),
 4991        &mut cx,
 4992    );
 4993
 4994    // Test that different comment prefixes are supported.
 4995    assert_rewrap(
 4996        indoc! {"
 4997            # ˇ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.
 4998        "},
 4999        indoc! {"
 5000            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 5001            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5002            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5003            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5004            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 5005            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 5006            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 5007            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 5008            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 5009            # accumsan eros.
 5010        "},
 5011        language_with_pound_comments.clone(),
 5012        &mut cx,
 5013    );
 5014
 5015    // Test that rewrapping is ignored outside of comments in most languages.
 5016    assert_rewrap(
 5017        indoc! {"
 5018            /// Adds two numbers.
 5019            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5020            fn add(a: u32, b: u32) -> u32 {
 5021                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ˇ
 5022            }
 5023        "},
 5024        indoc! {"
 5025            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 5026            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5027            fn add(a: u32, b: u32) -> u32 {
 5028                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ˇ
 5029            }
 5030        "},
 5031        language_with_doc_comments.clone(),
 5032        &mut cx,
 5033    );
 5034
 5035    // Test that rewrapping works in Markdown and Plain Text languages.
 5036    assert_rewrap(
 5037        indoc! {"
 5038            # Hello
 5039
 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.
 5041        "},
 5042        indoc! {"
 5043            # Hello
 5044
 5045            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5046            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5047            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5048            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5049            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5050            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5051            Integer sit amet scelerisque nisi.
 5052        "},
 5053        markdown_language,
 5054        &mut cx,
 5055    );
 5056
 5057    assert_rewrap(
 5058        indoc! {"
 5059            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.
 5060        "},
 5061        indoc! {"
 5062            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5063            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5064            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5065            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5066            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5067            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5068            Integer sit amet scelerisque nisi.
 5069        "},
 5070        plaintext_language,
 5071        &mut cx,
 5072    );
 5073
 5074    // Test rewrapping unaligned comments in a selection.
 5075    assert_rewrap(
 5076        indoc! {"
 5077            fn foo() {
 5078                if true {
 5079            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5080            // Praesent semper egestas tellus id dignissim.ˇ»
 5081                    do_something();
 5082                } else {
 5083                    //
 5084                }
 5085            }
 5086        "},
 5087        indoc! {"
 5088            fn foo() {
 5089                if true {
 5090            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5091                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5092                    // egestas tellus id dignissim.ˇ»
 5093                    do_something();
 5094                } else {
 5095                    //
 5096                }
 5097            }
 5098        "},
 5099        language_with_doc_comments.clone(),
 5100        &mut cx,
 5101    );
 5102
 5103    assert_rewrap(
 5104        indoc! {"
 5105            fn foo() {
 5106                if true {
 5107            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5108            // Praesent semper egestas tellus id dignissim.»
 5109                    do_something();
 5110                } else {
 5111                    //
 5112                }
 5113
 5114            }
 5115        "},
 5116        indoc! {"
 5117            fn foo() {
 5118                if true {
 5119            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5120                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5121                    // egestas tellus id dignissim.»
 5122                    do_something();
 5123                } else {
 5124                    //
 5125                }
 5126
 5127            }
 5128        "},
 5129        language_with_doc_comments.clone(),
 5130        &mut cx,
 5131    );
 5132
 5133    #[track_caller]
 5134    fn assert_rewrap(
 5135        unwrapped_text: &str,
 5136        wrapped_text: &str,
 5137        language: Arc<Language>,
 5138        cx: &mut EditorTestContext,
 5139    ) {
 5140        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5141        cx.set_state(unwrapped_text);
 5142        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5143        cx.assert_editor_state(wrapped_text);
 5144    }
 5145}
 5146
 5147#[gpui::test]
 5148async fn test_hard_wrap(cx: &mut TestAppContext) {
 5149    init_test(cx, |_| {});
 5150    let mut cx = EditorTestContext::new(cx).await;
 5151
 5152    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5153    cx.update_editor(|editor, _, cx| {
 5154        editor.set_hard_wrap(Some(14), cx);
 5155    });
 5156
 5157    cx.set_state(indoc!(
 5158        "
 5159        one two three ˇ
 5160        "
 5161    ));
 5162    cx.simulate_input("four");
 5163    cx.run_until_parked();
 5164
 5165    cx.assert_editor_state(indoc!(
 5166        "
 5167        one two three
 5168        fourˇ
 5169        "
 5170    ));
 5171
 5172    cx.update_editor(|editor, window, cx| {
 5173        editor.newline(&Default::default(), window, cx);
 5174    });
 5175    cx.run_until_parked();
 5176    cx.assert_editor_state(indoc!(
 5177        "
 5178        one two three
 5179        four
 5180        ˇ
 5181        "
 5182    ));
 5183
 5184    cx.simulate_input("five");
 5185    cx.run_until_parked();
 5186    cx.assert_editor_state(indoc!(
 5187        "
 5188        one two three
 5189        four
 5190        fiveˇ
 5191        "
 5192    ));
 5193
 5194    cx.update_editor(|editor, window, cx| {
 5195        editor.newline(&Default::default(), window, cx);
 5196    });
 5197    cx.run_until_parked();
 5198    cx.simulate_input("# ");
 5199    cx.run_until_parked();
 5200    cx.assert_editor_state(indoc!(
 5201        "
 5202        one two three
 5203        four
 5204        five
 5205        # ˇ
 5206        "
 5207    ));
 5208
 5209    cx.update_editor(|editor, window, cx| {
 5210        editor.newline(&Default::default(), window, cx);
 5211    });
 5212    cx.run_until_parked();
 5213    cx.assert_editor_state(indoc!(
 5214        "
 5215        one two three
 5216        four
 5217        five
 5218        #\x20
 5219 5220        "
 5221    ));
 5222
 5223    cx.simulate_input(" 6");
 5224    cx.run_until_parked();
 5225    cx.assert_editor_state(indoc!(
 5226        "
 5227        one two three
 5228        four
 5229        five
 5230        #
 5231        # 6ˇ
 5232        "
 5233    ));
 5234}
 5235
 5236#[gpui::test]
 5237async fn test_clipboard(cx: &mut TestAppContext) {
 5238    init_test(cx, |_| {});
 5239
 5240    let mut cx = EditorTestContext::new(cx).await;
 5241
 5242    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5243    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5244    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5245
 5246    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5247    cx.set_state("two ˇfour ˇsix ˇ");
 5248    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5249    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5250
 5251    // Paste again but with only two cursors. Since the number of cursors doesn't
 5252    // match the number of slices in the clipboard, the entire clipboard text
 5253    // is pasted at each cursor.
 5254    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5255    cx.update_editor(|e, window, cx| {
 5256        e.handle_input("( ", window, cx);
 5257        e.paste(&Paste, window, cx);
 5258        e.handle_input(") ", window, cx);
 5259    });
 5260    cx.assert_editor_state(
 5261        &([
 5262            "( one✅ ",
 5263            "three ",
 5264            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5265            "three ",
 5266            "five ) ˇ",
 5267        ]
 5268        .join("\n")),
 5269    );
 5270
 5271    // Cut with three selections, one of which is full-line.
 5272    cx.set_state(indoc! {"
 5273        1«2ˇ»3
 5274        4ˇ567
 5275        «8ˇ»9"});
 5276    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5277    cx.assert_editor_state(indoc! {"
 5278        1ˇ3
 5279        ˇ9"});
 5280
 5281    // Paste with three selections, noticing how the copied selection that was full-line
 5282    // gets inserted before the second cursor.
 5283    cx.set_state(indoc! {"
 5284        1ˇ3
 5285 5286        «oˇ»ne"});
 5287    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5288    cx.assert_editor_state(indoc! {"
 5289        12ˇ3
 5290        4567
 5291 5292        8ˇne"});
 5293
 5294    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5295    cx.set_state(indoc! {"
 5296        The quick brown
 5297        fox juˇmps over
 5298        the lazy dog"});
 5299    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5300    assert_eq!(
 5301        cx.read_from_clipboard()
 5302            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5303        Some("fox jumps over\n".to_string())
 5304    );
 5305
 5306    // Paste with three selections, noticing how the copied full-line selection is inserted
 5307    // before the empty selections but replaces the selection that is non-empty.
 5308    cx.set_state(indoc! {"
 5309        Tˇhe quick brown
 5310        «foˇ»x jumps over
 5311        tˇhe lazy dog"});
 5312    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5313    cx.assert_editor_state(indoc! {"
 5314        fox jumps over
 5315        Tˇhe quick brown
 5316        fox jumps over
 5317        ˇx jumps over
 5318        fox jumps over
 5319        tˇhe lazy dog"});
 5320}
 5321
 5322#[gpui::test]
 5323async fn test_copy_trim(cx: &mut TestAppContext) {
 5324    init_test(cx, |_| {});
 5325
 5326    let mut cx = EditorTestContext::new(cx).await;
 5327    cx.set_state(
 5328        r#"            «for selection in selections.iter() {
 5329            let mut start = selection.start;
 5330            let mut end = selection.end;
 5331            let is_entire_line = selection.is_empty();
 5332            if is_entire_line {
 5333                start = Point::new(start.row, 0);ˇ»
 5334                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5335            }
 5336        "#,
 5337    );
 5338    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5339    assert_eq!(
 5340        cx.read_from_clipboard()
 5341            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5342        Some(
 5343            "for selection in selections.iter() {
 5344            let mut start = selection.start;
 5345            let mut end = selection.end;
 5346            let is_entire_line = selection.is_empty();
 5347            if is_entire_line {
 5348                start = Point::new(start.row, 0);"
 5349                .to_string()
 5350        ),
 5351        "Regular copying preserves all indentation selected",
 5352    );
 5353    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5354    assert_eq!(
 5355        cx.read_from_clipboard()
 5356            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5357        Some(
 5358            "for selection in selections.iter() {
 5359let mut start = selection.start;
 5360let mut end = selection.end;
 5361let is_entire_line = selection.is_empty();
 5362if is_entire_line {
 5363    start = Point::new(start.row, 0);"
 5364                .to_string()
 5365        ),
 5366        "Copying with stripping should strip all leading whitespaces"
 5367    );
 5368
 5369    cx.set_state(
 5370        r#"       «     for selection in selections.iter() {
 5371            let mut start = selection.start;
 5372            let mut end = selection.end;
 5373            let is_entire_line = selection.is_empty();
 5374            if is_entire_line {
 5375                start = Point::new(start.row, 0);ˇ»
 5376                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5377            }
 5378        "#,
 5379    );
 5380    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5381    assert_eq!(
 5382        cx.read_from_clipboard()
 5383            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5384        Some(
 5385            "     for selection in selections.iter() {
 5386            let mut start = selection.start;
 5387            let mut end = selection.end;
 5388            let is_entire_line = selection.is_empty();
 5389            if is_entire_line {
 5390                start = Point::new(start.row, 0);"
 5391                .to_string()
 5392        ),
 5393        "Regular copying preserves all indentation selected",
 5394    );
 5395    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5396    assert_eq!(
 5397        cx.read_from_clipboard()
 5398            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5399        Some(
 5400            "for selection in selections.iter() {
 5401let mut start = selection.start;
 5402let mut end = selection.end;
 5403let is_entire_line = selection.is_empty();
 5404if is_entire_line {
 5405    start = Point::new(start.row, 0);"
 5406                .to_string()
 5407        ),
 5408        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5409    );
 5410
 5411    cx.set_state(
 5412        r#"       «ˇ     for selection in selections.iter() {
 5413            let mut start = selection.start;
 5414            let mut end = selection.end;
 5415            let is_entire_line = selection.is_empty();
 5416            if is_entire_line {
 5417                start = Point::new(start.row, 0);»
 5418                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5419            }
 5420        "#,
 5421    );
 5422    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5423    assert_eq!(
 5424        cx.read_from_clipboard()
 5425            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5426        Some(
 5427            "     for selection in selections.iter() {
 5428            let mut start = selection.start;
 5429            let mut end = selection.end;
 5430            let is_entire_line = selection.is_empty();
 5431            if is_entire_line {
 5432                start = Point::new(start.row, 0);"
 5433                .to_string()
 5434        ),
 5435        "Regular copying for reverse selection works the same",
 5436    );
 5437    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5438    assert_eq!(
 5439        cx.read_from_clipboard()
 5440            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5441        Some(
 5442            "for selection in selections.iter() {
 5443let mut start = selection.start;
 5444let mut end = selection.end;
 5445let is_entire_line = selection.is_empty();
 5446if is_entire_line {
 5447    start = Point::new(start.row, 0);"
 5448                .to_string()
 5449        ),
 5450        "Copying with stripping for reverse selection works the same"
 5451    );
 5452
 5453    cx.set_state(
 5454        r#"            for selection «in selections.iter() {
 5455            let mut start = selection.start;
 5456            let mut end = selection.end;
 5457            let is_entire_line = selection.is_empty();
 5458            if is_entire_line {
 5459                start = Point::new(start.row, 0);ˇ»
 5460                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5461            }
 5462        "#,
 5463    );
 5464    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5465    assert_eq!(
 5466        cx.read_from_clipboard()
 5467            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5468        Some(
 5469            "in selections.iter() {
 5470            let mut start = selection.start;
 5471            let mut end = selection.end;
 5472            let is_entire_line = selection.is_empty();
 5473            if is_entire_line {
 5474                start = Point::new(start.row, 0);"
 5475                .to_string()
 5476        ),
 5477        "When selecting past the indent, the copying works as usual",
 5478    );
 5479    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5480    assert_eq!(
 5481        cx.read_from_clipboard()
 5482            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5483        Some(
 5484            "in selections.iter() {
 5485            let mut start = selection.start;
 5486            let mut end = selection.end;
 5487            let is_entire_line = selection.is_empty();
 5488            if is_entire_line {
 5489                start = Point::new(start.row, 0);"
 5490                .to_string()
 5491        ),
 5492        "When selecting past the indent, nothing is trimmed"
 5493    );
 5494
 5495    cx.set_state(
 5496        r#"            «for selection in selections.iter() {
 5497            let mut start = selection.start;
 5498
 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_and_trim(&CopyAndTrim, 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            "for selection in selections.iter() {
 5513let mut start = selection.start;
 5514
 5515let mut end = selection.end;
 5516let is_entire_line = selection.is_empty();
 5517if is_entire_line {
 5518    start = Point::new(start.row, 0);
 5519"
 5520            .to_string()
 5521        ),
 5522        "Copying with stripping should ignore empty lines"
 5523    );
 5524}
 5525
 5526#[gpui::test]
 5527async fn test_paste_multiline(cx: &mut TestAppContext) {
 5528    init_test(cx, |_| {});
 5529
 5530    let mut cx = EditorTestContext::new(cx).await;
 5531    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5532
 5533    // Cut an indented block, without the leading whitespace.
 5534    cx.set_state(indoc! {"
 5535        const a: B = (
 5536            c(),
 5537            «d(
 5538                e,
 5539                f
 5540            )ˇ»
 5541        );
 5542    "});
 5543    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5544    cx.assert_editor_state(indoc! {"
 5545        const a: B = (
 5546            c(),
 5547            ˇ
 5548        );
 5549    "});
 5550
 5551    // Paste it at the same position.
 5552    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5553    cx.assert_editor_state(indoc! {"
 5554        const a: B = (
 5555            c(),
 5556            d(
 5557                e,
 5558                f
 5559 5560        );
 5561    "});
 5562
 5563    // Paste it at a line with a lower indent level.
 5564    cx.set_state(indoc! {"
 5565        ˇ
 5566        const a: B = (
 5567            c(),
 5568        );
 5569    "});
 5570    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5571    cx.assert_editor_state(indoc! {"
 5572        d(
 5573            e,
 5574            f
 5575 5576        const a: B = (
 5577            c(),
 5578        );
 5579    "});
 5580
 5581    // Cut an indented block, with the leading whitespace.
 5582    cx.set_state(indoc! {"
 5583        const a: B = (
 5584            c(),
 5585        «    d(
 5586                e,
 5587                f
 5588            )
 5589        ˇ»);
 5590    "});
 5591    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5592    cx.assert_editor_state(indoc! {"
 5593        const a: B = (
 5594            c(),
 5595        ˇ);
 5596    "});
 5597
 5598    // Paste it at the same position.
 5599    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5600    cx.assert_editor_state(indoc! {"
 5601        const a: B = (
 5602            c(),
 5603            d(
 5604                e,
 5605                f
 5606            )
 5607        ˇ);
 5608    "});
 5609
 5610    // Paste it at a line with a higher indent level.
 5611    cx.set_state(indoc! {"
 5612        const a: B = (
 5613            c(),
 5614            d(
 5615                e,
 5616 5617            )
 5618        );
 5619    "});
 5620    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5621    cx.assert_editor_state(indoc! {"
 5622        const a: B = (
 5623            c(),
 5624            d(
 5625                e,
 5626                f    d(
 5627                    e,
 5628                    f
 5629                )
 5630        ˇ
 5631            )
 5632        );
 5633    "});
 5634
 5635    // Copy an indented block, starting mid-line
 5636    cx.set_state(indoc! {"
 5637        const a: B = (
 5638            c(),
 5639            somethin«g(
 5640                e,
 5641                f
 5642            )ˇ»
 5643        );
 5644    "});
 5645    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5646
 5647    // Paste it on a line with a lower indent level
 5648    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5649    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5650    cx.assert_editor_state(indoc! {"
 5651        const a: B = (
 5652            c(),
 5653            something(
 5654                e,
 5655                f
 5656            )
 5657        );
 5658        g(
 5659            e,
 5660            f
 5661"});
 5662}
 5663
 5664#[gpui::test]
 5665async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5666    init_test(cx, |_| {});
 5667
 5668    cx.write_to_clipboard(ClipboardItem::new_string(
 5669        "    d(\n        e\n    );\n".into(),
 5670    ));
 5671
 5672    let mut cx = EditorTestContext::new(cx).await;
 5673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5674
 5675    cx.set_state(indoc! {"
 5676        fn a() {
 5677            b();
 5678            if c() {
 5679                ˇ
 5680            }
 5681        }
 5682    "});
 5683
 5684    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5685    cx.assert_editor_state(indoc! {"
 5686        fn a() {
 5687            b();
 5688            if c() {
 5689                d(
 5690                    e
 5691                );
 5692        ˇ
 5693            }
 5694        }
 5695    "});
 5696
 5697    cx.set_state(indoc! {"
 5698        fn a() {
 5699            b();
 5700            ˇ
 5701        }
 5702    "});
 5703
 5704    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5705    cx.assert_editor_state(indoc! {"
 5706        fn a() {
 5707            b();
 5708            d(
 5709                e
 5710            );
 5711        ˇ
 5712        }
 5713    "});
 5714}
 5715
 5716#[gpui::test]
 5717fn test_select_all(cx: &mut TestAppContext) {
 5718    init_test(cx, |_| {});
 5719
 5720    let editor = cx.add_window(|window, cx| {
 5721        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5722        build_editor(buffer, window, cx)
 5723    });
 5724    _ = editor.update(cx, |editor, window, cx| {
 5725        editor.select_all(&SelectAll, window, cx);
 5726        assert_eq!(
 5727            editor.selections.display_ranges(cx),
 5728            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5729        );
 5730    });
 5731}
 5732
 5733#[gpui::test]
 5734fn test_select_line(cx: &mut TestAppContext) {
 5735    init_test(cx, |_| {});
 5736
 5737    let editor = cx.add_window(|window, cx| {
 5738        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5739        build_editor(buffer, window, cx)
 5740    });
 5741    _ = editor.update(cx, |editor, window, cx| {
 5742        editor.change_selections(None, window, cx, |s| {
 5743            s.select_display_ranges([
 5744                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5745                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5746                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5747                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5748            ])
 5749        });
 5750        editor.select_line(&SelectLine, window, cx);
 5751        assert_eq!(
 5752            editor.selections.display_ranges(cx),
 5753            vec![
 5754                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5755                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5756            ]
 5757        );
 5758    });
 5759
 5760    _ = editor.update(cx, |editor, window, cx| {
 5761        editor.select_line(&SelectLine, window, cx);
 5762        assert_eq!(
 5763            editor.selections.display_ranges(cx),
 5764            vec![
 5765                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5766                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5767            ]
 5768        );
 5769    });
 5770
 5771    _ = editor.update(cx, |editor, window, cx| {
 5772        editor.select_line(&SelectLine, window, cx);
 5773        assert_eq!(
 5774            editor.selections.display_ranges(cx),
 5775            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5776        );
 5777    });
 5778}
 5779
 5780#[gpui::test]
 5781async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5782    init_test(cx, |_| {});
 5783    let mut cx = EditorTestContext::new(cx).await;
 5784
 5785    #[track_caller]
 5786    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5787        cx.set_state(initial_state);
 5788        cx.update_editor(|e, window, cx| {
 5789            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5790        });
 5791        cx.assert_editor_state(expected_state);
 5792    }
 5793
 5794    // Selection starts and ends at the middle of lines, left-to-right
 5795    test(
 5796        &mut cx,
 5797        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5798        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5799    );
 5800    // Same thing, right-to-left
 5801    test(
 5802        &mut cx,
 5803        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5804        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5805    );
 5806
 5807    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5808    test(
 5809        &mut cx,
 5810        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5811        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5812    );
 5813    // Same thing, right-to-left
 5814    test(
 5815        &mut cx,
 5816        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5817        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5818    );
 5819
 5820    // Whole buffer, left-to-right, last line ends with newline
 5821    test(
 5822        &mut cx,
 5823        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5824        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5825    );
 5826    // Same thing, right-to-left
 5827    test(
 5828        &mut cx,
 5829        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5830        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5831    );
 5832
 5833    // Starts at the end of a line, ends at the start of another
 5834    test(
 5835        &mut cx,
 5836        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5837        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5838    );
 5839}
 5840
 5841#[gpui::test]
 5842async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5843    init_test(cx, |_| {});
 5844
 5845    let editor = cx.add_window(|window, cx| {
 5846        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5847        build_editor(buffer, window, cx)
 5848    });
 5849
 5850    // setup
 5851    _ = editor.update(cx, |editor, window, cx| {
 5852        editor.fold_creases(
 5853            vec![
 5854                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5855                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5856                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5857            ],
 5858            true,
 5859            window,
 5860            cx,
 5861        );
 5862        assert_eq!(
 5863            editor.display_text(cx),
 5864            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5865        );
 5866    });
 5867
 5868    _ = editor.update(cx, |editor, window, cx| {
 5869        editor.change_selections(None, window, cx, |s| {
 5870            s.select_display_ranges([
 5871                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5872                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5873                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5874                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5875            ])
 5876        });
 5877        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5878        assert_eq!(
 5879            editor.display_text(cx),
 5880            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5881        );
 5882    });
 5883    EditorTestContext::for_editor(editor, cx)
 5884        .await
 5885        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5886
 5887    _ = editor.update(cx, |editor, window, cx| {
 5888        editor.change_selections(None, window, cx, |s| {
 5889            s.select_display_ranges([
 5890                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5891            ])
 5892        });
 5893        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5894        assert_eq!(
 5895            editor.display_text(cx),
 5896            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5897        );
 5898        assert_eq!(
 5899            editor.selections.display_ranges(cx),
 5900            [
 5901                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5902                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5903                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5904                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5905                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5906                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5907                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5908            ]
 5909        );
 5910    });
 5911    EditorTestContext::for_editor(editor, cx)
 5912        .await
 5913        .assert_editor_state(
 5914            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5915        );
 5916}
 5917
 5918#[gpui::test]
 5919async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5920    init_test(cx, |_| {});
 5921
 5922    let mut cx = EditorTestContext::new(cx).await;
 5923
 5924    cx.set_state(indoc!(
 5925        r#"abc
 5926           defˇghi
 5927
 5928           jk
 5929           nlmo
 5930           "#
 5931    ));
 5932
 5933    cx.update_editor(|editor, window, cx| {
 5934        editor.add_selection_above(&Default::default(), window, cx);
 5935    });
 5936
 5937    cx.assert_editor_state(indoc!(
 5938        r#"abcˇ
 5939           defˇghi
 5940
 5941           jk
 5942           nlmo
 5943           "#
 5944    ));
 5945
 5946    cx.update_editor(|editor, window, cx| {
 5947        editor.add_selection_above(&Default::default(), window, cx);
 5948    });
 5949
 5950    cx.assert_editor_state(indoc!(
 5951        r#"abcˇ
 5952            defˇghi
 5953
 5954            jk
 5955            nlmo
 5956            "#
 5957    ));
 5958
 5959    cx.update_editor(|editor, window, cx| {
 5960        editor.add_selection_below(&Default::default(), window, cx);
 5961    });
 5962
 5963    cx.assert_editor_state(indoc!(
 5964        r#"abc
 5965           defˇghi
 5966
 5967           jk
 5968           nlmo
 5969           "#
 5970    ));
 5971
 5972    cx.update_editor(|editor, window, cx| {
 5973        editor.undo_selection(&Default::default(), window, cx);
 5974    });
 5975
 5976    cx.assert_editor_state(indoc!(
 5977        r#"abcˇ
 5978           defˇghi
 5979
 5980           jk
 5981           nlmo
 5982           "#
 5983    ));
 5984
 5985    cx.update_editor(|editor, window, cx| {
 5986        editor.redo_selection(&Default::default(), window, cx);
 5987    });
 5988
 5989    cx.assert_editor_state(indoc!(
 5990        r#"abc
 5991           defˇghi
 5992
 5993           jk
 5994           nlmo
 5995           "#
 5996    ));
 5997
 5998    cx.update_editor(|editor, window, cx| {
 5999        editor.add_selection_below(&Default::default(), window, cx);
 6000    });
 6001
 6002    cx.assert_editor_state(indoc!(
 6003        r#"abc
 6004           defˇghi
 6005           ˇ
 6006           jk
 6007           nlmo
 6008           "#
 6009    ));
 6010
 6011    cx.update_editor(|editor, window, cx| {
 6012        editor.add_selection_below(&Default::default(), window, cx);
 6013    });
 6014
 6015    cx.assert_editor_state(indoc!(
 6016        r#"abc
 6017           defˇghi
 6018           ˇ
 6019           jkˇ
 6020           nlmo
 6021           "#
 6022    ));
 6023
 6024    cx.update_editor(|editor, window, cx| {
 6025        editor.add_selection_below(&Default::default(), window, cx);
 6026    });
 6027
 6028    cx.assert_editor_state(indoc!(
 6029        r#"abc
 6030           defˇghi
 6031           ˇ
 6032           jkˇ
 6033           nlmˇo
 6034           "#
 6035    ));
 6036
 6037    cx.update_editor(|editor, window, cx| {
 6038        editor.add_selection_below(&Default::default(), window, cx);
 6039    });
 6040
 6041    cx.assert_editor_state(indoc!(
 6042        r#"abc
 6043           defˇghi
 6044           ˇ
 6045           jkˇ
 6046           nlmˇo
 6047           ˇ"#
 6048    ));
 6049
 6050    // change selections
 6051    cx.set_state(indoc!(
 6052        r#"abc
 6053           def«ˇg»hi
 6054
 6055           jk
 6056           nlmo
 6057           "#
 6058    ));
 6059
 6060    cx.update_editor(|editor, window, cx| {
 6061        editor.add_selection_below(&Default::default(), window, cx);
 6062    });
 6063
 6064    cx.assert_editor_state(indoc!(
 6065        r#"abc
 6066           def«ˇg»hi
 6067
 6068           jk
 6069           nlm«ˇo»
 6070           "#
 6071    ));
 6072
 6073    cx.update_editor(|editor, window, cx| {
 6074        editor.add_selection_below(&Default::default(), window, cx);
 6075    });
 6076
 6077    cx.assert_editor_state(indoc!(
 6078        r#"abc
 6079           def«ˇg»hi
 6080
 6081           jk
 6082           nlm«ˇo»
 6083           "#
 6084    ));
 6085
 6086    cx.update_editor(|editor, window, cx| {
 6087        editor.add_selection_above(&Default::default(), window, cx);
 6088    });
 6089
 6090    cx.assert_editor_state(indoc!(
 6091        r#"abc
 6092           def«ˇg»hi
 6093
 6094           jk
 6095           nlmo
 6096           "#
 6097    ));
 6098
 6099    cx.update_editor(|editor, window, cx| {
 6100        editor.add_selection_above(&Default::default(), window, cx);
 6101    });
 6102
 6103    cx.assert_editor_state(indoc!(
 6104        r#"abc
 6105           def«ˇg»hi
 6106
 6107           jk
 6108           nlmo
 6109           "#
 6110    ));
 6111
 6112    // Change selections again
 6113    cx.set_state(indoc!(
 6114        r#"a«bc
 6115           defgˇ»hi
 6116
 6117           jk
 6118           nlmo
 6119           "#
 6120    ));
 6121
 6122    cx.update_editor(|editor, window, cx| {
 6123        editor.add_selection_below(&Default::default(), window, cx);
 6124    });
 6125
 6126    cx.assert_editor_state(indoc!(
 6127        r#"a«bcˇ»
 6128           d«efgˇ»hi
 6129
 6130           j«kˇ»
 6131           nlmo
 6132           "#
 6133    ));
 6134
 6135    cx.update_editor(|editor, window, cx| {
 6136        editor.add_selection_below(&Default::default(), window, cx);
 6137    });
 6138    cx.assert_editor_state(indoc!(
 6139        r#"a«bcˇ»
 6140           d«efgˇ»hi
 6141
 6142           j«kˇ»
 6143           n«lmoˇ»
 6144           "#
 6145    ));
 6146    cx.update_editor(|editor, window, cx| {
 6147        editor.add_selection_above(&Default::default(), window, cx);
 6148    });
 6149
 6150    cx.assert_editor_state(indoc!(
 6151        r#"a«bcˇ»
 6152           d«efgˇ»hi
 6153
 6154           j«kˇ»
 6155           nlmo
 6156           "#
 6157    ));
 6158
 6159    // Change selections again
 6160    cx.set_state(indoc!(
 6161        r#"abc
 6162           d«ˇefghi
 6163
 6164           jk
 6165           nlm»o
 6166           "#
 6167    ));
 6168
 6169    cx.update_editor(|editor, window, cx| {
 6170        editor.add_selection_above(&Default::default(), window, cx);
 6171    });
 6172
 6173    cx.assert_editor_state(indoc!(
 6174        r#"a«ˇbc»
 6175           d«ˇef»ghi
 6176
 6177           j«ˇk»
 6178           n«ˇlm»o
 6179           "#
 6180    ));
 6181
 6182    cx.update_editor(|editor, window, cx| {
 6183        editor.add_selection_below(&Default::default(), window, cx);
 6184    });
 6185
 6186    cx.assert_editor_state(indoc!(
 6187        r#"abc
 6188           d«ˇef»ghi
 6189
 6190           j«ˇk»
 6191           n«ˇlm»o
 6192           "#
 6193    ));
 6194}
 6195
 6196#[gpui::test]
 6197async fn test_select_next(cx: &mut TestAppContext) {
 6198    init_test(cx, |_| {});
 6199
 6200    let mut cx = EditorTestContext::new(cx).await;
 6201    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6202
 6203    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6204        .unwrap();
 6205    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6206
 6207    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6208        .unwrap();
 6209    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6210
 6211    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6212    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6213
 6214    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6215    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6216
 6217    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6218        .unwrap();
 6219    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6220
 6221    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6222        .unwrap();
 6223    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6224
 6225    // Test selection direction should be preserved
 6226    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6227
 6228    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6229        .unwrap();
 6230    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6231}
 6232
 6233#[gpui::test]
 6234async fn test_select_all_matches(cx: &mut TestAppContext) {
 6235    init_test(cx, |_| {});
 6236
 6237    let mut cx = EditorTestContext::new(cx).await;
 6238
 6239    // Test caret-only selections
 6240    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6241    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6242        .unwrap();
 6243    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6244
 6245    // Test left-to-right selections
 6246    cx.set_state("abc\n«abcˇ»\nabc");
 6247    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6248        .unwrap();
 6249    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6250
 6251    // Test right-to-left selections
 6252    cx.set_state("abc\n«ˇabc»\nabc");
 6253    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6254        .unwrap();
 6255    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6256
 6257    // Test selecting whitespace with caret selection
 6258    cx.set_state("abc\nˇ   abc\nabc");
 6259    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6260        .unwrap();
 6261    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6262
 6263    // Test selecting whitespace with left-to-right selection
 6264    cx.set_state("abc\n«ˇ  »abc\nabc");
 6265    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6266        .unwrap();
 6267    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6268
 6269    // Test no matches with right-to-left selection
 6270    cx.set_state("abc\n«  ˇ»abc\nabc");
 6271    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6272        .unwrap();
 6273    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6274}
 6275
 6276#[gpui::test]
 6277async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6278    init_test(cx, |_| {});
 6279
 6280    let mut cx = EditorTestContext::new(cx).await;
 6281
 6282    let large_body_1 = "\nd".repeat(200);
 6283    let large_body_2 = "\ne".repeat(200);
 6284
 6285    cx.set_state(&format!(
 6286        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6287    ));
 6288    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6289        let scroll_position = editor.scroll_position(cx);
 6290        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6291        scroll_position
 6292    });
 6293
 6294    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6295        .unwrap();
 6296    cx.assert_editor_state(&format!(
 6297        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6298    ));
 6299    let scroll_position_after_selection =
 6300        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6301    assert_eq!(
 6302        initial_scroll_position, scroll_position_after_selection,
 6303        "Scroll position should not change after selecting all matches"
 6304    );
 6305}
 6306
 6307#[gpui::test]
 6308async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6309    init_test(cx, |_| {});
 6310
 6311    let mut cx = EditorLspTestContext::new_rust(
 6312        lsp::ServerCapabilities {
 6313            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6314            ..Default::default()
 6315        },
 6316        cx,
 6317    )
 6318    .await;
 6319
 6320    cx.set_state(indoc! {"
 6321        line 1
 6322        line 2
 6323        linˇe 3
 6324        line 4
 6325        line 5
 6326    "});
 6327
 6328    // Make an edit
 6329    cx.update_editor(|editor, window, cx| {
 6330        editor.handle_input("X", window, cx);
 6331    });
 6332
 6333    // Move cursor to a different position
 6334    cx.update_editor(|editor, window, cx| {
 6335        editor.change_selections(None, window, cx, |s| {
 6336            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6337        });
 6338    });
 6339
 6340    cx.assert_editor_state(indoc! {"
 6341        line 1
 6342        line 2
 6343        linXe 3
 6344        line 4
 6345        liˇne 5
 6346    "});
 6347
 6348    cx.lsp
 6349        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6350            Ok(Some(vec![lsp::TextEdit::new(
 6351                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6352                "PREFIX ".to_string(),
 6353            )]))
 6354        });
 6355
 6356    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6357        .unwrap()
 6358        .await
 6359        .unwrap();
 6360
 6361    cx.assert_editor_state(indoc! {"
 6362        PREFIX line 1
 6363        line 2
 6364        linXe 3
 6365        line 4
 6366        liˇne 5
 6367    "});
 6368
 6369    // Undo formatting
 6370    cx.update_editor(|editor, window, cx| {
 6371        editor.undo(&Default::default(), window, cx);
 6372    });
 6373
 6374    // Verify cursor moved back to position after edit
 6375    cx.assert_editor_state(indoc! {"
 6376        line 1
 6377        line 2
 6378        linXˇe 3
 6379        line 4
 6380        line 5
 6381    "});
 6382}
 6383
 6384#[gpui::test]
 6385async fn test_undo_inline_completion_scrolls_to_edit_pos(cx: &mut TestAppContext) {
 6386    init_test(cx, |_| {});
 6387
 6388    let mut cx = EditorTestContext::new(cx).await;
 6389
 6390    let provider = cx.new(|_| FakeInlineCompletionProvider::default());
 6391    cx.update_editor(|editor, window, cx| {
 6392        editor.set_edit_prediction_provider(Some(provider.clone()), window, cx);
 6393    });
 6394
 6395    cx.set_state(indoc! {"
 6396        line 1
 6397        line 2
 6398        linˇe 3
 6399        line 4
 6400        line 5
 6401        line 6
 6402        line 7
 6403        line 8
 6404        line 9
 6405        line 10
 6406    "});
 6407
 6408    let snapshot = cx.buffer_snapshot();
 6409    let edit_position = snapshot.anchor_after(Point::new(2, 4));
 6410
 6411    cx.update(|_, cx| {
 6412        provider.update(cx, |provider, _| {
 6413            provider.set_inline_completion(Some(inline_completion::InlineCompletion {
 6414                id: None,
 6415                edits: vec![(edit_position..edit_position, "X".into())],
 6416                edit_preview: None,
 6417            }))
 6418        })
 6419    });
 6420
 6421    cx.update_editor(|editor, window, cx| editor.update_visible_inline_completion(window, cx));
 6422    cx.update_editor(|editor, window, cx| {
 6423        editor.accept_edit_prediction(&crate::AcceptEditPrediction, window, cx)
 6424    });
 6425
 6426    cx.assert_editor_state(indoc! {"
 6427        line 1
 6428        line 2
 6429        lineXˇ 3
 6430        line 4
 6431        line 5
 6432        line 6
 6433        line 7
 6434        line 8
 6435        line 9
 6436        line 10
 6437    "});
 6438
 6439    cx.update_editor(|editor, window, cx| {
 6440        editor.change_selections(None, window, cx, |s| {
 6441            s.select_ranges([Point::new(9, 2)..Point::new(9, 2)]);
 6442        });
 6443    });
 6444
 6445    cx.assert_editor_state(indoc! {"
 6446        line 1
 6447        line 2
 6448        lineX 3
 6449        line 4
 6450        line 5
 6451        line 6
 6452        line 7
 6453        line 8
 6454        line 9
 6455        liˇne 10
 6456    "});
 6457
 6458    cx.update_editor(|editor, window, cx| {
 6459        editor.undo(&Default::default(), window, cx);
 6460    });
 6461
 6462    cx.assert_editor_state(indoc! {"
 6463        line 1
 6464        line 2
 6465        lineˇ 3
 6466        line 4
 6467        line 5
 6468        line 6
 6469        line 7
 6470        line 8
 6471        line 9
 6472        line 10
 6473    "});
 6474}
 6475
 6476#[gpui::test]
 6477async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6478    init_test(cx, |_| {});
 6479
 6480    let mut cx = EditorTestContext::new(cx).await;
 6481    cx.set_state(
 6482        r#"let foo = 2;
 6483lˇet foo = 2;
 6484let fooˇ = 2;
 6485let foo = 2;
 6486let foo = ˇ2;"#,
 6487    );
 6488
 6489    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6490        .unwrap();
 6491    cx.assert_editor_state(
 6492        r#"let foo = 2;
 6493«letˇ» foo = 2;
 6494let «fooˇ» = 2;
 6495let foo = 2;
 6496let foo = «2ˇ»;"#,
 6497    );
 6498
 6499    // noop for multiple selections with different contents
 6500    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6501        .unwrap();
 6502    cx.assert_editor_state(
 6503        r#"let foo = 2;
 6504«letˇ» foo = 2;
 6505let «fooˇ» = 2;
 6506let foo = 2;
 6507let foo = «2ˇ»;"#,
 6508    );
 6509
 6510    // Test last selection direction should be preserved
 6511    cx.set_state(
 6512        r#"let foo = 2;
 6513let foo = 2;
 6514let «fooˇ» = 2;
 6515let «ˇfoo» = 2;
 6516let foo = 2;"#,
 6517    );
 6518
 6519    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6520        .unwrap();
 6521    cx.assert_editor_state(
 6522        r#"let foo = 2;
 6523let foo = 2;
 6524let «fooˇ» = 2;
 6525let «ˇfoo» = 2;
 6526let «ˇfoo» = 2;"#,
 6527    );
 6528}
 6529
 6530#[gpui::test]
 6531async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6532    init_test(cx, |_| {});
 6533
 6534    let mut cx =
 6535        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6536
 6537    cx.assert_editor_state(indoc! {"
 6538        ˇbbb
 6539        ccc
 6540
 6541        bbb
 6542        ccc
 6543        "});
 6544    cx.dispatch_action(SelectPrevious::default());
 6545    cx.assert_editor_state(indoc! {"
 6546                «bbbˇ»
 6547                ccc
 6548
 6549                bbb
 6550                ccc
 6551                "});
 6552    cx.dispatch_action(SelectPrevious::default());
 6553    cx.assert_editor_state(indoc! {"
 6554                «bbbˇ»
 6555                ccc
 6556
 6557                «bbbˇ»
 6558                ccc
 6559                "});
 6560}
 6561
 6562#[gpui::test]
 6563async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6564    init_test(cx, |_| {});
 6565
 6566    let mut cx = EditorTestContext::new(cx).await;
 6567    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6568
 6569    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6570        .unwrap();
 6571    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6572
 6573    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6574        .unwrap();
 6575    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6576
 6577    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6578    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6579
 6580    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6581    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6582
 6583    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6584        .unwrap();
 6585    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6586
 6587    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6588        .unwrap();
 6589    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6590}
 6591
 6592#[gpui::test]
 6593async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6594    init_test(cx, |_| {});
 6595
 6596    let mut cx = EditorTestContext::new(cx).await;
 6597    cx.set_state("");
 6598
 6599    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6600        .unwrap();
 6601    cx.assert_editor_state("«aˇ»");
 6602    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6603        .unwrap();
 6604    cx.assert_editor_state("«aˇ»");
 6605}
 6606
 6607#[gpui::test]
 6608async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6609    init_test(cx, |_| {});
 6610
 6611    let mut cx = EditorTestContext::new(cx).await;
 6612    cx.set_state(
 6613        r#"let foo = 2;
 6614lˇet foo = 2;
 6615let fooˇ = 2;
 6616let foo = 2;
 6617let foo = ˇ2;"#,
 6618    );
 6619
 6620    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6621        .unwrap();
 6622    cx.assert_editor_state(
 6623        r#"let foo = 2;
 6624«letˇ» foo = 2;
 6625let «fooˇ» = 2;
 6626let foo = 2;
 6627let foo = «2ˇ»;"#,
 6628    );
 6629
 6630    // noop for multiple selections with different contents
 6631    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6632        .unwrap();
 6633    cx.assert_editor_state(
 6634        r#"let foo = 2;
 6635«letˇ» foo = 2;
 6636let «fooˇ» = 2;
 6637let foo = 2;
 6638let foo = «2ˇ»;"#,
 6639    );
 6640}
 6641
 6642#[gpui::test]
 6643async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6644    init_test(cx, |_| {});
 6645
 6646    let mut cx = EditorTestContext::new(cx).await;
 6647    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6648
 6649    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6650        .unwrap();
 6651    // selection direction is preserved
 6652    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6653
 6654    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6655        .unwrap();
 6656    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6657
 6658    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6659    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6660
 6661    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6662    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6663
 6664    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6665        .unwrap();
 6666    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6667
 6668    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6669        .unwrap();
 6670    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6671}
 6672
 6673#[gpui::test]
 6674async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6675    init_test(cx, |_| {});
 6676
 6677    let language = Arc::new(Language::new(
 6678        LanguageConfig::default(),
 6679        Some(tree_sitter_rust::LANGUAGE.into()),
 6680    ));
 6681
 6682    let text = r#"
 6683        use mod1::mod2::{mod3, mod4};
 6684
 6685        fn fn_1(param1: bool, param2: &str) {
 6686            let var1 = "text";
 6687        }
 6688    "#
 6689    .unindent();
 6690
 6691    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6692    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6693    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6694
 6695    editor
 6696        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6697        .await;
 6698
 6699    editor.update_in(cx, |editor, window, cx| {
 6700        editor.change_selections(None, window, cx, |s| {
 6701            s.select_display_ranges([
 6702                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6703                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6704                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6705            ]);
 6706        });
 6707        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6708    });
 6709    editor.update(cx, |editor, cx| {
 6710        assert_text_with_selections(
 6711            editor,
 6712            indoc! {r#"
 6713                use mod1::mod2::{mod3, «mod4ˇ»};
 6714
 6715                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6716                    let var1 = "«ˇtext»";
 6717                }
 6718            "#},
 6719            cx,
 6720        );
 6721    });
 6722
 6723    editor.update_in(cx, |editor, window, cx| {
 6724        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6725    });
 6726    editor.update(cx, |editor, cx| {
 6727        assert_text_with_selections(
 6728            editor,
 6729            indoc! {r#"
 6730                use mod1::mod2::«{mod3, mod4}ˇ»;
 6731
 6732                «ˇfn fn_1(param1: bool, param2: &str) {
 6733                    let var1 = "text";
 6734 6735            "#},
 6736            cx,
 6737        );
 6738    });
 6739
 6740    editor.update_in(cx, |editor, window, cx| {
 6741        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6742    });
 6743    assert_eq!(
 6744        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6745        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6746    );
 6747
 6748    // Trying to expand the selected syntax node one more time has no effect.
 6749    editor.update_in(cx, |editor, window, cx| {
 6750        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6751    });
 6752    assert_eq!(
 6753        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6754        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6755    );
 6756
 6757    editor.update_in(cx, |editor, window, cx| {
 6758        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6759    });
 6760    editor.update(cx, |editor, cx| {
 6761        assert_text_with_selections(
 6762            editor,
 6763            indoc! {r#"
 6764                use mod1::mod2::«{mod3, mod4}ˇ»;
 6765
 6766                «ˇfn fn_1(param1: bool, param2: &str) {
 6767                    let var1 = "text";
 6768 6769            "#},
 6770            cx,
 6771        );
 6772    });
 6773
 6774    editor.update_in(cx, |editor, window, cx| {
 6775        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6776    });
 6777    editor.update(cx, |editor, cx| {
 6778        assert_text_with_selections(
 6779            editor,
 6780            indoc! {r#"
 6781                use mod1::mod2::{mod3, «mod4ˇ»};
 6782
 6783                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6784                    let var1 = "«ˇtext»";
 6785                }
 6786            "#},
 6787            cx,
 6788        );
 6789    });
 6790
 6791    editor.update_in(cx, |editor, window, cx| {
 6792        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6793    });
 6794    editor.update(cx, |editor, cx| {
 6795        assert_text_with_selections(
 6796            editor,
 6797            indoc! {r#"
 6798                use mod1::mod2::{mod3, mo«ˇ»d4};
 6799
 6800                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6801                    let var1 = "te«ˇ»xt";
 6802                }
 6803            "#},
 6804            cx,
 6805        );
 6806    });
 6807
 6808    // Trying to shrink the selected syntax node one more time has no effect.
 6809    editor.update_in(cx, |editor, window, cx| {
 6810        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6811    });
 6812    editor.update_in(cx, |editor, _, cx| {
 6813        assert_text_with_selections(
 6814            editor,
 6815            indoc! {r#"
 6816                use mod1::mod2::{mod3, mo«ˇ»d4};
 6817
 6818                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6819                    let var1 = "te«ˇ»xt";
 6820                }
 6821            "#},
 6822            cx,
 6823        );
 6824    });
 6825
 6826    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6827    // a fold.
 6828    editor.update_in(cx, |editor, window, cx| {
 6829        editor.fold_creases(
 6830            vec![
 6831                Crease::simple(
 6832                    Point::new(0, 21)..Point::new(0, 24),
 6833                    FoldPlaceholder::test(),
 6834                ),
 6835                Crease::simple(
 6836                    Point::new(3, 20)..Point::new(3, 22),
 6837                    FoldPlaceholder::test(),
 6838                ),
 6839            ],
 6840            true,
 6841            window,
 6842            cx,
 6843        );
 6844        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6845    });
 6846    editor.update(cx, |editor, cx| {
 6847        assert_text_with_selections(
 6848            editor,
 6849            indoc! {r#"
 6850                use mod1::mod2::«{mod3, mod4}ˇ»;
 6851
 6852                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6853                    let var1 = "«ˇtext»";
 6854                }
 6855            "#},
 6856            cx,
 6857        );
 6858    });
 6859}
 6860
 6861#[gpui::test]
 6862async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6863    init_test(cx, |_| {});
 6864
 6865    let language = Arc::new(Language::new(
 6866        LanguageConfig::default(),
 6867        Some(tree_sitter_rust::LANGUAGE.into()),
 6868    ));
 6869
 6870    let text = "let a = 2;";
 6871
 6872    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6873    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6874    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6875
 6876    editor
 6877        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6878        .await;
 6879
 6880    // Test case 1: Cursor at end of word
 6881    editor.update_in(cx, |editor, window, cx| {
 6882        editor.change_selections(None, window, cx, |s| {
 6883            s.select_display_ranges([
 6884                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6885            ]);
 6886        });
 6887    });
 6888    editor.update(cx, |editor, cx| {
 6889        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6890    });
 6891    editor.update_in(cx, |editor, window, cx| {
 6892        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6893    });
 6894    editor.update(cx, |editor, cx| {
 6895        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6896    });
 6897    editor.update_in(cx, |editor, window, cx| {
 6898        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6899    });
 6900    editor.update(cx, |editor, cx| {
 6901        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6902    });
 6903
 6904    // Test case 2: Cursor at end of statement
 6905    editor.update_in(cx, |editor, window, cx| {
 6906        editor.change_selections(None, window, cx, |s| {
 6907            s.select_display_ranges([
 6908                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6909            ]);
 6910        });
 6911    });
 6912    editor.update(cx, |editor, cx| {
 6913        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6914    });
 6915    editor.update_in(cx, |editor, window, cx| {
 6916        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6917    });
 6918    editor.update(cx, |editor, cx| {
 6919        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6920    });
 6921}
 6922
 6923#[gpui::test]
 6924async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6925    init_test(cx, |_| {});
 6926
 6927    let language = Arc::new(Language::new(
 6928        LanguageConfig::default(),
 6929        Some(tree_sitter_rust::LANGUAGE.into()),
 6930    ));
 6931
 6932    let text = r#"
 6933        use mod1::mod2::{mod3, mod4};
 6934
 6935        fn fn_1(param1: bool, param2: &str) {
 6936            let var1 = "hello world";
 6937        }
 6938    "#
 6939    .unindent();
 6940
 6941    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6942    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6943    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6944
 6945    editor
 6946        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6947        .await;
 6948
 6949    // Test 1: Cursor on a letter of a string word
 6950    editor.update_in(cx, |editor, window, cx| {
 6951        editor.change_selections(None, window, cx, |s| {
 6952            s.select_display_ranges([
 6953                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6954            ]);
 6955        });
 6956    });
 6957    editor.update_in(cx, |editor, window, cx| {
 6958        assert_text_with_selections(
 6959            editor,
 6960            indoc! {r#"
 6961                use mod1::mod2::{mod3, mod4};
 6962
 6963                fn fn_1(param1: bool, param2: &str) {
 6964                    let var1 = "hˇello world";
 6965                }
 6966            "#},
 6967            cx,
 6968        );
 6969        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6970        assert_text_with_selections(
 6971            editor,
 6972            indoc! {r#"
 6973                use mod1::mod2::{mod3, mod4};
 6974
 6975                fn fn_1(param1: bool, param2: &str) {
 6976                    let var1 = "«ˇhello» world";
 6977                }
 6978            "#},
 6979            cx,
 6980        );
 6981    });
 6982
 6983    // Test 2: Partial selection within a word
 6984    editor.update_in(cx, |editor, window, cx| {
 6985        editor.change_selections(None, window, cx, |s| {
 6986            s.select_display_ranges([
 6987                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6988            ]);
 6989        });
 6990    });
 6991    editor.update_in(cx, |editor, window, cx| {
 6992        assert_text_with_selections(
 6993            editor,
 6994            indoc! {r#"
 6995                use mod1::mod2::{mod3, mod4};
 6996
 6997                fn fn_1(param1: bool, param2: &str) {
 6998                    let var1 = "h«elˇ»lo world";
 6999                }
 7000            "#},
 7001            cx,
 7002        );
 7003        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7004        assert_text_with_selections(
 7005            editor,
 7006            indoc! {r#"
 7007                use mod1::mod2::{mod3, mod4};
 7008
 7009                fn fn_1(param1: bool, param2: &str) {
 7010                    let var1 = "«ˇhello» world";
 7011                }
 7012            "#},
 7013            cx,
 7014        );
 7015    });
 7016
 7017    // Test 3: Complete word already selected
 7018    editor.update_in(cx, |editor, window, cx| {
 7019        editor.change_selections(None, window, cx, |s| {
 7020            s.select_display_ranges([
 7021                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 7022            ]);
 7023        });
 7024    });
 7025    editor.update_in(cx, |editor, window, cx| {
 7026        assert_text_with_selections(
 7027            editor,
 7028            indoc! {r#"
 7029                use mod1::mod2::{mod3, mod4};
 7030
 7031                fn fn_1(param1: bool, param2: &str) {
 7032                    let var1 = "«helloˇ» world";
 7033                }
 7034            "#},
 7035            cx,
 7036        );
 7037        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7038        assert_text_with_selections(
 7039            editor,
 7040            indoc! {r#"
 7041                use mod1::mod2::{mod3, mod4};
 7042
 7043                fn fn_1(param1: bool, param2: &str) {
 7044                    let var1 = "«hello worldˇ»";
 7045                }
 7046            "#},
 7047            cx,
 7048        );
 7049    });
 7050
 7051    // Test 4: Selection spanning across words
 7052    editor.update_in(cx, |editor, window, cx| {
 7053        editor.change_selections(None, window, cx, |s| {
 7054            s.select_display_ranges([
 7055                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 7056            ]);
 7057        });
 7058    });
 7059    editor.update_in(cx, |editor, window, cx| {
 7060        assert_text_with_selections(
 7061            editor,
 7062            indoc! {r#"
 7063                use mod1::mod2::{mod3, mod4};
 7064
 7065                fn fn_1(param1: bool, param2: &str) {
 7066                    let var1 = "hel«lo woˇ»rld";
 7067                }
 7068            "#},
 7069            cx,
 7070        );
 7071        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7072        assert_text_with_selections(
 7073            editor,
 7074            indoc! {r#"
 7075                use mod1::mod2::{mod3, mod4};
 7076
 7077                fn fn_1(param1: bool, param2: &str) {
 7078                    let var1 = "«ˇhello world»";
 7079                }
 7080            "#},
 7081            cx,
 7082        );
 7083    });
 7084
 7085    // Test 5: Expansion beyond string
 7086    editor.update_in(cx, |editor, window, cx| {
 7087        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7088        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7089        assert_text_with_selections(
 7090            editor,
 7091            indoc! {r#"
 7092                use mod1::mod2::{mod3, mod4};
 7093
 7094                fn fn_1(param1: bool, param2: &str) {
 7095                    «ˇlet var1 = "hello world";»
 7096                }
 7097            "#},
 7098            cx,
 7099        );
 7100    });
 7101}
 7102
 7103#[gpui::test]
 7104async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 7105    init_test(cx, |_| {});
 7106
 7107    let base_text = r#"
 7108        impl A {
 7109            // this is an uncommitted comment
 7110
 7111            fn b() {
 7112                c();
 7113            }
 7114
 7115            // this is another uncommitted comment
 7116
 7117            fn d() {
 7118                // e
 7119                // f
 7120            }
 7121        }
 7122
 7123        fn g() {
 7124            // h
 7125        }
 7126    "#
 7127    .unindent();
 7128
 7129    let text = r#"
 7130        ˇimpl A {
 7131
 7132            fn b() {
 7133                c();
 7134            }
 7135
 7136            fn d() {
 7137                // e
 7138                // f
 7139            }
 7140        }
 7141
 7142        fn g() {
 7143            // h
 7144        }
 7145    "#
 7146    .unindent();
 7147
 7148    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7149    cx.set_state(&text);
 7150    cx.set_head_text(&base_text);
 7151    cx.update_editor(|editor, window, cx| {
 7152        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7153    });
 7154
 7155    cx.assert_state_with_diff(
 7156        "
 7157        ˇimpl A {
 7158      -     // this is an uncommitted comment
 7159
 7160            fn b() {
 7161                c();
 7162            }
 7163
 7164      -     // this is another uncommitted comment
 7165      -
 7166            fn d() {
 7167                // e
 7168                // f
 7169            }
 7170        }
 7171
 7172        fn g() {
 7173            // h
 7174        }
 7175    "
 7176        .unindent(),
 7177    );
 7178
 7179    let expected_display_text = "
 7180        impl A {
 7181            // this is an uncommitted comment
 7182
 7183            fn b() {
 7184 7185            }
 7186
 7187            // this is another uncommitted comment
 7188
 7189            fn d() {
 7190 7191            }
 7192        }
 7193
 7194        fn g() {
 7195 7196        }
 7197        "
 7198    .unindent();
 7199
 7200    cx.update_editor(|editor, window, cx| {
 7201        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7202        assert_eq!(editor.display_text(cx), expected_display_text);
 7203    });
 7204}
 7205
 7206#[gpui::test]
 7207async fn test_autoindent(cx: &mut TestAppContext) {
 7208    init_test(cx, |_| {});
 7209
 7210    let language = Arc::new(
 7211        Language::new(
 7212            LanguageConfig {
 7213                brackets: BracketPairConfig {
 7214                    pairs: vec![
 7215                        BracketPair {
 7216                            start: "{".to_string(),
 7217                            end: "}".to_string(),
 7218                            close: false,
 7219                            surround: false,
 7220                            newline: true,
 7221                        },
 7222                        BracketPair {
 7223                            start: "(".to_string(),
 7224                            end: ")".to_string(),
 7225                            close: false,
 7226                            surround: false,
 7227                            newline: true,
 7228                        },
 7229                    ],
 7230                    ..Default::default()
 7231                },
 7232                ..Default::default()
 7233            },
 7234            Some(tree_sitter_rust::LANGUAGE.into()),
 7235        )
 7236        .with_indents_query(
 7237            r#"
 7238                (_ "(" ")" @end) @indent
 7239                (_ "{" "}" @end) @indent
 7240            "#,
 7241        )
 7242        .unwrap(),
 7243    );
 7244
 7245    let text = "fn a() {}";
 7246
 7247    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7248    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7249    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7250    editor
 7251        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7252        .await;
 7253
 7254    editor.update_in(cx, |editor, window, cx| {
 7255        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7256        editor.newline(&Newline, window, cx);
 7257        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7258        assert_eq!(
 7259            editor.selections.ranges(cx),
 7260            &[
 7261                Point::new(1, 4)..Point::new(1, 4),
 7262                Point::new(3, 4)..Point::new(3, 4),
 7263                Point::new(5, 0)..Point::new(5, 0)
 7264            ]
 7265        );
 7266    });
 7267}
 7268
 7269#[gpui::test]
 7270async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7271    init_test(cx, |_| {});
 7272
 7273    {
 7274        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7275        cx.set_state(indoc! {"
 7276            impl A {
 7277
 7278                fn b() {}
 7279
 7280            «fn c() {
 7281
 7282            }ˇ»
 7283            }
 7284        "});
 7285
 7286        cx.update_editor(|editor, window, cx| {
 7287            editor.autoindent(&Default::default(), window, cx);
 7288        });
 7289
 7290        cx.assert_editor_state(indoc! {"
 7291            impl A {
 7292
 7293                fn b() {}
 7294
 7295                «fn c() {
 7296
 7297                }ˇ»
 7298            }
 7299        "});
 7300    }
 7301
 7302    {
 7303        let mut cx = EditorTestContext::new_multibuffer(
 7304            cx,
 7305            [indoc! { "
 7306                impl A {
 7307                «
 7308                // a
 7309                fn b(){}
 7310                »
 7311                «
 7312                    }
 7313                    fn c(){}
 7314                »
 7315            "}],
 7316        );
 7317
 7318        let buffer = cx.update_editor(|editor, _, cx| {
 7319            let buffer = editor.buffer().update(cx, |buffer, _| {
 7320                buffer.all_buffers().iter().next().unwrap().clone()
 7321            });
 7322            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7323            buffer
 7324        });
 7325
 7326        cx.run_until_parked();
 7327        cx.update_editor(|editor, window, cx| {
 7328            editor.select_all(&Default::default(), window, cx);
 7329            editor.autoindent(&Default::default(), window, cx)
 7330        });
 7331        cx.run_until_parked();
 7332
 7333        cx.update(|_, cx| {
 7334            assert_eq!(
 7335                buffer.read(cx).text(),
 7336                indoc! { "
 7337                    impl A {
 7338
 7339                        // a
 7340                        fn b(){}
 7341
 7342
 7343                    }
 7344                    fn c(){}
 7345
 7346                " }
 7347            )
 7348        });
 7349    }
 7350}
 7351
 7352#[gpui::test]
 7353async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7354    init_test(cx, |_| {});
 7355
 7356    let mut cx = EditorTestContext::new(cx).await;
 7357
 7358    let language = Arc::new(Language::new(
 7359        LanguageConfig {
 7360            brackets: BracketPairConfig {
 7361                pairs: vec![
 7362                    BracketPair {
 7363                        start: "{".to_string(),
 7364                        end: "}".to_string(),
 7365                        close: true,
 7366                        surround: true,
 7367                        newline: true,
 7368                    },
 7369                    BracketPair {
 7370                        start: "(".to_string(),
 7371                        end: ")".to_string(),
 7372                        close: true,
 7373                        surround: true,
 7374                        newline: true,
 7375                    },
 7376                    BracketPair {
 7377                        start: "/*".to_string(),
 7378                        end: " */".to_string(),
 7379                        close: true,
 7380                        surround: true,
 7381                        newline: true,
 7382                    },
 7383                    BracketPair {
 7384                        start: "[".to_string(),
 7385                        end: "]".to_string(),
 7386                        close: false,
 7387                        surround: false,
 7388                        newline: true,
 7389                    },
 7390                    BracketPair {
 7391                        start: "\"".to_string(),
 7392                        end: "\"".to_string(),
 7393                        close: true,
 7394                        surround: true,
 7395                        newline: false,
 7396                    },
 7397                    BracketPair {
 7398                        start: "<".to_string(),
 7399                        end: ">".to_string(),
 7400                        close: false,
 7401                        surround: true,
 7402                        newline: true,
 7403                    },
 7404                ],
 7405                ..Default::default()
 7406            },
 7407            autoclose_before: "})]".to_string(),
 7408            ..Default::default()
 7409        },
 7410        Some(tree_sitter_rust::LANGUAGE.into()),
 7411    ));
 7412
 7413    cx.language_registry().add(language.clone());
 7414    cx.update_buffer(|buffer, cx| {
 7415        buffer.set_language(Some(language), cx);
 7416    });
 7417
 7418    cx.set_state(
 7419        &r#"
 7420            🏀ˇ
 7421            εˇ
 7422            ❤️ˇ
 7423        "#
 7424        .unindent(),
 7425    );
 7426
 7427    // autoclose multiple nested brackets at multiple cursors
 7428    cx.update_editor(|editor, window, cx| {
 7429        editor.handle_input("{", window, cx);
 7430        editor.handle_input("{", window, cx);
 7431        editor.handle_input("{", window, cx);
 7432    });
 7433    cx.assert_editor_state(
 7434        &"
 7435            🏀{{{ˇ}}}
 7436            ε{{{ˇ}}}
 7437            ❤️{{{ˇ}}}
 7438        "
 7439        .unindent(),
 7440    );
 7441
 7442    // insert a different closing bracket
 7443    cx.update_editor(|editor, window, cx| {
 7444        editor.handle_input(")", window, cx);
 7445    });
 7446    cx.assert_editor_state(
 7447        &"
 7448            🏀{{{)ˇ}}}
 7449            ε{{{)ˇ}}}
 7450            ❤️{{{)ˇ}}}
 7451        "
 7452        .unindent(),
 7453    );
 7454
 7455    // skip over the auto-closed brackets when typing a closing bracket
 7456    cx.update_editor(|editor, window, cx| {
 7457        editor.move_right(&MoveRight, window, cx);
 7458        editor.handle_input("}", window, cx);
 7459        editor.handle_input("}", window, cx);
 7460        editor.handle_input("}", window, cx);
 7461    });
 7462    cx.assert_editor_state(
 7463        &"
 7464            🏀{{{)}}}}ˇ
 7465            ε{{{)}}}}ˇ
 7466            ❤️{{{)}}}}ˇ
 7467        "
 7468        .unindent(),
 7469    );
 7470
 7471    // autoclose multi-character pairs
 7472    cx.set_state(
 7473        &"
 7474            ˇ
 7475            ˇ
 7476        "
 7477        .unindent(),
 7478    );
 7479    cx.update_editor(|editor, window, cx| {
 7480        editor.handle_input("/", window, cx);
 7481        editor.handle_input("*", window, cx);
 7482    });
 7483    cx.assert_editor_state(
 7484        &"
 7485            /*ˇ */
 7486            /*ˇ */
 7487        "
 7488        .unindent(),
 7489    );
 7490
 7491    // one cursor autocloses a multi-character pair, one cursor
 7492    // does not autoclose.
 7493    cx.set_state(
 7494        &"
 7495 7496            ˇ
 7497        "
 7498        .unindent(),
 7499    );
 7500    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7501    cx.assert_editor_state(
 7502        &"
 7503            /*ˇ */
 7504 7505        "
 7506        .unindent(),
 7507    );
 7508
 7509    // Don't autoclose if the next character isn't whitespace and isn't
 7510    // listed in the language's "autoclose_before" section.
 7511    cx.set_state("ˇa b");
 7512    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7513    cx.assert_editor_state("{ˇa b");
 7514
 7515    // Don't autoclose if `close` is false for the bracket pair
 7516    cx.set_state("ˇ");
 7517    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7518    cx.assert_editor_state("");
 7519
 7520    // Surround with brackets if text is selected
 7521    cx.set_state("«aˇ» b");
 7522    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7523    cx.assert_editor_state("{«aˇ»} b");
 7524
 7525    // Autoclose when not immediately after a word character
 7526    cx.set_state("a ˇ");
 7527    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7528    cx.assert_editor_state("a \"ˇ\"");
 7529
 7530    // Autoclose pair where the start and end characters are the same
 7531    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7532    cx.assert_editor_state("a \"\"ˇ");
 7533
 7534    // Don't autoclose when immediately after a word character
 7535    cx.set_state("");
 7536    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7537    cx.assert_editor_state("a\"ˇ");
 7538
 7539    // Do autoclose when after a non-word character
 7540    cx.set_state("");
 7541    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7542    cx.assert_editor_state("{\"ˇ\"");
 7543
 7544    // Non identical pairs autoclose regardless of preceding character
 7545    cx.set_state("");
 7546    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7547    cx.assert_editor_state("a{ˇ}");
 7548
 7549    // Don't autoclose pair if autoclose is disabled
 7550    cx.set_state("ˇ");
 7551    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7552    cx.assert_editor_state("");
 7553
 7554    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7555    cx.set_state("«aˇ» b");
 7556    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7557    cx.assert_editor_state("<«aˇ»> b");
 7558}
 7559
 7560#[gpui::test]
 7561async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7562    init_test(cx, |settings| {
 7563        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7564    });
 7565
 7566    let mut cx = EditorTestContext::new(cx).await;
 7567
 7568    let language = Arc::new(Language::new(
 7569        LanguageConfig {
 7570            brackets: BracketPairConfig {
 7571                pairs: vec![
 7572                    BracketPair {
 7573                        start: "{".to_string(),
 7574                        end: "}".to_string(),
 7575                        close: true,
 7576                        surround: true,
 7577                        newline: true,
 7578                    },
 7579                    BracketPair {
 7580                        start: "(".to_string(),
 7581                        end: ")".to_string(),
 7582                        close: true,
 7583                        surround: true,
 7584                        newline: true,
 7585                    },
 7586                    BracketPair {
 7587                        start: "[".to_string(),
 7588                        end: "]".to_string(),
 7589                        close: false,
 7590                        surround: false,
 7591                        newline: true,
 7592                    },
 7593                ],
 7594                ..Default::default()
 7595            },
 7596            autoclose_before: "})]".to_string(),
 7597            ..Default::default()
 7598        },
 7599        Some(tree_sitter_rust::LANGUAGE.into()),
 7600    ));
 7601
 7602    cx.language_registry().add(language.clone());
 7603    cx.update_buffer(|buffer, cx| {
 7604        buffer.set_language(Some(language), cx);
 7605    });
 7606
 7607    cx.set_state(
 7608        &"
 7609            ˇ
 7610            ˇ
 7611            ˇ
 7612        "
 7613        .unindent(),
 7614    );
 7615
 7616    // ensure only matching closing brackets are skipped over
 7617    cx.update_editor(|editor, window, cx| {
 7618        editor.handle_input("}", window, cx);
 7619        editor.move_left(&MoveLeft, window, cx);
 7620        editor.handle_input(")", window, cx);
 7621        editor.move_left(&MoveLeft, window, cx);
 7622    });
 7623    cx.assert_editor_state(
 7624        &"
 7625            ˇ)}
 7626            ˇ)}
 7627            ˇ)}
 7628        "
 7629        .unindent(),
 7630    );
 7631
 7632    // skip-over closing brackets at multiple cursors
 7633    cx.update_editor(|editor, window, cx| {
 7634        editor.handle_input(")", window, cx);
 7635        editor.handle_input("}", window, cx);
 7636    });
 7637    cx.assert_editor_state(
 7638        &"
 7639            )}ˇ
 7640            )}ˇ
 7641            )}ˇ
 7642        "
 7643        .unindent(),
 7644    );
 7645
 7646    // ignore non-close brackets
 7647    cx.update_editor(|editor, window, cx| {
 7648        editor.handle_input("]", window, cx);
 7649        editor.move_left(&MoveLeft, window, cx);
 7650        editor.handle_input("]", window, cx);
 7651    });
 7652    cx.assert_editor_state(
 7653        &"
 7654            )}]ˇ]
 7655            )}]ˇ]
 7656            )}]ˇ]
 7657        "
 7658        .unindent(),
 7659    );
 7660}
 7661
 7662#[gpui::test]
 7663async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7664    init_test(cx, |_| {});
 7665
 7666    let mut cx = EditorTestContext::new(cx).await;
 7667
 7668    let html_language = Arc::new(
 7669        Language::new(
 7670            LanguageConfig {
 7671                name: "HTML".into(),
 7672                brackets: BracketPairConfig {
 7673                    pairs: vec![
 7674                        BracketPair {
 7675                            start: "<".into(),
 7676                            end: ">".into(),
 7677                            close: true,
 7678                            ..Default::default()
 7679                        },
 7680                        BracketPair {
 7681                            start: "{".into(),
 7682                            end: "}".into(),
 7683                            close: true,
 7684                            ..Default::default()
 7685                        },
 7686                        BracketPair {
 7687                            start: "(".into(),
 7688                            end: ")".into(),
 7689                            close: true,
 7690                            ..Default::default()
 7691                        },
 7692                    ],
 7693                    ..Default::default()
 7694                },
 7695                autoclose_before: "})]>".into(),
 7696                ..Default::default()
 7697            },
 7698            Some(tree_sitter_html::LANGUAGE.into()),
 7699        )
 7700        .with_injection_query(
 7701            r#"
 7702            (script_element
 7703                (raw_text) @injection.content
 7704                (#set! injection.language "javascript"))
 7705            "#,
 7706        )
 7707        .unwrap(),
 7708    );
 7709
 7710    let javascript_language = Arc::new(Language::new(
 7711        LanguageConfig {
 7712            name: "JavaScript".into(),
 7713            brackets: BracketPairConfig {
 7714                pairs: vec![
 7715                    BracketPair {
 7716                        start: "/*".into(),
 7717                        end: " */".into(),
 7718                        close: true,
 7719                        ..Default::default()
 7720                    },
 7721                    BracketPair {
 7722                        start: "{".into(),
 7723                        end: "}".into(),
 7724                        close: true,
 7725                        ..Default::default()
 7726                    },
 7727                    BracketPair {
 7728                        start: "(".into(),
 7729                        end: ")".into(),
 7730                        close: true,
 7731                        ..Default::default()
 7732                    },
 7733                ],
 7734                ..Default::default()
 7735            },
 7736            autoclose_before: "})]>".into(),
 7737            ..Default::default()
 7738        },
 7739        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7740    ));
 7741
 7742    cx.language_registry().add(html_language.clone());
 7743    cx.language_registry().add(javascript_language.clone());
 7744
 7745    cx.update_buffer(|buffer, cx| {
 7746        buffer.set_language(Some(html_language), cx);
 7747    });
 7748
 7749    cx.set_state(
 7750        &r#"
 7751            <body>ˇ
 7752                <script>
 7753                    var x = 1;ˇ
 7754                </script>
 7755            </body>ˇ
 7756        "#
 7757        .unindent(),
 7758    );
 7759
 7760    // Precondition: different languages are active at different locations.
 7761    cx.update_editor(|editor, window, cx| {
 7762        let snapshot = editor.snapshot(window, cx);
 7763        let cursors = editor.selections.ranges::<usize>(cx);
 7764        let languages = cursors
 7765            .iter()
 7766            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7767            .collect::<Vec<_>>();
 7768        assert_eq!(
 7769            languages,
 7770            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7771        );
 7772    });
 7773
 7774    // Angle brackets autoclose in HTML, but not JavaScript.
 7775    cx.update_editor(|editor, window, cx| {
 7776        editor.handle_input("<", window, cx);
 7777        editor.handle_input("a", window, cx);
 7778    });
 7779    cx.assert_editor_state(
 7780        &r#"
 7781            <body><aˇ>
 7782                <script>
 7783                    var x = 1;<aˇ
 7784                </script>
 7785            </body><aˇ>
 7786        "#
 7787        .unindent(),
 7788    );
 7789
 7790    // Curly braces and parens autoclose in both HTML and JavaScript.
 7791    cx.update_editor(|editor, window, cx| {
 7792        editor.handle_input(" b=", window, cx);
 7793        editor.handle_input("{", window, cx);
 7794        editor.handle_input("c", window, cx);
 7795        editor.handle_input("(", window, cx);
 7796    });
 7797    cx.assert_editor_state(
 7798        &r#"
 7799            <body><a b={c(ˇ)}>
 7800                <script>
 7801                    var x = 1;<a b={c(ˇ)}
 7802                </script>
 7803            </body><a b={c(ˇ)}>
 7804        "#
 7805        .unindent(),
 7806    );
 7807
 7808    // Brackets that were already autoclosed are skipped.
 7809    cx.update_editor(|editor, window, cx| {
 7810        editor.handle_input(")", window, cx);
 7811        editor.handle_input("d", window, cx);
 7812        editor.handle_input("}", window, cx);
 7813    });
 7814    cx.assert_editor_state(
 7815        &r#"
 7816            <body><a b={c()d}ˇ>
 7817                <script>
 7818                    var x = 1;<a b={c()d}ˇ
 7819                </script>
 7820            </body><a b={c()d}ˇ>
 7821        "#
 7822        .unindent(),
 7823    );
 7824    cx.update_editor(|editor, window, cx| {
 7825        editor.handle_input(">", window, cx);
 7826    });
 7827    cx.assert_editor_state(
 7828        &r#"
 7829            <body><a b={c()d}>ˇ
 7830                <script>
 7831                    var x = 1;<a b={c()d}>ˇ
 7832                </script>
 7833            </body><a b={c()d}>ˇ
 7834        "#
 7835        .unindent(),
 7836    );
 7837
 7838    // Reset
 7839    cx.set_state(
 7840        &r#"
 7841            <body>ˇ
 7842                <script>
 7843                    var x = 1;ˇ
 7844                </script>
 7845            </body>ˇ
 7846        "#
 7847        .unindent(),
 7848    );
 7849
 7850    cx.update_editor(|editor, window, cx| {
 7851        editor.handle_input("<", window, cx);
 7852    });
 7853    cx.assert_editor_state(
 7854        &r#"
 7855            <body><ˇ>
 7856                <script>
 7857                    var x = 1;<ˇ
 7858                </script>
 7859            </body><ˇ>
 7860        "#
 7861        .unindent(),
 7862    );
 7863
 7864    // When backspacing, the closing angle brackets are removed.
 7865    cx.update_editor(|editor, window, cx| {
 7866        editor.backspace(&Backspace, window, cx);
 7867    });
 7868    cx.assert_editor_state(
 7869        &r#"
 7870            <body>ˇ
 7871                <script>
 7872                    var x = 1;ˇ
 7873                </script>
 7874            </body>ˇ
 7875        "#
 7876        .unindent(),
 7877    );
 7878
 7879    // Block comments autoclose in JavaScript, but not HTML.
 7880    cx.update_editor(|editor, window, cx| {
 7881        editor.handle_input("/", window, cx);
 7882        editor.handle_input("*", window, cx);
 7883    });
 7884    cx.assert_editor_state(
 7885        &r#"
 7886            <body>/*ˇ
 7887                <script>
 7888                    var x = 1;/*ˇ */
 7889                </script>
 7890            </body>/*ˇ
 7891        "#
 7892        .unindent(),
 7893    );
 7894}
 7895
 7896#[gpui::test]
 7897async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7898    init_test(cx, |_| {});
 7899
 7900    let mut cx = EditorTestContext::new(cx).await;
 7901
 7902    let rust_language = Arc::new(
 7903        Language::new(
 7904            LanguageConfig {
 7905                name: "Rust".into(),
 7906                brackets: serde_json::from_value(json!([
 7907                    { "start": "{", "end": "}", "close": true, "newline": true },
 7908                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7909                ]))
 7910                .unwrap(),
 7911                autoclose_before: "})]>".into(),
 7912                ..Default::default()
 7913            },
 7914            Some(tree_sitter_rust::LANGUAGE.into()),
 7915        )
 7916        .with_override_query("(string_literal) @string")
 7917        .unwrap(),
 7918    );
 7919
 7920    cx.language_registry().add(rust_language.clone());
 7921    cx.update_buffer(|buffer, cx| {
 7922        buffer.set_language(Some(rust_language), cx);
 7923    });
 7924
 7925    cx.set_state(
 7926        &r#"
 7927            let x = ˇ
 7928        "#
 7929        .unindent(),
 7930    );
 7931
 7932    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7933    cx.update_editor(|editor, window, cx| {
 7934        editor.handle_input("\"", window, cx);
 7935    });
 7936    cx.assert_editor_state(
 7937        &r#"
 7938            let x = "ˇ"
 7939        "#
 7940        .unindent(),
 7941    );
 7942
 7943    // Inserting another quotation mark. The cursor moves across the existing
 7944    // automatically-inserted quotation mark.
 7945    cx.update_editor(|editor, window, cx| {
 7946        editor.handle_input("\"", window, cx);
 7947    });
 7948    cx.assert_editor_state(
 7949        &r#"
 7950            let x = ""ˇ
 7951        "#
 7952        .unindent(),
 7953    );
 7954
 7955    // Reset
 7956    cx.set_state(
 7957        &r#"
 7958            let x = ˇ
 7959        "#
 7960        .unindent(),
 7961    );
 7962
 7963    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7964    cx.update_editor(|editor, window, cx| {
 7965        editor.handle_input("\"", window, cx);
 7966        editor.handle_input(" ", window, cx);
 7967        editor.move_left(&Default::default(), window, cx);
 7968        editor.handle_input("\\", window, cx);
 7969        editor.handle_input("\"", window, cx);
 7970    });
 7971    cx.assert_editor_state(
 7972        &r#"
 7973            let x = "\"ˇ "
 7974        "#
 7975        .unindent(),
 7976    );
 7977
 7978    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7979    // mark. Nothing is inserted.
 7980    cx.update_editor(|editor, window, cx| {
 7981        editor.move_right(&Default::default(), window, cx);
 7982        editor.handle_input("\"", window, cx);
 7983    });
 7984    cx.assert_editor_state(
 7985        &r#"
 7986            let x = "\" "ˇ
 7987        "#
 7988        .unindent(),
 7989    );
 7990}
 7991
 7992#[gpui::test]
 7993async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7994    init_test(cx, |_| {});
 7995
 7996    let language = Arc::new(Language::new(
 7997        LanguageConfig {
 7998            brackets: BracketPairConfig {
 7999                pairs: vec![
 8000                    BracketPair {
 8001                        start: "{".to_string(),
 8002                        end: "}".to_string(),
 8003                        close: true,
 8004                        surround: true,
 8005                        newline: true,
 8006                    },
 8007                    BracketPair {
 8008                        start: "/* ".to_string(),
 8009                        end: "*/".to_string(),
 8010                        close: true,
 8011                        surround: true,
 8012                        ..Default::default()
 8013                    },
 8014                ],
 8015                ..Default::default()
 8016            },
 8017            ..Default::default()
 8018        },
 8019        Some(tree_sitter_rust::LANGUAGE.into()),
 8020    ));
 8021
 8022    let text = r#"
 8023        a
 8024        b
 8025        c
 8026    "#
 8027    .unindent();
 8028
 8029    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8030    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8031    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8032    editor
 8033        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8034        .await;
 8035
 8036    editor.update_in(cx, |editor, window, cx| {
 8037        editor.change_selections(None, window, cx, |s| {
 8038            s.select_display_ranges([
 8039                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8040                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8041                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 8042            ])
 8043        });
 8044
 8045        editor.handle_input("{", window, cx);
 8046        editor.handle_input("{", window, cx);
 8047        editor.handle_input("{", window, cx);
 8048        assert_eq!(
 8049            editor.text(cx),
 8050            "
 8051                {{{a}}}
 8052                {{{b}}}
 8053                {{{c}}}
 8054            "
 8055            .unindent()
 8056        );
 8057        assert_eq!(
 8058            editor.selections.display_ranges(cx),
 8059            [
 8060                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 8061                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 8062                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 8063            ]
 8064        );
 8065
 8066        editor.undo(&Undo, window, cx);
 8067        editor.undo(&Undo, window, cx);
 8068        editor.undo(&Undo, window, cx);
 8069        assert_eq!(
 8070            editor.text(cx),
 8071            "
 8072                a
 8073                b
 8074                c
 8075            "
 8076            .unindent()
 8077        );
 8078        assert_eq!(
 8079            editor.selections.display_ranges(cx),
 8080            [
 8081                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8082                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8083                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8084            ]
 8085        );
 8086
 8087        // Ensure inserting the first character of a multi-byte bracket pair
 8088        // doesn't surround the selections with the bracket.
 8089        editor.handle_input("/", window, cx);
 8090        assert_eq!(
 8091            editor.text(cx),
 8092            "
 8093                /
 8094                /
 8095                /
 8096            "
 8097            .unindent()
 8098        );
 8099        assert_eq!(
 8100            editor.selections.display_ranges(cx),
 8101            [
 8102                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8103                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8104                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8105            ]
 8106        );
 8107
 8108        editor.undo(&Undo, window, cx);
 8109        assert_eq!(
 8110            editor.text(cx),
 8111            "
 8112                a
 8113                b
 8114                c
 8115            "
 8116            .unindent()
 8117        );
 8118        assert_eq!(
 8119            editor.selections.display_ranges(cx),
 8120            [
 8121                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8122                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8123                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8124            ]
 8125        );
 8126
 8127        // Ensure inserting the last character of a multi-byte bracket pair
 8128        // doesn't surround the selections with the bracket.
 8129        editor.handle_input("*", window, cx);
 8130        assert_eq!(
 8131            editor.text(cx),
 8132            "
 8133                *
 8134                *
 8135                *
 8136            "
 8137            .unindent()
 8138        );
 8139        assert_eq!(
 8140            editor.selections.display_ranges(cx),
 8141            [
 8142                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8143                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8144                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8145            ]
 8146        );
 8147    });
 8148}
 8149
 8150#[gpui::test]
 8151async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8152    init_test(cx, |_| {});
 8153
 8154    let language = Arc::new(Language::new(
 8155        LanguageConfig {
 8156            brackets: BracketPairConfig {
 8157                pairs: vec![BracketPair {
 8158                    start: "{".to_string(),
 8159                    end: "}".to_string(),
 8160                    close: true,
 8161                    surround: true,
 8162                    newline: true,
 8163                }],
 8164                ..Default::default()
 8165            },
 8166            autoclose_before: "}".to_string(),
 8167            ..Default::default()
 8168        },
 8169        Some(tree_sitter_rust::LANGUAGE.into()),
 8170    ));
 8171
 8172    let text = r#"
 8173        a
 8174        b
 8175        c
 8176    "#
 8177    .unindent();
 8178
 8179    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8180    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8181    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8182    editor
 8183        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8184        .await;
 8185
 8186    editor.update_in(cx, |editor, window, cx| {
 8187        editor.change_selections(None, window, cx, |s| {
 8188            s.select_ranges([
 8189                Point::new(0, 1)..Point::new(0, 1),
 8190                Point::new(1, 1)..Point::new(1, 1),
 8191                Point::new(2, 1)..Point::new(2, 1),
 8192            ])
 8193        });
 8194
 8195        editor.handle_input("{", window, cx);
 8196        editor.handle_input("{", window, cx);
 8197        editor.handle_input("_", window, cx);
 8198        assert_eq!(
 8199            editor.text(cx),
 8200            "
 8201                a{{_}}
 8202                b{{_}}
 8203                c{{_}}
 8204            "
 8205            .unindent()
 8206        );
 8207        assert_eq!(
 8208            editor.selections.ranges::<Point>(cx),
 8209            [
 8210                Point::new(0, 4)..Point::new(0, 4),
 8211                Point::new(1, 4)..Point::new(1, 4),
 8212                Point::new(2, 4)..Point::new(2, 4)
 8213            ]
 8214        );
 8215
 8216        editor.backspace(&Default::default(), window, cx);
 8217        editor.backspace(&Default::default(), window, cx);
 8218        assert_eq!(
 8219            editor.text(cx),
 8220            "
 8221                a{}
 8222                b{}
 8223                c{}
 8224            "
 8225            .unindent()
 8226        );
 8227        assert_eq!(
 8228            editor.selections.ranges::<Point>(cx),
 8229            [
 8230                Point::new(0, 2)..Point::new(0, 2),
 8231                Point::new(1, 2)..Point::new(1, 2),
 8232                Point::new(2, 2)..Point::new(2, 2)
 8233            ]
 8234        );
 8235
 8236        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8237        assert_eq!(
 8238            editor.text(cx),
 8239            "
 8240                a
 8241                b
 8242                c
 8243            "
 8244            .unindent()
 8245        );
 8246        assert_eq!(
 8247            editor.selections.ranges::<Point>(cx),
 8248            [
 8249                Point::new(0, 1)..Point::new(0, 1),
 8250                Point::new(1, 1)..Point::new(1, 1),
 8251                Point::new(2, 1)..Point::new(2, 1)
 8252            ]
 8253        );
 8254    });
 8255}
 8256
 8257#[gpui::test]
 8258async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8259    init_test(cx, |settings| {
 8260        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8261    });
 8262
 8263    let mut cx = EditorTestContext::new(cx).await;
 8264
 8265    let language = Arc::new(Language::new(
 8266        LanguageConfig {
 8267            brackets: BracketPairConfig {
 8268                pairs: vec![
 8269                    BracketPair {
 8270                        start: "{".to_string(),
 8271                        end: "}".to_string(),
 8272                        close: true,
 8273                        surround: true,
 8274                        newline: true,
 8275                    },
 8276                    BracketPair {
 8277                        start: "(".to_string(),
 8278                        end: ")".to_string(),
 8279                        close: true,
 8280                        surround: true,
 8281                        newline: true,
 8282                    },
 8283                    BracketPair {
 8284                        start: "[".to_string(),
 8285                        end: "]".to_string(),
 8286                        close: false,
 8287                        surround: true,
 8288                        newline: true,
 8289                    },
 8290                ],
 8291                ..Default::default()
 8292            },
 8293            autoclose_before: "})]".to_string(),
 8294            ..Default::default()
 8295        },
 8296        Some(tree_sitter_rust::LANGUAGE.into()),
 8297    ));
 8298
 8299    cx.language_registry().add(language.clone());
 8300    cx.update_buffer(|buffer, cx| {
 8301        buffer.set_language(Some(language), cx);
 8302    });
 8303
 8304    cx.set_state(
 8305        &"
 8306            {(ˇ)}
 8307            [[ˇ]]
 8308            {(ˇ)}
 8309        "
 8310        .unindent(),
 8311    );
 8312
 8313    cx.update_editor(|editor, window, cx| {
 8314        editor.backspace(&Default::default(), window, cx);
 8315        editor.backspace(&Default::default(), window, cx);
 8316    });
 8317
 8318    cx.assert_editor_state(
 8319        &"
 8320            ˇ
 8321            ˇ]]
 8322            ˇ
 8323        "
 8324        .unindent(),
 8325    );
 8326
 8327    cx.update_editor(|editor, window, cx| {
 8328        editor.handle_input("{", window, cx);
 8329        editor.handle_input("{", window, cx);
 8330        editor.move_right(&MoveRight, window, cx);
 8331        editor.move_right(&MoveRight, window, cx);
 8332        editor.move_left(&MoveLeft, window, cx);
 8333        editor.move_left(&MoveLeft, window, cx);
 8334        editor.backspace(&Default::default(), window, cx);
 8335    });
 8336
 8337    cx.assert_editor_state(
 8338        &"
 8339            {ˇ}
 8340            {ˇ}]]
 8341            {ˇ}
 8342        "
 8343        .unindent(),
 8344    );
 8345
 8346    cx.update_editor(|editor, window, cx| {
 8347        editor.backspace(&Default::default(), window, cx);
 8348    });
 8349
 8350    cx.assert_editor_state(
 8351        &"
 8352            ˇ
 8353            ˇ]]
 8354            ˇ
 8355        "
 8356        .unindent(),
 8357    );
 8358}
 8359
 8360#[gpui::test]
 8361async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8362    init_test(cx, |_| {});
 8363
 8364    let language = Arc::new(Language::new(
 8365        LanguageConfig::default(),
 8366        Some(tree_sitter_rust::LANGUAGE.into()),
 8367    ));
 8368
 8369    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8370    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8371    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8372    editor
 8373        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8374        .await;
 8375
 8376    editor.update_in(cx, |editor, window, cx| {
 8377        editor.set_auto_replace_emoji_shortcode(true);
 8378
 8379        editor.handle_input("Hello ", window, cx);
 8380        editor.handle_input(":wave", window, cx);
 8381        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8382
 8383        editor.handle_input(":", window, cx);
 8384        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8385
 8386        editor.handle_input(" :smile", window, cx);
 8387        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8388
 8389        editor.handle_input(":", window, cx);
 8390        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8391
 8392        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8393        editor.handle_input(":wave", window, cx);
 8394        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8395
 8396        editor.handle_input(":", window, cx);
 8397        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8398
 8399        editor.handle_input(":1", window, cx);
 8400        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8401
 8402        editor.handle_input(":", window, cx);
 8403        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8404
 8405        // Ensure shortcode does not get replaced when it is part of a word
 8406        editor.handle_input(" Test:wave", window, cx);
 8407        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8408
 8409        editor.handle_input(":", window, cx);
 8410        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8411
 8412        editor.set_auto_replace_emoji_shortcode(false);
 8413
 8414        // Ensure shortcode does not get replaced when auto replace is off
 8415        editor.handle_input(" :wave", window, cx);
 8416        assert_eq!(
 8417            editor.text(cx),
 8418            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8419        );
 8420
 8421        editor.handle_input(":", window, cx);
 8422        assert_eq!(
 8423            editor.text(cx),
 8424            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8425        );
 8426    });
 8427}
 8428
 8429#[gpui::test]
 8430async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8431    init_test(cx, |_| {});
 8432
 8433    let (text, insertion_ranges) = marked_text_ranges(
 8434        indoc! {"
 8435            ˇ
 8436        "},
 8437        false,
 8438    );
 8439
 8440    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8441    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8442
 8443    _ = editor.update_in(cx, |editor, window, cx| {
 8444        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8445
 8446        editor
 8447            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8448            .unwrap();
 8449
 8450        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8451            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8452            assert_eq!(editor.text(cx), expected_text);
 8453            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8454        }
 8455
 8456        assert(
 8457            editor,
 8458            cx,
 8459            indoc! {"
 8460            type «» =•
 8461            "},
 8462        );
 8463
 8464        assert!(editor.context_menu_visible(), "There should be a matches");
 8465    });
 8466}
 8467
 8468#[gpui::test]
 8469async fn test_snippets(cx: &mut TestAppContext) {
 8470    init_test(cx, |_| {});
 8471
 8472    let (text, insertion_ranges) = marked_text_ranges(
 8473        indoc! {"
 8474            a.ˇ b
 8475            a.ˇ b
 8476            a.ˇ b
 8477        "},
 8478        false,
 8479    );
 8480
 8481    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8482    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8483
 8484    editor.update_in(cx, |editor, window, cx| {
 8485        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8486
 8487        editor
 8488            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8489            .unwrap();
 8490
 8491        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8492            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8493            assert_eq!(editor.text(cx), expected_text);
 8494            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8495        }
 8496
 8497        assert(
 8498            editor,
 8499            cx,
 8500            indoc! {"
 8501                a.f(«one», two, «three») b
 8502                a.f(«one», two, «three») b
 8503                a.f(«one», two, «three») b
 8504            "},
 8505        );
 8506
 8507        // Can't move earlier than the first tab stop
 8508        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8509        assert(
 8510            editor,
 8511            cx,
 8512            indoc! {"
 8513                a.f(«one», two, «three») b
 8514                a.f(«one», two, «three») b
 8515                a.f(«one», two, «three») b
 8516            "},
 8517        );
 8518
 8519        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8520        assert(
 8521            editor,
 8522            cx,
 8523            indoc! {"
 8524                a.f(one, «two», three) b
 8525                a.f(one, «two», three) b
 8526                a.f(one, «two», three) b
 8527            "},
 8528        );
 8529
 8530        editor.move_to_prev_snippet_tabstop(window, cx);
 8531        assert(
 8532            editor,
 8533            cx,
 8534            indoc! {"
 8535                a.f(«one», two, «three») b
 8536                a.f(«one», two, «three») b
 8537                a.f(«one», two, «three») b
 8538            "},
 8539        );
 8540
 8541        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8542        assert(
 8543            editor,
 8544            cx,
 8545            indoc! {"
 8546                a.f(one, «two», three) b
 8547                a.f(one, «two», three) b
 8548                a.f(one, «two», three) b
 8549            "},
 8550        );
 8551        assert!(editor.move_to_next_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        // As soon as the last tab stop is reached, snippet state is gone
 8563        editor.move_to_prev_snippet_tabstop(window, cx);
 8564        assert(
 8565            editor,
 8566            cx,
 8567            indoc! {"
 8568                a.f(one, two, three)ˇ b
 8569                a.f(one, two, three)ˇ b
 8570                a.f(one, two, three)ˇ b
 8571            "},
 8572        );
 8573    });
 8574}
 8575
 8576#[gpui::test]
 8577async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8578    init_test(cx, |_| {});
 8579
 8580    let fs = FakeFs::new(cx.executor());
 8581    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8582
 8583    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8584
 8585    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8586    language_registry.add(rust_lang());
 8587    let mut fake_servers = language_registry.register_fake_lsp(
 8588        "Rust",
 8589        FakeLspAdapter {
 8590            capabilities: lsp::ServerCapabilities {
 8591                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8592                ..Default::default()
 8593            },
 8594            ..Default::default()
 8595        },
 8596    );
 8597
 8598    let buffer = project
 8599        .update(cx, |project, cx| {
 8600            project.open_local_buffer(path!("/file.rs"), cx)
 8601        })
 8602        .await
 8603        .unwrap();
 8604
 8605    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8606    let (editor, cx) = cx.add_window_view(|window, cx| {
 8607        build_editor_with_project(project.clone(), buffer, window, cx)
 8608    });
 8609    editor.update_in(cx, |editor, window, cx| {
 8610        editor.set_text("one\ntwo\nthree\n", window, cx)
 8611    });
 8612    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8613
 8614    cx.executor().start_waiting();
 8615    let fake_server = fake_servers.next().await.unwrap();
 8616
 8617    {
 8618        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8619            move |params, _| async move {
 8620                assert_eq!(
 8621                    params.text_document.uri,
 8622                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8623                );
 8624                assert_eq!(params.options.tab_size, 4);
 8625                Ok(Some(vec![lsp::TextEdit::new(
 8626                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8627                    ", ".to_string(),
 8628                )]))
 8629            },
 8630        );
 8631        let save = editor
 8632            .update_in(cx, |editor, window, cx| {
 8633                editor.save(true, project.clone(), window, cx)
 8634            })
 8635            .unwrap();
 8636        cx.executor().start_waiting();
 8637        save.await;
 8638
 8639        assert_eq!(
 8640            editor.update(cx, |editor, cx| editor.text(cx)),
 8641            "one, two\nthree\n"
 8642        );
 8643        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8644    }
 8645
 8646    {
 8647        editor.update_in(cx, |editor, window, cx| {
 8648            editor.set_text("one\ntwo\nthree\n", window, cx)
 8649        });
 8650        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8651
 8652        // Ensure we can still save even if formatting hangs.
 8653        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8654            move |params, _| async move {
 8655                assert_eq!(
 8656                    params.text_document.uri,
 8657                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8658                );
 8659                futures::future::pending::<()>().await;
 8660                unreachable!()
 8661            },
 8662        );
 8663        let save = editor
 8664            .update_in(cx, |editor, window, cx| {
 8665                editor.save(true, project.clone(), window, cx)
 8666            })
 8667            .unwrap();
 8668        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8669        cx.executor().start_waiting();
 8670        save.await;
 8671        assert_eq!(
 8672            editor.update(cx, |editor, cx| editor.text(cx)),
 8673            "one\ntwo\nthree\n"
 8674        );
 8675    }
 8676
 8677    // For non-dirty buffer, no formatting request should be sent
 8678    {
 8679        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8680
 8681        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8682            panic!("Should not be invoked on non-dirty buffer");
 8683        });
 8684        let save = editor
 8685            .update_in(cx, |editor, window, cx| {
 8686                editor.save(true, project.clone(), window, cx)
 8687            })
 8688            .unwrap();
 8689        cx.executor().start_waiting();
 8690        save.await;
 8691    }
 8692
 8693    // Set rust language override and assert overridden tabsize is sent to language server
 8694    update_test_language_settings(cx, |settings| {
 8695        settings.languages.insert(
 8696            "Rust".into(),
 8697            LanguageSettingsContent {
 8698                tab_size: NonZeroU32::new(8),
 8699                ..Default::default()
 8700            },
 8701        );
 8702    });
 8703
 8704    {
 8705        editor.update_in(cx, |editor, window, cx| {
 8706            editor.set_text("somehting_new\n", window, cx)
 8707        });
 8708        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8709        let _formatting_request_signal = fake_server
 8710            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8711                assert_eq!(
 8712                    params.text_document.uri,
 8713                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8714                );
 8715                assert_eq!(params.options.tab_size, 8);
 8716                Ok(Some(vec![]))
 8717            });
 8718        let save = editor
 8719            .update_in(cx, |editor, window, cx| {
 8720                editor.save(true, project.clone(), window, cx)
 8721            })
 8722            .unwrap();
 8723        cx.executor().start_waiting();
 8724        save.await;
 8725    }
 8726}
 8727
 8728#[gpui::test]
 8729async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8730    init_test(cx, |_| {});
 8731
 8732    let cols = 4;
 8733    let rows = 10;
 8734    let sample_text_1 = sample_text(rows, cols, 'a');
 8735    assert_eq!(
 8736        sample_text_1,
 8737        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8738    );
 8739    let sample_text_2 = sample_text(rows, cols, 'l');
 8740    assert_eq!(
 8741        sample_text_2,
 8742        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8743    );
 8744    let sample_text_3 = sample_text(rows, cols, 'v');
 8745    assert_eq!(
 8746        sample_text_3,
 8747        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8748    );
 8749
 8750    let fs = FakeFs::new(cx.executor());
 8751    fs.insert_tree(
 8752        path!("/a"),
 8753        json!({
 8754            "main.rs": sample_text_1,
 8755            "other.rs": sample_text_2,
 8756            "lib.rs": sample_text_3,
 8757        }),
 8758    )
 8759    .await;
 8760
 8761    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8762    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8763    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8764
 8765    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8766    language_registry.add(rust_lang());
 8767    let mut fake_servers = language_registry.register_fake_lsp(
 8768        "Rust",
 8769        FakeLspAdapter {
 8770            capabilities: lsp::ServerCapabilities {
 8771                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8772                ..Default::default()
 8773            },
 8774            ..Default::default()
 8775        },
 8776    );
 8777
 8778    let worktree = project.update(cx, |project, cx| {
 8779        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8780        assert_eq!(worktrees.len(), 1);
 8781        worktrees.pop().unwrap()
 8782    });
 8783    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8784
 8785    let buffer_1 = project
 8786        .update(cx, |project, cx| {
 8787            project.open_buffer((worktree_id, "main.rs"), cx)
 8788        })
 8789        .await
 8790        .unwrap();
 8791    let buffer_2 = project
 8792        .update(cx, |project, cx| {
 8793            project.open_buffer((worktree_id, "other.rs"), cx)
 8794        })
 8795        .await
 8796        .unwrap();
 8797    let buffer_3 = project
 8798        .update(cx, |project, cx| {
 8799            project.open_buffer((worktree_id, "lib.rs"), cx)
 8800        })
 8801        .await
 8802        .unwrap();
 8803
 8804    let multi_buffer = cx.new(|cx| {
 8805        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8806        multi_buffer.push_excerpts(
 8807            buffer_1.clone(),
 8808            [
 8809                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8810                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8811                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8812            ],
 8813            cx,
 8814        );
 8815        multi_buffer.push_excerpts(
 8816            buffer_2.clone(),
 8817            [
 8818                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8819                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8820                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8821            ],
 8822            cx,
 8823        );
 8824        multi_buffer.push_excerpts(
 8825            buffer_3.clone(),
 8826            [
 8827                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8828                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8829                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8830            ],
 8831            cx,
 8832        );
 8833        multi_buffer
 8834    });
 8835    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8836        Editor::new(
 8837            EditorMode::full(),
 8838            multi_buffer,
 8839            Some(project.clone()),
 8840            window,
 8841            cx,
 8842        )
 8843    });
 8844
 8845    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8846        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8847            s.select_ranges(Some(1..2))
 8848        });
 8849        editor.insert("|one|two|three|", window, cx);
 8850    });
 8851    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8852    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8853        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8854            s.select_ranges(Some(60..70))
 8855        });
 8856        editor.insert("|four|five|six|", window, cx);
 8857    });
 8858    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8859
 8860    // First two buffers should be edited, but not the third one.
 8861    assert_eq!(
 8862        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8863        "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}",
 8864    );
 8865    buffer_1.update(cx, |buffer, _| {
 8866        assert!(buffer.is_dirty());
 8867        assert_eq!(
 8868            buffer.text(),
 8869            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8870        )
 8871    });
 8872    buffer_2.update(cx, |buffer, _| {
 8873        assert!(buffer.is_dirty());
 8874        assert_eq!(
 8875            buffer.text(),
 8876            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8877        )
 8878    });
 8879    buffer_3.update(cx, |buffer, _| {
 8880        assert!(!buffer.is_dirty());
 8881        assert_eq!(buffer.text(), sample_text_3,)
 8882    });
 8883    cx.executor().run_until_parked();
 8884
 8885    cx.executor().start_waiting();
 8886    let save = multi_buffer_editor
 8887        .update_in(cx, |editor, window, cx| {
 8888            editor.save(true, project.clone(), window, cx)
 8889        })
 8890        .unwrap();
 8891
 8892    let fake_server = fake_servers.next().await.unwrap();
 8893    fake_server
 8894        .server
 8895        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8896            Ok(Some(vec![lsp::TextEdit::new(
 8897                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8898                format!("[{} formatted]", params.text_document.uri),
 8899            )]))
 8900        })
 8901        .detach();
 8902    save.await;
 8903
 8904    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8905    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8906    assert_eq!(
 8907        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8908        uri!(
 8909            "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}"
 8910        ),
 8911    );
 8912    buffer_1.update(cx, |buffer, _| {
 8913        assert!(!buffer.is_dirty());
 8914        assert_eq!(
 8915            buffer.text(),
 8916            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8917        )
 8918    });
 8919    buffer_2.update(cx, |buffer, _| {
 8920        assert!(!buffer.is_dirty());
 8921        assert_eq!(
 8922            buffer.text(),
 8923            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8924        )
 8925    });
 8926    buffer_3.update(cx, |buffer, _| {
 8927        assert!(!buffer.is_dirty());
 8928        assert_eq!(buffer.text(), sample_text_3,)
 8929    });
 8930}
 8931
 8932#[gpui::test]
 8933async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8934    init_test(cx, |_| {});
 8935
 8936    let fs = FakeFs::new(cx.executor());
 8937    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8938
 8939    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8940
 8941    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8942    language_registry.add(rust_lang());
 8943    let mut fake_servers = language_registry.register_fake_lsp(
 8944        "Rust",
 8945        FakeLspAdapter {
 8946            capabilities: lsp::ServerCapabilities {
 8947                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8948                ..Default::default()
 8949            },
 8950            ..Default::default()
 8951        },
 8952    );
 8953
 8954    let buffer = project
 8955        .update(cx, |project, cx| {
 8956            project.open_local_buffer(path!("/file.rs"), cx)
 8957        })
 8958        .await
 8959        .unwrap();
 8960
 8961    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8962    let (editor, cx) = cx.add_window_view(|window, cx| {
 8963        build_editor_with_project(project.clone(), buffer, window, cx)
 8964    });
 8965    editor.update_in(cx, |editor, window, cx| {
 8966        editor.set_text("one\ntwo\nthree\n", window, cx)
 8967    });
 8968    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8969
 8970    cx.executor().start_waiting();
 8971    let fake_server = fake_servers.next().await.unwrap();
 8972
 8973    let save = editor
 8974        .update_in(cx, |editor, window, cx| {
 8975            editor.save(true, project.clone(), window, cx)
 8976        })
 8977        .unwrap();
 8978    fake_server
 8979        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8980            assert_eq!(
 8981                params.text_document.uri,
 8982                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8983            );
 8984            assert_eq!(params.options.tab_size, 4);
 8985            Ok(Some(vec![lsp::TextEdit::new(
 8986                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8987                ", ".to_string(),
 8988            )]))
 8989        })
 8990        .next()
 8991        .await;
 8992    cx.executor().start_waiting();
 8993    save.await;
 8994    assert_eq!(
 8995        editor.update(cx, |editor, cx| editor.text(cx)),
 8996        "one, two\nthree\n"
 8997    );
 8998    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8999
 9000    editor.update_in(cx, |editor, window, cx| {
 9001        editor.set_text("one\ntwo\nthree\n", window, cx)
 9002    });
 9003    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9004
 9005    // Ensure we can still save even if formatting hangs.
 9006    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 9007        move |params, _| async move {
 9008            assert_eq!(
 9009                params.text_document.uri,
 9010                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9011            );
 9012            futures::future::pending::<()>().await;
 9013            unreachable!()
 9014        },
 9015    );
 9016    let save = editor
 9017        .update_in(cx, |editor, window, cx| {
 9018            editor.save(true, project.clone(), window, cx)
 9019        })
 9020        .unwrap();
 9021    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9022    cx.executor().start_waiting();
 9023    save.await;
 9024    assert_eq!(
 9025        editor.update(cx, |editor, cx| editor.text(cx)),
 9026        "one\ntwo\nthree\n"
 9027    );
 9028    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 9029
 9030    // For non-dirty buffer, no formatting request should be sent
 9031    let save = editor
 9032        .update_in(cx, |editor, window, cx| {
 9033            editor.save(true, project.clone(), window, cx)
 9034        })
 9035        .unwrap();
 9036    let _pending_format_request = fake_server
 9037        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 9038            panic!("Should not be invoked on non-dirty buffer");
 9039        })
 9040        .next();
 9041    cx.executor().start_waiting();
 9042    save.await;
 9043
 9044    // Set Rust language override and assert overridden tabsize is sent to language server
 9045    update_test_language_settings(cx, |settings| {
 9046        settings.languages.insert(
 9047            "Rust".into(),
 9048            LanguageSettingsContent {
 9049                tab_size: NonZeroU32::new(8),
 9050                ..Default::default()
 9051            },
 9052        );
 9053    });
 9054
 9055    editor.update_in(cx, |editor, window, cx| {
 9056        editor.set_text("somehting_new\n", window, cx)
 9057    });
 9058    assert!(cx.read(|cx| editor.is_dirty(cx)));
 9059    let save = editor
 9060        .update_in(cx, |editor, window, cx| {
 9061            editor.save(true, project.clone(), window, cx)
 9062        })
 9063        .unwrap();
 9064    fake_server
 9065        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 9066            assert_eq!(
 9067                params.text_document.uri,
 9068                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9069            );
 9070            assert_eq!(params.options.tab_size, 8);
 9071            Ok(Some(vec![]))
 9072        })
 9073        .next()
 9074        .await;
 9075    cx.executor().start_waiting();
 9076    save.await;
 9077}
 9078
 9079#[gpui::test]
 9080async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 9081    init_test(cx, |settings| {
 9082        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9083            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9084        ))
 9085    });
 9086
 9087    let fs = FakeFs::new(cx.executor());
 9088    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9089
 9090    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9091
 9092    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9093    language_registry.add(Arc::new(Language::new(
 9094        LanguageConfig {
 9095            name: "Rust".into(),
 9096            matcher: LanguageMatcher {
 9097                path_suffixes: vec!["rs".to_string()],
 9098                ..Default::default()
 9099            },
 9100            ..LanguageConfig::default()
 9101        },
 9102        Some(tree_sitter_rust::LANGUAGE.into()),
 9103    )));
 9104    update_test_language_settings(cx, |settings| {
 9105        // Enable Prettier formatting for the same buffer, and ensure
 9106        // LSP is called instead of Prettier.
 9107        settings.defaults.prettier = Some(PrettierSettings {
 9108            allowed: true,
 9109            ..PrettierSettings::default()
 9110        });
 9111    });
 9112    let mut fake_servers = language_registry.register_fake_lsp(
 9113        "Rust",
 9114        FakeLspAdapter {
 9115            capabilities: lsp::ServerCapabilities {
 9116                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9117                ..Default::default()
 9118            },
 9119            ..Default::default()
 9120        },
 9121    );
 9122
 9123    let buffer = project
 9124        .update(cx, |project, cx| {
 9125            project.open_local_buffer(path!("/file.rs"), cx)
 9126        })
 9127        .await
 9128        .unwrap();
 9129
 9130    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9131    let (editor, cx) = cx.add_window_view(|window, cx| {
 9132        build_editor_with_project(project.clone(), buffer, window, cx)
 9133    });
 9134    editor.update_in(cx, |editor, window, cx| {
 9135        editor.set_text("one\ntwo\nthree\n", window, cx)
 9136    });
 9137
 9138    cx.executor().start_waiting();
 9139    let fake_server = fake_servers.next().await.unwrap();
 9140
 9141    let format = editor
 9142        .update_in(cx, |editor, window, cx| {
 9143            editor.perform_format(
 9144                project.clone(),
 9145                FormatTrigger::Manual,
 9146                FormatTarget::Buffers,
 9147                window,
 9148                cx,
 9149            )
 9150        })
 9151        .unwrap();
 9152    fake_server
 9153        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9154            assert_eq!(
 9155                params.text_document.uri,
 9156                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9157            );
 9158            assert_eq!(params.options.tab_size, 4);
 9159            Ok(Some(vec![lsp::TextEdit::new(
 9160                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9161                ", ".to_string(),
 9162            )]))
 9163        })
 9164        .next()
 9165        .await;
 9166    cx.executor().start_waiting();
 9167    format.await;
 9168    assert_eq!(
 9169        editor.update(cx, |editor, cx| editor.text(cx)),
 9170        "one, two\nthree\n"
 9171    );
 9172
 9173    editor.update_in(cx, |editor, window, cx| {
 9174        editor.set_text("one\ntwo\nthree\n", window, cx)
 9175    });
 9176    // Ensure we don't lock if formatting hangs.
 9177    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9178        move |params, _| async move {
 9179            assert_eq!(
 9180                params.text_document.uri,
 9181                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9182            );
 9183            futures::future::pending::<()>().await;
 9184            unreachable!()
 9185        },
 9186    );
 9187    let format = editor
 9188        .update_in(cx, |editor, window, cx| {
 9189            editor.perform_format(
 9190                project,
 9191                FormatTrigger::Manual,
 9192                FormatTarget::Buffers,
 9193                window,
 9194                cx,
 9195            )
 9196        })
 9197        .unwrap();
 9198    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9199    cx.executor().start_waiting();
 9200    format.await;
 9201    assert_eq!(
 9202        editor.update(cx, |editor, cx| editor.text(cx)),
 9203        "one\ntwo\nthree\n"
 9204    );
 9205}
 9206
 9207#[gpui::test]
 9208async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9209    init_test(cx, |settings| {
 9210        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9211        settings.defaults.formatter =
 9212            Some(language_settings::SelectedFormatter::List(FormatterList(
 9213                vec![
 9214                    Formatter::LanguageServer { name: None },
 9215                    Formatter::CodeActions(
 9216                        [
 9217                            ("code-action-1".into(), true),
 9218                            ("code-action-2".into(), true),
 9219                        ]
 9220                        .into_iter()
 9221                        .collect(),
 9222                    ),
 9223                ]
 9224                .into(),
 9225            )))
 9226    });
 9227
 9228    let fs = FakeFs::new(cx.executor());
 9229    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9230        .await;
 9231
 9232    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9233    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9234    language_registry.add(rust_lang());
 9235
 9236    let mut fake_servers = language_registry.register_fake_lsp(
 9237        "Rust",
 9238        FakeLspAdapter {
 9239            capabilities: lsp::ServerCapabilities {
 9240                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9241                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9242                    commands: vec!["the-command-for-code-action-1".into()],
 9243                    ..Default::default()
 9244                }),
 9245                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9246                ..Default::default()
 9247            },
 9248            ..Default::default()
 9249        },
 9250    );
 9251
 9252    let buffer = project
 9253        .update(cx, |project, cx| {
 9254            project.open_local_buffer(path!("/file.rs"), cx)
 9255        })
 9256        .await
 9257        .unwrap();
 9258
 9259    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9260    let (editor, cx) = cx.add_window_view(|window, cx| {
 9261        build_editor_with_project(project.clone(), buffer, window, cx)
 9262    });
 9263
 9264    cx.executor().start_waiting();
 9265
 9266    let fake_server = fake_servers.next().await.unwrap();
 9267    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9268        move |_params, _| async move {
 9269            Ok(Some(vec![lsp::TextEdit::new(
 9270                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9271                "applied-formatting\n".to_string(),
 9272            )]))
 9273        },
 9274    );
 9275    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9276        move |params, _| async move {
 9277            assert_eq!(
 9278                params.context.only,
 9279                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9280            );
 9281            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9282            Ok(Some(vec![
 9283                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9284                    kind: Some("code-action-1".into()),
 9285                    edit: Some(lsp::WorkspaceEdit::new(
 9286                        [(
 9287                            uri.clone(),
 9288                            vec![lsp::TextEdit::new(
 9289                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9290                                "applied-code-action-1-edit\n".to_string(),
 9291                            )],
 9292                        )]
 9293                        .into_iter()
 9294                        .collect(),
 9295                    )),
 9296                    command: Some(lsp::Command {
 9297                        command: "the-command-for-code-action-1".into(),
 9298                        ..Default::default()
 9299                    }),
 9300                    ..Default::default()
 9301                }),
 9302                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9303                    kind: Some("code-action-2".into()),
 9304                    edit: Some(lsp::WorkspaceEdit::new(
 9305                        [(
 9306                            uri.clone(),
 9307                            vec![lsp::TextEdit::new(
 9308                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9309                                "applied-code-action-2-edit\n".to_string(),
 9310                            )],
 9311                        )]
 9312                        .into_iter()
 9313                        .collect(),
 9314                    )),
 9315                    ..Default::default()
 9316                }),
 9317            ]))
 9318        },
 9319    );
 9320
 9321    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9322        move |params, _| async move { Ok(params) }
 9323    });
 9324
 9325    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9326    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9327        let fake = fake_server.clone();
 9328        let lock = command_lock.clone();
 9329        move |params, _| {
 9330            assert_eq!(params.command, "the-command-for-code-action-1");
 9331            let fake = fake.clone();
 9332            let lock = lock.clone();
 9333            async move {
 9334                lock.lock().await;
 9335                fake.server
 9336                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9337                        label: None,
 9338                        edit: lsp::WorkspaceEdit {
 9339                            changes: Some(
 9340                                [(
 9341                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9342                                    vec![lsp::TextEdit {
 9343                                        range: lsp::Range::new(
 9344                                            lsp::Position::new(0, 0),
 9345                                            lsp::Position::new(0, 0),
 9346                                        ),
 9347                                        new_text: "applied-code-action-1-command\n".into(),
 9348                                    }],
 9349                                )]
 9350                                .into_iter()
 9351                                .collect(),
 9352                            ),
 9353                            ..Default::default()
 9354                        },
 9355                    })
 9356                    .await
 9357                    .into_response()
 9358                    .unwrap();
 9359                Ok(Some(json!(null)))
 9360            }
 9361        }
 9362    });
 9363
 9364    cx.executor().start_waiting();
 9365    editor
 9366        .update_in(cx, |editor, window, cx| {
 9367            editor.perform_format(
 9368                project.clone(),
 9369                FormatTrigger::Manual,
 9370                FormatTarget::Buffers,
 9371                window,
 9372                cx,
 9373            )
 9374        })
 9375        .unwrap()
 9376        .await;
 9377    editor.update(cx, |editor, cx| {
 9378        assert_eq!(
 9379            editor.text(cx),
 9380            r#"
 9381                applied-code-action-2-edit
 9382                applied-code-action-1-command
 9383                applied-code-action-1-edit
 9384                applied-formatting
 9385                one
 9386                two
 9387                three
 9388            "#
 9389            .unindent()
 9390        );
 9391    });
 9392
 9393    editor.update_in(cx, |editor, window, cx| {
 9394        editor.undo(&Default::default(), window, cx);
 9395        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9396    });
 9397
 9398    // Perform a manual edit while waiting for an LSP command
 9399    // that's being run as part of a formatting code action.
 9400    let lock_guard = command_lock.lock().await;
 9401    let format = editor
 9402        .update_in(cx, |editor, window, cx| {
 9403            editor.perform_format(
 9404                project.clone(),
 9405                FormatTrigger::Manual,
 9406                FormatTarget::Buffers,
 9407                window,
 9408                cx,
 9409            )
 9410        })
 9411        .unwrap();
 9412    cx.run_until_parked();
 9413    editor.update(cx, |editor, cx| {
 9414        assert_eq!(
 9415            editor.text(cx),
 9416            r#"
 9417                applied-code-action-1-edit
 9418                applied-formatting
 9419                one
 9420                two
 9421                three
 9422            "#
 9423            .unindent()
 9424        );
 9425
 9426        editor.buffer.update(cx, |buffer, cx| {
 9427            let ix = buffer.len(cx);
 9428            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9429        });
 9430    });
 9431
 9432    // Allow the LSP command to proceed. Because the buffer was edited,
 9433    // the second code action will not be run.
 9434    drop(lock_guard);
 9435    format.await;
 9436    editor.update_in(cx, |editor, window, cx| {
 9437        assert_eq!(
 9438            editor.text(cx),
 9439            r#"
 9440                applied-code-action-1-command
 9441                applied-code-action-1-edit
 9442                applied-formatting
 9443                one
 9444                two
 9445                three
 9446                edited
 9447            "#
 9448            .unindent()
 9449        );
 9450
 9451        // The manual edit is undone first, because it is the last thing the user did
 9452        // (even though the command completed afterwards).
 9453        editor.undo(&Default::default(), window, cx);
 9454        assert_eq!(
 9455            editor.text(cx),
 9456            r#"
 9457                applied-code-action-1-command
 9458                applied-code-action-1-edit
 9459                applied-formatting
 9460                one
 9461                two
 9462                three
 9463            "#
 9464            .unindent()
 9465        );
 9466
 9467        // All the formatting (including the command, which completed after the manual edit)
 9468        // is undone together.
 9469        editor.undo(&Default::default(), window, cx);
 9470        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9471    });
 9472}
 9473
 9474#[gpui::test]
 9475async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9476    init_test(cx, |settings| {
 9477        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9478            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9479        ))
 9480    });
 9481
 9482    let fs = FakeFs::new(cx.executor());
 9483    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9484
 9485    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9486
 9487    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9488    language_registry.add(Arc::new(Language::new(
 9489        LanguageConfig {
 9490            name: "TypeScript".into(),
 9491            matcher: LanguageMatcher {
 9492                path_suffixes: vec!["ts".to_string()],
 9493                ..Default::default()
 9494            },
 9495            ..LanguageConfig::default()
 9496        },
 9497        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9498    )));
 9499    update_test_language_settings(cx, |settings| {
 9500        settings.defaults.prettier = Some(PrettierSettings {
 9501            allowed: true,
 9502            ..PrettierSettings::default()
 9503        });
 9504    });
 9505    let mut fake_servers = language_registry.register_fake_lsp(
 9506        "TypeScript",
 9507        FakeLspAdapter {
 9508            capabilities: lsp::ServerCapabilities {
 9509                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9510                ..Default::default()
 9511            },
 9512            ..Default::default()
 9513        },
 9514    );
 9515
 9516    let buffer = project
 9517        .update(cx, |project, cx| {
 9518            project.open_local_buffer(path!("/file.ts"), cx)
 9519        })
 9520        .await
 9521        .unwrap();
 9522
 9523    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9524    let (editor, cx) = cx.add_window_view(|window, cx| {
 9525        build_editor_with_project(project.clone(), buffer, window, cx)
 9526    });
 9527    editor.update_in(cx, |editor, window, cx| {
 9528        editor.set_text(
 9529            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9530            window,
 9531            cx,
 9532        )
 9533    });
 9534
 9535    cx.executor().start_waiting();
 9536    let fake_server = fake_servers.next().await.unwrap();
 9537
 9538    let format = editor
 9539        .update_in(cx, |editor, window, cx| {
 9540            editor.perform_code_action_kind(
 9541                project.clone(),
 9542                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9543                window,
 9544                cx,
 9545            )
 9546        })
 9547        .unwrap();
 9548    fake_server
 9549        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9550            assert_eq!(
 9551                params.text_document.uri,
 9552                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9553            );
 9554            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9555                lsp::CodeAction {
 9556                    title: "Organize Imports".to_string(),
 9557                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9558                    edit: Some(lsp::WorkspaceEdit {
 9559                        changes: Some(
 9560                            [(
 9561                                params.text_document.uri.clone(),
 9562                                vec![lsp::TextEdit::new(
 9563                                    lsp::Range::new(
 9564                                        lsp::Position::new(1, 0),
 9565                                        lsp::Position::new(2, 0),
 9566                                    ),
 9567                                    "".to_string(),
 9568                                )],
 9569                            )]
 9570                            .into_iter()
 9571                            .collect(),
 9572                        ),
 9573                        ..Default::default()
 9574                    }),
 9575                    ..Default::default()
 9576                },
 9577            )]))
 9578        })
 9579        .next()
 9580        .await;
 9581    cx.executor().start_waiting();
 9582    format.await;
 9583    assert_eq!(
 9584        editor.update(cx, |editor, cx| editor.text(cx)),
 9585        "import { a } from 'module';\n\nconst x = a;\n"
 9586    );
 9587
 9588    editor.update_in(cx, |editor, window, cx| {
 9589        editor.set_text(
 9590            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9591            window,
 9592            cx,
 9593        )
 9594    });
 9595    // Ensure we don't lock if code action hangs.
 9596    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9597        move |params, _| async move {
 9598            assert_eq!(
 9599                params.text_document.uri,
 9600                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9601            );
 9602            futures::future::pending::<()>().await;
 9603            unreachable!()
 9604        },
 9605    );
 9606    let format = editor
 9607        .update_in(cx, |editor, window, cx| {
 9608            editor.perform_code_action_kind(
 9609                project,
 9610                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9611                window,
 9612                cx,
 9613            )
 9614        })
 9615        .unwrap();
 9616    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9617    cx.executor().start_waiting();
 9618    format.await;
 9619    assert_eq!(
 9620        editor.update(cx, |editor, cx| editor.text(cx)),
 9621        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9622    );
 9623}
 9624
 9625#[gpui::test]
 9626async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9627    init_test(cx, |_| {});
 9628
 9629    let mut cx = EditorLspTestContext::new_rust(
 9630        lsp::ServerCapabilities {
 9631            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9632            ..Default::default()
 9633        },
 9634        cx,
 9635    )
 9636    .await;
 9637
 9638    cx.set_state(indoc! {"
 9639        one.twoˇ
 9640    "});
 9641
 9642    // The format request takes a long time. When it completes, it inserts
 9643    // a newline and an indent before the `.`
 9644    cx.lsp
 9645        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9646            let executor = cx.background_executor().clone();
 9647            async move {
 9648                executor.timer(Duration::from_millis(100)).await;
 9649                Ok(Some(vec![lsp::TextEdit {
 9650                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9651                    new_text: "\n    ".into(),
 9652                }]))
 9653            }
 9654        });
 9655
 9656    // Submit a format request.
 9657    let format_1 = cx
 9658        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9659        .unwrap();
 9660    cx.executor().run_until_parked();
 9661
 9662    // Submit a second format request.
 9663    let format_2 = cx
 9664        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9665        .unwrap();
 9666    cx.executor().run_until_parked();
 9667
 9668    // Wait for both format requests to complete
 9669    cx.executor().advance_clock(Duration::from_millis(200));
 9670    cx.executor().start_waiting();
 9671    format_1.await.unwrap();
 9672    cx.executor().start_waiting();
 9673    format_2.await.unwrap();
 9674
 9675    // The formatting edits only happens once.
 9676    cx.assert_editor_state(indoc! {"
 9677        one
 9678            .twoˇ
 9679    "});
 9680}
 9681
 9682#[gpui::test]
 9683async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9684    init_test(cx, |settings| {
 9685        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9686    });
 9687
 9688    let mut cx = EditorLspTestContext::new_rust(
 9689        lsp::ServerCapabilities {
 9690            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9691            ..Default::default()
 9692        },
 9693        cx,
 9694    )
 9695    .await;
 9696
 9697    // Set up a buffer white some trailing whitespace and no trailing newline.
 9698    cx.set_state(
 9699        &[
 9700            "one ",   //
 9701            "twoˇ",   //
 9702            "three ", //
 9703            "four",   //
 9704        ]
 9705        .join("\n"),
 9706    );
 9707
 9708    // Submit a format request.
 9709    let format = cx
 9710        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9711        .unwrap();
 9712
 9713    // Record which buffer changes have been sent to the language server
 9714    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9715    cx.lsp
 9716        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9717            let buffer_changes = buffer_changes.clone();
 9718            move |params, _| {
 9719                buffer_changes.lock().extend(
 9720                    params
 9721                        .content_changes
 9722                        .into_iter()
 9723                        .map(|e| (e.range.unwrap(), e.text)),
 9724                );
 9725            }
 9726        });
 9727
 9728    // Handle formatting requests to the language server.
 9729    cx.lsp
 9730        .set_request_handler::<lsp::request::Formatting, _, _>({
 9731            let buffer_changes = buffer_changes.clone();
 9732            move |_, _| {
 9733                // When formatting is requested, trailing whitespace has already been stripped,
 9734                // and the trailing newline has already been added.
 9735                assert_eq!(
 9736                    &buffer_changes.lock()[1..],
 9737                    &[
 9738                        (
 9739                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9740                            "".into()
 9741                        ),
 9742                        (
 9743                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9744                            "".into()
 9745                        ),
 9746                        (
 9747                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9748                            "\n".into()
 9749                        ),
 9750                    ]
 9751                );
 9752
 9753                // Insert blank lines between each line of the buffer.
 9754                async move {
 9755                    Ok(Some(vec![
 9756                        lsp::TextEdit {
 9757                            range: lsp::Range::new(
 9758                                lsp::Position::new(1, 0),
 9759                                lsp::Position::new(1, 0),
 9760                            ),
 9761                            new_text: "\n".into(),
 9762                        },
 9763                        lsp::TextEdit {
 9764                            range: lsp::Range::new(
 9765                                lsp::Position::new(2, 0),
 9766                                lsp::Position::new(2, 0),
 9767                            ),
 9768                            new_text: "\n".into(),
 9769                        },
 9770                    ]))
 9771                }
 9772            }
 9773        });
 9774
 9775    // After formatting the buffer, the trailing whitespace is stripped,
 9776    // a newline is appended, and the edits provided by the language server
 9777    // have been applied.
 9778    format.await.unwrap();
 9779    cx.assert_editor_state(
 9780        &[
 9781            "one",   //
 9782            "",      //
 9783            "twoˇ",  //
 9784            "",      //
 9785            "three", //
 9786            "four",  //
 9787            "",      //
 9788        ]
 9789        .join("\n"),
 9790    );
 9791
 9792    // Undoing the formatting undoes the trailing whitespace removal, the
 9793    // trailing newline, and the LSP edits.
 9794    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9795    cx.assert_editor_state(
 9796        &[
 9797            "one ",   //
 9798            "twoˇ",   //
 9799            "three ", //
 9800            "four",   //
 9801        ]
 9802        .join("\n"),
 9803    );
 9804}
 9805
 9806#[gpui::test]
 9807async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9808    cx: &mut TestAppContext,
 9809) {
 9810    init_test(cx, |_| {});
 9811
 9812    cx.update(|cx| {
 9813        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9814            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9815                settings.auto_signature_help = Some(true);
 9816            });
 9817        });
 9818    });
 9819
 9820    let mut cx = EditorLspTestContext::new_rust(
 9821        lsp::ServerCapabilities {
 9822            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9823                ..Default::default()
 9824            }),
 9825            ..Default::default()
 9826        },
 9827        cx,
 9828    )
 9829    .await;
 9830
 9831    let language = Language::new(
 9832        LanguageConfig {
 9833            name: "Rust".into(),
 9834            brackets: BracketPairConfig {
 9835                pairs: vec![
 9836                    BracketPair {
 9837                        start: "{".to_string(),
 9838                        end: "}".to_string(),
 9839                        close: true,
 9840                        surround: true,
 9841                        newline: true,
 9842                    },
 9843                    BracketPair {
 9844                        start: "(".to_string(),
 9845                        end: ")".to_string(),
 9846                        close: true,
 9847                        surround: true,
 9848                        newline: true,
 9849                    },
 9850                    BracketPair {
 9851                        start: "/*".to_string(),
 9852                        end: " */".to_string(),
 9853                        close: true,
 9854                        surround: true,
 9855                        newline: true,
 9856                    },
 9857                    BracketPair {
 9858                        start: "[".to_string(),
 9859                        end: "]".to_string(),
 9860                        close: false,
 9861                        surround: false,
 9862                        newline: true,
 9863                    },
 9864                    BracketPair {
 9865                        start: "\"".to_string(),
 9866                        end: "\"".to_string(),
 9867                        close: true,
 9868                        surround: true,
 9869                        newline: false,
 9870                    },
 9871                    BracketPair {
 9872                        start: "<".to_string(),
 9873                        end: ">".to_string(),
 9874                        close: false,
 9875                        surround: true,
 9876                        newline: true,
 9877                    },
 9878                ],
 9879                ..Default::default()
 9880            },
 9881            autoclose_before: "})]".to_string(),
 9882            ..Default::default()
 9883        },
 9884        Some(tree_sitter_rust::LANGUAGE.into()),
 9885    );
 9886    let language = Arc::new(language);
 9887
 9888    cx.language_registry().add(language.clone());
 9889    cx.update_buffer(|buffer, cx| {
 9890        buffer.set_language(Some(language), cx);
 9891    });
 9892
 9893    cx.set_state(
 9894        &r#"
 9895            fn main() {
 9896                sampleˇ
 9897            }
 9898        "#
 9899        .unindent(),
 9900    );
 9901
 9902    cx.update_editor(|editor, window, cx| {
 9903        editor.handle_input("(", window, cx);
 9904    });
 9905    cx.assert_editor_state(
 9906        &"
 9907            fn main() {
 9908                sample(ˇ)
 9909            }
 9910        "
 9911        .unindent(),
 9912    );
 9913
 9914    let mocked_response = lsp::SignatureHelp {
 9915        signatures: vec![lsp::SignatureInformation {
 9916            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9917            documentation: None,
 9918            parameters: Some(vec![
 9919                lsp::ParameterInformation {
 9920                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9921                    documentation: None,
 9922                },
 9923                lsp::ParameterInformation {
 9924                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9925                    documentation: None,
 9926                },
 9927            ]),
 9928            active_parameter: None,
 9929        }],
 9930        active_signature: Some(0),
 9931        active_parameter: Some(0),
 9932    };
 9933    handle_signature_help_request(&mut cx, mocked_response).await;
 9934
 9935    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9936        .await;
 9937
 9938    cx.editor(|editor, _, _| {
 9939        let signature_help_state = editor.signature_help_state.popover().cloned();
 9940        assert_eq!(
 9941            signature_help_state.unwrap().label,
 9942            "param1: u8, param2: u8"
 9943        );
 9944    });
 9945}
 9946
 9947#[gpui::test]
 9948async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9949    init_test(cx, |_| {});
 9950
 9951    cx.update(|cx| {
 9952        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9953            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9954                settings.auto_signature_help = Some(false);
 9955                settings.show_signature_help_after_edits = Some(false);
 9956            });
 9957        });
 9958    });
 9959
 9960    let mut cx = EditorLspTestContext::new_rust(
 9961        lsp::ServerCapabilities {
 9962            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9963                ..Default::default()
 9964            }),
 9965            ..Default::default()
 9966        },
 9967        cx,
 9968    )
 9969    .await;
 9970
 9971    let language = Language::new(
 9972        LanguageConfig {
 9973            name: "Rust".into(),
 9974            brackets: BracketPairConfig {
 9975                pairs: vec![
 9976                    BracketPair {
 9977                        start: "{".to_string(),
 9978                        end: "}".to_string(),
 9979                        close: true,
 9980                        surround: true,
 9981                        newline: true,
 9982                    },
 9983                    BracketPair {
 9984                        start: "(".to_string(),
 9985                        end: ")".to_string(),
 9986                        close: true,
 9987                        surround: true,
 9988                        newline: true,
 9989                    },
 9990                    BracketPair {
 9991                        start: "/*".to_string(),
 9992                        end: " */".to_string(),
 9993                        close: true,
 9994                        surround: true,
 9995                        newline: true,
 9996                    },
 9997                    BracketPair {
 9998                        start: "[".to_string(),
 9999                        end: "]".to_string(),
10000                        close: false,
10001                        surround: false,
10002                        newline: true,
10003                    },
10004                    BracketPair {
10005                        start: "\"".to_string(),
10006                        end: "\"".to_string(),
10007                        close: true,
10008                        surround: true,
10009                        newline: false,
10010                    },
10011                    BracketPair {
10012                        start: "<".to_string(),
10013                        end: ">".to_string(),
10014                        close: false,
10015                        surround: true,
10016                        newline: true,
10017                    },
10018                ],
10019                ..Default::default()
10020            },
10021            autoclose_before: "})]".to_string(),
10022            ..Default::default()
10023        },
10024        Some(tree_sitter_rust::LANGUAGE.into()),
10025    );
10026    let language = Arc::new(language);
10027
10028    cx.language_registry().add(language.clone());
10029    cx.update_buffer(|buffer, cx| {
10030        buffer.set_language(Some(language), cx);
10031    });
10032
10033    // Ensure that signature_help is not called when no signature help is enabled.
10034    cx.set_state(
10035        &r#"
10036            fn main() {
10037                sampleˇ
10038            }
10039        "#
10040        .unindent(),
10041    );
10042    cx.update_editor(|editor, window, cx| {
10043        editor.handle_input("(", window, cx);
10044    });
10045    cx.assert_editor_state(
10046        &"
10047            fn main() {
10048                sample(ˇ)
10049            }
10050        "
10051        .unindent(),
10052    );
10053    cx.editor(|editor, _, _| {
10054        assert!(editor.signature_help_state.task().is_none());
10055    });
10056
10057    let mocked_response = lsp::SignatureHelp {
10058        signatures: vec![lsp::SignatureInformation {
10059            label: "fn sample(param1: u8, param2: u8)".to_string(),
10060            documentation: None,
10061            parameters: Some(vec![
10062                lsp::ParameterInformation {
10063                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10064                    documentation: None,
10065                },
10066                lsp::ParameterInformation {
10067                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10068                    documentation: None,
10069                },
10070            ]),
10071            active_parameter: None,
10072        }],
10073        active_signature: Some(0),
10074        active_parameter: Some(0),
10075    };
10076
10077    // Ensure that signature_help is called when enabled afte edits
10078    cx.update(|_, cx| {
10079        cx.update_global::<SettingsStore, _>(|settings, cx| {
10080            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10081                settings.auto_signature_help = Some(false);
10082                settings.show_signature_help_after_edits = Some(true);
10083            });
10084        });
10085    });
10086    cx.set_state(
10087        &r#"
10088            fn main() {
10089                sampleˇ
10090            }
10091        "#
10092        .unindent(),
10093    );
10094    cx.update_editor(|editor, window, cx| {
10095        editor.handle_input("(", window, cx);
10096    });
10097    cx.assert_editor_state(
10098        &"
10099            fn main() {
10100                sample(ˇ)
10101            }
10102        "
10103        .unindent(),
10104    );
10105    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10106    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10107        .await;
10108    cx.update_editor(|editor, _, _| {
10109        let signature_help_state = editor.signature_help_state.popover().cloned();
10110        assert!(signature_help_state.is_some());
10111        assert_eq!(
10112            signature_help_state.unwrap().label,
10113            "param1: u8, param2: u8"
10114        );
10115        editor.signature_help_state = SignatureHelpState::default();
10116    });
10117
10118    // Ensure that signature_help is called when auto signature help override is enabled
10119    cx.update(|_, cx| {
10120        cx.update_global::<SettingsStore, _>(|settings, cx| {
10121            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10122                settings.auto_signature_help = Some(true);
10123                settings.show_signature_help_after_edits = Some(false);
10124            });
10125        });
10126    });
10127    cx.set_state(
10128        &r#"
10129            fn main() {
10130                sampleˇ
10131            }
10132        "#
10133        .unindent(),
10134    );
10135    cx.update_editor(|editor, window, cx| {
10136        editor.handle_input("(", window, cx);
10137    });
10138    cx.assert_editor_state(
10139        &"
10140            fn main() {
10141                sample(ˇ)
10142            }
10143        "
10144        .unindent(),
10145    );
10146    handle_signature_help_request(&mut cx, mocked_response).await;
10147    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10148        .await;
10149    cx.editor(|editor, _, _| {
10150        let signature_help_state = editor.signature_help_state.popover().cloned();
10151        assert!(signature_help_state.is_some());
10152        assert_eq!(
10153            signature_help_state.unwrap().label,
10154            "param1: u8, param2: u8"
10155        );
10156    });
10157}
10158
10159#[gpui::test]
10160async fn test_signature_help(cx: &mut TestAppContext) {
10161    init_test(cx, |_| {});
10162    cx.update(|cx| {
10163        cx.update_global::<SettingsStore, _>(|settings, cx| {
10164            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10165                settings.auto_signature_help = Some(true);
10166            });
10167        });
10168    });
10169
10170    let mut cx = EditorLspTestContext::new_rust(
10171        lsp::ServerCapabilities {
10172            signature_help_provider: Some(lsp::SignatureHelpOptions {
10173                ..Default::default()
10174            }),
10175            ..Default::default()
10176        },
10177        cx,
10178    )
10179    .await;
10180
10181    // A test that directly calls `show_signature_help`
10182    cx.update_editor(|editor, window, cx| {
10183        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10184    });
10185
10186    let mocked_response = lsp::SignatureHelp {
10187        signatures: vec![lsp::SignatureInformation {
10188            label: "fn sample(param1: u8, param2: u8)".to_string(),
10189            documentation: None,
10190            parameters: Some(vec![
10191                lsp::ParameterInformation {
10192                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10193                    documentation: None,
10194                },
10195                lsp::ParameterInformation {
10196                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10197                    documentation: None,
10198                },
10199            ]),
10200            active_parameter: None,
10201        }],
10202        active_signature: Some(0),
10203        active_parameter: Some(0),
10204    };
10205    handle_signature_help_request(&mut cx, mocked_response).await;
10206
10207    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10208        .await;
10209
10210    cx.editor(|editor, _, _| {
10211        let signature_help_state = editor.signature_help_state.popover().cloned();
10212        assert!(signature_help_state.is_some());
10213        assert_eq!(
10214            signature_help_state.unwrap().label,
10215            "param1: u8, param2: u8"
10216        );
10217    });
10218
10219    // When exiting outside from inside the brackets, `signature_help` is closed.
10220    cx.set_state(indoc! {"
10221        fn main() {
10222            sample(ˇ);
10223        }
10224
10225        fn sample(param1: u8, param2: u8) {}
10226    "});
10227
10228    cx.update_editor(|editor, window, cx| {
10229        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10230    });
10231
10232    let mocked_response = lsp::SignatureHelp {
10233        signatures: Vec::new(),
10234        active_signature: None,
10235        active_parameter: None,
10236    };
10237    handle_signature_help_request(&mut cx, mocked_response).await;
10238
10239    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10240        .await;
10241
10242    cx.editor(|editor, _, _| {
10243        assert!(!editor.signature_help_state.is_shown());
10244    });
10245
10246    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10247    cx.set_state(indoc! {"
10248        fn main() {
10249            sample(ˇ);
10250        }
10251
10252        fn sample(param1: u8, param2: u8) {}
10253    "});
10254
10255    let mocked_response = lsp::SignatureHelp {
10256        signatures: vec![lsp::SignatureInformation {
10257            label: "fn sample(param1: u8, param2: u8)".to_string(),
10258            documentation: None,
10259            parameters: Some(vec![
10260                lsp::ParameterInformation {
10261                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10262                    documentation: None,
10263                },
10264                lsp::ParameterInformation {
10265                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10266                    documentation: None,
10267                },
10268            ]),
10269            active_parameter: None,
10270        }],
10271        active_signature: Some(0),
10272        active_parameter: Some(0),
10273    };
10274    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10275    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10276        .await;
10277    cx.editor(|editor, _, _| {
10278        assert!(editor.signature_help_state.is_shown());
10279    });
10280
10281    // Restore the popover with more parameter input
10282    cx.set_state(indoc! {"
10283        fn main() {
10284            sample(param1, param2ˇ);
10285        }
10286
10287        fn sample(param1: u8, param2: u8) {}
10288    "});
10289
10290    let mocked_response = lsp::SignatureHelp {
10291        signatures: vec![lsp::SignatureInformation {
10292            label: "fn sample(param1: u8, param2: u8)".to_string(),
10293            documentation: None,
10294            parameters: Some(vec![
10295                lsp::ParameterInformation {
10296                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10297                    documentation: None,
10298                },
10299                lsp::ParameterInformation {
10300                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10301                    documentation: None,
10302                },
10303            ]),
10304            active_parameter: None,
10305        }],
10306        active_signature: Some(0),
10307        active_parameter: Some(1),
10308    };
10309    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10310    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10311        .await;
10312
10313    // When selecting a range, the popover is gone.
10314    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10315    cx.update_editor(|editor, window, cx| {
10316        editor.change_selections(None, window, cx, |s| {
10317            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10318        })
10319    });
10320    cx.assert_editor_state(indoc! {"
10321        fn main() {
10322            sample(param1, «ˇparam2»);
10323        }
10324
10325        fn sample(param1: u8, param2: u8) {}
10326    "});
10327    cx.editor(|editor, _, _| {
10328        assert!(!editor.signature_help_state.is_shown());
10329    });
10330
10331    // When unselecting again, the popover is back if within the brackets.
10332    cx.update_editor(|editor, window, cx| {
10333        editor.change_selections(None, window, cx, |s| {
10334            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10335        })
10336    });
10337    cx.assert_editor_state(indoc! {"
10338        fn main() {
10339            sample(param1, ˇparam2);
10340        }
10341
10342        fn sample(param1: u8, param2: u8) {}
10343    "});
10344    handle_signature_help_request(&mut cx, mocked_response).await;
10345    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10346        .await;
10347    cx.editor(|editor, _, _| {
10348        assert!(editor.signature_help_state.is_shown());
10349    });
10350
10351    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10352    cx.update_editor(|editor, window, cx| {
10353        editor.change_selections(None, window, cx, |s| {
10354            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10355            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10356        })
10357    });
10358    cx.assert_editor_state(indoc! {"
10359        fn main() {
10360            sample(param1, ˇparam2);
10361        }
10362
10363        fn sample(param1: u8, param2: u8) {}
10364    "});
10365
10366    let mocked_response = lsp::SignatureHelp {
10367        signatures: vec![lsp::SignatureInformation {
10368            label: "fn sample(param1: u8, param2: u8)".to_string(),
10369            documentation: None,
10370            parameters: Some(vec![
10371                lsp::ParameterInformation {
10372                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10373                    documentation: None,
10374                },
10375                lsp::ParameterInformation {
10376                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10377                    documentation: None,
10378                },
10379            ]),
10380            active_parameter: None,
10381        }],
10382        active_signature: Some(0),
10383        active_parameter: Some(1),
10384    };
10385    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10386    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10387        .await;
10388    cx.update_editor(|editor, _, cx| {
10389        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10390    });
10391    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10392        .await;
10393    cx.update_editor(|editor, window, cx| {
10394        editor.change_selections(None, window, cx, |s| {
10395            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10396        })
10397    });
10398    cx.assert_editor_state(indoc! {"
10399        fn main() {
10400            sample(param1, «ˇparam2»);
10401        }
10402
10403        fn sample(param1: u8, param2: u8) {}
10404    "});
10405    cx.update_editor(|editor, window, cx| {
10406        editor.change_selections(None, window, cx, |s| {
10407            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10408        })
10409    });
10410    cx.assert_editor_state(indoc! {"
10411        fn main() {
10412            sample(param1, ˇparam2);
10413        }
10414
10415        fn sample(param1: u8, param2: u8) {}
10416    "});
10417    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10418        .await;
10419}
10420
10421#[gpui::test]
10422async fn test_completion_mode(cx: &mut TestAppContext) {
10423    init_test(cx, |_| {});
10424    let mut cx = EditorLspTestContext::new_rust(
10425        lsp::ServerCapabilities {
10426            completion_provider: Some(lsp::CompletionOptions {
10427                resolve_provider: Some(true),
10428                ..Default::default()
10429            }),
10430            ..Default::default()
10431        },
10432        cx,
10433    )
10434    .await;
10435
10436    struct Run {
10437        run_description: &'static str,
10438        initial_state: String,
10439        buffer_marked_text: String,
10440        completion_text: &'static str,
10441        expected_with_insert_mode: String,
10442        expected_with_replace_mode: String,
10443        expected_with_replace_subsequence_mode: String,
10444        expected_with_replace_suffix_mode: String,
10445    }
10446
10447    let runs = [
10448        Run {
10449            run_description: "Start of word matches completion text",
10450            initial_state: "before ediˇ after".into(),
10451            buffer_marked_text: "before <edi|> after".into(),
10452            completion_text: "editor",
10453            expected_with_insert_mode: "before editorˇ after".into(),
10454            expected_with_replace_mode: "before editorˇ after".into(),
10455            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10456            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10457        },
10458        Run {
10459            run_description: "Accept same text at the middle of the word",
10460            initial_state: "before ediˇtor after".into(),
10461            buffer_marked_text: "before <edi|tor> after".into(),
10462            completion_text: "editor",
10463            expected_with_insert_mode: "before editorˇtor after".into(),
10464            expected_with_replace_mode: "before editorˇ after".into(),
10465            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10466            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10467        },
10468        Run {
10469            run_description: "End of word matches completion text -- cursor at end",
10470            initial_state: "before torˇ after".into(),
10471            buffer_marked_text: "before <tor|> after".into(),
10472            completion_text: "editor",
10473            expected_with_insert_mode: "before editorˇ after".into(),
10474            expected_with_replace_mode: "before editorˇ after".into(),
10475            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10476            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10477        },
10478        Run {
10479            run_description: "End of word matches completion text -- cursor at start",
10480            initial_state: "before ˇtor after".into(),
10481            buffer_marked_text: "before <|tor> after".into(),
10482            completion_text: "editor",
10483            expected_with_insert_mode: "before editorˇtor after".into(),
10484            expected_with_replace_mode: "before editorˇ after".into(),
10485            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10486            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10487        },
10488        Run {
10489            run_description: "Prepend text containing whitespace",
10490            initial_state: "pˇfield: bool".into(),
10491            buffer_marked_text: "<p|field>: bool".into(),
10492            completion_text: "pub ",
10493            expected_with_insert_mode: "pub ˇfield: bool".into(),
10494            expected_with_replace_mode: "pub ˇ: bool".into(),
10495            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10496            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10497        },
10498        Run {
10499            run_description: "Add element to start of list",
10500            initial_state: "[element_ˇelement_2]".into(),
10501            buffer_marked_text: "[<element_|element_2>]".into(),
10502            completion_text: "element_1",
10503            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10504            expected_with_replace_mode: "[element_1ˇ]".into(),
10505            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10506            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10507        },
10508        Run {
10509            run_description: "Add element to start of list -- first and second elements are equal",
10510            initial_state: "[elˇelement]".into(),
10511            buffer_marked_text: "[<el|element>]".into(),
10512            completion_text: "element",
10513            expected_with_insert_mode: "[elementˇelement]".into(),
10514            expected_with_replace_mode: "[elementˇ]".into(),
10515            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10516            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10517        },
10518        Run {
10519            run_description: "Ends with matching suffix",
10520            initial_state: "SubˇError".into(),
10521            buffer_marked_text: "<Sub|Error>".into(),
10522            completion_text: "SubscriptionError",
10523            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10524            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10525            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10526            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10527        },
10528        Run {
10529            run_description: "Suffix is a subsequence -- contiguous",
10530            initial_state: "SubˇErr".into(),
10531            buffer_marked_text: "<Sub|Err>".into(),
10532            completion_text: "SubscriptionError",
10533            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10534            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10535            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10536            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10537        },
10538        Run {
10539            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10540            initial_state: "Suˇscrirr".into(),
10541            buffer_marked_text: "<Su|scrirr>".into(),
10542            completion_text: "SubscriptionError",
10543            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10544            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10545            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10546            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10547        },
10548        Run {
10549            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10550            initial_state: "foo(indˇix)".into(),
10551            buffer_marked_text: "foo(<ind|ix>)".into(),
10552            completion_text: "node_index",
10553            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10554            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10555            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10556            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10557        },
10558    ];
10559
10560    for run in runs {
10561        let run_variations = [
10562            (LspInsertMode::Insert, run.expected_with_insert_mode),
10563            (LspInsertMode::Replace, run.expected_with_replace_mode),
10564            (
10565                LspInsertMode::ReplaceSubsequence,
10566                run.expected_with_replace_subsequence_mode,
10567            ),
10568            (
10569                LspInsertMode::ReplaceSuffix,
10570                run.expected_with_replace_suffix_mode,
10571            ),
10572        ];
10573
10574        for (lsp_insert_mode, expected_text) in run_variations {
10575            eprintln!(
10576                "run = {:?}, mode = {lsp_insert_mode:.?}",
10577                run.run_description,
10578            );
10579
10580            update_test_language_settings(&mut cx, |settings| {
10581                settings.defaults.completions = Some(CompletionSettings {
10582                    lsp_insert_mode,
10583                    words: WordsCompletionMode::Disabled,
10584                    lsp: true,
10585                    lsp_fetch_timeout_ms: 0,
10586                });
10587            });
10588
10589            cx.set_state(&run.initial_state);
10590            cx.update_editor(|editor, window, cx| {
10591                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10592            });
10593
10594            let counter = Arc::new(AtomicUsize::new(0));
10595            handle_completion_request_with_insert_and_replace(
10596                &mut cx,
10597                &run.buffer_marked_text,
10598                vec![run.completion_text],
10599                counter.clone(),
10600            )
10601            .await;
10602            cx.condition(|editor, _| editor.context_menu_visible())
10603                .await;
10604            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10605
10606            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10607                editor
10608                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10609                    .unwrap()
10610            });
10611            cx.assert_editor_state(&expected_text);
10612            handle_resolve_completion_request(&mut cx, None).await;
10613            apply_additional_edits.await.unwrap();
10614        }
10615    }
10616}
10617
10618#[gpui::test]
10619async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10620    init_test(cx, |_| {});
10621    let mut cx = EditorLspTestContext::new_rust(
10622        lsp::ServerCapabilities {
10623            completion_provider: Some(lsp::CompletionOptions {
10624                resolve_provider: Some(true),
10625                ..Default::default()
10626            }),
10627            ..Default::default()
10628        },
10629        cx,
10630    )
10631    .await;
10632
10633    let initial_state = "SubˇError";
10634    let buffer_marked_text = "<Sub|Error>";
10635    let completion_text = "SubscriptionError";
10636    let expected_with_insert_mode = "SubscriptionErrorˇError";
10637    let expected_with_replace_mode = "SubscriptionErrorˇ";
10638
10639    update_test_language_settings(&mut cx, |settings| {
10640        settings.defaults.completions = Some(CompletionSettings {
10641            words: WordsCompletionMode::Disabled,
10642            // set the opposite here to ensure that the action is overriding the default behavior
10643            lsp_insert_mode: LspInsertMode::Insert,
10644            lsp: true,
10645            lsp_fetch_timeout_ms: 0,
10646        });
10647    });
10648
10649    cx.set_state(initial_state);
10650    cx.update_editor(|editor, window, cx| {
10651        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10652    });
10653
10654    let counter = Arc::new(AtomicUsize::new(0));
10655    handle_completion_request_with_insert_and_replace(
10656        &mut cx,
10657        &buffer_marked_text,
10658        vec![completion_text],
10659        counter.clone(),
10660    )
10661    .await;
10662    cx.condition(|editor, _| editor.context_menu_visible())
10663        .await;
10664    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10665
10666    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10667        editor
10668            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10669            .unwrap()
10670    });
10671    cx.assert_editor_state(&expected_with_replace_mode);
10672    handle_resolve_completion_request(&mut cx, None).await;
10673    apply_additional_edits.await.unwrap();
10674
10675    update_test_language_settings(&mut cx, |settings| {
10676        settings.defaults.completions = Some(CompletionSettings {
10677            words: WordsCompletionMode::Disabled,
10678            // set the opposite here to ensure that the action is overriding the default behavior
10679            lsp_insert_mode: LspInsertMode::Replace,
10680            lsp: true,
10681            lsp_fetch_timeout_ms: 0,
10682        });
10683    });
10684
10685    cx.set_state(initial_state);
10686    cx.update_editor(|editor, window, cx| {
10687        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10688    });
10689    handle_completion_request_with_insert_and_replace(
10690        &mut cx,
10691        &buffer_marked_text,
10692        vec![completion_text],
10693        counter.clone(),
10694    )
10695    .await;
10696    cx.condition(|editor, _| editor.context_menu_visible())
10697        .await;
10698    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10699
10700    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10701        editor
10702            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10703            .unwrap()
10704    });
10705    cx.assert_editor_state(&expected_with_insert_mode);
10706    handle_resolve_completion_request(&mut cx, None).await;
10707    apply_additional_edits.await.unwrap();
10708}
10709
10710#[gpui::test]
10711async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10712    init_test(cx, |_| {});
10713    let mut cx = EditorLspTestContext::new_rust(
10714        lsp::ServerCapabilities {
10715            completion_provider: Some(lsp::CompletionOptions {
10716                resolve_provider: Some(true),
10717                ..Default::default()
10718            }),
10719            ..Default::default()
10720        },
10721        cx,
10722    )
10723    .await;
10724
10725    // scenario: surrounding text matches completion text
10726    let completion_text = "to_offset";
10727    let initial_state = indoc! {"
10728        1. buf.to_offˇsuffix
10729        2. buf.to_offˇsuf
10730        3. buf.to_offˇfix
10731        4. buf.to_offˇ
10732        5. into_offˇensive
10733        6. ˇsuffix
10734        7. let ˇ //
10735        8. aaˇzz
10736        9. buf.to_off«zzzzzˇ»suffix
10737        10. buf.«ˇzzzzz»suffix
10738        11. to_off«ˇzzzzz»
10739
10740        buf.to_offˇsuffix  // newest cursor
10741    "};
10742    let completion_marked_buffer = indoc! {"
10743        1. buf.to_offsuffix
10744        2. buf.to_offsuf
10745        3. buf.to_offfix
10746        4. buf.to_off
10747        5. into_offensive
10748        6. suffix
10749        7. let  //
10750        8. aazz
10751        9. buf.to_offzzzzzsuffix
10752        10. buf.zzzzzsuffix
10753        11. to_offzzzzz
10754
10755        buf.<to_off|suffix>  // newest cursor
10756    "};
10757    let expected = indoc! {"
10758        1. buf.to_offsetˇ
10759        2. buf.to_offsetˇsuf
10760        3. buf.to_offsetˇfix
10761        4. buf.to_offsetˇ
10762        5. into_offsetˇensive
10763        6. to_offsetˇsuffix
10764        7. let to_offsetˇ //
10765        8. aato_offsetˇzz
10766        9. buf.to_offsetˇ
10767        10. buf.to_offsetˇsuffix
10768        11. to_offsetˇ
10769
10770        buf.to_offsetˇ  // newest cursor
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        completion_marked_buffer,
10779        vec![completion_text],
10780        Arc::new(AtomicUsize::new(0)),
10781    )
10782    .await;
10783    cx.condition(|editor, _| editor.context_menu_visible())
10784        .await;
10785    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10786        editor
10787            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10788            .unwrap()
10789    });
10790    cx.assert_editor_state(expected);
10791    handle_resolve_completion_request(&mut cx, None).await;
10792    apply_additional_edits.await.unwrap();
10793
10794    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10795    let completion_text = "foo_and_bar";
10796    let initial_state = indoc! {"
10797        1. ooanbˇ
10798        2. zooanbˇ
10799        3. ooanbˇz
10800        4. zooanbˇz
10801        5. ooanˇ
10802        6. oanbˇ
10803
10804        ooanbˇ
10805    "};
10806    let completion_marked_buffer = indoc! {"
10807        1. ooanb
10808        2. zooanb
10809        3. ooanbz
10810        4. zooanbz
10811        5. ooan
10812        6. oanb
10813
10814        <ooanb|>
10815    "};
10816    let expected = indoc! {"
10817        1. foo_and_barˇ
10818        2. zfoo_and_barˇ
10819        3. foo_and_barˇz
10820        4. zfoo_and_barˇz
10821        5. ooanfoo_and_barˇ
10822        6. oanbfoo_and_barˇ
10823
10824        foo_and_barˇ
10825    "};
10826    cx.set_state(initial_state);
10827    cx.update_editor(|editor, window, cx| {
10828        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10829    });
10830    handle_completion_request_with_insert_and_replace(
10831        &mut cx,
10832        completion_marked_buffer,
10833        vec![completion_text],
10834        Arc::new(AtomicUsize::new(0)),
10835    )
10836    .await;
10837    cx.condition(|editor, _| editor.context_menu_visible())
10838        .await;
10839    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10840        editor
10841            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10842            .unwrap()
10843    });
10844    cx.assert_editor_state(expected);
10845    handle_resolve_completion_request(&mut cx, None).await;
10846    apply_additional_edits.await.unwrap();
10847
10848    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10849    // (expects the same as if it was inserted at the end)
10850    let completion_text = "foo_and_bar";
10851    let initial_state = indoc! {"
10852        1. ooˇanb
10853        2. zooˇanb
10854        3. ooˇanbz
10855        4. zooˇanbz
10856
10857        ooˇanb
10858    "};
10859    let completion_marked_buffer = indoc! {"
10860        1. ooanb
10861        2. zooanb
10862        3. ooanbz
10863        4. zooanbz
10864
10865        <oo|anb>
10866    "};
10867    let expected = indoc! {"
10868        1. foo_and_barˇ
10869        2. zfoo_and_barˇ
10870        3. foo_and_barˇz
10871        4. zfoo_and_barˇz
10872
10873        foo_and_barˇ
10874    "};
10875    cx.set_state(initial_state);
10876    cx.update_editor(|editor, window, cx| {
10877        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10878    });
10879    handle_completion_request_with_insert_and_replace(
10880        &mut cx,
10881        completion_marked_buffer,
10882        vec![completion_text],
10883        Arc::new(AtomicUsize::new(0)),
10884    )
10885    .await;
10886    cx.condition(|editor, _| editor.context_menu_visible())
10887        .await;
10888    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10889        editor
10890            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10891            .unwrap()
10892    });
10893    cx.assert_editor_state(expected);
10894    handle_resolve_completion_request(&mut cx, None).await;
10895    apply_additional_edits.await.unwrap();
10896}
10897
10898// This used to crash
10899#[gpui::test]
10900async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10901    init_test(cx, |_| {});
10902
10903    let buffer_text = indoc! {"
10904        fn main() {
10905            10.satu;
10906
10907            //
10908            // separate cursors so they open in different excerpts (manually reproducible)
10909            //
10910
10911            10.satu20;
10912        }
10913    "};
10914    let multibuffer_text_with_selections = indoc! {"
10915        fn main() {
10916            10.satuˇ;
10917
10918            //
10919
10920            //
10921
10922            10.satuˇ20;
10923        }
10924    "};
10925    let expected_multibuffer = indoc! {"
10926        fn main() {
10927            10.saturating_sub()ˇ;
10928
10929            //
10930
10931            //
10932
10933            10.saturating_sub()ˇ;
10934        }
10935    "};
10936
10937    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10938    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10939
10940    let fs = FakeFs::new(cx.executor());
10941    fs.insert_tree(
10942        path!("/a"),
10943        json!({
10944            "main.rs": buffer_text,
10945        }),
10946    )
10947    .await;
10948
10949    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10950    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10951    language_registry.add(rust_lang());
10952    let mut fake_servers = language_registry.register_fake_lsp(
10953        "Rust",
10954        FakeLspAdapter {
10955            capabilities: lsp::ServerCapabilities {
10956                completion_provider: Some(lsp::CompletionOptions {
10957                    resolve_provider: None,
10958                    ..lsp::CompletionOptions::default()
10959                }),
10960                ..lsp::ServerCapabilities::default()
10961            },
10962            ..FakeLspAdapter::default()
10963        },
10964    );
10965    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10966    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10967    let buffer = project
10968        .update(cx, |project, cx| {
10969            project.open_local_buffer(path!("/a/main.rs"), cx)
10970        })
10971        .await
10972        .unwrap();
10973
10974    let multi_buffer = cx.new(|cx| {
10975        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10976        multi_buffer.push_excerpts(
10977            buffer.clone(),
10978            [ExcerptRange::new(0..first_excerpt_end)],
10979            cx,
10980        );
10981        multi_buffer.push_excerpts(
10982            buffer.clone(),
10983            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10984            cx,
10985        );
10986        multi_buffer
10987    });
10988
10989    let editor = workspace
10990        .update(cx, |_, window, cx| {
10991            cx.new(|cx| {
10992                Editor::new(
10993                    EditorMode::Full {
10994                        scale_ui_elements_with_buffer_font_size: false,
10995                        show_active_line_background: false,
10996                        sized_by_content: false,
10997                    },
10998                    multi_buffer.clone(),
10999                    Some(project.clone()),
11000                    window,
11001                    cx,
11002                )
11003            })
11004        })
11005        .unwrap();
11006
11007    let pane = workspace
11008        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11009        .unwrap();
11010    pane.update_in(cx, |pane, window, cx| {
11011        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
11012    });
11013
11014    let fake_server = fake_servers.next().await.unwrap();
11015
11016    editor.update_in(cx, |editor, window, cx| {
11017        editor.change_selections(None, window, cx, |s| {
11018            s.select_ranges([
11019                Point::new(1, 11)..Point::new(1, 11),
11020                Point::new(7, 11)..Point::new(7, 11),
11021            ])
11022        });
11023
11024        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
11025    });
11026
11027    editor.update_in(cx, |editor, window, cx| {
11028        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11029    });
11030
11031    fake_server
11032        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11033            let completion_item = lsp::CompletionItem {
11034                label: "saturating_sub()".into(),
11035                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11036                    lsp::InsertReplaceEdit {
11037                        new_text: "saturating_sub()".to_owned(),
11038                        insert: lsp::Range::new(
11039                            lsp::Position::new(7, 7),
11040                            lsp::Position::new(7, 11),
11041                        ),
11042                        replace: lsp::Range::new(
11043                            lsp::Position::new(7, 7),
11044                            lsp::Position::new(7, 13),
11045                        ),
11046                    },
11047                )),
11048                ..lsp::CompletionItem::default()
11049            };
11050
11051            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
11052        })
11053        .next()
11054        .await
11055        .unwrap();
11056
11057    cx.condition(&editor, |editor, _| editor.context_menu_visible())
11058        .await;
11059
11060    editor
11061        .update_in(cx, |editor, window, cx| {
11062            editor
11063                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
11064                .unwrap()
11065        })
11066        .await
11067        .unwrap();
11068
11069    editor.update(cx, |editor, cx| {
11070        assert_text_with_selections(editor, expected_multibuffer, cx);
11071    })
11072}
11073
11074#[gpui::test]
11075async fn test_completion(cx: &mut TestAppContext) {
11076    init_test(cx, |_| {});
11077
11078    let mut cx = EditorLspTestContext::new_rust(
11079        lsp::ServerCapabilities {
11080            completion_provider: Some(lsp::CompletionOptions {
11081                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11082                resolve_provider: Some(true),
11083                ..Default::default()
11084            }),
11085            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11086            ..Default::default()
11087        },
11088        cx,
11089    )
11090    .await;
11091    let counter = Arc::new(AtomicUsize::new(0));
11092
11093    cx.set_state(indoc! {"
11094        oneˇ
11095        two
11096        three
11097    "});
11098    cx.simulate_keystroke(".");
11099    handle_completion_request(
11100        &mut cx,
11101        indoc! {"
11102            one.|<>
11103            two
11104            three
11105        "},
11106        vec!["first_completion", "second_completion"],
11107        counter.clone(),
11108    )
11109    .await;
11110    cx.condition(|editor, _| editor.context_menu_visible())
11111        .await;
11112    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11113
11114    let _handler = handle_signature_help_request(
11115        &mut cx,
11116        lsp::SignatureHelp {
11117            signatures: vec![lsp::SignatureInformation {
11118                label: "test signature".to_string(),
11119                documentation: None,
11120                parameters: Some(vec![lsp::ParameterInformation {
11121                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
11122                    documentation: None,
11123                }]),
11124                active_parameter: None,
11125            }],
11126            active_signature: None,
11127            active_parameter: None,
11128        },
11129    );
11130    cx.update_editor(|editor, window, cx| {
11131        assert!(
11132            !editor.signature_help_state.is_shown(),
11133            "No signature help was called for"
11134        );
11135        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11136    });
11137    cx.run_until_parked();
11138    cx.update_editor(|editor, _, _| {
11139        assert!(
11140            !editor.signature_help_state.is_shown(),
11141            "No signature help should be shown when completions menu is open"
11142        );
11143    });
11144
11145    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11146        editor.context_menu_next(&Default::default(), window, cx);
11147        editor
11148            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11149            .unwrap()
11150    });
11151    cx.assert_editor_state(indoc! {"
11152        one.second_completionˇ
11153        two
11154        three
11155    "});
11156
11157    handle_resolve_completion_request(
11158        &mut cx,
11159        Some(vec![
11160            (
11161                //This overlaps with the primary completion edit which is
11162                //misbehavior from the LSP spec, test that we filter it out
11163                indoc! {"
11164                    one.second_ˇcompletion
11165                    two
11166                    threeˇ
11167                "},
11168                "overlapping additional edit",
11169            ),
11170            (
11171                indoc! {"
11172                    one.second_completion
11173                    two
11174                    threeˇ
11175                "},
11176                "\nadditional edit",
11177            ),
11178        ]),
11179    )
11180    .await;
11181    apply_additional_edits.await.unwrap();
11182    cx.assert_editor_state(indoc! {"
11183        one.second_completionˇ
11184        two
11185        three
11186        additional edit
11187    "});
11188
11189    cx.set_state(indoc! {"
11190        one.second_completion
11191        twoˇ
11192        threeˇ
11193        additional edit
11194    "});
11195    cx.simulate_keystroke(" ");
11196    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11197    cx.simulate_keystroke("s");
11198    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11199
11200    cx.assert_editor_state(indoc! {"
11201        one.second_completion
11202        two sˇ
11203        three sˇ
11204        additional edit
11205    "});
11206    handle_completion_request(
11207        &mut cx,
11208        indoc! {"
11209            one.second_completion
11210            two s
11211            three <s|>
11212            additional edit
11213        "},
11214        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11215        counter.clone(),
11216    )
11217    .await;
11218    cx.condition(|editor, _| editor.context_menu_visible())
11219        .await;
11220    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11221
11222    cx.simulate_keystroke("i");
11223
11224    handle_completion_request(
11225        &mut cx,
11226        indoc! {"
11227            one.second_completion
11228            two si
11229            three <si|>
11230            additional edit
11231        "},
11232        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11233        counter.clone(),
11234    )
11235    .await;
11236    cx.condition(|editor, _| editor.context_menu_visible())
11237        .await;
11238    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11239
11240    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11241        editor
11242            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11243            .unwrap()
11244    });
11245    cx.assert_editor_state(indoc! {"
11246        one.second_completion
11247        two sixth_completionˇ
11248        three sixth_completionˇ
11249        additional edit
11250    "});
11251
11252    apply_additional_edits.await.unwrap();
11253
11254    update_test_language_settings(&mut cx, |settings| {
11255        settings.defaults.show_completions_on_input = Some(false);
11256    });
11257    cx.set_state("editorˇ");
11258    cx.simulate_keystroke(".");
11259    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11260    cx.simulate_keystrokes("c l o");
11261    cx.assert_editor_state("editor.cloˇ");
11262    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11263    cx.update_editor(|editor, window, cx| {
11264        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11265    });
11266    handle_completion_request(
11267        &mut cx,
11268        "editor.<clo|>",
11269        vec!["close", "clobber"],
11270        counter.clone(),
11271    )
11272    .await;
11273    cx.condition(|editor, _| editor.context_menu_visible())
11274        .await;
11275    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11276
11277    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11278        editor
11279            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11280            .unwrap()
11281    });
11282    cx.assert_editor_state("editor.closeˇ");
11283    handle_resolve_completion_request(&mut cx, None).await;
11284    apply_additional_edits.await.unwrap();
11285}
11286
11287#[gpui::test]
11288async fn test_word_completion(cx: &mut TestAppContext) {
11289    let lsp_fetch_timeout_ms = 10;
11290    init_test(cx, |language_settings| {
11291        language_settings.defaults.completions = Some(CompletionSettings {
11292            words: WordsCompletionMode::Fallback,
11293            lsp: true,
11294            lsp_fetch_timeout_ms: 10,
11295            lsp_insert_mode: LspInsertMode::Insert,
11296        });
11297    });
11298
11299    let mut cx = EditorLspTestContext::new_rust(
11300        lsp::ServerCapabilities {
11301            completion_provider: Some(lsp::CompletionOptions {
11302                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11303                ..lsp::CompletionOptions::default()
11304            }),
11305            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11306            ..lsp::ServerCapabilities::default()
11307        },
11308        cx,
11309    )
11310    .await;
11311
11312    let throttle_completions = Arc::new(AtomicBool::new(false));
11313
11314    let lsp_throttle_completions = throttle_completions.clone();
11315    let _completion_requests_handler =
11316        cx.lsp
11317            .server
11318            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11319                let lsp_throttle_completions = lsp_throttle_completions.clone();
11320                let cx = cx.clone();
11321                async move {
11322                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11323                        cx.background_executor()
11324                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11325                            .await;
11326                    }
11327                    Ok(Some(lsp::CompletionResponse::Array(vec![
11328                        lsp::CompletionItem {
11329                            label: "first".into(),
11330                            ..lsp::CompletionItem::default()
11331                        },
11332                        lsp::CompletionItem {
11333                            label: "last".into(),
11334                            ..lsp::CompletionItem::default()
11335                        },
11336                    ])))
11337                }
11338            });
11339
11340    cx.set_state(indoc! {"
11341        oneˇ
11342        two
11343        three
11344    "});
11345    cx.simulate_keystroke(".");
11346    cx.executor().run_until_parked();
11347    cx.condition(|editor, _| editor.context_menu_visible())
11348        .await;
11349    cx.update_editor(|editor, window, cx| {
11350        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11351        {
11352            assert_eq!(
11353                completion_menu_entries(&menu),
11354                &["first", "last"],
11355                "When LSP server is fast to reply, no fallback word completions are used"
11356            );
11357        } else {
11358            panic!("expected completion menu to be open");
11359        }
11360        editor.cancel(&Cancel, window, cx);
11361    });
11362    cx.executor().run_until_parked();
11363    cx.condition(|editor, _| !editor.context_menu_visible())
11364        .await;
11365
11366    throttle_completions.store(true, atomic::Ordering::Release);
11367    cx.simulate_keystroke(".");
11368    cx.executor()
11369        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11370    cx.executor().run_until_parked();
11371    cx.condition(|editor, _| editor.context_menu_visible())
11372        .await;
11373    cx.update_editor(|editor, _, _| {
11374        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11375        {
11376            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11377                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11378        } else {
11379            panic!("expected completion menu to be open");
11380        }
11381    });
11382}
11383
11384#[gpui::test]
11385async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11386    init_test(cx, |language_settings| {
11387        language_settings.defaults.completions = Some(CompletionSettings {
11388            words: WordsCompletionMode::Enabled,
11389            lsp: true,
11390            lsp_fetch_timeout_ms: 0,
11391            lsp_insert_mode: LspInsertMode::Insert,
11392        });
11393    });
11394
11395    let mut cx = EditorLspTestContext::new_rust(
11396        lsp::ServerCapabilities {
11397            completion_provider: Some(lsp::CompletionOptions {
11398                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11399                ..lsp::CompletionOptions::default()
11400            }),
11401            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11402            ..lsp::ServerCapabilities::default()
11403        },
11404        cx,
11405    )
11406    .await;
11407
11408    let _completion_requests_handler =
11409        cx.lsp
11410            .server
11411            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11412                Ok(Some(lsp::CompletionResponse::Array(vec![
11413                    lsp::CompletionItem {
11414                        label: "first".into(),
11415                        ..lsp::CompletionItem::default()
11416                    },
11417                    lsp::CompletionItem {
11418                        label: "last".into(),
11419                        ..lsp::CompletionItem::default()
11420                    },
11421                ])))
11422            });
11423
11424    cx.set_state(indoc! {"ˇ
11425        first
11426        last
11427        second
11428    "});
11429    cx.simulate_keystroke(".");
11430    cx.executor().run_until_parked();
11431    cx.condition(|editor, _| editor.context_menu_visible())
11432        .await;
11433    cx.update_editor(|editor, _, _| {
11434        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11435        {
11436            assert_eq!(
11437                completion_menu_entries(&menu),
11438                &["first", "last", "second"],
11439                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11440            );
11441        } else {
11442            panic!("expected completion menu to be open");
11443        }
11444    });
11445}
11446
11447#[gpui::test]
11448async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11449    init_test(cx, |language_settings| {
11450        language_settings.defaults.completions = Some(CompletionSettings {
11451            words: WordsCompletionMode::Disabled,
11452            lsp: true,
11453            lsp_fetch_timeout_ms: 0,
11454            lsp_insert_mode: LspInsertMode::Insert,
11455        });
11456    });
11457
11458    let mut cx = EditorLspTestContext::new_rust(
11459        lsp::ServerCapabilities {
11460            completion_provider: Some(lsp::CompletionOptions {
11461                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11462                ..lsp::CompletionOptions::default()
11463            }),
11464            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11465            ..lsp::ServerCapabilities::default()
11466        },
11467        cx,
11468    )
11469    .await;
11470
11471    let _completion_requests_handler =
11472        cx.lsp
11473            .server
11474            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11475                panic!("LSP completions should not be queried when dealing with word completions")
11476            });
11477
11478    cx.set_state(indoc! {"ˇ
11479        first
11480        last
11481        second
11482    "});
11483    cx.update_editor(|editor, window, cx| {
11484        editor.show_word_completions(&ShowWordCompletions, window, cx);
11485    });
11486    cx.executor().run_until_parked();
11487    cx.condition(|editor, _| editor.context_menu_visible())
11488        .await;
11489    cx.update_editor(|editor, _, _| {
11490        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11491        {
11492            assert_eq!(
11493                completion_menu_entries(&menu),
11494                &["first", "last", "second"],
11495                "`ShowWordCompletions` action should show word completions"
11496            );
11497        } else {
11498            panic!("expected completion menu to be open");
11499        }
11500    });
11501
11502    cx.simulate_keystroke("l");
11503    cx.executor().run_until_parked();
11504    cx.condition(|editor, _| editor.context_menu_visible())
11505        .await;
11506    cx.update_editor(|editor, _, _| {
11507        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11508        {
11509            assert_eq!(
11510                completion_menu_entries(&menu),
11511                &["last"],
11512                "After showing word completions, further editing should filter them and not query the LSP"
11513            );
11514        } else {
11515            panic!("expected completion menu to be open");
11516        }
11517    });
11518}
11519
11520#[gpui::test]
11521async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11522    init_test(cx, |language_settings| {
11523        language_settings.defaults.completions = Some(CompletionSettings {
11524            words: WordsCompletionMode::Fallback,
11525            lsp: false,
11526            lsp_fetch_timeout_ms: 0,
11527            lsp_insert_mode: LspInsertMode::Insert,
11528        });
11529    });
11530
11531    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11532
11533    cx.set_state(indoc! {"ˇ
11534        0_usize
11535        let
11536        33
11537        4.5f32
11538    "});
11539    cx.update_editor(|editor, window, cx| {
11540        editor.show_completions(&ShowCompletions::default(), window, cx);
11541    });
11542    cx.executor().run_until_parked();
11543    cx.condition(|editor, _| editor.context_menu_visible())
11544        .await;
11545    cx.update_editor(|editor, window, cx| {
11546        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11547        {
11548            assert_eq!(
11549                completion_menu_entries(&menu),
11550                &["let"],
11551                "With no digits in the completion query, no digits should be in the word completions"
11552            );
11553        } else {
11554            panic!("expected completion menu to be open");
11555        }
11556        editor.cancel(&Cancel, window, cx);
11557    });
11558
11559    cx.set_state(indoc! {"11560        0_usize
11561        let
11562        3
11563        33.35f32
11564    "});
11565    cx.update_editor(|editor, window, cx| {
11566        editor.show_completions(&ShowCompletions::default(), window, cx);
11567    });
11568    cx.executor().run_until_parked();
11569    cx.condition(|editor, _| editor.context_menu_visible())
11570        .await;
11571    cx.update_editor(|editor, _, _| {
11572        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11573        {
11574            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11575                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11576        } else {
11577            panic!("expected completion menu to be open");
11578        }
11579    });
11580}
11581
11582fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11583    let position = || lsp::Position {
11584        line: params.text_document_position.position.line,
11585        character: params.text_document_position.position.character,
11586    };
11587    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11588        range: lsp::Range {
11589            start: position(),
11590            end: position(),
11591        },
11592        new_text: text.to_string(),
11593    }))
11594}
11595
11596#[gpui::test]
11597async fn test_multiline_completion(cx: &mut TestAppContext) {
11598    init_test(cx, |_| {});
11599
11600    let fs = FakeFs::new(cx.executor());
11601    fs.insert_tree(
11602        path!("/a"),
11603        json!({
11604            "main.ts": "a",
11605        }),
11606    )
11607    .await;
11608
11609    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11610    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11611    let typescript_language = Arc::new(Language::new(
11612        LanguageConfig {
11613            name: "TypeScript".into(),
11614            matcher: LanguageMatcher {
11615                path_suffixes: vec!["ts".to_string()],
11616                ..LanguageMatcher::default()
11617            },
11618            line_comments: vec!["// ".into()],
11619            ..LanguageConfig::default()
11620        },
11621        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11622    ));
11623    language_registry.add(typescript_language.clone());
11624    let mut fake_servers = language_registry.register_fake_lsp(
11625        "TypeScript",
11626        FakeLspAdapter {
11627            capabilities: lsp::ServerCapabilities {
11628                completion_provider: Some(lsp::CompletionOptions {
11629                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11630                    ..lsp::CompletionOptions::default()
11631                }),
11632                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11633                ..lsp::ServerCapabilities::default()
11634            },
11635            // Emulate vtsls label generation
11636            label_for_completion: Some(Box::new(|item, _| {
11637                let text = if let Some(description) = item
11638                    .label_details
11639                    .as_ref()
11640                    .and_then(|label_details| label_details.description.as_ref())
11641                {
11642                    format!("{} {}", item.label, description)
11643                } else if let Some(detail) = &item.detail {
11644                    format!("{} {}", item.label, detail)
11645                } else {
11646                    item.label.clone()
11647                };
11648                let len = text.len();
11649                Some(language::CodeLabel {
11650                    text,
11651                    runs: Vec::new(),
11652                    filter_range: 0..len,
11653                })
11654            })),
11655            ..FakeLspAdapter::default()
11656        },
11657    );
11658    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11659    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11660    let worktree_id = workspace
11661        .update(cx, |workspace, _window, cx| {
11662            workspace.project().update(cx, |project, cx| {
11663                project.worktrees(cx).next().unwrap().read(cx).id()
11664            })
11665        })
11666        .unwrap();
11667    let _buffer = project
11668        .update(cx, |project, cx| {
11669            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11670        })
11671        .await
11672        .unwrap();
11673    let editor = workspace
11674        .update(cx, |workspace, window, cx| {
11675            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11676        })
11677        .unwrap()
11678        .await
11679        .unwrap()
11680        .downcast::<Editor>()
11681        .unwrap();
11682    let fake_server = fake_servers.next().await.unwrap();
11683
11684    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11685    let multiline_label_2 = "a\nb\nc\n";
11686    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11687    let multiline_description = "d\ne\nf\n";
11688    let multiline_detail_2 = "g\nh\ni\n";
11689
11690    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11691        move |params, _| async move {
11692            Ok(Some(lsp::CompletionResponse::Array(vec![
11693                lsp::CompletionItem {
11694                    label: multiline_label.to_string(),
11695                    text_edit: gen_text_edit(&params, "new_text_1"),
11696                    ..lsp::CompletionItem::default()
11697                },
11698                lsp::CompletionItem {
11699                    label: "single line label 1".to_string(),
11700                    detail: Some(multiline_detail.to_string()),
11701                    text_edit: gen_text_edit(&params, "new_text_2"),
11702                    ..lsp::CompletionItem::default()
11703                },
11704                lsp::CompletionItem {
11705                    label: "single line label 2".to_string(),
11706                    label_details: Some(lsp::CompletionItemLabelDetails {
11707                        description: Some(multiline_description.to_string()),
11708                        detail: None,
11709                    }),
11710                    text_edit: gen_text_edit(&params, "new_text_2"),
11711                    ..lsp::CompletionItem::default()
11712                },
11713                lsp::CompletionItem {
11714                    label: multiline_label_2.to_string(),
11715                    detail: Some(multiline_detail_2.to_string()),
11716                    text_edit: gen_text_edit(&params, "new_text_3"),
11717                    ..lsp::CompletionItem::default()
11718                },
11719                lsp::CompletionItem {
11720                    label: "Label with many     spaces and \t but without newlines".to_string(),
11721                    detail: Some(
11722                        "Details with many     spaces and \t but without newlines".to_string(),
11723                    ),
11724                    text_edit: gen_text_edit(&params, "new_text_4"),
11725                    ..lsp::CompletionItem::default()
11726                },
11727            ])))
11728        },
11729    );
11730
11731    editor.update_in(cx, |editor, window, cx| {
11732        cx.focus_self(window);
11733        editor.move_to_end(&MoveToEnd, window, cx);
11734        editor.handle_input(".", window, cx);
11735    });
11736    cx.run_until_parked();
11737    completion_handle.next().await.unwrap();
11738
11739    editor.update(cx, |editor, _| {
11740        assert!(editor.context_menu_visible());
11741        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11742        {
11743            let completion_labels = menu
11744                .completions
11745                .borrow()
11746                .iter()
11747                .map(|c| c.label.text.clone())
11748                .collect::<Vec<_>>();
11749            assert_eq!(
11750                completion_labels,
11751                &[
11752                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11753                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11754                    "single line label 2 d e f ",
11755                    "a b c g h i ",
11756                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11757                ],
11758                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11759            );
11760
11761            for completion in menu
11762                .completions
11763                .borrow()
11764                .iter() {
11765                    assert_eq!(
11766                        completion.label.filter_range,
11767                        0..completion.label.text.len(),
11768                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11769                    );
11770                }
11771        } else {
11772            panic!("expected completion menu to be open");
11773        }
11774    });
11775}
11776
11777#[gpui::test]
11778async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11779    init_test(cx, |_| {});
11780    let mut cx = EditorLspTestContext::new_rust(
11781        lsp::ServerCapabilities {
11782            completion_provider: Some(lsp::CompletionOptions {
11783                trigger_characters: Some(vec![".".to_string()]),
11784                ..Default::default()
11785            }),
11786            ..Default::default()
11787        },
11788        cx,
11789    )
11790    .await;
11791    cx.lsp
11792        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11793            Ok(Some(lsp::CompletionResponse::Array(vec![
11794                lsp::CompletionItem {
11795                    label: "first".into(),
11796                    ..Default::default()
11797                },
11798                lsp::CompletionItem {
11799                    label: "last".into(),
11800                    ..Default::default()
11801                },
11802            ])))
11803        });
11804    cx.set_state("variableˇ");
11805    cx.simulate_keystroke(".");
11806    cx.executor().run_until_parked();
11807
11808    cx.update_editor(|editor, _, _| {
11809        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11810        {
11811            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11812        } else {
11813            panic!("expected completion menu to be open");
11814        }
11815    });
11816
11817    cx.update_editor(|editor, window, cx| {
11818        editor.move_page_down(&MovePageDown::default(), window, cx);
11819        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11820        {
11821            assert!(
11822                menu.selected_item == 1,
11823                "expected PageDown to select the last item from the context menu"
11824            );
11825        } else {
11826            panic!("expected completion menu to stay open after PageDown");
11827        }
11828    });
11829
11830    cx.update_editor(|editor, window, cx| {
11831        editor.move_page_up(&MovePageUp::default(), window, cx);
11832        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11833        {
11834            assert!(
11835                menu.selected_item == 0,
11836                "expected PageUp to select the first item from the context menu"
11837            );
11838        } else {
11839            panic!("expected completion menu to stay open after PageUp");
11840        }
11841    });
11842}
11843
11844#[gpui::test]
11845async fn test_as_is_completions(cx: &mut TestAppContext) {
11846    init_test(cx, |_| {});
11847    let mut cx = EditorLspTestContext::new_rust(
11848        lsp::ServerCapabilities {
11849            completion_provider: Some(lsp::CompletionOptions {
11850                ..Default::default()
11851            }),
11852            ..Default::default()
11853        },
11854        cx,
11855    )
11856    .await;
11857    cx.lsp
11858        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11859            Ok(Some(lsp::CompletionResponse::Array(vec![
11860                lsp::CompletionItem {
11861                    label: "unsafe".into(),
11862                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11863                        range: lsp::Range {
11864                            start: lsp::Position {
11865                                line: 1,
11866                                character: 2,
11867                            },
11868                            end: lsp::Position {
11869                                line: 1,
11870                                character: 3,
11871                            },
11872                        },
11873                        new_text: "unsafe".to_string(),
11874                    })),
11875                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11876                    ..Default::default()
11877                },
11878            ])))
11879        });
11880    cx.set_state("fn a() {}\n");
11881    cx.executor().run_until_parked();
11882    cx.update_editor(|editor, window, cx| {
11883        editor.show_completions(
11884            &ShowCompletions {
11885                trigger: Some("\n".into()),
11886            },
11887            window,
11888            cx,
11889        );
11890    });
11891    cx.executor().run_until_parked();
11892
11893    cx.update_editor(|editor, window, cx| {
11894        editor.confirm_completion(&Default::default(), window, cx)
11895    });
11896    cx.executor().run_until_parked();
11897    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11898}
11899
11900#[gpui::test]
11901async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11902    init_test(cx, |_| {});
11903
11904    let mut cx = EditorLspTestContext::new_rust(
11905        lsp::ServerCapabilities {
11906            completion_provider: Some(lsp::CompletionOptions {
11907                trigger_characters: Some(vec![".".to_string()]),
11908                resolve_provider: Some(true),
11909                ..Default::default()
11910            }),
11911            ..Default::default()
11912        },
11913        cx,
11914    )
11915    .await;
11916
11917    cx.set_state("fn main() { let a = 2ˇ; }");
11918    cx.simulate_keystroke(".");
11919    let completion_item = lsp::CompletionItem {
11920        label: "Some".into(),
11921        kind: Some(lsp::CompletionItemKind::SNIPPET),
11922        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11923        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11924            kind: lsp::MarkupKind::Markdown,
11925            value: "```rust\nSome(2)\n```".to_string(),
11926        })),
11927        deprecated: Some(false),
11928        sort_text: Some("Some".to_string()),
11929        filter_text: Some("Some".to_string()),
11930        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11931        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11932            range: lsp::Range {
11933                start: lsp::Position {
11934                    line: 0,
11935                    character: 22,
11936                },
11937                end: lsp::Position {
11938                    line: 0,
11939                    character: 22,
11940                },
11941            },
11942            new_text: "Some(2)".to_string(),
11943        })),
11944        additional_text_edits: Some(vec![lsp::TextEdit {
11945            range: lsp::Range {
11946                start: lsp::Position {
11947                    line: 0,
11948                    character: 20,
11949                },
11950                end: lsp::Position {
11951                    line: 0,
11952                    character: 22,
11953                },
11954            },
11955            new_text: "".to_string(),
11956        }]),
11957        ..Default::default()
11958    };
11959
11960    let closure_completion_item = completion_item.clone();
11961    let counter = Arc::new(AtomicUsize::new(0));
11962    let counter_clone = counter.clone();
11963    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11964        let task_completion_item = closure_completion_item.clone();
11965        counter_clone.fetch_add(1, atomic::Ordering::Release);
11966        async move {
11967            Ok(Some(lsp::CompletionResponse::Array(vec![
11968                task_completion_item,
11969            ])))
11970        }
11971    });
11972
11973    cx.condition(|editor, _| editor.context_menu_visible())
11974        .await;
11975    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11976    assert!(request.next().await.is_some());
11977    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11978
11979    cx.simulate_keystrokes("S o m");
11980    cx.condition(|editor, _| editor.context_menu_visible())
11981        .await;
11982    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11983    assert!(request.next().await.is_some());
11984    assert!(request.next().await.is_some());
11985    assert!(request.next().await.is_some());
11986    request.close();
11987    assert!(request.next().await.is_none());
11988    assert_eq!(
11989        counter.load(atomic::Ordering::Acquire),
11990        4,
11991        "With the completions menu open, only one LSP request should happen per input"
11992    );
11993}
11994
11995#[gpui::test]
11996async fn test_toggle_comment(cx: &mut TestAppContext) {
11997    init_test(cx, |_| {});
11998    let mut cx = EditorTestContext::new(cx).await;
11999    let language = Arc::new(Language::new(
12000        LanguageConfig {
12001            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12002            ..Default::default()
12003        },
12004        Some(tree_sitter_rust::LANGUAGE.into()),
12005    ));
12006    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12007
12008    // If multiple selections intersect a line, the line is only toggled once.
12009    cx.set_state(indoc! {"
12010        fn a() {
12011            «//b();
12012            ˇ»// «c();
12013            //ˇ»  d();
12014        }
12015    "});
12016
12017    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12018
12019    cx.assert_editor_state(indoc! {"
12020        fn a() {
12021            «b();
12022            c();
12023            ˇ» d();
12024        }
12025    "});
12026
12027    // The comment prefix is inserted at the same column for every line in a
12028    // selection.
12029    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12030
12031    cx.assert_editor_state(indoc! {"
12032        fn a() {
12033            // «b();
12034            // c();
12035            ˇ»//  d();
12036        }
12037    "});
12038
12039    // If a selection ends at the beginning of a line, that line is not toggled.
12040    cx.set_selections_state(indoc! {"
12041        fn a() {
12042            // b();
12043            «// c();
12044        ˇ»    //  d();
12045        }
12046    "});
12047
12048    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12049
12050    cx.assert_editor_state(indoc! {"
12051        fn a() {
12052            // b();
12053            «c();
12054        ˇ»    //  d();
12055        }
12056    "});
12057
12058    // If a selection span a single line and is empty, the line is toggled.
12059    cx.set_state(indoc! {"
12060        fn a() {
12061            a();
12062            b();
12063        ˇ
12064        }
12065    "});
12066
12067    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12068
12069    cx.assert_editor_state(indoc! {"
12070        fn a() {
12071            a();
12072            b();
12073        //•ˇ
12074        }
12075    "});
12076
12077    // If a selection span multiple lines, empty lines are not toggled.
12078    cx.set_state(indoc! {"
12079        fn a() {
12080            «a();
12081
12082            c();ˇ»
12083        }
12084    "});
12085
12086    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12087
12088    cx.assert_editor_state(indoc! {"
12089        fn a() {
12090            // «a();
12091
12092            // c();ˇ»
12093        }
12094    "});
12095
12096    // If a selection includes multiple comment prefixes, all lines are uncommented.
12097    cx.set_state(indoc! {"
12098        fn a() {
12099            «// a();
12100            /// b();
12101            //! c();ˇ»
12102        }
12103    "});
12104
12105    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12106
12107    cx.assert_editor_state(indoc! {"
12108        fn a() {
12109            «a();
12110            b();
12111            c();ˇ»
12112        }
12113    "});
12114}
12115
12116#[gpui::test]
12117async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
12118    init_test(cx, |_| {});
12119    let mut cx = EditorTestContext::new(cx).await;
12120    let language = Arc::new(Language::new(
12121        LanguageConfig {
12122            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12123            ..Default::default()
12124        },
12125        Some(tree_sitter_rust::LANGUAGE.into()),
12126    ));
12127    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12128
12129    let toggle_comments = &ToggleComments {
12130        advance_downwards: false,
12131        ignore_indent: true,
12132    };
12133
12134    // If multiple selections intersect a line, the line is only toggled once.
12135    cx.set_state(indoc! {"
12136        fn a() {
12137        //    «b();
12138        //    c();
12139        //    ˇ» d();
12140        }
12141    "});
12142
12143    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12144
12145    cx.assert_editor_state(indoc! {"
12146        fn a() {
12147            «b();
12148            c();
12149            ˇ» d();
12150        }
12151    "});
12152
12153    // The comment prefix is inserted at the beginning of each line
12154    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12155
12156    cx.assert_editor_state(indoc! {"
12157        fn a() {
12158        //    «b();
12159        //    c();
12160        //    ˇ» d();
12161        }
12162    "});
12163
12164    // If a selection ends at the beginning of a line, that line is not toggled.
12165    cx.set_selections_state(indoc! {"
12166        fn a() {
12167        //    b();
12168        //    «c();
12169        ˇ»//     d();
12170        }
12171    "});
12172
12173    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12174
12175    cx.assert_editor_state(indoc! {"
12176        fn a() {
12177        //    b();
12178            «c();
12179        ˇ»//     d();
12180        }
12181    "});
12182
12183    // If a selection span a single line and is empty, the line is toggled.
12184    cx.set_state(indoc! {"
12185        fn a() {
12186            a();
12187            b();
12188        ˇ
12189        }
12190    "});
12191
12192    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12193
12194    cx.assert_editor_state(indoc! {"
12195        fn a() {
12196            a();
12197            b();
12198        //ˇ
12199        }
12200    "});
12201
12202    // If a selection span multiple lines, empty lines are not toggled.
12203    cx.set_state(indoc! {"
12204        fn a() {
12205            «a();
12206
12207            c();ˇ»
12208        }
12209    "});
12210
12211    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12212
12213    cx.assert_editor_state(indoc! {"
12214        fn a() {
12215        //    «a();
12216
12217        //    c();ˇ»
12218        }
12219    "});
12220
12221    // If a selection includes multiple comment prefixes, all lines are uncommented.
12222    cx.set_state(indoc! {"
12223        fn a() {
12224        //    «a();
12225        ///    b();
12226        //!    c();ˇ»
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            «a();
12235            b();
12236            c();ˇ»
12237        }
12238    "});
12239}
12240
12241#[gpui::test]
12242async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12243    init_test(cx, |_| {});
12244
12245    let language = Arc::new(Language::new(
12246        LanguageConfig {
12247            line_comments: vec!["// ".into()],
12248            ..Default::default()
12249        },
12250        Some(tree_sitter_rust::LANGUAGE.into()),
12251    ));
12252
12253    let mut cx = EditorTestContext::new(cx).await;
12254
12255    cx.language_registry().add(language.clone());
12256    cx.update_buffer(|buffer, cx| {
12257        buffer.set_language(Some(language), cx);
12258    });
12259
12260    let toggle_comments = &ToggleComments {
12261        advance_downwards: true,
12262        ignore_indent: false,
12263    };
12264
12265    // Single cursor on one line -> advance
12266    // Cursor moves horizontally 3 characters as well on non-blank line
12267    cx.set_state(indoc!(
12268        "fn a() {
12269             ˇdog();
12270             cat();
12271        }"
12272    ));
12273    cx.update_editor(|editor, window, cx| {
12274        editor.toggle_comments(toggle_comments, window, cx);
12275    });
12276    cx.assert_editor_state(indoc!(
12277        "fn a() {
12278             // dog();
12279             catˇ();
12280        }"
12281    ));
12282
12283    // Single selection on one line -> don't advance
12284    cx.set_state(indoc!(
12285        "fn a() {
12286             «dog()ˇ»;
12287             cat();
12288        }"
12289    ));
12290    cx.update_editor(|editor, window, cx| {
12291        editor.toggle_comments(toggle_comments, window, cx);
12292    });
12293    cx.assert_editor_state(indoc!(
12294        "fn a() {
12295             // «dog()ˇ»;
12296             cat();
12297        }"
12298    ));
12299
12300    // Multiple cursors on one line -> advance
12301    cx.set_state(indoc!(
12302        "fn a() {
12303             ˇdˇog();
12304             cat();
12305        }"
12306    ));
12307    cx.update_editor(|editor, window, cx| {
12308        editor.toggle_comments(toggle_comments, window, cx);
12309    });
12310    cx.assert_editor_state(indoc!(
12311        "fn a() {
12312             // dog();
12313             catˇ(ˇ);
12314        }"
12315    ));
12316
12317    // Multiple cursors on one line, with selection -> don't advance
12318    cx.set_state(indoc!(
12319        "fn a() {
12320             ˇdˇog«()ˇ»;
12321             cat();
12322        }"
12323    ));
12324    cx.update_editor(|editor, window, cx| {
12325        editor.toggle_comments(toggle_comments, window, cx);
12326    });
12327    cx.assert_editor_state(indoc!(
12328        "fn a() {
12329             // ˇdˇog«()ˇ»;
12330             cat();
12331        }"
12332    ));
12333
12334    // Single cursor on one line -> advance
12335    // Cursor moves to column 0 on blank line
12336    cx.set_state(indoc!(
12337        "fn a() {
12338             ˇdog();
12339
12340             cat();
12341        }"
12342    ));
12343    cx.update_editor(|editor, window, cx| {
12344        editor.toggle_comments(toggle_comments, window, cx);
12345    });
12346    cx.assert_editor_state(indoc!(
12347        "fn a() {
12348             // dog();
12349        ˇ
12350             cat();
12351        }"
12352    ));
12353
12354    // Single cursor on one line -> advance
12355    // Cursor starts and ends at column 0
12356    cx.set_state(indoc!(
12357        "fn a() {
12358         ˇ    dog();
12359             cat();
12360        }"
12361    ));
12362    cx.update_editor(|editor, window, cx| {
12363        editor.toggle_comments(toggle_comments, window, cx);
12364    });
12365    cx.assert_editor_state(indoc!(
12366        "fn a() {
12367             // dog();
12368         ˇ    cat();
12369        }"
12370    ));
12371}
12372
12373#[gpui::test]
12374async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12375    init_test(cx, |_| {});
12376
12377    let mut cx = EditorTestContext::new(cx).await;
12378
12379    let html_language = Arc::new(
12380        Language::new(
12381            LanguageConfig {
12382                name: "HTML".into(),
12383                block_comment: Some(("<!-- ".into(), " -->".into())),
12384                ..Default::default()
12385            },
12386            Some(tree_sitter_html::LANGUAGE.into()),
12387        )
12388        .with_injection_query(
12389            r#"
12390            (script_element
12391                (raw_text) @injection.content
12392                (#set! injection.language "javascript"))
12393            "#,
12394        )
12395        .unwrap(),
12396    );
12397
12398    let javascript_language = Arc::new(Language::new(
12399        LanguageConfig {
12400            name: "JavaScript".into(),
12401            line_comments: vec!["// ".into()],
12402            ..Default::default()
12403        },
12404        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12405    ));
12406
12407    cx.language_registry().add(html_language.clone());
12408    cx.language_registry().add(javascript_language.clone());
12409    cx.update_buffer(|buffer, cx| {
12410        buffer.set_language(Some(html_language), cx);
12411    });
12412
12413    // Toggle comments for empty selections
12414    cx.set_state(
12415        &r#"
12416            <p>A</p>ˇ
12417            <p>B</p>ˇ
12418            <p>C</p>ˇ
12419        "#
12420        .unindent(),
12421    );
12422    cx.update_editor(|editor, window, cx| {
12423        editor.toggle_comments(&ToggleComments::default(), window, cx)
12424    });
12425    cx.assert_editor_state(
12426        &r#"
12427            <!-- <p>A</p>ˇ -->
12428            <!-- <p>B</p>ˇ -->
12429            <!-- <p>C</p>ˇ -->
12430        "#
12431        .unindent(),
12432    );
12433    cx.update_editor(|editor, window, cx| {
12434        editor.toggle_comments(&ToggleComments::default(), window, cx)
12435    });
12436    cx.assert_editor_state(
12437        &r#"
12438            <p>A</p>ˇ
12439            <p>B</p>ˇ
12440            <p>C</p>ˇ
12441        "#
12442        .unindent(),
12443    );
12444
12445    // Toggle comments for mixture of empty and non-empty selections, where
12446    // multiple selections occupy a given line.
12447    cx.set_state(
12448        &r#"
12449            <p>A«</p>
12450            <p>ˇ»B</p>ˇ
12451            <p>C«</p>
12452            <p>ˇ»D</p>ˇ
12453        "#
12454        .unindent(),
12455    );
12456
12457    cx.update_editor(|editor, window, cx| {
12458        editor.toggle_comments(&ToggleComments::default(), window, cx)
12459    });
12460    cx.assert_editor_state(
12461        &r#"
12462            <!-- <p>A«</p>
12463            <p>ˇ»B</p>ˇ -->
12464            <!-- <p>C«</p>
12465            <p>ˇ»D</p>ˇ -->
12466        "#
12467        .unindent(),
12468    );
12469    cx.update_editor(|editor, window, cx| {
12470        editor.toggle_comments(&ToggleComments::default(), window, cx)
12471    });
12472    cx.assert_editor_state(
12473        &r#"
12474            <p>A«</p>
12475            <p>ˇ»B</p>ˇ
12476            <p>C«</p>
12477            <p>ˇ»D</p>ˇ
12478        "#
12479        .unindent(),
12480    );
12481
12482    // Toggle comments when different languages are active for different
12483    // selections.
12484    cx.set_state(
12485        &r#"
12486            ˇ<script>
12487                ˇvar x = new Y();
12488            ˇ</script>
12489        "#
12490        .unindent(),
12491    );
12492    cx.executor().run_until_parked();
12493    cx.update_editor(|editor, window, cx| {
12494        editor.toggle_comments(&ToggleComments::default(), window, cx)
12495    });
12496    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12497    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12498    cx.assert_editor_state(
12499        &r#"
12500            <!-- ˇ<script> -->
12501                // ˇvar x = new Y();
12502            <!-- ˇ</script> -->
12503        "#
12504        .unindent(),
12505    );
12506}
12507
12508#[gpui::test]
12509fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12510    init_test(cx, |_| {});
12511
12512    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12513    let multibuffer = cx.new(|cx| {
12514        let mut multibuffer = MultiBuffer::new(ReadWrite);
12515        multibuffer.push_excerpts(
12516            buffer.clone(),
12517            [
12518                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12519                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12520            ],
12521            cx,
12522        );
12523        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12524        multibuffer
12525    });
12526
12527    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12528    editor.update_in(cx, |editor, window, cx| {
12529        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12530        editor.change_selections(None, window, cx, |s| {
12531            s.select_ranges([
12532                Point::new(0, 0)..Point::new(0, 0),
12533                Point::new(1, 0)..Point::new(1, 0),
12534            ])
12535        });
12536
12537        editor.handle_input("X", window, cx);
12538        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12539        assert_eq!(
12540            editor.selections.ranges(cx),
12541            [
12542                Point::new(0, 1)..Point::new(0, 1),
12543                Point::new(1, 1)..Point::new(1, 1),
12544            ]
12545        );
12546
12547        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12548        editor.change_selections(None, window, cx, |s| {
12549            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12550        });
12551        editor.backspace(&Default::default(), window, cx);
12552        assert_eq!(editor.text(cx), "Xa\nbbb");
12553        assert_eq!(
12554            editor.selections.ranges(cx),
12555            [Point::new(1, 0)..Point::new(1, 0)]
12556        );
12557
12558        editor.change_selections(None, window, cx, |s| {
12559            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12560        });
12561        editor.backspace(&Default::default(), window, cx);
12562        assert_eq!(editor.text(cx), "X\nbb");
12563        assert_eq!(
12564            editor.selections.ranges(cx),
12565            [Point::new(0, 1)..Point::new(0, 1)]
12566        );
12567    });
12568}
12569
12570#[gpui::test]
12571fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12572    init_test(cx, |_| {});
12573
12574    let markers = vec![('[', ']').into(), ('(', ')').into()];
12575    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12576        indoc! {"
12577            [aaaa
12578            (bbbb]
12579            cccc)",
12580        },
12581        markers.clone(),
12582    );
12583    let excerpt_ranges = markers.into_iter().map(|marker| {
12584        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12585        ExcerptRange::new(context.clone())
12586    });
12587    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12588    let multibuffer = cx.new(|cx| {
12589        let mut multibuffer = MultiBuffer::new(ReadWrite);
12590        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12591        multibuffer
12592    });
12593
12594    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12595    editor.update_in(cx, |editor, window, cx| {
12596        let (expected_text, selection_ranges) = marked_text_ranges(
12597            indoc! {"
12598                aaaa
12599                bˇbbb
12600                bˇbbˇb
12601                cccc"
12602            },
12603            true,
12604        );
12605        assert_eq!(editor.text(cx), expected_text);
12606        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12607
12608        editor.handle_input("X", window, cx);
12609
12610        let (expected_text, expected_selections) = marked_text_ranges(
12611            indoc! {"
12612                aaaa
12613                bXˇbbXb
12614                bXˇbbXˇb
12615                cccc"
12616            },
12617            false,
12618        );
12619        assert_eq!(editor.text(cx), expected_text);
12620        assert_eq!(editor.selections.ranges(cx), expected_selections);
12621
12622        editor.newline(&Newline, window, cx);
12623        let (expected_text, expected_selections) = marked_text_ranges(
12624            indoc! {"
12625                aaaa
12626                bX
12627                ˇbbX
12628                b
12629                bX
12630                ˇbbX
12631                ˇb
12632                cccc"
12633            },
12634            false,
12635        );
12636        assert_eq!(editor.text(cx), expected_text);
12637        assert_eq!(editor.selections.ranges(cx), expected_selections);
12638    });
12639}
12640
12641#[gpui::test]
12642fn test_refresh_selections(cx: &mut TestAppContext) {
12643    init_test(cx, |_| {});
12644
12645    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12646    let mut excerpt1_id = None;
12647    let multibuffer = cx.new(|cx| {
12648        let mut multibuffer = MultiBuffer::new(ReadWrite);
12649        excerpt1_id = multibuffer
12650            .push_excerpts(
12651                buffer.clone(),
12652                [
12653                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12654                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12655                ],
12656                cx,
12657            )
12658            .into_iter()
12659            .next();
12660        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12661        multibuffer
12662    });
12663
12664    let editor = cx.add_window(|window, cx| {
12665        let mut editor = build_editor(multibuffer.clone(), window, cx);
12666        let snapshot = editor.snapshot(window, cx);
12667        editor.change_selections(None, window, cx, |s| {
12668            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12669        });
12670        editor.begin_selection(
12671            Point::new(2, 1).to_display_point(&snapshot),
12672            true,
12673            1,
12674            window,
12675            cx,
12676        );
12677        assert_eq!(
12678            editor.selections.ranges(cx),
12679            [
12680                Point::new(1, 3)..Point::new(1, 3),
12681                Point::new(2, 1)..Point::new(2, 1),
12682            ]
12683        );
12684        editor
12685    });
12686
12687    // Refreshing selections is a no-op when excerpts haven't changed.
12688    _ = editor.update(cx, |editor, window, cx| {
12689        editor.change_selections(None, window, cx, |s| s.refresh());
12690        assert_eq!(
12691            editor.selections.ranges(cx),
12692            [
12693                Point::new(1, 3)..Point::new(1, 3),
12694                Point::new(2, 1)..Point::new(2, 1),
12695            ]
12696        );
12697    });
12698
12699    multibuffer.update(cx, |multibuffer, cx| {
12700        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12701    });
12702    _ = editor.update(cx, |editor, window, cx| {
12703        // Removing an excerpt causes the first selection to become degenerate.
12704        assert_eq!(
12705            editor.selections.ranges(cx),
12706            [
12707                Point::new(0, 0)..Point::new(0, 0),
12708                Point::new(0, 1)..Point::new(0, 1)
12709            ]
12710        );
12711
12712        // Refreshing selections will relocate the first selection to the original buffer
12713        // location.
12714        editor.change_selections(None, window, cx, |s| s.refresh());
12715        assert_eq!(
12716            editor.selections.ranges(cx),
12717            [
12718                Point::new(0, 1)..Point::new(0, 1),
12719                Point::new(0, 3)..Point::new(0, 3)
12720            ]
12721        );
12722        assert!(editor.selections.pending_anchor().is_some());
12723    });
12724}
12725
12726#[gpui::test]
12727fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12728    init_test(cx, |_| {});
12729
12730    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12731    let mut excerpt1_id = None;
12732    let multibuffer = cx.new(|cx| {
12733        let mut multibuffer = MultiBuffer::new(ReadWrite);
12734        excerpt1_id = multibuffer
12735            .push_excerpts(
12736                buffer.clone(),
12737                [
12738                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12739                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12740                ],
12741                cx,
12742            )
12743            .into_iter()
12744            .next();
12745        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12746        multibuffer
12747    });
12748
12749    let editor = cx.add_window(|window, cx| {
12750        let mut editor = build_editor(multibuffer.clone(), window, cx);
12751        let snapshot = editor.snapshot(window, cx);
12752        editor.begin_selection(
12753            Point::new(1, 3).to_display_point(&snapshot),
12754            false,
12755            1,
12756            window,
12757            cx,
12758        );
12759        assert_eq!(
12760            editor.selections.ranges(cx),
12761            [Point::new(1, 3)..Point::new(1, 3)]
12762        );
12763        editor
12764    });
12765
12766    multibuffer.update(cx, |multibuffer, cx| {
12767        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12768    });
12769    _ = editor.update(cx, |editor, window, cx| {
12770        assert_eq!(
12771            editor.selections.ranges(cx),
12772            [Point::new(0, 0)..Point::new(0, 0)]
12773        );
12774
12775        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12776        editor.change_selections(None, window, cx, |s| s.refresh());
12777        assert_eq!(
12778            editor.selections.ranges(cx),
12779            [Point::new(0, 3)..Point::new(0, 3)]
12780        );
12781        assert!(editor.selections.pending_anchor().is_some());
12782    });
12783}
12784
12785#[gpui::test]
12786async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12787    init_test(cx, |_| {});
12788
12789    let language = Arc::new(
12790        Language::new(
12791            LanguageConfig {
12792                brackets: BracketPairConfig {
12793                    pairs: vec![
12794                        BracketPair {
12795                            start: "{".to_string(),
12796                            end: "}".to_string(),
12797                            close: true,
12798                            surround: true,
12799                            newline: true,
12800                        },
12801                        BracketPair {
12802                            start: "/* ".to_string(),
12803                            end: " */".to_string(),
12804                            close: true,
12805                            surround: true,
12806                            newline: true,
12807                        },
12808                    ],
12809                    ..Default::default()
12810                },
12811                ..Default::default()
12812            },
12813            Some(tree_sitter_rust::LANGUAGE.into()),
12814        )
12815        .with_indents_query("")
12816        .unwrap(),
12817    );
12818
12819    let text = concat!(
12820        "{   }\n",     //
12821        "  x\n",       //
12822        "  /*   */\n", //
12823        "x\n",         //
12824        "{{} }\n",     //
12825    );
12826
12827    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12828    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12829    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12830    editor
12831        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12832        .await;
12833
12834    editor.update_in(cx, |editor, window, cx| {
12835        editor.change_selections(None, window, cx, |s| {
12836            s.select_display_ranges([
12837                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12838                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12839                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12840            ])
12841        });
12842        editor.newline(&Newline, window, cx);
12843
12844        assert_eq!(
12845            editor.buffer().read(cx).read(cx).text(),
12846            concat!(
12847                "{ \n",    // Suppress rustfmt
12848                "\n",      //
12849                "}\n",     //
12850                "  x\n",   //
12851                "  /* \n", //
12852                "  \n",    //
12853                "  */\n",  //
12854                "x\n",     //
12855                "{{} \n",  //
12856                "}\n",     //
12857            )
12858        );
12859    });
12860}
12861
12862#[gpui::test]
12863fn test_highlighted_ranges(cx: &mut TestAppContext) {
12864    init_test(cx, |_| {});
12865
12866    let editor = cx.add_window(|window, cx| {
12867        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12868        build_editor(buffer.clone(), window, cx)
12869    });
12870
12871    _ = editor.update(cx, |editor, window, cx| {
12872        struct Type1;
12873        struct Type2;
12874
12875        let buffer = editor.buffer.read(cx).snapshot(cx);
12876
12877        let anchor_range =
12878            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12879
12880        editor.highlight_background::<Type1>(
12881            &[
12882                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12883                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12884                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12885                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12886            ],
12887            |_| Hsla::red(),
12888            cx,
12889        );
12890        editor.highlight_background::<Type2>(
12891            &[
12892                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12893                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12894                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12895                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12896            ],
12897            |_| Hsla::green(),
12898            cx,
12899        );
12900
12901        let snapshot = editor.snapshot(window, cx);
12902        let mut highlighted_ranges = editor.background_highlights_in_range(
12903            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12904            &snapshot,
12905            cx.theme().colors(),
12906        );
12907        // Enforce a consistent ordering based on color without relying on the ordering of the
12908        // highlight's `TypeId` which is non-executor.
12909        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12910        assert_eq!(
12911            highlighted_ranges,
12912            &[
12913                (
12914                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12915                    Hsla::red(),
12916                ),
12917                (
12918                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12919                    Hsla::red(),
12920                ),
12921                (
12922                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12923                    Hsla::green(),
12924                ),
12925                (
12926                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12927                    Hsla::green(),
12928                ),
12929            ]
12930        );
12931        assert_eq!(
12932            editor.background_highlights_in_range(
12933                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12934                &snapshot,
12935                cx.theme().colors(),
12936            ),
12937            &[(
12938                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12939                Hsla::red(),
12940            )]
12941        );
12942    });
12943}
12944
12945#[gpui::test]
12946async fn test_following(cx: &mut TestAppContext) {
12947    init_test(cx, |_| {});
12948
12949    let fs = FakeFs::new(cx.executor());
12950    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12951
12952    let buffer = project.update(cx, |project, cx| {
12953        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12954        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12955    });
12956    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12957    let follower = cx.update(|cx| {
12958        cx.open_window(
12959            WindowOptions {
12960                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12961                    gpui::Point::new(px(0.), px(0.)),
12962                    gpui::Point::new(px(10.), px(80.)),
12963                ))),
12964                ..Default::default()
12965            },
12966            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12967        )
12968        .unwrap()
12969    });
12970
12971    let is_still_following = Rc::new(RefCell::new(true));
12972    let follower_edit_event_count = Rc::new(RefCell::new(0));
12973    let pending_update = Rc::new(RefCell::new(None));
12974    let leader_entity = leader.root(cx).unwrap();
12975    let follower_entity = follower.root(cx).unwrap();
12976    _ = follower.update(cx, {
12977        let update = pending_update.clone();
12978        let is_still_following = is_still_following.clone();
12979        let follower_edit_event_count = follower_edit_event_count.clone();
12980        |_, window, cx| {
12981            cx.subscribe_in(
12982                &leader_entity,
12983                window,
12984                move |_, leader, event, window, cx| {
12985                    leader.read(cx).add_event_to_update_proto(
12986                        event,
12987                        &mut update.borrow_mut(),
12988                        window,
12989                        cx,
12990                    );
12991                },
12992            )
12993            .detach();
12994
12995            cx.subscribe_in(
12996                &follower_entity,
12997                window,
12998                move |_, _, event: &EditorEvent, _window, _cx| {
12999                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
13000                        *is_still_following.borrow_mut() = false;
13001                    }
13002
13003                    if let EditorEvent::BufferEdited = event {
13004                        *follower_edit_event_count.borrow_mut() += 1;
13005                    }
13006                },
13007            )
13008            .detach();
13009        }
13010    });
13011
13012    // Update the selections only
13013    _ = leader.update(cx, |leader, window, cx| {
13014        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13015    });
13016    follower
13017        .update(cx, |follower, window, cx| {
13018            follower.apply_update_proto(
13019                &project,
13020                pending_update.borrow_mut().take().unwrap(),
13021                window,
13022                cx,
13023            )
13024        })
13025        .unwrap()
13026        .await
13027        .unwrap();
13028    _ = follower.update(cx, |follower, _, cx| {
13029        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
13030    });
13031    assert!(*is_still_following.borrow());
13032    assert_eq!(*follower_edit_event_count.borrow(), 0);
13033
13034    // Update the scroll position only
13035    _ = leader.update(cx, |leader, window, cx| {
13036        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13037    });
13038    follower
13039        .update(cx, |follower, window, cx| {
13040            follower.apply_update_proto(
13041                &project,
13042                pending_update.borrow_mut().take().unwrap(),
13043                window,
13044                cx,
13045            )
13046        })
13047        .unwrap()
13048        .await
13049        .unwrap();
13050    assert_eq!(
13051        follower
13052            .update(cx, |follower, _, cx| follower.scroll_position(cx))
13053            .unwrap(),
13054        gpui::Point::new(1.5, 3.5)
13055    );
13056    assert!(*is_still_following.borrow());
13057    assert_eq!(*follower_edit_event_count.borrow(), 0);
13058
13059    // Update the selections and scroll position. The follower's scroll position is updated
13060    // via autoscroll, not via the leader's exact scroll position.
13061    _ = leader.update(cx, |leader, window, cx| {
13062        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
13063        leader.request_autoscroll(Autoscroll::newest(), cx);
13064        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
13065    });
13066    follower
13067        .update(cx, |follower, window, cx| {
13068            follower.apply_update_proto(
13069                &project,
13070                pending_update.borrow_mut().take().unwrap(),
13071                window,
13072                cx,
13073            )
13074        })
13075        .unwrap()
13076        .await
13077        .unwrap();
13078    _ = follower.update(cx, |follower, _, cx| {
13079        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
13080        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
13081    });
13082    assert!(*is_still_following.borrow());
13083
13084    // Creating a pending selection that precedes another selection
13085    _ = leader.update(cx, |leader, window, cx| {
13086        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13087        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
13088    });
13089    follower
13090        .update(cx, |follower, window, cx| {
13091            follower.apply_update_proto(
13092                &project,
13093                pending_update.borrow_mut().take().unwrap(),
13094                window,
13095                cx,
13096            )
13097        })
13098        .unwrap()
13099        .await
13100        .unwrap();
13101    _ = follower.update(cx, |follower, _, cx| {
13102        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
13103    });
13104    assert!(*is_still_following.borrow());
13105
13106    // Extend the pending selection so that it surrounds another selection
13107    _ = leader.update(cx, |leader, window, cx| {
13108        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
13109    });
13110    follower
13111        .update(cx, |follower, window, cx| {
13112            follower.apply_update_proto(
13113                &project,
13114                pending_update.borrow_mut().take().unwrap(),
13115                window,
13116                cx,
13117            )
13118        })
13119        .unwrap()
13120        .await
13121        .unwrap();
13122    _ = follower.update(cx, |follower, _, cx| {
13123        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
13124    });
13125
13126    // Scrolling locally breaks the follow
13127    _ = follower.update(cx, |follower, window, cx| {
13128        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13129        follower.set_scroll_anchor(
13130            ScrollAnchor {
13131                anchor: top_anchor,
13132                offset: gpui::Point::new(0.0, 0.5),
13133            },
13134            window,
13135            cx,
13136        );
13137    });
13138    assert!(!(*is_still_following.borrow()));
13139}
13140
13141#[gpui::test]
13142async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13143    init_test(cx, |_| {});
13144
13145    let fs = FakeFs::new(cx.executor());
13146    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13147    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13148    let pane = workspace
13149        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13150        .unwrap();
13151
13152    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13153
13154    let leader = pane.update_in(cx, |_, window, cx| {
13155        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13156        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13157    });
13158
13159    // Start following the editor when it has no excerpts.
13160    let mut state_message =
13161        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13162    let workspace_entity = workspace.root(cx).unwrap();
13163    let follower_1 = cx
13164        .update_window(*workspace.deref(), |_, window, cx| {
13165            Editor::from_state_proto(
13166                workspace_entity,
13167                ViewId {
13168                    creator: CollaboratorId::PeerId(PeerId::default()),
13169                    id: 0,
13170                },
13171                &mut state_message,
13172                window,
13173                cx,
13174            )
13175        })
13176        .unwrap()
13177        .unwrap()
13178        .await
13179        .unwrap();
13180
13181    let update_message = Rc::new(RefCell::new(None));
13182    follower_1.update_in(cx, {
13183        let update = update_message.clone();
13184        |_, window, cx| {
13185            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13186                leader.read(cx).add_event_to_update_proto(
13187                    event,
13188                    &mut update.borrow_mut(),
13189                    window,
13190                    cx,
13191                );
13192            })
13193            .detach();
13194        }
13195    });
13196
13197    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13198        (
13199            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13200            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13201        )
13202    });
13203
13204    // Insert some excerpts.
13205    leader.update(cx, |leader, cx| {
13206        leader.buffer.update(cx, |multibuffer, cx| {
13207            multibuffer.set_excerpts_for_path(
13208                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13209                buffer_1.clone(),
13210                vec![
13211                    Point::row_range(0..3),
13212                    Point::row_range(1..6),
13213                    Point::row_range(12..15),
13214                ],
13215                0,
13216                cx,
13217            );
13218            multibuffer.set_excerpts_for_path(
13219                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13220                buffer_2.clone(),
13221                vec![Point::row_range(0..6), Point::row_range(8..12)],
13222                0,
13223                cx,
13224            );
13225        });
13226    });
13227
13228    // Apply the update of adding the excerpts.
13229    follower_1
13230        .update_in(cx, |follower, window, cx| {
13231            follower.apply_update_proto(
13232                &project,
13233                update_message.borrow().clone().unwrap(),
13234                window,
13235                cx,
13236            )
13237        })
13238        .await
13239        .unwrap();
13240    assert_eq!(
13241        follower_1.update(cx, |editor, cx| editor.text(cx)),
13242        leader.update(cx, |editor, cx| editor.text(cx))
13243    );
13244    update_message.borrow_mut().take();
13245
13246    // Start following separately after it already has 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_2 = 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    assert_eq!(
13268        follower_2.update(cx, |editor, cx| editor.text(cx)),
13269        leader.update(cx, |editor, cx| editor.text(cx))
13270    );
13271
13272    // Remove some excerpts.
13273    leader.update(cx, |leader, cx| {
13274        leader.buffer.update(cx, |multibuffer, cx| {
13275            let excerpt_ids = multibuffer.excerpt_ids();
13276            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13277            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13278        });
13279    });
13280
13281    // Apply the update of removing the excerpts.
13282    follower_1
13283        .update_in(cx, |follower, window, cx| {
13284            follower.apply_update_proto(
13285                &project,
13286                update_message.borrow().clone().unwrap(),
13287                window,
13288                cx,
13289            )
13290        })
13291        .await
13292        .unwrap();
13293    follower_2
13294        .update_in(cx, |follower, window, cx| {
13295            follower.apply_update_proto(
13296                &project,
13297                update_message.borrow().clone().unwrap(),
13298                window,
13299                cx,
13300            )
13301        })
13302        .await
13303        .unwrap();
13304    update_message.borrow_mut().take();
13305    assert_eq!(
13306        follower_1.update(cx, |editor, cx| editor.text(cx)),
13307        leader.update(cx, |editor, cx| editor.text(cx))
13308    );
13309}
13310
13311#[gpui::test]
13312async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13313    init_test(cx, |_| {});
13314
13315    let mut cx = EditorTestContext::new(cx).await;
13316    let lsp_store =
13317        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13318
13319    cx.set_state(indoc! {"
13320        ˇfn func(abc def: i32) -> u32 {
13321        }
13322    "});
13323
13324    cx.update(|_, cx| {
13325        lsp_store.update(cx, |lsp_store, cx| {
13326            lsp_store
13327                .update_diagnostics(
13328                    LanguageServerId(0),
13329                    lsp::PublishDiagnosticsParams {
13330                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13331                        version: None,
13332                        diagnostics: vec![
13333                            lsp::Diagnostic {
13334                                range: lsp::Range::new(
13335                                    lsp::Position::new(0, 11),
13336                                    lsp::Position::new(0, 12),
13337                                ),
13338                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13339                                ..Default::default()
13340                            },
13341                            lsp::Diagnostic {
13342                                range: lsp::Range::new(
13343                                    lsp::Position::new(0, 12),
13344                                    lsp::Position::new(0, 15),
13345                                ),
13346                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13347                                ..Default::default()
13348                            },
13349                            lsp::Diagnostic {
13350                                range: lsp::Range::new(
13351                                    lsp::Position::new(0, 25),
13352                                    lsp::Position::new(0, 28),
13353                                ),
13354                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13355                                ..Default::default()
13356                            },
13357                        ],
13358                    },
13359                    &[],
13360                    cx,
13361                )
13362                .unwrap()
13363        });
13364    });
13365
13366    executor.run_until_parked();
13367
13368    cx.update_editor(|editor, window, cx| {
13369        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13370    });
13371
13372    cx.assert_editor_state(indoc! {"
13373        fn func(abc def: i32) -> ˇu32 {
13374        }
13375    "});
13376
13377    cx.update_editor(|editor, window, cx| {
13378        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13379    });
13380
13381    cx.assert_editor_state(indoc! {"
13382        fn func(abc ˇdef: i32) -> u32 {
13383        }
13384    "});
13385
13386    cx.update_editor(|editor, window, cx| {
13387        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13388    });
13389
13390    cx.assert_editor_state(indoc! {"
13391        fn func(abcˇ def: i32) -> u32 {
13392        }
13393    "});
13394
13395    cx.update_editor(|editor, window, cx| {
13396        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13397    });
13398
13399    cx.assert_editor_state(indoc! {"
13400        fn func(abc def: i32) -> ˇu32 {
13401        }
13402    "});
13403}
13404
13405#[gpui::test]
13406async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13407    init_test(cx, |_| {});
13408
13409    let mut cx = EditorTestContext::new(cx).await;
13410
13411    let diff_base = r#"
13412        use some::mod;
13413
13414        const A: u32 = 42;
13415
13416        fn main() {
13417            println!("hello");
13418
13419            println!("world");
13420        }
13421        "#
13422    .unindent();
13423
13424    // Edits are modified, removed, modified, added
13425    cx.set_state(
13426        &r#"
13427        use some::modified;
13428
13429        ˇ
13430        fn main() {
13431            println!("hello there");
13432
13433            println!("around the");
13434            println!("world");
13435        }
13436        "#
13437        .unindent(),
13438    );
13439
13440    cx.set_head_text(&diff_base);
13441    executor.run_until_parked();
13442
13443    cx.update_editor(|editor, window, cx| {
13444        //Wrap around the bottom of the buffer
13445        for _ in 0..3 {
13446            editor.go_to_next_hunk(&GoToHunk, window, cx);
13447        }
13448    });
13449
13450    cx.assert_editor_state(
13451        &r#"
13452        ˇuse some::modified;
13453
13454
13455        fn main() {
13456            println!("hello there");
13457
13458            println!("around the");
13459            println!("world");
13460        }
13461        "#
13462        .unindent(),
13463    );
13464
13465    cx.update_editor(|editor, window, cx| {
13466        //Wrap around the top of the buffer
13467        for _ in 0..2 {
13468            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13469        }
13470    });
13471
13472    cx.assert_editor_state(
13473        &r#"
13474        use some::modified;
13475
13476
13477        fn main() {
13478        ˇ    println!("hello there");
13479
13480            println!("around the");
13481            println!("world");
13482        }
13483        "#
13484        .unindent(),
13485    );
13486
13487    cx.update_editor(|editor, window, cx| {
13488        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13489    });
13490
13491    cx.assert_editor_state(
13492        &r#"
13493        use some::modified;
13494
13495        ˇ
13496        fn main() {
13497            println!("hello there");
13498
13499            println!("around the");
13500            println!("world");
13501        }
13502        "#
13503        .unindent(),
13504    );
13505
13506    cx.update_editor(|editor, window, cx| {
13507        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13508    });
13509
13510    cx.assert_editor_state(
13511        &r#"
13512        ˇuse some::modified;
13513
13514
13515        fn main() {
13516            println!("hello there");
13517
13518            println!("around the");
13519            println!("world");
13520        }
13521        "#
13522        .unindent(),
13523    );
13524
13525    cx.update_editor(|editor, window, cx| {
13526        for _ in 0..2 {
13527            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13528        }
13529    });
13530
13531    cx.assert_editor_state(
13532        &r#"
13533        use some::modified;
13534
13535
13536        fn main() {
13537        ˇ    println!("hello there");
13538
13539            println!("around the");
13540            println!("world");
13541        }
13542        "#
13543        .unindent(),
13544    );
13545
13546    cx.update_editor(|editor, window, cx| {
13547        editor.fold(&Fold, window, cx);
13548    });
13549
13550    cx.update_editor(|editor, window, cx| {
13551        editor.go_to_next_hunk(&GoToHunk, window, cx);
13552    });
13553
13554    cx.assert_editor_state(
13555        &r#"
13556        ˇuse some::modified;
13557
13558
13559        fn main() {
13560            println!("hello there");
13561
13562            println!("around the");
13563            println!("world");
13564        }
13565        "#
13566        .unindent(),
13567    );
13568}
13569
13570#[test]
13571fn test_split_words() {
13572    fn split(text: &str) -> Vec<&str> {
13573        split_words(text).collect()
13574    }
13575
13576    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13577    assert_eq!(split("hello_world"), &["hello_", "world"]);
13578    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13579    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13580    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13581    assert_eq!(split("helloworld"), &["helloworld"]);
13582
13583    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13584}
13585
13586#[gpui::test]
13587async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13588    init_test(cx, |_| {});
13589
13590    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13591    let mut assert = |before, after| {
13592        let _state_context = cx.set_state(before);
13593        cx.run_until_parked();
13594        cx.update_editor(|editor, window, cx| {
13595            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13596        });
13597        cx.run_until_parked();
13598        cx.assert_editor_state(after);
13599    };
13600
13601    // Outside bracket jumps to outside of matching bracket
13602    assert("console.logˇ(var);", "console.log(var)ˇ;");
13603    assert("console.log(var)ˇ;", "console.logˇ(var);");
13604
13605    // Inside bracket jumps to inside of matching bracket
13606    assert("console.log(ˇvar);", "console.log(varˇ);");
13607    assert("console.log(varˇ);", "console.log(ˇvar);");
13608
13609    // When outside a bracket and inside, favor jumping to the inside bracket
13610    assert(
13611        "console.log('foo', [1, 2, 3]ˇ);",
13612        "console.log(ˇ'foo', [1, 2, 3]);",
13613    );
13614    assert(
13615        "console.log(ˇ'foo', [1, 2, 3]);",
13616        "console.log('foo', [1, 2, 3]ˇ);",
13617    );
13618
13619    // Bias forward if two options are equally likely
13620    assert(
13621        "let result = curried_fun()ˇ();",
13622        "let result = curried_fun()()ˇ;",
13623    );
13624
13625    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13626    assert(
13627        indoc! {"
13628            function test() {
13629                console.log('test')ˇ
13630            }"},
13631        indoc! {"
13632            function test() {
13633                console.logˇ('test')
13634            }"},
13635    );
13636}
13637
13638#[gpui::test]
13639async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13640    init_test(cx, |_| {});
13641
13642    let fs = FakeFs::new(cx.executor());
13643    fs.insert_tree(
13644        path!("/a"),
13645        json!({
13646            "main.rs": "fn main() { let a = 5; }",
13647            "other.rs": "// Test file",
13648        }),
13649    )
13650    .await;
13651    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13652
13653    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13654    language_registry.add(Arc::new(Language::new(
13655        LanguageConfig {
13656            name: "Rust".into(),
13657            matcher: LanguageMatcher {
13658                path_suffixes: vec!["rs".to_string()],
13659                ..Default::default()
13660            },
13661            brackets: BracketPairConfig {
13662                pairs: vec![BracketPair {
13663                    start: "{".to_string(),
13664                    end: "}".to_string(),
13665                    close: true,
13666                    surround: true,
13667                    newline: true,
13668                }],
13669                disabled_scopes_by_bracket_ix: Vec::new(),
13670            },
13671            ..Default::default()
13672        },
13673        Some(tree_sitter_rust::LANGUAGE.into()),
13674    )));
13675    let mut fake_servers = language_registry.register_fake_lsp(
13676        "Rust",
13677        FakeLspAdapter {
13678            capabilities: lsp::ServerCapabilities {
13679                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13680                    first_trigger_character: "{".to_string(),
13681                    more_trigger_character: None,
13682                }),
13683                ..Default::default()
13684            },
13685            ..Default::default()
13686        },
13687    );
13688
13689    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13690
13691    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13692
13693    let worktree_id = workspace
13694        .update(cx, |workspace, _, cx| {
13695            workspace.project().update(cx, |project, cx| {
13696                project.worktrees(cx).next().unwrap().read(cx).id()
13697            })
13698        })
13699        .unwrap();
13700
13701    let buffer = project
13702        .update(cx, |project, cx| {
13703            project.open_local_buffer(path!("/a/main.rs"), cx)
13704        })
13705        .await
13706        .unwrap();
13707    let editor_handle = workspace
13708        .update(cx, |workspace, window, cx| {
13709            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13710        })
13711        .unwrap()
13712        .await
13713        .unwrap()
13714        .downcast::<Editor>()
13715        .unwrap();
13716
13717    cx.executor().start_waiting();
13718    let fake_server = fake_servers.next().await.unwrap();
13719
13720    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13721        |params, _| async move {
13722            assert_eq!(
13723                params.text_document_position.text_document.uri,
13724                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13725            );
13726            assert_eq!(
13727                params.text_document_position.position,
13728                lsp::Position::new(0, 21),
13729            );
13730
13731            Ok(Some(vec![lsp::TextEdit {
13732                new_text: "]".to_string(),
13733                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13734            }]))
13735        },
13736    );
13737
13738    editor_handle.update_in(cx, |editor, window, cx| {
13739        window.focus(&editor.focus_handle(cx));
13740        editor.change_selections(None, window, cx, |s| {
13741            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13742        });
13743        editor.handle_input("{", window, cx);
13744    });
13745
13746    cx.executor().run_until_parked();
13747
13748    buffer.update(cx, |buffer, _| {
13749        assert_eq!(
13750            buffer.text(),
13751            "fn main() { let a = {5}; }",
13752            "No extra braces from on type formatting should appear in the buffer"
13753        )
13754    });
13755}
13756
13757#[gpui::test]
13758async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13759    init_test(cx, |_| {});
13760
13761    let fs = FakeFs::new(cx.executor());
13762    fs.insert_tree(
13763        path!("/a"),
13764        json!({
13765            "main.rs": "fn main() { let a = 5; }",
13766            "other.rs": "// Test file",
13767        }),
13768    )
13769    .await;
13770
13771    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13772
13773    let server_restarts = Arc::new(AtomicUsize::new(0));
13774    let closure_restarts = Arc::clone(&server_restarts);
13775    let language_server_name = "test language server";
13776    let language_name: LanguageName = "Rust".into();
13777
13778    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13779    language_registry.add(Arc::new(Language::new(
13780        LanguageConfig {
13781            name: language_name.clone(),
13782            matcher: LanguageMatcher {
13783                path_suffixes: vec!["rs".to_string()],
13784                ..Default::default()
13785            },
13786            ..Default::default()
13787        },
13788        Some(tree_sitter_rust::LANGUAGE.into()),
13789    )));
13790    let mut fake_servers = language_registry.register_fake_lsp(
13791        "Rust",
13792        FakeLspAdapter {
13793            name: language_server_name,
13794            initialization_options: Some(json!({
13795                "testOptionValue": true
13796            })),
13797            initializer: Some(Box::new(move |fake_server| {
13798                let task_restarts = Arc::clone(&closure_restarts);
13799                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13800                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13801                    futures::future::ready(Ok(()))
13802                });
13803            })),
13804            ..Default::default()
13805        },
13806    );
13807
13808    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13809    let _buffer = project
13810        .update(cx, |project, cx| {
13811            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13812        })
13813        .await
13814        .unwrap();
13815    let _fake_server = fake_servers.next().await.unwrap();
13816    update_test_language_settings(cx, |language_settings| {
13817        language_settings.languages.insert(
13818            language_name.clone(),
13819            LanguageSettingsContent {
13820                tab_size: NonZeroU32::new(8),
13821                ..Default::default()
13822            },
13823        );
13824    });
13825    cx.executor().run_until_parked();
13826    assert_eq!(
13827        server_restarts.load(atomic::Ordering::Acquire),
13828        0,
13829        "Should not restart LSP server on an unrelated change"
13830    );
13831
13832    update_test_project_settings(cx, |project_settings| {
13833        project_settings.lsp.insert(
13834            "Some other server name".into(),
13835            LspSettings {
13836                binary: None,
13837                settings: None,
13838                initialization_options: Some(json!({
13839                    "some other init value": false
13840                })),
13841                enable_lsp_tasks: false,
13842            },
13843        );
13844    });
13845    cx.executor().run_until_parked();
13846    assert_eq!(
13847        server_restarts.load(atomic::Ordering::Acquire),
13848        0,
13849        "Should not restart LSP server on an unrelated LSP settings change"
13850    );
13851
13852    update_test_project_settings(cx, |project_settings| {
13853        project_settings.lsp.insert(
13854            language_server_name.into(),
13855            LspSettings {
13856                binary: None,
13857                settings: None,
13858                initialization_options: Some(json!({
13859                    "anotherInitValue": false
13860                })),
13861                enable_lsp_tasks: false,
13862            },
13863        );
13864    });
13865    cx.executor().run_until_parked();
13866    assert_eq!(
13867        server_restarts.load(atomic::Ordering::Acquire),
13868        1,
13869        "Should restart LSP server on a related LSP settings change"
13870    );
13871
13872    update_test_project_settings(cx, |project_settings| {
13873        project_settings.lsp.insert(
13874            language_server_name.into(),
13875            LspSettings {
13876                binary: None,
13877                settings: None,
13878                initialization_options: Some(json!({
13879                    "anotherInitValue": false
13880                })),
13881                enable_lsp_tasks: false,
13882            },
13883        );
13884    });
13885    cx.executor().run_until_parked();
13886    assert_eq!(
13887        server_restarts.load(atomic::Ordering::Acquire),
13888        1,
13889        "Should not restart LSP server on a related LSP settings change that is the same"
13890    );
13891
13892    update_test_project_settings(cx, |project_settings| {
13893        project_settings.lsp.insert(
13894            language_server_name.into(),
13895            LspSettings {
13896                binary: None,
13897                settings: None,
13898                initialization_options: None,
13899                enable_lsp_tasks: false,
13900            },
13901        );
13902    });
13903    cx.executor().run_until_parked();
13904    assert_eq!(
13905        server_restarts.load(atomic::Ordering::Acquire),
13906        2,
13907        "Should restart LSP server on another related LSP settings change"
13908    );
13909}
13910
13911#[gpui::test]
13912async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13913    init_test(cx, |_| {});
13914
13915    let mut cx = EditorLspTestContext::new_rust(
13916        lsp::ServerCapabilities {
13917            completion_provider: Some(lsp::CompletionOptions {
13918                trigger_characters: Some(vec![".".to_string()]),
13919                resolve_provider: Some(true),
13920                ..Default::default()
13921            }),
13922            ..Default::default()
13923        },
13924        cx,
13925    )
13926    .await;
13927
13928    cx.set_state("fn main() { let a = 2ˇ; }");
13929    cx.simulate_keystroke(".");
13930    let completion_item = lsp::CompletionItem {
13931        label: "some".into(),
13932        kind: Some(lsp::CompletionItemKind::SNIPPET),
13933        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13934        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13935            kind: lsp::MarkupKind::Markdown,
13936            value: "```rust\nSome(2)\n```".to_string(),
13937        })),
13938        deprecated: Some(false),
13939        sort_text: Some("fffffff2".to_string()),
13940        filter_text: Some("some".to_string()),
13941        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13942        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13943            range: lsp::Range {
13944                start: lsp::Position {
13945                    line: 0,
13946                    character: 22,
13947                },
13948                end: lsp::Position {
13949                    line: 0,
13950                    character: 22,
13951                },
13952            },
13953            new_text: "Some(2)".to_string(),
13954        })),
13955        additional_text_edits: Some(vec![lsp::TextEdit {
13956            range: lsp::Range {
13957                start: lsp::Position {
13958                    line: 0,
13959                    character: 20,
13960                },
13961                end: lsp::Position {
13962                    line: 0,
13963                    character: 22,
13964                },
13965            },
13966            new_text: "".to_string(),
13967        }]),
13968        ..Default::default()
13969    };
13970
13971    let closure_completion_item = completion_item.clone();
13972    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13973        let task_completion_item = closure_completion_item.clone();
13974        async move {
13975            Ok(Some(lsp::CompletionResponse::Array(vec![
13976                task_completion_item,
13977            ])))
13978        }
13979    });
13980
13981    request.next().await;
13982
13983    cx.condition(|editor, _| editor.context_menu_visible())
13984        .await;
13985    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13986        editor
13987            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13988            .unwrap()
13989    });
13990    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13991
13992    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13993        let task_completion_item = completion_item.clone();
13994        async move { Ok(task_completion_item) }
13995    })
13996    .next()
13997    .await
13998    .unwrap();
13999    apply_additional_edits.await.unwrap();
14000    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
14001}
14002
14003#[gpui::test]
14004async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
14005    init_test(cx, |_| {});
14006
14007    let mut cx = EditorLspTestContext::new_rust(
14008        lsp::ServerCapabilities {
14009            completion_provider: Some(lsp::CompletionOptions {
14010                trigger_characters: Some(vec![".".to_string()]),
14011                resolve_provider: Some(true),
14012                ..Default::default()
14013            }),
14014            ..Default::default()
14015        },
14016        cx,
14017    )
14018    .await;
14019
14020    cx.set_state("fn main() { let a = 2ˇ; }");
14021    cx.simulate_keystroke(".");
14022
14023    let item1 = lsp::CompletionItem {
14024        label: "method id()".to_string(),
14025        filter_text: Some("id".to_string()),
14026        detail: None,
14027        documentation: None,
14028        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14029            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14030            new_text: ".id".to_string(),
14031        })),
14032        ..lsp::CompletionItem::default()
14033    };
14034
14035    let item2 = lsp::CompletionItem {
14036        label: "other".to_string(),
14037        filter_text: Some("other".to_string()),
14038        detail: None,
14039        documentation: None,
14040        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14041            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14042            new_text: ".other".to_string(),
14043        })),
14044        ..lsp::CompletionItem::default()
14045    };
14046
14047    let item1 = item1.clone();
14048    cx.set_request_handler::<lsp::request::Completion, _, _>({
14049        let item1 = item1.clone();
14050        move |_, _, _| {
14051            let item1 = item1.clone();
14052            let item2 = item2.clone();
14053            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
14054        }
14055    })
14056    .next()
14057    .await;
14058
14059    cx.condition(|editor, _| editor.context_menu_visible())
14060        .await;
14061    cx.update_editor(|editor, _, _| {
14062        let context_menu = editor.context_menu.borrow_mut();
14063        let context_menu = context_menu
14064            .as_ref()
14065            .expect("Should have the context menu deployed");
14066        match context_menu {
14067            CodeContextMenu::Completions(completions_menu) => {
14068                let completions = completions_menu.completions.borrow_mut();
14069                assert_eq!(
14070                    completions
14071                        .iter()
14072                        .map(|completion| &completion.label.text)
14073                        .collect::<Vec<_>>(),
14074                    vec!["method id()", "other"]
14075                )
14076            }
14077            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14078        }
14079    });
14080
14081    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
14082        let item1 = item1.clone();
14083        move |_, item_to_resolve, _| {
14084            let item1 = item1.clone();
14085            async move {
14086                if item1 == item_to_resolve {
14087                    Ok(lsp::CompletionItem {
14088                        label: "method id()".to_string(),
14089                        filter_text: Some("id".to_string()),
14090                        detail: Some("Now resolved!".to_string()),
14091                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
14092                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14093                            range: lsp::Range::new(
14094                                lsp::Position::new(0, 22),
14095                                lsp::Position::new(0, 22),
14096                            ),
14097                            new_text: ".id".to_string(),
14098                        })),
14099                        ..lsp::CompletionItem::default()
14100                    })
14101                } else {
14102                    Ok(item_to_resolve)
14103                }
14104            }
14105        }
14106    })
14107    .next()
14108    .await
14109    .unwrap();
14110    cx.run_until_parked();
14111
14112    cx.update_editor(|editor, window, cx| {
14113        editor.context_menu_next(&Default::default(), window, cx);
14114    });
14115
14116    cx.update_editor(|editor, _, _| {
14117        let context_menu = editor.context_menu.borrow_mut();
14118        let context_menu = context_menu
14119            .as_ref()
14120            .expect("Should have the context menu deployed");
14121        match context_menu {
14122            CodeContextMenu::Completions(completions_menu) => {
14123                let completions = completions_menu.completions.borrow_mut();
14124                assert_eq!(
14125                    completions
14126                        .iter()
14127                        .map(|completion| &completion.label.text)
14128                        .collect::<Vec<_>>(),
14129                    vec!["method id() Now resolved!", "other"],
14130                    "Should update first completion label, but not second as the filter text did not match."
14131                );
14132            }
14133            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14134        }
14135    });
14136}
14137
14138#[gpui::test]
14139async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14140    init_test(cx, |_| {});
14141    let mut cx = EditorLspTestContext::new_rust(
14142        lsp::ServerCapabilities {
14143            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14144            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14145            completion_provider: Some(lsp::CompletionOptions {
14146                resolve_provider: Some(true),
14147                ..Default::default()
14148            }),
14149            ..Default::default()
14150        },
14151        cx,
14152    )
14153    .await;
14154    cx.set_state(indoc! {"
14155        struct TestStruct {
14156            field: i32
14157        }
14158
14159        fn mainˇ() {
14160            let unused_var = 42;
14161            let test_struct = TestStruct { field: 42 };
14162        }
14163    "});
14164    let symbol_range = cx.lsp_range(indoc! {"
14165        struct TestStruct {
14166            field: i32
14167        }
14168
14169        «fn main»() {
14170            let unused_var = 42;
14171            let test_struct = TestStruct { field: 42 };
14172        }
14173    "});
14174    let mut hover_requests =
14175        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14176            Ok(Some(lsp::Hover {
14177                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14178                    kind: lsp::MarkupKind::Markdown,
14179                    value: "Function documentation".to_string(),
14180                }),
14181                range: Some(symbol_range),
14182            }))
14183        });
14184
14185    // Case 1: Test that code action menu hide hover popover
14186    cx.dispatch_action(Hover);
14187    hover_requests.next().await;
14188    cx.condition(|editor, _| editor.hover_state.visible()).await;
14189    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14190        move |_, _, _| async move {
14191            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14192                lsp::CodeAction {
14193                    title: "Remove unused variable".to_string(),
14194                    kind: Some(CodeActionKind::QUICKFIX),
14195                    edit: Some(lsp::WorkspaceEdit {
14196                        changes: Some(
14197                            [(
14198                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14199                                vec![lsp::TextEdit {
14200                                    range: lsp::Range::new(
14201                                        lsp::Position::new(5, 4),
14202                                        lsp::Position::new(5, 27),
14203                                    ),
14204                                    new_text: "".to_string(),
14205                                }],
14206                            )]
14207                            .into_iter()
14208                            .collect(),
14209                        ),
14210                        ..Default::default()
14211                    }),
14212                    ..Default::default()
14213                },
14214            )]))
14215        },
14216    );
14217    cx.update_editor(|editor, window, cx| {
14218        editor.toggle_code_actions(
14219            &ToggleCodeActions {
14220                deployed_from_indicator: None,
14221                quick_launch: false,
14222            },
14223            window,
14224            cx,
14225        );
14226    });
14227    code_action_requests.next().await;
14228    cx.run_until_parked();
14229    cx.condition(|editor, _| editor.context_menu_visible())
14230        .await;
14231    cx.update_editor(|editor, _, _| {
14232        assert!(
14233            !editor.hover_state.visible(),
14234            "Hover popover should be hidden when code action menu is shown"
14235        );
14236        // Hide code actions
14237        editor.context_menu.take();
14238    });
14239
14240    // Case 2: Test that code completions hide hover popover
14241    cx.dispatch_action(Hover);
14242    hover_requests.next().await;
14243    cx.condition(|editor, _| editor.hover_state.visible()).await;
14244    let counter = Arc::new(AtomicUsize::new(0));
14245    let mut completion_requests =
14246        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14247            let counter = counter.clone();
14248            async move {
14249                counter.fetch_add(1, atomic::Ordering::Release);
14250                Ok(Some(lsp::CompletionResponse::Array(vec![
14251                    lsp::CompletionItem {
14252                        label: "main".into(),
14253                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14254                        detail: Some("() -> ()".to_string()),
14255                        ..Default::default()
14256                    },
14257                    lsp::CompletionItem {
14258                        label: "TestStruct".into(),
14259                        kind: Some(lsp::CompletionItemKind::STRUCT),
14260                        detail: Some("struct TestStruct".to_string()),
14261                        ..Default::default()
14262                    },
14263                ])))
14264            }
14265        });
14266    cx.update_editor(|editor, window, cx| {
14267        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14268    });
14269    completion_requests.next().await;
14270    cx.condition(|editor, _| editor.context_menu_visible())
14271        .await;
14272    cx.update_editor(|editor, _, _| {
14273        assert!(
14274            !editor.hover_state.visible(),
14275            "Hover popover should be hidden when completion menu is shown"
14276        );
14277    });
14278}
14279
14280#[gpui::test]
14281async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14282    init_test(cx, |_| {});
14283
14284    let mut cx = EditorLspTestContext::new_rust(
14285        lsp::ServerCapabilities {
14286            completion_provider: Some(lsp::CompletionOptions {
14287                trigger_characters: Some(vec![".".to_string()]),
14288                resolve_provider: Some(true),
14289                ..Default::default()
14290            }),
14291            ..Default::default()
14292        },
14293        cx,
14294    )
14295    .await;
14296
14297    cx.set_state("fn main() { let a = 2ˇ; }");
14298    cx.simulate_keystroke(".");
14299
14300    let unresolved_item_1 = lsp::CompletionItem {
14301        label: "id".to_string(),
14302        filter_text: Some("id".to_string()),
14303        detail: None,
14304        documentation: None,
14305        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14306            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14307            new_text: ".id".to_string(),
14308        })),
14309        ..lsp::CompletionItem::default()
14310    };
14311    let resolved_item_1 = lsp::CompletionItem {
14312        additional_text_edits: Some(vec![lsp::TextEdit {
14313            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14314            new_text: "!!".to_string(),
14315        }]),
14316        ..unresolved_item_1.clone()
14317    };
14318    let unresolved_item_2 = lsp::CompletionItem {
14319        label: "other".to_string(),
14320        filter_text: Some("other".to_string()),
14321        detail: None,
14322        documentation: None,
14323        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14324            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14325            new_text: ".other".to_string(),
14326        })),
14327        ..lsp::CompletionItem::default()
14328    };
14329    let resolved_item_2 = lsp::CompletionItem {
14330        additional_text_edits: Some(vec![lsp::TextEdit {
14331            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14332            new_text: "??".to_string(),
14333        }]),
14334        ..unresolved_item_2.clone()
14335    };
14336
14337    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14338    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14339    cx.lsp
14340        .server
14341        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14342            let unresolved_item_1 = unresolved_item_1.clone();
14343            let resolved_item_1 = resolved_item_1.clone();
14344            let unresolved_item_2 = unresolved_item_2.clone();
14345            let resolved_item_2 = resolved_item_2.clone();
14346            let resolve_requests_1 = resolve_requests_1.clone();
14347            let resolve_requests_2 = resolve_requests_2.clone();
14348            move |unresolved_request, _| {
14349                let unresolved_item_1 = unresolved_item_1.clone();
14350                let resolved_item_1 = resolved_item_1.clone();
14351                let unresolved_item_2 = unresolved_item_2.clone();
14352                let resolved_item_2 = resolved_item_2.clone();
14353                let resolve_requests_1 = resolve_requests_1.clone();
14354                let resolve_requests_2 = resolve_requests_2.clone();
14355                async move {
14356                    if unresolved_request == unresolved_item_1 {
14357                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14358                        Ok(resolved_item_1.clone())
14359                    } else if unresolved_request == unresolved_item_2 {
14360                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14361                        Ok(resolved_item_2.clone())
14362                    } else {
14363                        panic!("Unexpected completion item {unresolved_request:?}")
14364                    }
14365                }
14366            }
14367        })
14368        .detach();
14369
14370    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14371        let unresolved_item_1 = unresolved_item_1.clone();
14372        let unresolved_item_2 = unresolved_item_2.clone();
14373        async move {
14374            Ok(Some(lsp::CompletionResponse::Array(vec![
14375                unresolved_item_1,
14376                unresolved_item_2,
14377            ])))
14378        }
14379    })
14380    .next()
14381    .await;
14382
14383    cx.condition(|editor, _| editor.context_menu_visible())
14384        .await;
14385    cx.update_editor(|editor, _, _| {
14386        let context_menu = editor.context_menu.borrow_mut();
14387        let context_menu = context_menu
14388            .as_ref()
14389            .expect("Should have the context menu deployed");
14390        match context_menu {
14391            CodeContextMenu::Completions(completions_menu) => {
14392                let completions = completions_menu.completions.borrow_mut();
14393                assert_eq!(
14394                    completions
14395                        .iter()
14396                        .map(|completion| &completion.label.text)
14397                        .collect::<Vec<_>>(),
14398                    vec!["id", "other"]
14399                )
14400            }
14401            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14402        }
14403    });
14404    cx.run_until_parked();
14405
14406    cx.update_editor(|editor, window, cx| {
14407        editor.context_menu_next(&ContextMenuNext, window, cx);
14408    });
14409    cx.run_until_parked();
14410    cx.update_editor(|editor, window, cx| {
14411        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14412    });
14413    cx.run_until_parked();
14414    cx.update_editor(|editor, window, cx| {
14415        editor.context_menu_next(&ContextMenuNext, window, cx);
14416    });
14417    cx.run_until_parked();
14418    cx.update_editor(|editor, window, cx| {
14419        editor
14420            .compose_completion(&ComposeCompletion::default(), window, cx)
14421            .expect("No task returned")
14422    })
14423    .await
14424    .expect("Completion failed");
14425    cx.run_until_parked();
14426
14427    cx.update_editor(|editor, _, cx| {
14428        assert_eq!(
14429            resolve_requests_1.load(atomic::Ordering::Acquire),
14430            1,
14431            "Should always resolve once despite multiple selections"
14432        );
14433        assert_eq!(
14434            resolve_requests_2.load(atomic::Ordering::Acquire),
14435            1,
14436            "Should always resolve once after multiple selections and applying the completion"
14437        );
14438        assert_eq!(
14439            editor.text(cx),
14440            "fn main() { let a = ??.other; }",
14441            "Should use resolved data when applying the completion"
14442        );
14443    });
14444}
14445
14446#[gpui::test]
14447async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14448    init_test(cx, |_| {});
14449
14450    let item_0 = lsp::CompletionItem {
14451        label: "abs".into(),
14452        insert_text: Some("abs".into()),
14453        data: Some(json!({ "very": "special"})),
14454        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14455        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14456            lsp::InsertReplaceEdit {
14457                new_text: "abs".to_string(),
14458                insert: lsp::Range::default(),
14459                replace: lsp::Range::default(),
14460            },
14461        )),
14462        ..lsp::CompletionItem::default()
14463    };
14464    let items = iter::once(item_0.clone())
14465        .chain((11..51).map(|i| lsp::CompletionItem {
14466            label: format!("item_{}", i),
14467            insert_text: Some(format!("item_{}", i)),
14468            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14469            ..lsp::CompletionItem::default()
14470        }))
14471        .collect::<Vec<_>>();
14472
14473    let default_commit_characters = vec!["?".to_string()];
14474    let default_data = json!({ "default": "data"});
14475    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14476    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14477    let default_edit_range = lsp::Range {
14478        start: lsp::Position {
14479            line: 0,
14480            character: 5,
14481        },
14482        end: lsp::Position {
14483            line: 0,
14484            character: 5,
14485        },
14486    };
14487
14488    let mut cx = EditorLspTestContext::new_rust(
14489        lsp::ServerCapabilities {
14490            completion_provider: Some(lsp::CompletionOptions {
14491                trigger_characters: Some(vec![".".to_string()]),
14492                resolve_provider: Some(true),
14493                ..Default::default()
14494            }),
14495            ..Default::default()
14496        },
14497        cx,
14498    )
14499    .await;
14500
14501    cx.set_state("fn main() { let a = 2ˇ; }");
14502    cx.simulate_keystroke(".");
14503
14504    let completion_data = default_data.clone();
14505    let completion_characters = default_commit_characters.clone();
14506    let completion_items = items.clone();
14507    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14508        let default_data = completion_data.clone();
14509        let default_commit_characters = completion_characters.clone();
14510        let items = completion_items.clone();
14511        async move {
14512            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14513                items,
14514                item_defaults: Some(lsp::CompletionListItemDefaults {
14515                    data: Some(default_data.clone()),
14516                    commit_characters: Some(default_commit_characters.clone()),
14517                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14518                        default_edit_range,
14519                    )),
14520                    insert_text_format: Some(default_insert_text_format),
14521                    insert_text_mode: Some(default_insert_text_mode),
14522                }),
14523                ..lsp::CompletionList::default()
14524            })))
14525        }
14526    })
14527    .next()
14528    .await;
14529
14530    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14531    cx.lsp
14532        .server
14533        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14534            let closure_resolved_items = resolved_items.clone();
14535            move |item_to_resolve, _| {
14536                let closure_resolved_items = closure_resolved_items.clone();
14537                async move {
14538                    closure_resolved_items.lock().push(item_to_resolve.clone());
14539                    Ok(item_to_resolve)
14540                }
14541            }
14542        })
14543        .detach();
14544
14545    cx.condition(|editor, _| editor.context_menu_visible())
14546        .await;
14547    cx.run_until_parked();
14548    cx.update_editor(|editor, _, _| {
14549        let menu = editor.context_menu.borrow_mut();
14550        match menu.as_ref().expect("should have the completions menu") {
14551            CodeContextMenu::Completions(completions_menu) => {
14552                assert_eq!(
14553                    completions_menu
14554                        .entries
14555                        .borrow()
14556                        .iter()
14557                        .map(|mat| mat.string.clone())
14558                        .collect::<Vec<String>>(),
14559                    items
14560                        .iter()
14561                        .map(|completion| completion.label.clone())
14562                        .collect::<Vec<String>>()
14563                );
14564            }
14565            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14566        }
14567    });
14568    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14569    // with 4 from the end.
14570    assert_eq!(
14571        *resolved_items.lock(),
14572        [&items[0..16], &items[items.len() - 4..items.len()]]
14573            .concat()
14574            .iter()
14575            .cloned()
14576            .map(|mut item| {
14577                if item.data.is_none() {
14578                    item.data = Some(default_data.clone());
14579                }
14580                item
14581            })
14582            .collect::<Vec<lsp::CompletionItem>>(),
14583        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14584    );
14585    resolved_items.lock().clear();
14586
14587    cx.update_editor(|editor, window, cx| {
14588        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14589    });
14590    cx.run_until_parked();
14591    // Completions that have already been resolved are skipped.
14592    assert_eq!(
14593        *resolved_items.lock(),
14594        items[items.len() - 16..items.len() - 4]
14595            .iter()
14596            .cloned()
14597            .map(|mut item| {
14598                if item.data.is_none() {
14599                    item.data = Some(default_data.clone());
14600                }
14601                item
14602            })
14603            .collect::<Vec<lsp::CompletionItem>>()
14604    );
14605    resolved_items.lock().clear();
14606}
14607
14608#[gpui::test]
14609async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14610    init_test(cx, |_| {});
14611
14612    let mut cx = EditorLspTestContext::new(
14613        Language::new(
14614            LanguageConfig {
14615                matcher: LanguageMatcher {
14616                    path_suffixes: vec!["jsx".into()],
14617                    ..Default::default()
14618                },
14619                overrides: [(
14620                    "element".into(),
14621                    LanguageConfigOverride {
14622                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14623                        ..Default::default()
14624                    },
14625                )]
14626                .into_iter()
14627                .collect(),
14628                ..Default::default()
14629            },
14630            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14631        )
14632        .with_override_query("(jsx_self_closing_element) @element")
14633        .unwrap(),
14634        lsp::ServerCapabilities {
14635            completion_provider: Some(lsp::CompletionOptions {
14636                trigger_characters: Some(vec![":".to_string()]),
14637                ..Default::default()
14638            }),
14639            ..Default::default()
14640        },
14641        cx,
14642    )
14643    .await;
14644
14645    cx.lsp
14646        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14647            Ok(Some(lsp::CompletionResponse::Array(vec![
14648                lsp::CompletionItem {
14649                    label: "bg-blue".into(),
14650                    ..Default::default()
14651                },
14652                lsp::CompletionItem {
14653                    label: "bg-red".into(),
14654                    ..Default::default()
14655                },
14656                lsp::CompletionItem {
14657                    label: "bg-yellow".into(),
14658                    ..Default::default()
14659                },
14660            ])))
14661        });
14662
14663    cx.set_state(r#"<p class="bgˇ" />"#);
14664
14665    // Trigger completion when typing a dash, because the dash is an extra
14666    // word character in the 'element' scope, which contains the cursor.
14667    cx.simulate_keystroke("-");
14668    cx.executor().run_until_parked();
14669    cx.update_editor(|editor, _, _| {
14670        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14671        {
14672            assert_eq!(
14673                completion_menu_entries(&menu),
14674                &["bg-red", "bg-blue", "bg-yellow"]
14675            );
14676        } else {
14677            panic!("expected completion menu to be open");
14678        }
14679    });
14680
14681    cx.simulate_keystroke("l");
14682    cx.executor().run_until_parked();
14683    cx.update_editor(|editor, _, _| {
14684        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14685        {
14686            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14687        } else {
14688            panic!("expected completion menu to be open");
14689        }
14690    });
14691
14692    // When filtering completions, consider the character after the '-' to
14693    // be the start of a subword.
14694    cx.set_state(r#"<p class="yelˇ" />"#);
14695    cx.simulate_keystroke("l");
14696    cx.executor().run_until_parked();
14697    cx.update_editor(|editor, _, _| {
14698        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14699        {
14700            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14701        } else {
14702            panic!("expected completion menu to be open");
14703        }
14704    });
14705}
14706
14707fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14708    let entries = menu.entries.borrow();
14709    entries.iter().map(|mat| mat.string.clone()).collect()
14710}
14711
14712#[gpui::test]
14713async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14714    init_test(cx, |settings| {
14715        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14716            FormatterList(vec![Formatter::Prettier].into()),
14717        ))
14718    });
14719
14720    let fs = FakeFs::new(cx.executor());
14721    fs.insert_file(path!("/file.ts"), Default::default()).await;
14722
14723    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14724    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14725
14726    language_registry.add(Arc::new(Language::new(
14727        LanguageConfig {
14728            name: "TypeScript".into(),
14729            matcher: LanguageMatcher {
14730                path_suffixes: vec!["ts".to_string()],
14731                ..Default::default()
14732            },
14733            ..Default::default()
14734        },
14735        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14736    )));
14737    update_test_language_settings(cx, |settings| {
14738        settings.defaults.prettier = Some(PrettierSettings {
14739            allowed: true,
14740            ..PrettierSettings::default()
14741        });
14742    });
14743
14744    let test_plugin = "test_plugin";
14745    let _ = language_registry.register_fake_lsp(
14746        "TypeScript",
14747        FakeLspAdapter {
14748            prettier_plugins: vec![test_plugin],
14749            ..Default::default()
14750        },
14751    );
14752
14753    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14754    let buffer = project
14755        .update(cx, |project, cx| {
14756            project.open_local_buffer(path!("/file.ts"), cx)
14757        })
14758        .await
14759        .unwrap();
14760
14761    let buffer_text = "one\ntwo\nthree\n";
14762    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14763    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14764    editor.update_in(cx, |editor, window, cx| {
14765        editor.set_text(buffer_text, window, cx)
14766    });
14767
14768    editor
14769        .update_in(cx, |editor, window, cx| {
14770            editor.perform_format(
14771                project.clone(),
14772                FormatTrigger::Manual,
14773                FormatTarget::Buffers,
14774                window,
14775                cx,
14776            )
14777        })
14778        .unwrap()
14779        .await;
14780    assert_eq!(
14781        editor.update(cx, |editor, cx| editor.text(cx)),
14782        buffer_text.to_string() + prettier_format_suffix,
14783        "Test prettier formatting was not applied to the original buffer text",
14784    );
14785
14786    update_test_language_settings(cx, |settings| {
14787        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14788    });
14789    let format = editor.update_in(cx, |editor, window, cx| {
14790        editor.perform_format(
14791            project.clone(),
14792            FormatTrigger::Manual,
14793            FormatTarget::Buffers,
14794            window,
14795            cx,
14796        )
14797    });
14798    format.await.unwrap();
14799    assert_eq!(
14800        editor.update(cx, |editor, cx| editor.text(cx)),
14801        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14802        "Autoformatting (via test prettier) was not applied to the original buffer text",
14803    );
14804}
14805
14806#[gpui::test]
14807async fn test_addition_reverts(cx: &mut TestAppContext) {
14808    init_test(cx, |_| {});
14809    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14810    let base_text = indoc! {r#"
14811        struct Row;
14812        struct Row1;
14813        struct Row2;
14814
14815        struct Row4;
14816        struct Row5;
14817        struct Row6;
14818
14819        struct Row8;
14820        struct Row9;
14821        struct Row10;"#};
14822
14823    // When addition hunks are not adjacent to carets, no hunk revert is performed
14824    assert_hunk_revert(
14825        indoc! {r#"struct Row;
14826                   struct Row1;
14827                   struct Row1.1;
14828                   struct Row1.2;
14829                   struct Row2;ˇ
14830
14831                   struct Row4;
14832                   struct Row5;
14833                   struct Row6;
14834
14835                   struct Row8;
14836                   ˇstruct Row9;
14837                   struct Row9.1;
14838                   struct Row9.2;
14839                   struct Row9.3;
14840                   struct Row10;"#},
14841        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14842        indoc! {r#"struct Row;
14843                   struct Row1;
14844                   struct Row1.1;
14845                   struct Row1.2;
14846                   struct Row2;ˇ
14847
14848                   struct Row4;
14849                   struct Row5;
14850                   struct Row6;
14851
14852                   struct Row8;
14853                   ˇstruct Row9;
14854                   struct Row9.1;
14855                   struct Row9.2;
14856                   struct Row9.3;
14857                   struct Row10;"#},
14858        base_text,
14859        &mut cx,
14860    );
14861    // Same for selections
14862    assert_hunk_revert(
14863        indoc! {r#"struct Row;
14864                   struct Row1;
14865                   struct Row2;
14866                   struct Row2.1;
14867                   struct Row2.2;
14868                   «ˇ
14869                   struct Row4;
14870                   struct» Row5;
14871                   «struct Row6;
14872                   ˇ»
14873                   struct Row9.1;
14874                   struct Row9.2;
14875                   struct Row9.3;
14876                   struct Row8;
14877                   struct Row9;
14878                   struct Row10;"#},
14879        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14880        indoc! {r#"struct Row;
14881                   struct Row1;
14882                   struct Row2;
14883                   struct Row2.1;
14884                   struct Row2.2;
14885                   «ˇ
14886                   struct Row4;
14887                   struct» Row5;
14888                   «struct Row6;
14889                   ˇ»
14890                   struct Row9.1;
14891                   struct Row9.2;
14892                   struct Row9.3;
14893                   struct Row8;
14894                   struct Row9;
14895                   struct Row10;"#},
14896        base_text,
14897        &mut cx,
14898    );
14899
14900    // When carets and selections intersect the addition hunks, those are reverted.
14901    // Adjacent carets got merged.
14902    assert_hunk_revert(
14903        indoc! {r#"struct Row;
14904                   ˇ// something on the top
14905                   struct Row1;
14906                   struct Row2;
14907                   struct Roˇw3.1;
14908                   struct Row2.2;
14909                   struct Row2.3;ˇ
14910
14911                   struct Row4;
14912                   struct ˇRow5.1;
14913                   struct Row5.2;
14914                   struct «Rowˇ»5.3;
14915                   struct Row5;
14916                   struct Row6;
14917                   ˇ
14918                   struct Row9.1;
14919                   struct «Rowˇ»9.2;
14920                   struct «ˇRow»9.3;
14921                   struct Row8;
14922                   struct Row9;
14923                   «ˇ// something on bottom»
14924                   struct Row10;"#},
14925        vec![
14926            DiffHunkStatusKind::Added,
14927            DiffHunkStatusKind::Added,
14928            DiffHunkStatusKind::Added,
14929            DiffHunkStatusKind::Added,
14930            DiffHunkStatusKind::Added,
14931        ],
14932        indoc! {r#"struct Row;
14933                   ˇstruct Row1;
14934                   struct Row2;
14935                   ˇ
14936                   struct Row4;
14937                   ˇstruct Row5;
14938                   struct Row6;
14939                   ˇ
14940                   ˇstruct Row8;
14941                   struct Row9;
14942                   ˇstruct Row10;"#},
14943        base_text,
14944        &mut cx,
14945    );
14946}
14947
14948#[gpui::test]
14949async fn test_modification_reverts(cx: &mut TestAppContext) {
14950    init_test(cx, |_| {});
14951    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14952    let base_text = indoc! {r#"
14953        struct Row;
14954        struct Row1;
14955        struct Row2;
14956
14957        struct Row4;
14958        struct Row5;
14959        struct Row6;
14960
14961        struct Row8;
14962        struct Row9;
14963        struct Row10;"#};
14964
14965    // Modification hunks behave the same as the addition ones.
14966    assert_hunk_revert(
14967        indoc! {r#"struct Row;
14968                   struct Row1;
14969                   struct Row33;
14970                   ˇ
14971                   struct Row4;
14972                   struct Row5;
14973                   struct Row6;
14974                   ˇ
14975                   struct Row99;
14976                   struct Row9;
14977                   struct Row10;"#},
14978        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14979        indoc! {r#"struct Row;
14980                   struct Row1;
14981                   struct Row33;
14982                   ˇ
14983                   struct Row4;
14984                   struct Row5;
14985                   struct Row6;
14986                   ˇ
14987                   struct Row99;
14988                   struct Row9;
14989                   struct Row10;"#},
14990        base_text,
14991        &mut cx,
14992    );
14993    assert_hunk_revert(
14994        indoc! {r#"struct Row;
14995                   struct Row1;
14996                   struct Row33;
14997                   «ˇ
14998                   struct Row4;
14999                   struct» Row5;
15000                   «struct Row6;
15001                   ˇ»
15002                   struct Row99;
15003                   struct Row9;
15004                   struct Row10;"#},
15005        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
15006        indoc! {r#"struct Row;
15007                   struct Row1;
15008                   struct Row33;
15009                   «ˇ
15010                   struct Row4;
15011                   struct» Row5;
15012                   «struct Row6;
15013                   ˇ»
15014                   struct Row99;
15015                   struct Row9;
15016                   struct Row10;"#},
15017        base_text,
15018        &mut cx,
15019    );
15020
15021    assert_hunk_revert(
15022        indoc! {r#"ˇstruct Row1.1;
15023                   struct Row1;
15024                   «ˇstr»uct Row22;
15025
15026                   struct ˇRow44;
15027                   struct Row5;
15028                   struct «Rˇ»ow66;ˇ
15029
15030                   «struˇ»ct Row88;
15031                   struct Row9;
15032                   struct Row1011;ˇ"#},
15033        vec![
15034            DiffHunkStatusKind::Modified,
15035            DiffHunkStatusKind::Modified,
15036            DiffHunkStatusKind::Modified,
15037            DiffHunkStatusKind::Modified,
15038            DiffHunkStatusKind::Modified,
15039            DiffHunkStatusKind::Modified,
15040        ],
15041        indoc! {r#"struct Row;
15042                   ˇstruct Row1;
15043                   struct Row2;
15044                   ˇ
15045                   struct Row4;
15046                   ˇstruct Row5;
15047                   struct Row6;
15048                   ˇ
15049                   struct Row8;
15050                   ˇstruct Row9;
15051                   struct Row10;ˇ"#},
15052        base_text,
15053        &mut cx,
15054    );
15055}
15056
15057#[gpui::test]
15058async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
15059    init_test(cx, |_| {});
15060    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15061    let base_text = indoc! {r#"
15062        one
15063
15064        two
15065        three
15066        "#};
15067
15068    cx.set_head_text(base_text);
15069    cx.set_state("\nˇ\n");
15070    cx.executor().run_until_parked();
15071    cx.update_editor(|editor, _window, cx| {
15072        editor.expand_selected_diff_hunks(cx);
15073    });
15074    cx.executor().run_until_parked();
15075    cx.update_editor(|editor, window, cx| {
15076        editor.backspace(&Default::default(), window, cx);
15077    });
15078    cx.run_until_parked();
15079    cx.assert_state_with_diff(
15080        indoc! {r#"
15081
15082        - two
15083        - threeˇ
15084        +
15085        "#}
15086        .to_string(),
15087    );
15088}
15089
15090#[gpui::test]
15091async fn test_deletion_reverts(cx: &mut TestAppContext) {
15092    init_test(cx, |_| {});
15093    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15094    let base_text = indoc! {r#"struct Row;
15095struct Row1;
15096struct Row2;
15097
15098struct Row4;
15099struct Row5;
15100struct Row6;
15101
15102struct Row8;
15103struct Row9;
15104struct Row10;"#};
15105
15106    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
15107    assert_hunk_revert(
15108        indoc! {r#"struct Row;
15109                   struct Row2;
15110
15111                   ˇstruct Row4;
15112                   struct Row5;
15113                   struct Row6;
15114                   ˇ
15115                   struct Row8;
15116                   struct Row10;"#},
15117        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15118        indoc! {r#"struct Row;
15119                   struct Row2;
15120
15121                   ˇstruct Row4;
15122                   struct Row5;
15123                   struct Row6;
15124                   ˇ
15125                   struct Row8;
15126                   struct Row10;"#},
15127        base_text,
15128        &mut cx,
15129    );
15130    assert_hunk_revert(
15131        indoc! {r#"struct Row;
15132                   struct Row2;
15133
15134                   «ˇstruct Row4;
15135                   struct» Row5;
15136                   «struct Row6;
15137                   ˇ»
15138                   struct Row8;
15139                   struct Row10;"#},
15140        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15141        indoc! {r#"struct Row;
15142                   struct Row2;
15143
15144                   «ˇstruct Row4;
15145                   struct» Row5;
15146                   «struct Row6;
15147                   ˇ»
15148                   struct Row8;
15149                   struct Row10;"#},
15150        base_text,
15151        &mut cx,
15152    );
15153
15154    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15155    assert_hunk_revert(
15156        indoc! {r#"struct Row;
15157                   ˇstruct Row2;
15158
15159                   struct Row4;
15160                   struct Row5;
15161                   struct Row6;
15162
15163                   struct Row8;ˇ
15164                   struct Row10;"#},
15165        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15166        indoc! {r#"struct Row;
15167                   struct Row1;
15168                   ˇstruct Row2;
15169
15170                   struct Row4;
15171                   struct Row5;
15172                   struct Row6;
15173
15174                   struct Row8;ˇ
15175                   struct Row9;
15176                   struct Row10;"#},
15177        base_text,
15178        &mut cx,
15179    );
15180    assert_hunk_revert(
15181        indoc! {r#"struct Row;
15182                   struct Row2«ˇ;
15183                   struct Row4;
15184                   struct» Row5;
15185                   «struct Row6;
15186
15187                   struct Row8;ˇ»
15188                   struct Row10;"#},
15189        vec![
15190            DiffHunkStatusKind::Deleted,
15191            DiffHunkStatusKind::Deleted,
15192            DiffHunkStatusKind::Deleted,
15193        ],
15194        indoc! {r#"struct Row;
15195                   struct Row1;
15196                   struct Row2«ˇ;
15197
15198                   struct Row4;
15199                   struct» Row5;
15200                   «struct Row6;
15201
15202                   struct Row8;ˇ»
15203                   struct Row9;
15204                   struct Row10;"#},
15205        base_text,
15206        &mut cx,
15207    );
15208}
15209
15210#[gpui::test]
15211async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15212    init_test(cx, |_| {});
15213
15214    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15215    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15216    let base_text_3 =
15217        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15218
15219    let text_1 = edit_first_char_of_every_line(base_text_1);
15220    let text_2 = edit_first_char_of_every_line(base_text_2);
15221    let text_3 = edit_first_char_of_every_line(base_text_3);
15222
15223    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15224    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15225    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15226
15227    let multibuffer = cx.new(|cx| {
15228        let mut multibuffer = MultiBuffer::new(ReadWrite);
15229        multibuffer.push_excerpts(
15230            buffer_1.clone(),
15231            [
15232                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15233                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15234                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15235            ],
15236            cx,
15237        );
15238        multibuffer.push_excerpts(
15239            buffer_2.clone(),
15240            [
15241                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15242                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15243                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15244            ],
15245            cx,
15246        );
15247        multibuffer.push_excerpts(
15248            buffer_3.clone(),
15249            [
15250                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15251                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15252                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15253            ],
15254            cx,
15255        );
15256        multibuffer
15257    });
15258
15259    let fs = FakeFs::new(cx.executor());
15260    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15261    let (editor, cx) = cx
15262        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15263    editor.update_in(cx, |editor, _window, cx| {
15264        for (buffer, diff_base) in [
15265            (buffer_1.clone(), base_text_1),
15266            (buffer_2.clone(), base_text_2),
15267            (buffer_3.clone(), base_text_3),
15268        ] {
15269            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15270            editor
15271                .buffer
15272                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15273        }
15274    });
15275    cx.executor().run_until_parked();
15276
15277    editor.update_in(cx, |editor, window, cx| {
15278        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}");
15279        editor.select_all(&SelectAll, window, cx);
15280        editor.git_restore(&Default::default(), window, cx);
15281    });
15282    cx.executor().run_until_parked();
15283
15284    // When all ranges are selected, all buffer hunks are reverted.
15285    editor.update(cx, |editor, cx| {
15286        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");
15287    });
15288    buffer_1.update(cx, |buffer, _| {
15289        assert_eq!(buffer.text(), base_text_1);
15290    });
15291    buffer_2.update(cx, |buffer, _| {
15292        assert_eq!(buffer.text(), base_text_2);
15293    });
15294    buffer_3.update(cx, |buffer, _| {
15295        assert_eq!(buffer.text(), base_text_3);
15296    });
15297
15298    editor.update_in(cx, |editor, window, cx| {
15299        editor.undo(&Default::default(), window, cx);
15300    });
15301
15302    editor.update_in(cx, |editor, window, cx| {
15303        editor.change_selections(None, window, cx, |s| {
15304            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15305        });
15306        editor.git_restore(&Default::default(), window, cx);
15307    });
15308
15309    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15310    // but not affect buffer_2 and its related excerpts.
15311    editor.update(cx, |editor, cx| {
15312        assert_eq!(
15313            editor.text(cx),
15314            "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}"
15315        );
15316    });
15317    buffer_1.update(cx, |buffer, _| {
15318        assert_eq!(buffer.text(), base_text_1);
15319    });
15320    buffer_2.update(cx, |buffer, _| {
15321        assert_eq!(
15322            buffer.text(),
15323            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15324        );
15325    });
15326    buffer_3.update(cx, |buffer, _| {
15327        assert_eq!(
15328            buffer.text(),
15329            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15330        );
15331    });
15332
15333    fn edit_first_char_of_every_line(text: &str) -> String {
15334        text.split('\n')
15335            .map(|line| format!("X{}", &line[1..]))
15336            .collect::<Vec<_>>()
15337            .join("\n")
15338    }
15339}
15340
15341#[gpui::test]
15342async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15343    init_test(cx, |_| {});
15344
15345    let cols = 4;
15346    let rows = 10;
15347    let sample_text_1 = sample_text(rows, cols, 'a');
15348    assert_eq!(
15349        sample_text_1,
15350        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15351    );
15352    let sample_text_2 = sample_text(rows, cols, 'l');
15353    assert_eq!(
15354        sample_text_2,
15355        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15356    );
15357    let sample_text_3 = sample_text(rows, cols, 'v');
15358    assert_eq!(
15359        sample_text_3,
15360        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15361    );
15362
15363    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15364    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15365    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15366
15367    let multi_buffer = cx.new(|cx| {
15368        let mut multibuffer = MultiBuffer::new(ReadWrite);
15369        multibuffer.push_excerpts(
15370            buffer_1.clone(),
15371            [
15372                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15373                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15374                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15375            ],
15376            cx,
15377        );
15378        multibuffer.push_excerpts(
15379            buffer_2.clone(),
15380            [
15381                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15382                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15383                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15384            ],
15385            cx,
15386        );
15387        multibuffer.push_excerpts(
15388            buffer_3.clone(),
15389            [
15390                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15391                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15392                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15393            ],
15394            cx,
15395        );
15396        multibuffer
15397    });
15398
15399    let fs = FakeFs::new(cx.executor());
15400    fs.insert_tree(
15401        "/a",
15402        json!({
15403            "main.rs": sample_text_1,
15404            "other.rs": sample_text_2,
15405            "lib.rs": sample_text_3,
15406        }),
15407    )
15408    .await;
15409    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15410    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15411    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15412    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15413        Editor::new(
15414            EditorMode::full(),
15415            multi_buffer,
15416            Some(project.clone()),
15417            window,
15418            cx,
15419        )
15420    });
15421    let multibuffer_item_id = workspace
15422        .update(cx, |workspace, window, cx| {
15423            assert!(
15424                workspace.active_item(cx).is_none(),
15425                "active item should be None before the first item is added"
15426            );
15427            workspace.add_item_to_active_pane(
15428                Box::new(multi_buffer_editor.clone()),
15429                None,
15430                true,
15431                window,
15432                cx,
15433            );
15434            let active_item = workspace
15435                .active_item(cx)
15436                .expect("should have an active item after adding the multi buffer");
15437            assert!(
15438                !active_item.is_singleton(cx),
15439                "A multi buffer was expected to active after adding"
15440            );
15441            active_item.item_id()
15442        })
15443        .unwrap();
15444    cx.executor().run_until_parked();
15445
15446    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15447        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15448            s.select_ranges(Some(1..2))
15449        });
15450        editor.open_excerpts(&OpenExcerpts, window, cx);
15451    });
15452    cx.executor().run_until_parked();
15453    let first_item_id = workspace
15454        .update(cx, |workspace, window, cx| {
15455            let active_item = workspace
15456                .active_item(cx)
15457                .expect("should have an active item after navigating into the 1st buffer");
15458            let first_item_id = active_item.item_id();
15459            assert_ne!(
15460                first_item_id, multibuffer_item_id,
15461                "Should navigate into the 1st buffer and activate it"
15462            );
15463            assert!(
15464                active_item.is_singleton(cx),
15465                "New active item should be a singleton buffer"
15466            );
15467            assert_eq!(
15468                active_item
15469                    .act_as::<Editor>(cx)
15470                    .expect("should have navigated into an editor for the 1st buffer")
15471                    .read(cx)
15472                    .text(cx),
15473                sample_text_1
15474            );
15475
15476            workspace
15477                .go_back(workspace.active_pane().downgrade(), window, cx)
15478                .detach_and_log_err(cx);
15479
15480            first_item_id
15481        })
15482        .unwrap();
15483    cx.executor().run_until_parked();
15484    workspace
15485        .update(cx, |workspace, _, cx| {
15486            let active_item = workspace
15487                .active_item(cx)
15488                .expect("should have an active item after navigating back");
15489            assert_eq!(
15490                active_item.item_id(),
15491                multibuffer_item_id,
15492                "Should navigate back to the multi buffer"
15493            );
15494            assert!(!active_item.is_singleton(cx));
15495        })
15496        .unwrap();
15497
15498    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15499        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15500            s.select_ranges(Some(39..40))
15501        });
15502        editor.open_excerpts(&OpenExcerpts, window, cx);
15503    });
15504    cx.executor().run_until_parked();
15505    let second_item_id = workspace
15506        .update(cx, |workspace, window, cx| {
15507            let active_item = workspace
15508                .active_item(cx)
15509                .expect("should have an active item after navigating into the 2nd buffer");
15510            let second_item_id = active_item.item_id();
15511            assert_ne!(
15512                second_item_id, multibuffer_item_id,
15513                "Should navigate away from the multibuffer"
15514            );
15515            assert_ne!(
15516                second_item_id, first_item_id,
15517                "Should navigate into the 2nd buffer and activate it"
15518            );
15519            assert!(
15520                active_item.is_singleton(cx),
15521                "New active item should be a singleton buffer"
15522            );
15523            assert_eq!(
15524                active_item
15525                    .act_as::<Editor>(cx)
15526                    .expect("should have navigated into an editor")
15527                    .read(cx)
15528                    .text(cx),
15529                sample_text_2
15530            );
15531
15532            workspace
15533                .go_back(workspace.active_pane().downgrade(), window, cx)
15534                .detach_and_log_err(cx);
15535
15536            second_item_id
15537        })
15538        .unwrap();
15539    cx.executor().run_until_parked();
15540    workspace
15541        .update(cx, |workspace, _, cx| {
15542            let active_item = workspace
15543                .active_item(cx)
15544                .expect("should have an active item after navigating back from the 2nd buffer");
15545            assert_eq!(
15546                active_item.item_id(),
15547                multibuffer_item_id,
15548                "Should navigate back from the 2nd buffer to the multi buffer"
15549            );
15550            assert!(!active_item.is_singleton(cx));
15551        })
15552        .unwrap();
15553
15554    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15555        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15556            s.select_ranges(Some(70..70))
15557        });
15558        editor.open_excerpts(&OpenExcerpts, window, cx);
15559    });
15560    cx.executor().run_until_parked();
15561    workspace
15562        .update(cx, |workspace, window, cx| {
15563            let active_item = workspace
15564                .active_item(cx)
15565                .expect("should have an active item after navigating into the 3rd buffer");
15566            let third_item_id = active_item.item_id();
15567            assert_ne!(
15568                third_item_id, multibuffer_item_id,
15569                "Should navigate into the 3rd buffer and activate it"
15570            );
15571            assert_ne!(third_item_id, first_item_id);
15572            assert_ne!(third_item_id, second_item_id);
15573            assert!(
15574                active_item.is_singleton(cx),
15575                "New active item should be a singleton buffer"
15576            );
15577            assert_eq!(
15578                active_item
15579                    .act_as::<Editor>(cx)
15580                    .expect("should have navigated into an editor")
15581                    .read(cx)
15582                    .text(cx),
15583                sample_text_3
15584            );
15585
15586            workspace
15587                .go_back(workspace.active_pane().downgrade(), window, cx)
15588                .detach_and_log_err(cx);
15589        })
15590        .unwrap();
15591    cx.executor().run_until_parked();
15592    workspace
15593        .update(cx, |workspace, _, cx| {
15594            let active_item = workspace
15595                .active_item(cx)
15596                .expect("should have an active item after navigating back from the 3rd buffer");
15597            assert_eq!(
15598                active_item.item_id(),
15599                multibuffer_item_id,
15600                "Should navigate back from the 3rd buffer to the multi buffer"
15601            );
15602            assert!(!active_item.is_singleton(cx));
15603        })
15604        .unwrap();
15605}
15606
15607#[gpui::test]
15608async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15609    init_test(cx, |_| {});
15610
15611    let mut cx = EditorTestContext::new(cx).await;
15612
15613    let diff_base = r#"
15614        use some::mod;
15615
15616        const A: u32 = 42;
15617
15618        fn main() {
15619            println!("hello");
15620
15621            println!("world");
15622        }
15623        "#
15624    .unindent();
15625
15626    cx.set_state(
15627        &r#"
15628        use some::modified;
15629
15630        ˇ
15631        fn main() {
15632            println!("hello there");
15633
15634            println!("around the");
15635            println!("world");
15636        }
15637        "#
15638        .unindent(),
15639    );
15640
15641    cx.set_head_text(&diff_base);
15642    executor.run_until_parked();
15643
15644    cx.update_editor(|editor, window, cx| {
15645        editor.go_to_next_hunk(&GoToHunk, window, cx);
15646        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15647    });
15648    executor.run_until_parked();
15649    cx.assert_state_with_diff(
15650        r#"
15651          use some::modified;
15652
15653
15654          fn main() {
15655        -     println!("hello");
15656        + ˇ    println!("hello there");
15657
15658              println!("around the");
15659              println!("world");
15660          }
15661        "#
15662        .unindent(),
15663    );
15664
15665    cx.update_editor(|editor, window, cx| {
15666        for _ in 0..2 {
15667            editor.go_to_next_hunk(&GoToHunk, window, cx);
15668            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15669        }
15670    });
15671    executor.run_until_parked();
15672    cx.assert_state_with_diff(
15673        r#"
15674        - use some::mod;
15675        + ˇuse some::modified;
15676
15677
15678          fn main() {
15679        -     println!("hello");
15680        +     println!("hello there");
15681
15682        +     println!("around the");
15683              println!("world");
15684          }
15685        "#
15686        .unindent(),
15687    );
15688
15689    cx.update_editor(|editor, window, cx| {
15690        editor.go_to_next_hunk(&GoToHunk, window, cx);
15691        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15692    });
15693    executor.run_until_parked();
15694    cx.assert_state_with_diff(
15695        r#"
15696        - use some::mod;
15697        + use some::modified;
15698
15699        - const A: u32 = 42;
15700          ˇ
15701          fn main() {
15702        -     println!("hello");
15703        +     println!("hello there");
15704
15705        +     println!("around the");
15706              println!("world");
15707          }
15708        "#
15709        .unindent(),
15710    );
15711
15712    cx.update_editor(|editor, window, cx| {
15713        editor.cancel(&Cancel, window, cx);
15714    });
15715
15716    cx.assert_state_with_diff(
15717        r#"
15718          use some::modified;
15719
15720          ˇ
15721          fn main() {
15722              println!("hello there");
15723
15724              println!("around the");
15725              println!("world");
15726          }
15727        "#
15728        .unindent(),
15729    );
15730}
15731
15732#[gpui::test]
15733async fn test_diff_base_change_with_expanded_diff_hunks(
15734    executor: BackgroundExecutor,
15735    cx: &mut TestAppContext,
15736) {
15737    init_test(cx, |_| {});
15738
15739    let mut cx = EditorTestContext::new(cx).await;
15740
15741    let diff_base = r#"
15742        use some::mod1;
15743        use some::mod2;
15744
15745        const A: u32 = 42;
15746        const B: u32 = 42;
15747        const C: u32 = 42;
15748
15749        fn main() {
15750            println!("hello");
15751
15752            println!("world");
15753        }
15754        "#
15755    .unindent();
15756
15757    cx.set_state(
15758        &r#"
15759        use some::mod2;
15760
15761        const A: u32 = 42;
15762        const C: u32 = 42;
15763
15764        fn main(ˇ) {
15765            //println!("hello");
15766
15767            println!("world");
15768            //
15769            //
15770        }
15771        "#
15772        .unindent(),
15773    );
15774
15775    cx.set_head_text(&diff_base);
15776    executor.run_until_parked();
15777
15778    cx.update_editor(|editor, window, cx| {
15779        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15780    });
15781    executor.run_until_parked();
15782    cx.assert_state_with_diff(
15783        r#"
15784        - use some::mod1;
15785          use some::mod2;
15786
15787          const A: u32 = 42;
15788        - const B: u32 = 42;
15789          const C: u32 = 42;
15790
15791          fn main(ˇ) {
15792        -     println!("hello");
15793        +     //println!("hello");
15794
15795              println!("world");
15796        +     //
15797        +     //
15798          }
15799        "#
15800        .unindent(),
15801    );
15802
15803    cx.set_head_text("new diff base!");
15804    executor.run_until_parked();
15805    cx.assert_state_with_diff(
15806        r#"
15807        - new diff base!
15808        + use some::mod2;
15809        +
15810        + const A: u32 = 42;
15811        + const C: u32 = 42;
15812        +
15813        + fn main(ˇ) {
15814        +     //println!("hello");
15815        +
15816        +     println!("world");
15817        +     //
15818        +     //
15819        + }
15820        "#
15821        .unindent(),
15822    );
15823}
15824
15825#[gpui::test]
15826async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15827    init_test(cx, |_| {});
15828
15829    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15830    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15831    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15832    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15833    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15834    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15835
15836    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15837    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15838    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15839
15840    let multi_buffer = cx.new(|cx| {
15841        let mut multibuffer = MultiBuffer::new(ReadWrite);
15842        multibuffer.push_excerpts(
15843            buffer_1.clone(),
15844            [
15845                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15846                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15847                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15848            ],
15849            cx,
15850        );
15851        multibuffer.push_excerpts(
15852            buffer_2.clone(),
15853            [
15854                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15855                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15856                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15857            ],
15858            cx,
15859        );
15860        multibuffer.push_excerpts(
15861            buffer_3.clone(),
15862            [
15863                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15864                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15865                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15866            ],
15867            cx,
15868        );
15869        multibuffer
15870    });
15871
15872    let editor =
15873        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15874    editor
15875        .update(cx, |editor, _window, cx| {
15876            for (buffer, diff_base) in [
15877                (buffer_1.clone(), file_1_old),
15878                (buffer_2.clone(), file_2_old),
15879                (buffer_3.clone(), file_3_old),
15880            ] {
15881                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15882                editor
15883                    .buffer
15884                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15885            }
15886        })
15887        .unwrap();
15888
15889    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15890    cx.run_until_parked();
15891
15892    cx.assert_editor_state(
15893        &"
15894            ˇaaa
15895            ccc
15896            ddd
15897
15898            ggg
15899            hhh
15900
15901
15902            lll
15903            mmm
15904            NNN
15905
15906            qqq
15907            rrr
15908
15909            uuu
15910            111
15911            222
15912            333
15913
15914            666
15915            777
15916
15917            000
15918            !!!"
15919        .unindent(),
15920    );
15921
15922    cx.update_editor(|editor, window, cx| {
15923        editor.select_all(&SelectAll, window, cx);
15924        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15925    });
15926    cx.executor().run_until_parked();
15927
15928    cx.assert_state_with_diff(
15929        "
15930            «aaa
15931          - bbb
15932            ccc
15933            ddd
15934
15935            ggg
15936            hhh
15937
15938
15939            lll
15940            mmm
15941          - nnn
15942          + NNN
15943
15944            qqq
15945            rrr
15946
15947            uuu
15948            111
15949            222
15950            333
15951
15952          + 666
15953            777
15954
15955            000
15956            !!!ˇ»"
15957            .unindent(),
15958    );
15959}
15960
15961#[gpui::test]
15962async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15963    init_test(cx, |_| {});
15964
15965    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15966    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15967
15968    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15969    let multi_buffer = cx.new(|cx| {
15970        let mut multibuffer = MultiBuffer::new(ReadWrite);
15971        multibuffer.push_excerpts(
15972            buffer.clone(),
15973            [
15974                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15975                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15976                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15977            ],
15978            cx,
15979        );
15980        multibuffer
15981    });
15982
15983    let editor =
15984        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15985    editor
15986        .update(cx, |editor, _window, cx| {
15987            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15988            editor
15989                .buffer
15990                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15991        })
15992        .unwrap();
15993
15994    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15995    cx.run_until_parked();
15996
15997    cx.update_editor(|editor, window, cx| {
15998        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15999    });
16000    cx.executor().run_until_parked();
16001
16002    // When the start of a hunk coincides with the start of its excerpt,
16003    // the hunk is expanded. When the start of a a hunk is earlier than
16004    // the start of its excerpt, the hunk is not expanded.
16005    cx.assert_state_with_diff(
16006        "
16007            ˇaaa
16008          - bbb
16009          + BBB
16010
16011          - ddd
16012          - eee
16013          + DDD
16014          + EEE
16015            fff
16016
16017            iii
16018        "
16019        .unindent(),
16020    );
16021}
16022
16023#[gpui::test]
16024async fn test_edits_around_expanded_insertion_hunks(
16025    executor: BackgroundExecutor,
16026    cx: &mut TestAppContext,
16027) {
16028    init_test(cx, |_| {});
16029
16030    let mut cx = EditorTestContext::new(cx).await;
16031
16032    let diff_base = r#"
16033        use some::mod1;
16034        use some::mod2;
16035
16036        const A: u32 = 42;
16037
16038        fn main() {
16039            println!("hello");
16040
16041            println!("world");
16042        }
16043        "#
16044    .unindent();
16045    executor.run_until_parked();
16046    cx.set_state(
16047        &r#"
16048        use some::mod1;
16049        use some::mod2;
16050
16051        const A: u32 = 42;
16052        const B: u32 = 42;
16053        const C: u32 = 42;
16054        ˇ
16055
16056        fn main() {
16057            println!("hello");
16058
16059            println!("world");
16060        }
16061        "#
16062        .unindent(),
16063    );
16064
16065    cx.set_head_text(&diff_base);
16066    executor.run_until_parked();
16067
16068    cx.update_editor(|editor, window, cx| {
16069        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16070    });
16071    executor.run_until_parked();
16072
16073    cx.assert_state_with_diff(
16074        r#"
16075        use some::mod1;
16076        use some::mod2;
16077
16078        const A: u32 = 42;
16079      + const B: u32 = 42;
16080      + const C: u32 = 42;
16081      + ˇ
16082
16083        fn main() {
16084            println!("hello");
16085
16086            println!("world");
16087        }
16088      "#
16089        .unindent(),
16090    );
16091
16092    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
16093    executor.run_until_parked();
16094
16095    cx.assert_state_with_diff(
16096        r#"
16097        use some::mod1;
16098        use some::mod2;
16099
16100        const A: u32 = 42;
16101      + const B: u32 = 42;
16102      + const C: u32 = 42;
16103      + const D: u32 = 42;
16104      + ˇ
16105
16106        fn main() {
16107            println!("hello");
16108
16109            println!("world");
16110        }
16111      "#
16112        .unindent(),
16113    );
16114
16115    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
16116    executor.run_until_parked();
16117
16118    cx.assert_state_with_diff(
16119        r#"
16120        use some::mod1;
16121        use some::mod2;
16122
16123        const A: u32 = 42;
16124      + const B: u32 = 42;
16125      + const C: u32 = 42;
16126      + const D: u32 = 42;
16127      + const E: u32 = 42;
16128      + ˇ
16129
16130        fn main() {
16131            println!("hello");
16132
16133            println!("world");
16134        }
16135      "#
16136        .unindent(),
16137    );
16138
16139    cx.update_editor(|editor, window, cx| {
16140        editor.delete_line(&DeleteLine, window, cx);
16141    });
16142    executor.run_until_parked();
16143
16144    cx.assert_state_with_diff(
16145        r#"
16146        use some::mod1;
16147        use some::mod2;
16148
16149        const A: u32 = 42;
16150      + const B: u32 = 42;
16151      + const C: u32 = 42;
16152      + const D: u32 = 42;
16153      + const E: u32 = 42;
16154        ˇ
16155        fn main() {
16156            println!("hello");
16157
16158            println!("world");
16159        }
16160      "#
16161        .unindent(),
16162    );
16163
16164    cx.update_editor(|editor, window, cx| {
16165        editor.move_up(&MoveUp, window, cx);
16166        editor.delete_line(&DeleteLine, window, cx);
16167        editor.move_up(&MoveUp, window, cx);
16168        editor.delete_line(&DeleteLine, window, cx);
16169        editor.move_up(&MoveUp, window, cx);
16170        editor.delete_line(&DeleteLine, window, cx);
16171    });
16172    executor.run_until_parked();
16173    cx.assert_state_with_diff(
16174        r#"
16175        use some::mod1;
16176        use some::mod2;
16177
16178        const A: u32 = 42;
16179      + const B: u32 = 42;
16180        ˇ
16181        fn main() {
16182            println!("hello");
16183
16184            println!("world");
16185        }
16186      "#
16187        .unindent(),
16188    );
16189
16190    cx.update_editor(|editor, window, cx| {
16191        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16192        editor.delete_line(&DeleteLine, window, cx);
16193    });
16194    executor.run_until_parked();
16195    cx.assert_state_with_diff(
16196        r#"
16197        ˇ
16198        fn main() {
16199            println!("hello");
16200
16201            println!("world");
16202        }
16203      "#
16204        .unindent(),
16205    );
16206}
16207
16208#[gpui::test]
16209async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16210    init_test(cx, |_| {});
16211
16212    let mut cx = EditorTestContext::new(cx).await;
16213    cx.set_head_text(indoc! { "
16214        one
16215        two
16216        three
16217        four
16218        five
16219        "
16220    });
16221    cx.set_state(indoc! { "
16222        one
16223        ˇthree
16224        five
16225    "});
16226    cx.run_until_parked();
16227    cx.update_editor(|editor, window, cx| {
16228        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16229    });
16230    cx.assert_state_with_diff(
16231        indoc! { "
16232        one
16233      - two
16234        ˇthree
16235      - four
16236        five
16237    "}
16238        .to_string(),
16239    );
16240    cx.update_editor(|editor, window, cx| {
16241        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16242    });
16243
16244    cx.assert_state_with_diff(
16245        indoc! { "
16246        one
16247        ˇthree
16248        five
16249    "}
16250        .to_string(),
16251    );
16252
16253    cx.set_state(indoc! { "
16254        one
16255        ˇTWO
16256        three
16257        four
16258        five
16259    "});
16260    cx.run_until_parked();
16261    cx.update_editor(|editor, window, cx| {
16262        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16263    });
16264
16265    cx.assert_state_with_diff(
16266        indoc! { "
16267            one
16268          - two
16269          + ˇTWO
16270            three
16271            four
16272            five
16273        "}
16274        .to_string(),
16275    );
16276    cx.update_editor(|editor, window, cx| {
16277        editor.move_up(&Default::default(), window, cx);
16278        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16279    });
16280    cx.assert_state_with_diff(
16281        indoc! { "
16282            one
16283            ˇTWO
16284            three
16285            four
16286            five
16287        "}
16288        .to_string(),
16289    );
16290}
16291
16292#[gpui::test]
16293async fn test_edits_around_expanded_deletion_hunks(
16294    executor: BackgroundExecutor,
16295    cx: &mut TestAppContext,
16296) {
16297    init_test(cx, |_| {});
16298
16299    let mut cx = EditorTestContext::new(cx).await;
16300
16301    let diff_base = r#"
16302        use some::mod1;
16303        use some::mod2;
16304
16305        const A: u32 = 42;
16306        const B: u32 = 42;
16307        const C: u32 = 42;
16308
16309
16310        fn main() {
16311            println!("hello");
16312
16313            println!("world");
16314        }
16315    "#
16316    .unindent();
16317    executor.run_until_parked();
16318    cx.set_state(
16319        &r#"
16320        use some::mod1;
16321        use some::mod2;
16322
16323        ˇconst B: u32 = 42;
16324        const C: u32 = 42;
16325
16326
16327        fn main() {
16328            println!("hello");
16329
16330            println!("world");
16331        }
16332        "#
16333        .unindent(),
16334    );
16335
16336    cx.set_head_text(&diff_base);
16337    executor.run_until_parked();
16338
16339    cx.update_editor(|editor, window, cx| {
16340        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16341    });
16342    executor.run_until_parked();
16343
16344    cx.assert_state_with_diff(
16345        r#"
16346        use some::mod1;
16347        use some::mod2;
16348
16349      - const A: u32 = 42;
16350        ˇconst B: u32 = 42;
16351        const C: u32 = 42;
16352
16353
16354        fn main() {
16355            println!("hello");
16356
16357            println!("world");
16358        }
16359      "#
16360        .unindent(),
16361    );
16362
16363    cx.update_editor(|editor, window, cx| {
16364        editor.delete_line(&DeleteLine, window, cx);
16365    });
16366    executor.run_until_parked();
16367    cx.assert_state_with_diff(
16368        r#"
16369        use some::mod1;
16370        use some::mod2;
16371
16372      - const A: u32 = 42;
16373      - const B: u32 = 42;
16374        ˇconst C: u32 = 42;
16375
16376
16377        fn main() {
16378            println!("hello");
16379
16380            println!("world");
16381        }
16382      "#
16383        .unindent(),
16384    );
16385
16386    cx.update_editor(|editor, window, cx| {
16387        editor.delete_line(&DeleteLine, window, cx);
16388    });
16389    executor.run_until_parked();
16390    cx.assert_state_with_diff(
16391        r#"
16392        use some::mod1;
16393        use some::mod2;
16394
16395      - const A: u32 = 42;
16396      - const B: u32 = 42;
16397      - const C: u32 = 42;
16398        ˇ
16399
16400        fn main() {
16401            println!("hello");
16402
16403            println!("world");
16404        }
16405      "#
16406        .unindent(),
16407    );
16408
16409    cx.update_editor(|editor, window, cx| {
16410        editor.handle_input("replacement", window, cx);
16411    });
16412    executor.run_until_parked();
16413    cx.assert_state_with_diff(
16414        r#"
16415        use some::mod1;
16416        use some::mod2;
16417
16418      - const A: u32 = 42;
16419      - const B: u32 = 42;
16420      - const C: u32 = 42;
16421      -
16422      + replacementˇ
16423
16424        fn main() {
16425            println!("hello");
16426
16427            println!("world");
16428        }
16429      "#
16430        .unindent(),
16431    );
16432}
16433
16434#[gpui::test]
16435async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16436    init_test(cx, |_| {});
16437
16438    let mut cx = EditorTestContext::new(cx).await;
16439
16440    let base_text = r#"
16441        one
16442        two
16443        three
16444        four
16445        five
16446    "#
16447    .unindent();
16448    executor.run_until_parked();
16449    cx.set_state(
16450        &r#"
16451        one
16452        two
16453        fˇour
16454        five
16455        "#
16456        .unindent(),
16457    );
16458
16459    cx.set_head_text(&base_text);
16460    executor.run_until_parked();
16461
16462    cx.update_editor(|editor, window, cx| {
16463        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16464    });
16465    executor.run_until_parked();
16466
16467    cx.assert_state_with_diff(
16468        r#"
16469          one
16470          two
16471        - three
16472          fˇour
16473          five
16474        "#
16475        .unindent(),
16476    );
16477
16478    cx.update_editor(|editor, window, cx| {
16479        editor.backspace(&Backspace, window, cx);
16480        editor.backspace(&Backspace, window, cx);
16481    });
16482    executor.run_until_parked();
16483    cx.assert_state_with_diff(
16484        r#"
16485          one
16486          two
16487        - threeˇ
16488        - four
16489        + our
16490          five
16491        "#
16492        .unindent(),
16493    );
16494}
16495
16496#[gpui::test]
16497async fn test_edit_after_expanded_modification_hunk(
16498    executor: BackgroundExecutor,
16499    cx: &mut TestAppContext,
16500) {
16501    init_test(cx, |_| {});
16502
16503    let mut cx = EditorTestContext::new(cx).await;
16504
16505    let diff_base = r#"
16506        use some::mod1;
16507        use some::mod2;
16508
16509        const A: u32 = 42;
16510        const B: u32 = 42;
16511        const C: u32 = 42;
16512        const D: u32 = 42;
16513
16514
16515        fn main() {
16516            println!("hello");
16517
16518            println!("world");
16519        }"#
16520    .unindent();
16521
16522    cx.set_state(
16523        &r#"
16524        use some::mod1;
16525        use some::mod2;
16526
16527        const A: u32 = 42;
16528        const B: u32 = 42;
16529        const C: u32 = 43ˇ
16530        const D: u32 = 42;
16531
16532
16533        fn main() {
16534            println!("hello");
16535
16536            println!("world");
16537        }"#
16538        .unindent(),
16539    );
16540
16541    cx.set_head_text(&diff_base);
16542    executor.run_until_parked();
16543    cx.update_editor(|editor, window, cx| {
16544        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16545    });
16546    executor.run_until_parked();
16547
16548    cx.assert_state_with_diff(
16549        r#"
16550        use some::mod1;
16551        use some::mod2;
16552
16553        const A: u32 = 42;
16554        const B: u32 = 42;
16555      - const C: u32 = 42;
16556      + const C: u32 = 43ˇ
16557        const D: u32 = 42;
16558
16559
16560        fn main() {
16561            println!("hello");
16562
16563            println!("world");
16564        }"#
16565        .unindent(),
16566    );
16567
16568    cx.update_editor(|editor, window, cx| {
16569        editor.handle_input("\nnew_line\n", window, cx);
16570    });
16571    executor.run_until_parked();
16572
16573    cx.assert_state_with_diff(
16574        r#"
16575        use some::mod1;
16576        use some::mod2;
16577
16578        const A: u32 = 42;
16579        const B: u32 = 42;
16580      - const C: u32 = 42;
16581      + const C: u32 = 43
16582      + new_line
16583      + ˇ
16584        const D: u32 = 42;
16585
16586
16587        fn main() {
16588            println!("hello");
16589
16590            println!("world");
16591        }"#
16592        .unindent(),
16593    );
16594}
16595
16596#[gpui::test]
16597async fn test_stage_and_unstage_added_file_hunk(
16598    executor: BackgroundExecutor,
16599    cx: &mut TestAppContext,
16600) {
16601    init_test(cx, |_| {});
16602
16603    let mut cx = EditorTestContext::new(cx).await;
16604    cx.update_editor(|editor, _, cx| {
16605        editor.set_expand_all_diff_hunks(cx);
16606    });
16607
16608    let working_copy = r#"
16609            ˇfn main() {
16610                println!("hello, world!");
16611            }
16612        "#
16613    .unindent();
16614
16615    cx.set_state(&working_copy);
16616    executor.run_until_parked();
16617
16618    cx.assert_state_with_diff(
16619        r#"
16620            + ˇfn main() {
16621            +     println!("hello, world!");
16622            + }
16623        "#
16624        .unindent(),
16625    );
16626    cx.assert_index_text(None);
16627
16628    cx.update_editor(|editor, window, cx| {
16629        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16630    });
16631    executor.run_until_parked();
16632    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16633    cx.assert_state_with_diff(
16634        r#"
16635            + ˇfn main() {
16636            +     println!("hello, world!");
16637            + }
16638        "#
16639        .unindent(),
16640    );
16641
16642    cx.update_editor(|editor, window, cx| {
16643        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16644    });
16645    executor.run_until_parked();
16646    cx.assert_index_text(None);
16647}
16648
16649async fn setup_indent_guides_editor(
16650    text: &str,
16651    cx: &mut TestAppContext,
16652) -> (BufferId, EditorTestContext) {
16653    init_test(cx, |_| {});
16654
16655    let mut cx = EditorTestContext::new(cx).await;
16656
16657    let buffer_id = cx.update_editor(|editor, window, cx| {
16658        editor.set_text(text, window, cx);
16659        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16660
16661        buffer_ids[0]
16662    });
16663
16664    (buffer_id, cx)
16665}
16666
16667fn assert_indent_guides(
16668    range: Range<u32>,
16669    expected: Vec<IndentGuide>,
16670    active_indices: Option<Vec<usize>>,
16671    cx: &mut EditorTestContext,
16672) {
16673    let indent_guides = cx.update_editor(|editor, window, cx| {
16674        let snapshot = editor.snapshot(window, cx).display_snapshot;
16675        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16676            editor,
16677            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16678            true,
16679            &snapshot,
16680            cx,
16681        );
16682
16683        indent_guides.sort_by(|a, b| {
16684            a.depth.cmp(&b.depth).then(
16685                a.start_row
16686                    .cmp(&b.start_row)
16687                    .then(a.end_row.cmp(&b.end_row)),
16688            )
16689        });
16690        indent_guides
16691    });
16692
16693    if let Some(expected) = active_indices {
16694        let active_indices = cx.update_editor(|editor, window, cx| {
16695            let snapshot = editor.snapshot(window, cx).display_snapshot;
16696            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16697        });
16698
16699        assert_eq!(
16700            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16701            expected,
16702            "Active indent guide indices do not match"
16703        );
16704    }
16705
16706    assert_eq!(indent_guides, expected, "Indent guides do not match");
16707}
16708
16709fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16710    IndentGuide {
16711        buffer_id,
16712        start_row: MultiBufferRow(start_row),
16713        end_row: MultiBufferRow(end_row),
16714        depth,
16715        tab_size: 4,
16716        settings: IndentGuideSettings {
16717            enabled: true,
16718            line_width: 1,
16719            active_line_width: 1,
16720            ..Default::default()
16721        },
16722    }
16723}
16724
16725#[gpui::test]
16726async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16727    let (buffer_id, mut cx) = setup_indent_guides_editor(
16728        &"
16729    fn main() {
16730        let a = 1;
16731    }"
16732        .unindent(),
16733        cx,
16734    )
16735    .await;
16736
16737    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16738}
16739
16740#[gpui::test]
16741async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16742    let (buffer_id, mut cx) = setup_indent_guides_editor(
16743        &"
16744    fn main() {
16745        let a = 1;
16746        let b = 2;
16747    }"
16748        .unindent(),
16749        cx,
16750    )
16751    .await;
16752
16753    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16754}
16755
16756#[gpui::test]
16757async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16758    let (buffer_id, mut cx) = setup_indent_guides_editor(
16759        &"
16760    fn main() {
16761        let a = 1;
16762        if a == 3 {
16763            let b = 2;
16764        } else {
16765            let c = 3;
16766        }
16767    }"
16768        .unindent(),
16769        cx,
16770    )
16771    .await;
16772
16773    assert_indent_guides(
16774        0..8,
16775        vec![
16776            indent_guide(buffer_id, 1, 6, 0),
16777            indent_guide(buffer_id, 3, 3, 1),
16778            indent_guide(buffer_id, 5, 5, 1),
16779        ],
16780        None,
16781        &mut cx,
16782    );
16783}
16784
16785#[gpui::test]
16786async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16787    let (buffer_id, mut cx) = setup_indent_guides_editor(
16788        &"
16789    fn main() {
16790        let a = 1;
16791            let b = 2;
16792        let c = 3;
16793    }"
16794        .unindent(),
16795        cx,
16796    )
16797    .await;
16798
16799    assert_indent_guides(
16800        0..5,
16801        vec![
16802            indent_guide(buffer_id, 1, 3, 0),
16803            indent_guide(buffer_id, 2, 2, 1),
16804        ],
16805        None,
16806        &mut cx,
16807    );
16808}
16809
16810#[gpui::test]
16811async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16812    let (buffer_id, mut cx) = setup_indent_guides_editor(
16813        &"
16814        fn main() {
16815            let a = 1;
16816
16817            let c = 3;
16818        }"
16819        .unindent(),
16820        cx,
16821    )
16822    .await;
16823
16824    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16825}
16826
16827#[gpui::test]
16828async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16829    let (buffer_id, mut cx) = setup_indent_guides_editor(
16830        &"
16831        fn main() {
16832            let a = 1;
16833
16834            let c = 3;
16835
16836            if a == 3 {
16837                let b = 2;
16838            } else {
16839                let c = 3;
16840            }
16841        }"
16842        .unindent(),
16843        cx,
16844    )
16845    .await;
16846
16847    assert_indent_guides(
16848        0..11,
16849        vec![
16850            indent_guide(buffer_id, 1, 9, 0),
16851            indent_guide(buffer_id, 6, 6, 1),
16852            indent_guide(buffer_id, 8, 8, 1),
16853        ],
16854        None,
16855        &mut cx,
16856    );
16857}
16858
16859#[gpui::test]
16860async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16861    let (buffer_id, mut cx) = setup_indent_guides_editor(
16862        &"
16863        fn main() {
16864            let a = 1;
16865
16866            let c = 3;
16867
16868            if a == 3 {
16869                let b = 2;
16870            } else {
16871                let c = 3;
16872            }
16873        }"
16874        .unindent(),
16875        cx,
16876    )
16877    .await;
16878
16879    assert_indent_guides(
16880        1..11,
16881        vec![
16882            indent_guide(buffer_id, 1, 9, 0),
16883            indent_guide(buffer_id, 6, 6, 1),
16884            indent_guide(buffer_id, 8, 8, 1),
16885        ],
16886        None,
16887        &mut cx,
16888    );
16889}
16890
16891#[gpui::test]
16892async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16893    let (buffer_id, mut cx) = setup_indent_guides_editor(
16894        &"
16895        fn main() {
16896            let a = 1;
16897
16898            let c = 3;
16899
16900            if a == 3 {
16901                let b = 2;
16902            } else {
16903                let c = 3;
16904            }
16905        }"
16906        .unindent(),
16907        cx,
16908    )
16909    .await;
16910
16911    assert_indent_guides(
16912        1..10,
16913        vec![
16914            indent_guide(buffer_id, 1, 9, 0),
16915            indent_guide(buffer_id, 6, 6, 1),
16916            indent_guide(buffer_id, 8, 8, 1),
16917        ],
16918        None,
16919        &mut cx,
16920    );
16921}
16922
16923#[gpui::test]
16924async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16925    let (buffer_id, mut cx) = setup_indent_guides_editor(
16926        &"
16927        block1
16928            block2
16929                block3
16930                    block4
16931            block2
16932        block1
16933        block1"
16934            .unindent(),
16935        cx,
16936    )
16937    .await;
16938
16939    assert_indent_guides(
16940        1..10,
16941        vec![
16942            indent_guide(buffer_id, 1, 4, 0),
16943            indent_guide(buffer_id, 2, 3, 1),
16944            indent_guide(buffer_id, 3, 3, 2),
16945        ],
16946        None,
16947        &mut cx,
16948    );
16949}
16950
16951#[gpui::test]
16952async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16953    let (buffer_id, mut cx) = setup_indent_guides_editor(
16954        &"
16955        block1
16956            block2
16957                block3
16958
16959        block1
16960        block1"
16961            .unindent(),
16962        cx,
16963    )
16964    .await;
16965
16966    assert_indent_guides(
16967        0..6,
16968        vec![
16969            indent_guide(buffer_id, 1, 2, 0),
16970            indent_guide(buffer_id, 2, 2, 1),
16971        ],
16972        None,
16973        &mut cx,
16974    );
16975}
16976
16977#[gpui::test]
16978async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16979    let (buffer_id, mut cx) = setup_indent_guides_editor(
16980        &"
16981        block1
16982
16983
16984
16985            block2
16986        "
16987        .unindent(),
16988        cx,
16989    )
16990    .await;
16991
16992    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16993}
16994
16995#[gpui::test]
16996async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16997    let (buffer_id, mut cx) = setup_indent_guides_editor(
16998        &"
16999        def a:
17000        \tb = 3
17001        \tif True:
17002        \t\tc = 4
17003        \t\td = 5
17004        \tprint(b)
17005        "
17006        .unindent(),
17007        cx,
17008    )
17009    .await;
17010
17011    assert_indent_guides(
17012        0..6,
17013        vec![
17014            indent_guide(buffer_id, 1, 5, 0),
17015            indent_guide(buffer_id, 3, 4, 1),
17016        ],
17017        None,
17018        &mut cx,
17019    );
17020}
17021
17022#[gpui::test]
17023async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
17024    let (buffer_id, mut cx) = setup_indent_guides_editor(
17025        &"
17026    fn main() {
17027        let a = 1;
17028    }"
17029        .unindent(),
17030        cx,
17031    )
17032    .await;
17033
17034    cx.update_editor(|editor, window, cx| {
17035        editor.change_selections(None, window, cx, |s| {
17036            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17037        });
17038    });
17039
17040    assert_indent_guides(
17041        0..3,
17042        vec![indent_guide(buffer_id, 1, 1, 0)],
17043        Some(vec![0]),
17044        &mut cx,
17045    );
17046}
17047
17048#[gpui::test]
17049async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
17050    let (buffer_id, mut cx) = setup_indent_guides_editor(
17051        &"
17052    fn main() {
17053        if 1 == 2 {
17054            let a = 1;
17055        }
17056    }"
17057        .unindent(),
17058        cx,
17059    )
17060    .await;
17061
17062    cx.update_editor(|editor, window, cx| {
17063        editor.change_selections(None, window, cx, |s| {
17064            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17065        });
17066    });
17067
17068    assert_indent_guides(
17069        0..4,
17070        vec![
17071            indent_guide(buffer_id, 1, 3, 0),
17072            indent_guide(buffer_id, 2, 2, 1),
17073        ],
17074        Some(vec![1]),
17075        &mut cx,
17076    );
17077
17078    cx.update_editor(|editor, window, cx| {
17079        editor.change_selections(None, window, cx, |s| {
17080            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17081        });
17082    });
17083
17084    assert_indent_guides(
17085        0..4,
17086        vec![
17087            indent_guide(buffer_id, 1, 3, 0),
17088            indent_guide(buffer_id, 2, 2, 1),
17089        ],
17090        Some(vec![1]),
17091        &mut cx,
17092    );
17093
17094    cx.update_editor(|editor, window, cx| {
17095        editor.change_selections(None, window, cx, |s| {
17096            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
17097        });
17098    });
17099
17100    assert_indent_guides(
17101        0..4,
17102        vec![
17103            indent_guide(buffer_id, 1, 3, 0),
17104            indent_guide(buffer_id, 2, 2, 1),
17105        ],
17106        Some(vec![0]),
17107        &mut cx,
17108    );
17109}
17110
17111#[gpui::test]
17112async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
17113    let (buffer_id, mut cx) = setup_indent_guides_editor(
17114        &"
17115    fn main() {
17116        let a = 1;
17117
17118        let b = 2;
17119    }"
17120        .unindent(),
17121        cx,
17122    )
17123    .await;
17124
17125    cx.update_editor(|editor, window, cx| {
17126        editor.change_selections(None, window, cx, |s| {
17127            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17128        });
17129    });
17130
17131    assert_indent_guides(
17132        0..5,
17133        vec![indent_guide(buffer_id, 1, 3, 0)],
17134        Some(vec![0]),
17135        &mut cx,
17136    );
17137}
17138
17139#[gpui::test]
17140async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17141    let (buffer_id, mut cx) = setup_indent_guides_editor(
17142        &"
17143    def m:
17144        a = 1
17145        pass"
17146            .unindent(),
17147        cx,
17148    )
17149    .await;
17150
17151    cx.update_editor(|editor, window, cx| {
17152        editor.change_selections(None, window, cx, |s| {
17153            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17154        });
17155    });
17156
17157    assert_indent_guides(
17158        0..3,
17159        vec![indent_guide(buffer_id, 1, 2, 0)],
17160        Some(vec![0]),
17161        &mut cx,
17162    );
17163}
17164
17165#[gpui::test]
17166async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17167    init_test(cx, |_| {});
17168    let mut cx = EditorTestContext::new(cx).await;
17169    let text = indoc! {
17170        "
17171        impl A {
17172            fn b() {
17173                0;
17174                3;
17175                5;
17176                6;
17177                7;
17178            }
17179        }
17180        "
17181    };
17182    let base_text = indoc! {
17183        "
17184        impl A {
17185            fn b() {
17186                0;
17187                1;
17188                2;
17189                3;
17190                4;
17191            }
17192            fn c() {
17193                5;
17194                6;
17195                7;
17196            }
17197        }
17198        "
17199    };
17200
17201    cx.update_editor(|editor, window, cx| {
17202        editor.set_text(text, window, cx);
17203
17204        editor.buffer().update(cx, |multibuffer, cx| {
17205            let buffer = multibuffer.as_singleton().unwrap();
17206            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17207
17208            multibuffer.set_all_diff_hunks_expanded(cx);
17209            multibuffer.add_diff(diff, cx);
17210
17211            buffer.read(cx).remote_id()
17212        })
17213    });
17214    cx.run_until_parked();
17215
17216    cx.assert_state_with_diff(
17217        indoc! { "
17218          impl A {
17219              fn b() {
17220                  0;
17221        -         1;
17222        -         2;
17223                  3;
17224        -         4;
17225        -     }
17226        -     fn c() {
17227                  5;
17228                  6;
17229                  7;
17230              }
17231          }
17232          ˇ"
17233        }
17234        .to_string(),
17235    );
17236
17237    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17238        editor
17239            .snapshot(window, cx)
17240            .buffer_snapshot
17241            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17242            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17243            .collect::<Vec<_>>()
17244    });
17245    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17246    assert_eq!(
17247        actual_guides,
17248        vec![
17249            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17250            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17251            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17252        ]
17253    );
17254}
17255
17256#[gpui::test]
17257async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17258    init_test(cx, |_| {});
17259    let mut cx = EditorTestContext::new(cx).await;
17260
17261    let diff_base = r#"
17262        a
17263        b
17264        c
17265        "#
17266    .unindent();
17267
17268    cx.set_state(
17269        &r#"
17270        ˇA
17271        b
17272        C
17273        "#
17274        .unindent(),
17275    );
17276    cx.set_head_text(&diff_base);
17277    cx.update_editor(|editor, window, cx| {
17278        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17279    });
17280    executor.run_until_parked();
17281
17282    let both_hunks_expanded = r#"
17283        - a
17284        + ˇA
17285          b
17286        - c
17287        + C
17288        "#
17289    .unindent();
17290
17291    cx.assert_state_with_diff(both_hunks_expanded.clone());
17292
17293    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17294        let snapshot = editor.snapshot(window, cx);
17295        let hunks = editor
17296            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17297            .collect::<Vec<_>>();
17298        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17299        let buffer_id = hunks[0].buffer_id;
17300        hunks
17301            .into_iter()
17302            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17303            .collect::<Vec<_>>()
17304    });
17305    assert_eq!(hunk_ranges.len(), 2);
17306
17307    cx.update_editor(|editor, _, cx| {
17308        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17309    });
17310    executor.run_until_parked();
17311
17312    let second_hunk_expanded = r#"
17313          ˇA
17314          b
17315        - c
17316        + C
17317        "#
17318    .unindent();
17319
17320    cx.assert_state_with_diff(second_hunk_expanded);
17321
17322    cx.update_editor(|editor, _, cx| {
17323        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17324    });
17325    executor.run_until_parked();
17326
17327    cx.assert_state_with_diff(both_hunks_expanded.clone());
17328
17329    cx.update_editor(|editor, _, cx| {
17330        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17331    });
17332    executor.run_until_parked();
17333
17334    let first_hunk_expanded = r#"
17335        - a
17336        + ˇA
17337          b
17338          C
17339        "#
17340    .unindent();
17341
17342    cx.assert_state_with_diff(first_hunk_expanded);
17343
17344    cx.update_editor(|editor, _, cx| {
17345        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17346    });
17347    executor.run_until_parked();
17348
17349    cx.assert_state_with_diff(both_hunks_expanded);
17350
17351    cx.set_state(
17352        &r#"
17353        ˇA
17354        b
17355        "#
17356        .unindent(),
17357    );
17358    cx.run_until_parked();
17359
17360    // TODO this cursor position seems bad
17361    cx.assert_state_with_diff(
17362        r#"
17363        - ˇa
17364        + A
17365          b
17366        "#
17367        .unindent(),
17368    );
17369
17370    cx.update_editor(|editor, window, cx| {
17371        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17372    });
17373
17374    cx.assert_state_with_diff(
17375        r#"
17376            - ˇa
17377            + A
17378              b
17379            - c
17380            "#
17381        .unindent(),
17382    );
17383
17384    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17385        let snapshot = editor.snapshot(window, cx);
17386        let hunks = editor
17387            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17388            .collect::<Vec<_>>();
17389        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17390        let buffer_id = hunks[0].buffer_id;
17391        hunks
17392            .into_iter()
17393            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17394            .collect::<Vec<_>>()
17395    });
17396    assert_eq!(hunk_ranges.len(), 2);
17397
17398    cx.update_editor(|editor, _, cx| {
17399        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17400    });
17401    executor.run_until_parked();
17402
17403    cx.assert_state_with_diff(
17404        r#"
17405        - ˇa
17406        + A
17407          b
17408        "#
17409        .unindent(),
17410    );
17411}
17412
17413#[gpui::test]
17414async fn test_toggle_deletion_hunk_at_start_of_file(
17415    executor: BackgroundExecutor,
17416    cx: &mut TestAppContext,
17417) {
17418    init_test(cx, |_| {});
17419    let mut cx = EditorTestContext::new(cx).await;
17420
17421    let diff_base = r#"
17422        a
17423        b
17424        c
17425        "#
17426    .unindent();
17427
17428    cx.set_state(
17429        &r#"
17430        ˇb
17431        c
17432        "#
17433        .unindent(),
17434    );
17435    cx.set_head_text(&diff_base);
17436    cx.update_editor(|editor, window, cx| {
17437        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17438    });
17439    executor.run_until_parked();
17440
17441    let hunk_expanded = r#"
17442        - a
17443          ˇb
17444          c
17445        "#
17446    .unindent();
17447
17448    cx.assert_state_with_diff(hunk_expanded.clone());
17449
17450    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17451        let snapshot = editor.snapshot(window, cx);
17452        let hunks = editor
17453            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17454            .collect::<Vec<_>>();
17455        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17456        let buffer_id = hunks[0].buffer_id;
17457        hunks
17458            .into_iter()
17459            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17460            .collect::<Vec<_>>()
17461    });
17462    assert_eq!(hunk_ranges.len(), 1);
17463
17464    cx.update_editor(|editor, _, cx| {
17465        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17466    });
17467    executor.run_until_parked();
17468
17469    let hunk_collapsed = r#"
17470          ˇb
17471          c
17472        "#
17473    .unindent();
17474
17475    cx.assert_state_with_diff(hunk_collapsed);
17476
17477    cx.update_editor(|editor, _, cx| {
17478        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17479    });
17480    executor.run_until_parked();
17481
17482    cx.assert_state_with_diff(hunk_expanded.clone());
17483}
17484
17485#[gpui::test]
17486async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17487    init_test(cx, |_| {});
17488
17489    let fs = FakeFs::new(cx.executor());
17490    fs.insert_tree(
17491        path!("/test"),
17492        json!({
17493            ".git": {},
17494            "file-1": "ONE\n",
17495            "file-2": "TWO\n",
17496            "file-3": "THREE\n",
17497        }),
17498    )
17499    .await;
17500
17501    fs.set_head_for_repo(
17502        path!("/test/.git").as_ref(),
17503        &[
17504            ("file-1".into(), "one\n".into()),
17505            ("file-2".into(), "two\n".into()),
17506            ("file-3".into(), "three\n".into()),
17507        ],
17508    );
17509
17510    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17511    let mut buffers = vec![];
17512    for i in 1..=3 {
17513        let buffer = project
17514            .update(cx, |project, cx| {
17515                let path = format!(path!("/test/file-{}"), i);
17516                project.open_local_buffer(path, cx)
17517            })
17518            .await
17519            .unwrap();
17520        buffers.push(buffer);
17521    }
17522
17523    let multibuffer = cx.new(|cx| {
17524        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17525        multibuffer.set_all_diff_hunks_expanded(cx);
17526        for buffer in &buffers {
17527            let snapshot = buffer.read(cx).snapshot();
17528            multibuffer.set_excerpts_for_path(
17529                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17530                buffer.clone(),
17531                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17532                DEFAULT_MULTIBUFFER_CONTEXT,
17533                cx,
17534            );
17535        }
17536        multibuffer
17537    });
17538
17539    let editor = cx.add_window(|window, cx| {
17540        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17541    });
17542    cx.run_until_parked();
17543
17544    let snapshot = editor
17545        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17546        .unwrap();
17547    let hunks = snapshot
17548        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17549        .map(|hunk| match hunk {
17550            DisplayDiffHunk::Unfolded {
17551                display_row_range, ..
17552            } => display_row_range,
17553            DisplayDiffHunk::Folded { .. } => unreachable!(),
17554        })
17555        .collect::<Vec<_>>();
17556    assert_eq!(
17557        hunks,
17558        [
17559            DisplayRow(2)..DisplayRow(4),
17560            DisplayRow(7)..DisplayRow(9),
17561            DisplayRow(12)..DisplayRow(14),
17562        ]
17563    );
17564}
17565
17566#[gpui::test]
17567async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17568    init_test(cx, |_| {});
17569
17570    let mut cx = EditorTestContext::new(cx).await;
17571    cx.set_head_text(indoc! { "
17572        one
17573        two
17574        three
17575        four
17576        five
17577        "
17578    });
17579    cx.set_index_text(indoc! { "
17580        one
17581        two
17582        three
17583        four
17584        five
17585        "
17586    });
17587    cx.set_state(indoc! {"
17588        one
17589        TWO
17590        ˇTHREE
17591        FOUR
17592        five
17593    "});
17594    cx.run_until_parked();
17595    cx.update_editor(|editor, window, cx| {
17596        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17597    });
17598    cx.run_until_parked();
17599    cx.assert_index_text(Some(indoc! {"
17600        one
17601        TWO
17602        THREE
17603        FOUR
17604        five
17605    "}));
17606    cx.set_state(indoc! { "
17607        one
17608        TWO
17609        ˇTHREE-HUNDRED
17610        FOUR
17611        five
17612    "});
17613    cx.run_until_parked();
17614    cx.update_editor(|editor, window, cx| {
17615        let snapshot = editor.snapshot(window, cx);
17616        let hunks = editor
17617            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17618            .collect::<Vec<_>>();
17619        assert_eq!(hunks.len(), 1);
17620        assert_eq!(
17621            hunks[0].status(),
17622            DiffHunkStatus {
17623                kind: DiffHunkStatusKind::Modified,
17624                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17625            }
17626        );
17627
17628        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17629    });
17630    cx.run_until_parked();
17631    cx.assert_index_text(Some(indoc! {"
17632        one
17633        TWO
17634        THREE-HUNDRED
17635        FOUR
17636        five
17637    "}));
17638}
17639
17640#[gpui::test]
17641fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17642    init_test(cx, |_| {});
17643
17644    let editor = cx.add_window(|window, cx| {
17645        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17646        build_editor(buffer, window, cx)
17647    });
17648
17649    let render_args = Arc::new(Mutex::new(None));
17650    let snapshot = editor
17651        .update(cx, |editor, window, cx| {
17652            let snapshot = editor.buffer().read(cx).snapshot(cx);
17653            let range =
17654                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17655
17656            struct RenderArgs {
17657                row: MultiBufferRow,
17658                folded: bool,
17659                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17660            }
17661
17662            let crease = Crease::inline(
17663                range,
17664                FoldPlaceholder::test(),
17665                {
17666                    let toggle_callback = render_args.clone();
17667                    move |row, folded, callback, _window, _cx| {
17668                        *toggle_callback.lock() = Some(RenderArgs {
17669                            row,
17670                            folded,
17671                            callback,
17672                        });
17673                        div()
17674                    }
17675                },
17676                |_row, _folded, _window, _cx| div(),
17677            );
17678
17679            editor.insert_creases(Some(crease), cx);
17680            let snapshot = editor.snapshot(window, cx);
17681            let _div = snapshot.render_crease_toggle(
17682                MultiBufferRow(1),
17683                false,
17684                cx.entity().clone(),
17685                window,
17686                cx,
17687            );
17688            snapshot
17689        })
17690        .unwrap();
17691
17692    let render_args = render_args.lock().take().unwrap();
17693    assert_eq!(render_args.row, MultiBufferRow(1));
17694    assert!(!render_args.folded);
17695    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17696
17697    cx.update_window(*editor, |_, window, cx| {
17698        (render_args.callback)(true, window, cx)
17699    })
17700    .unwrap();
17701    let snapshot = editor
17702        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17703        .unwrap();
17704    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17705
17706    cx.update_window(*editor, |_, window, cx| {
17707        (render_args.callback)(false, window, cx)
17708    })
17709    .unwrap();
17710    let snapshot = editor
17711        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17712        .unwrap();
17713    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17714}
17715
17716#[gpui::test]
17717async fn test_input_text(cx: &mut TestAppContext) {
17718    init_test(cx, |_| {});
17719    let mut cx = EditorTestContext::new(cx).await;
17720
17721    cx.set_state(
17722        &r#"ˇone
17723        two
17724
17725        three
17726        fourˇ
17727        five
17728
17729        siˇx"#
17730            .unindent(),
17731    );
17732
17733    cx.dispatch_action(HandleInput(String::new()));
17734    cx.assert_editor_state(
17735        &r#"ˇone
17736        two
17737
17738        three
17739        fourˇ
17740        five
17741
17742        siˇx"#
17743            .unindent(),
17744    );
17745
17746    cx.dispatch_action(HandleInput("AAAA".to_string()));
17747    cx.assert_editor_state(
17748        &r#"AAAAˇone
17749        two
17750
17751        three
17752        fourAAAAˇ
17753        five
17754
17755        siAAAAˇx"#
17756            .unindent(),
17757    );
17758}
17759
17760#[gpui::test]
17761async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17762    init_test(cx, |_| {});
17763
17764    let mut cx = EditorTestContext::new(cx).await;
17765    cx.set_state(
17766        r#"let foo = 1;
17767let foo = 2;
17768let foo = 3;
17769let fooˇ = 4;
17770let foo = 5;
17771let foo = 6;
17772let foo = 7;
17773let foo = 8;
17774let foo = 9;
17775let foo = 10;
17776let foo = 11;
17777let foo = 12;
17778let foo = 13;
17779let foo = 14;
17780let foo = 15;"#,
17781    );
17782
17783    cx.update_editor(|e, window, cx| {
17784        assert_eq!(
17785            e.next_scroll_position,
17786            NextScrollCursorCenterTopBottom::Center,
17787            "Default next scroll direction is center",
17788        );
17789
17790        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17791        assert_eq!(
17792            e.next_scroll_position,
17793            NextScrollCursorCenterTopBottom::Top,
17794            "After center, next scroll direction should be top",
17795        );
17796
17797        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17798        assert_eq!(
17799            e.next_scroll_position,
17800            NextScrollCursorCenterTopBottom::Bottom,
17801            "After top, next scroll direction should be bottom",
17802        );
17803
17804        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17805        assert_eq!(
17806            e.next_scroll_position,
17807            NextScrollCursorCenterTopBottom::Center,
17808            "After bottom, scrolling should start over",
17809        );
17810
17811        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17812        assert_eq!(
17813            e.next_scroll_position,
17814            NextScrollCursorCenterTopBottom::Top,
17815            "Scrolling continues if retriggered fast enough"
17816        );
17817    });
17818
17819    cx.executor()
17820        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17821    cx.executor().run_until_parked();
17822    cx.update_editor(|e, _, _| {
17823        assert_eq!(
17824            e.next_scroll_position,
17825            NextScrollCursorCenterTopBottom::Center,
17826            "If scrolling is not triggered fast enough, it should reset"
17827        );
17828    });
17829}
17830
17831#[gpui::test]
17832async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17833    init_test(cx, |_| {});
17834    let mut cx = EditorLspTestContext::new_rust(
17835        lsp::ServerCapabilities {
17836            definition_provider: Some(lsp::OneOf::Left(true)),
17837            references_provider: Some(lsp::OneOf::Left(true)),
17838            ..lsp::ServerCapabilities::default()
17839        },
17840        cx,
17841    )
17842    .await;
17843
17844    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17845        let go_to_definition = cx
17846            .lsp
17847            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17848                move |params, _| async move {
17849                    if empty_go_to_definition {
17850                        Ok(None)
17851                    } else {
17852                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17853                            uri: params.text_document_position_params.text_document.uri,
17854                            range: lsp::Range::new(
17855                                lsp::Position::new(4, 3),
17856                                lsp::Position::new(4, 6),
17857                            ),
17858                        })))
17859                    }
17860                },
17861            );
17862        let references = cx
17863            .lsp
17864            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17865                Ok(Some(vec![lsp::Location {
17866                    uri: params.text_document_position.text_document.uri,
17867                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17868                }]))
17869            });
17870        (go_to_definition, references)
17871    };
17872
17873    cx.set_state(
17874        &r#"fn one() {
17875            let mut a = ˇtwo();
17876        }
17877
17878        fn two() {}"#
17879            .unindent(),
17880    );
17881    set_up_lsp_handlers(false, &mut cx);
17882    let navigated = cx
17883        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17884        .await
17885        .expect("Failed to navigate to definition");
17886    assert_eq!(
17887        navigated,
17888        Navigated::Yes,
17889        "Should have navigated to definition from the GetDefinition response"
17890    );
17891    cx.assert_editor_state(
17892        &r#"fn one() {
17893            let mut a = two();
17894        }
17895
17896        fn «twoˇ»() {}"#
17897            .unindent(),
17898    );
17899
17900    let editors = cx.update_workspace(|workspace, _, cx| {
17901        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17902    });
17903    cx.update_editor(|_, _, test_editor_cx| {
17904        assert_eq!(
17905            editors.len(),
17906            1,
17907            "Initially, only one, test, editor should be open in the workspace"
17908        );
17909        assert_eq!(
17910            test_editor_cx.entity(),
17911            editors.last().expect("Asserted len is 1").clone()
17912        );
17913    });
17914
17915    set_up_lsp_handlers(true, &mut cx);
17916    let navigated = cx
17917        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17918        .await
17919        .expect("Failed to navigate to lookup references");
17920    assert_eq!(
17921        navigated,
17922        Navigated::Yes,
17923        "Should have navigated to references as a fallback after empty GoToDefinition response"
17924    );
17925    // We should not change the selections in the existing file,
17926    // if opening another milti buffer with the references
17927    cx.assert_editor_state(
17928        &r#"fn one() {
17929            let mut a = two();
17930        }
17931
17932        fn «twoˇ»() {}"#
17933            .unindent(),
17934    );
17935    let editors = cx.update_workspace(|workspace, _, cx| {
17936        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17937    });
17938    cx.update_editor(|_, _, test_editor_cx| {
17939        assert_eq!(
17940            editors.len(),
17941            2,
17942            "After falling back to references search, we open a new editor with the results"
17943        );
17944        let references_fallback_text = editors
17945            .into_iter()
17946            .find(|new_editor| *new_editor != test_editor_cx.entity())
17947            .expect("Should have one non-test editor now")
17948            .read(test_editor_cx)
17949            .text(test_editor_cx);
17950        assert_eq!(
17951            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17952            "Should use the range from the references response and not the GoToDefinition one"
17953        );
17954    });
17955}
17956
17957#[gpui::test]
17958async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17959    init_test(cx, |_| {});
17960    cx.update(|cx| {
17961        let mut editor_settings = EditorSettings::get_global(cx).clone();
17962        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17963        EditorSettings::override_global(editor_settings, cx);
17964    });
17965    let mut cx = EditorLspTestContext::new_rust(
17966        lsp::ServerCapabilities {
17967            definition_provider: Some(lsp::OneOf::Left(true)),
17968            references_provider: Some(lsp::OneOf::Left(true)),
17969            ..lsp::ServerCapabilities::default()
17970        },
17971        cx,
17972    )
17973    .await;
17974    let original_state = r#"fn one() {
17975        let mut a = ˇtwo();
17976    }
17977
17978    fn two() {}"#
17979        .unindent();
17980    cx.set_state(&original_state);
17981
17982    let mut go_to_definition = cx
17983        .lsp
17984        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17985            move |_, _| async move { Ok(None) },
17986        );
17987    let _references = cx
17988        .lsp
17989        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17990            panic!("Should not call for references with no go to definition fallback")
17991        });
17992
17993    let navigated = cx
17994        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17995        .await
17996        .expect("Failed to navigate to lookup references");
17997    go_to_definition
17998        .next()
17999        .await
18000        .expect("Should have called the go_to_definition handler");
18001
18002    assert_eq!(
18003        navigated,
18004        Navigated::No,
18005        "Should have navigated to references as a fallback after empty GoToDefinition response"
18006    );
18007    cx.assert_editor_state(&original_state);
18008    let editors = cx.update_workspace(|workspace, _, cx| {
18009        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
18010    });
18011    cx.update_editor(|_, _, _| {
18012        assert_eq!(
18013            editors.len(),
18014            1,
18015            "After unsuccessful fallback, no other editor should have been opened"
18016        );
18017    });
18018}
18019
18020#[gpui::test]
18021async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
18022    init_test(cx, |_| {});
18023
18024    let language = Arc::new(Language::new(
18025        LanguageConfig::default(),
18026        Some(tree_sitter_rust::LANGUAGE.into()),
18027    ));
18028
18029    let text = r#"
18030        #[cfg(test)]
18031        mod tests() {
18032            #[test]
18033            fn runnable_1() {
18034                let a = 1;
18035            }
18036
18037            #[test]
18038            fn runnable_2() {
18039                let a = 1;
18040                let b = 2;
18041            }
18042        }
18043    "#
18044    .unindent();
18045
18046    let fs = FakeFs::new(cx.executor());
18047    fs.insert_file("/file.rs", Default::default()).await;
18048
18049    let project = Project::test(fs, ["/a".as_ref()], cx).await;
18050    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18051    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18052    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
18053    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
18054
18055    let editor = cx.new_window_entity(|window, cx| {
18056        Editor::new(
18057            EditorMode::full(),
18058            multi_buffer,
18059            Some(project.clone()),
18060            window,
18061            cx,
18062        )
18063    });
18064
18065    editor.update_in(cx, |editor, window, cx| {
18066        let snapshot = editor.buffer().read(cx).snapshot(cx);
18067        editor.tasks.insert(
18068            (buffer.read(cx).remote_id(), 3),
18069            RunnableTasks {
18070                templates: vec![],
18071                offset: snapshot.anchor_before(43),
18072                column: 0,
18073                extra_variables: HashMap::default(),
18074                context_range: BufferOffset(43)..BufferOffset(85),
18075            },
18076        );
18077        editor.tasks.insert(
18078            (buffer.read(cx).remote_id(), 8),
18079            RunnableTasks {
18080                templates: vec![],
18081                offset: snapshot.anchor_before(86),
18082                column: 0,
18083                extra_variables: HashMap::default(),
18084                context_range: BufferOffset(86)..BufferOffset(191),
18085            },
18086        );
18087
18088        // Test finding task when cursor is inside function body
18089        editor.change_selections(None, window, cx, |s| {
18090            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
18091        });
18092        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18093        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
18094
18095        // Test finding task when cursor is on function name
18096        editor.change_selections(None, window, cx, |s| {
18097            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
18098        });
18099        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18100        assert_eq!(row, 8, "Should find task when cursor is on function name");
18101    });
18102}
18103
18104#[gpui::test]
18105async fn test_folding_buffers(cx: &mut TestAppContext) {
18106    init_test(cx, |_| {});
18107
18108    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18109    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
18110    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
18111
18112    let fs = FakeFs::new(cx.executor());
18113    fs.insert_tree(
18114        path!("/a"),
18115        json!({
18116            "first.rs": sample_text_1,
18117            "second.rs": sample_text_2,
18118            "third.rs": sample_text_3,
18119        }),
18120    )
18121    .await;
18122    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18123    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18124    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18125    let worktree = project.update(cx, |project, cx| {
18126        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18127        assert_eq!(worktrees.len(), 1);
18128        worktrees.pop().unwrap()
18129    });
18130    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18131
18132    let buffer_1 = project
18133        .update(cx, |project, cx| {
18134            project.open_buffer((worktree_id, "first.rs"), cx)
18135        })
18136        .await
18137        .unwrap();
18138    let buffer_2 = project
18139        .update(cx, |project, cx| {
18140            project.open_buffer((worktree_id, "second.rs"), cx)
18141        })
18142        .await
18143        .unwrap();
18144    let buffer_3 = project
18145        .update(cx, |project, cx| {
18146            project.open_buffer((worktree_id, "third.rs"), cx)
18147        })
18148        .await
18149        .unwrap();
18150
18151    let multi_buffer = cx.new(|cx| {
18152        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18153        multi_buffer.push_excerpts(
18154            buffer_1.clone(),
18155            [
18156                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18157                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18158                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18159            ],
18160            cx,
18161        );
18162        multi_buffer.push_excerpts(
18163            buffer_2.clone(),
18164            [
18165                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18166                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18167                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18168            ],
18169            cx,
18170        );
18171        multi_buffer.push_excerpts(
18172            buffer_3.clone(),
18173            [
18174                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18175                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18176                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18177            ],
18178            cx,
18179        );
18180        multi_buffer
18181    });
18182    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18183        Editor::new(
18184            EditorMode::full(),
18185            multi_buffer.clone(),
18186            Some(project.clone()),
18187            window,
18188            cx,
18189        )
18190    });
18191
18192    assert_eq!(
18193        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18194        "\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",
18195    );
18196
18197    multi_buffer_editor.update(cx, |editor, cx| {
18198        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18199    });
18200    assert_eq!(
18201        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18202        "\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",
18203        "After folding the first buffer, its text should not be displayed"
18204    );
18205
18206    multi_buffer_editor.update(cx, |editor, cx| {
18207        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18208    });
18209    assert_eq!(
18210        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18211        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18212        "After folding the second buffer, its text should not be displayed"
18213    );
18214
18215    multi_buffer_editor.update(cx, |editor, cx| {
18216        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18217    });
18218    assert_eq!(
18219        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18220        "\n\n\n\n\n",
18221        "After folding the third buffer, its text should not be displayed"
18222    );
18223
18224    // Emulate selection inside the fold logic, that should work
18225    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18226        editor
18227            .snapshot(window, cx)
18228            .next_line_boundary(Point::new(0, 4));
18229    });
18230
18231    multi_buffer_editor.update(cx, |editor, cx| {
18232        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18233    });
18234    assert_eq!(
18235        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18236        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18237        "After unfolding the second buffer, its text should be displayed"
18238    );
18239
18240    // Typing inside of buffer 1 causes that buffer to be unfolded.
18241    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18242        assert_eq!(
18243            multi_buffer
18244                .read(cx)
18245                .snapshot(cx)
18246                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18247                .collect::<String>(),
18248            "bbbb"
18249        );
18250        editor.change_selections(None, window, cx, |selections| {
18251            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18252        });
18253        editor.handle_input("B", window, cx);
18254    });
18255
18256    assert_eq!(
18257        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18258        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18259        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18260    );
18261
18262    multi_buffer_editor.update(cx, |editor, cx| {
18263        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18264    });
18265    assert_eq!(
18266        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18267        "\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",
18268        "After unfolding the all buffers, all original text should be displayed"
18269    );
18270}
18271
18272#[gpui::test]
18273async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18274    init_test(cx, |_| {});
18275
18276    let sample_text_1 = "1111\n2222\n3333".to_string();
18277    let sample_text_2 = "4444\n5555\n6666".to_string();
18278    let sample_text_3 = "7777\n8888\n9999".to_string();
18279
18280    let fs = FakeFs::new(cx.executor());
18281    fs.insert_tree(
18282        path!("/a"),
18283        json!({
18284            "first.rs": sample_text_1,
18285            "second.rs": sample_text_2,
18286            "third.rs": sample_text_3,
18287        }),
18288    )
18289    .await;
18290    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18291    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18292    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18293    let worktree = project.update(cx, |project, cx| {
18294        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18295        assert_eq!(worktrees.len(), 1);
18296        worktrees.pop().unwrap()
18297    });
18298    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18299
18300    let buffer_1 = project
18301        .update(cx, |project, cx| {
18302            project.open_buffer((worktree_id, "first.rs"), cx)
18303        })
18304        .await
18305        .unwrap();
18306    let buffer_2 = project
18307        .update(cx, |project, cx| {
18308            project.open_buffer((worktree_id, "second.rs"), cx)
18309        })
18310        .await
18311        .unwrap();
18312    let buffer_3 = project
18313        .update(cx, |project, cx| {
18314            project.open_buffer((worktree_id, "third.rs"), cx)
18315        })
18316        .await
18317        .unwrap();
18318
18319    let multi_buffer = cx.new(|cx| {
18320        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18321        multi_buffer.push_excerpts(
18322            buffer_1.clone(),
18323            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18324            cx,
18325        );
18326        multi_buffer.push_excerpts(
18327            buffer_2.clone(),
18328            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18329            cx,
18330        );
18331        multi_buffer.push_excerpts(
18332            buffer_3.clone(),
18333            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18334            cx,
18335        );
18336        multi_buffer
18337    });
18338
18339    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18340        Editor::new(
18341            EditorMode::full(),
18342            multi_buffer,
18343            Some(project.clone()),
18344            window,
18345            cx,
18346        )
18347    });
18348
18349    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18350    assert_eq!(
18351        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18352        full_text,
18353    );
18354
18355    multi_buffer_editor.update(cx, |editor, cx| {
18356        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18357    });
18358    assert_eq!(
18359        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18360        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18361        "After folding the first buffer, its text should not be displayed"
18362    );
18363
18364    multi_buffer_editor.update(cx, |editor, cx| {
18365        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18366    });
18367
18368    assert_eq!(
18369        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18370        "\n\n\n\n\n\n7777\n8888\n9999",
18371        "After folding the second buffer, its text should not be displayed"
18372    );
18373
18374    multi_buffer_editor.update(cx, |editor, cx| {
18375        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18376    });
18377    assert_eq!(
18378        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18379        "\n\n\n\n\n",
18380        "After folding the third buffer, its text should not be displayed"
18381    );
18382
18383    multi_buffer_editor.update(cx, |editor, cx| {
18384        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18385    });
18386    assert_eq!(
18387        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18388        "\n\n\n\n4444\n5555\n6666\n\n",
18389        "After unfolding the second buffer, its text should be displayed"
18390    );
18391
18392    multi_buffer_editor.update(cx, |editor, cx| {
18393        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18394    });
18395    assert_eq!(
18396        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18397        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18398        "After unfolding the first buffer, its text should be displayed"
18399    );
18400
18401    multi_buffer_editor.update(cx, |editor, cx| {
18402        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18403    });
18404    assert_eq!(
18405        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18406        full_text,
18407        "After unfolding all buffers, all original text should be displayed"
18408    );
18409}
18410
18411#[gpui::test]
18412async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18413    init_test(cx, |_| {});
18414
18415    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18416
18417    let fs = FakeFs::new(cx.executor());
18418    fs.insert_tree(
18419        path!("/a"),
18420        json!({
18421            "main.rs": sample_text,
18422        }),
18423    )
18424    .await;
18425    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18426    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18427    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18428    let worktree = project.update(cx, |project, cx| {
18429        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18430        assert_eq!(worktrees.len(), 1);
18431        worktrees.pop().unwrap()
18432    });
18433    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18434
18435    let buffer_1 = project
18436        .update(cx, |project, cx| {
18437            project.open_buffer((worktree_id, "main.rs"), cx)
18438        })
18439        .await
18440        .unwrap();
18441
18442    let multi_buffer = cx.new(|cx| {
18443        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18444        multi_buffer.push_excerpts(
18445            buffer_1.clone(),
18446            [ExcerptRange::new(
18447                Point::new(0, 0)
18448                    ..Point::new(
18449                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18450                        0,
18451                    ),
18452            )],
18453            cx,
18454        );
18455        multi_buffer
18456    });
18457    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18458        Editor::new(
18459            EditorMode::full(),
18460            multi_buffer,
18461            Some(project.clone()),
18462            window,
18463            cx,
18464        )
18465    });
18466
18467    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18468    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18469        enum TestHighlight {}
18470        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18471        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18472        editor.highlight_text::<TestHighlight>(
18473            vec![highlight_range.clone()],
18474            HighlightStyle::color(Hsla::green()),
18475            cx,
18476        );
18477        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18478    });
18479
18480    let full_text = format!("\n\n{sample_text}");
18481    assert_eq!(
18482        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18483        full_text,
18484    );
18485}
18486
18487#[gpui::test]
18488async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18489    init_test(cx, |_| {});
18490    cx.update(|cx| {
18491        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18492            "keymaps/default-linux.json",
18493            cx,
18494        )
18495        .unwrap();
18496        cx.bind_keys(default_key_bindings);
18497    });
18498
18499    let (editor, cx) = cx.add_window_view(|window, cx| {
18500        let multi_buffer = MultiBuffer::build_multi(
18501            [
18502                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18503                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18504                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18505                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18506            ],
18507            cx,
18508        );
18509        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18510
18511        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18512        // fold all but the second buffer, so that we test navigating between two
18513        // adjacent folded buffers, as well as folded buffers at the start and
18514        // end the multibuffer
18515        editor.fold_buffer(buffer_ids[0], cx);
18516        editor.fold_buffer(buffer_ids[2], cx);
18517        editor.fold_buffer(buffer_ids[3], cx);
18518
18519        editor
18520    });
18521    cx.simulate_resize(size(px(1000.), px(1000.)));
18522
18523    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18524    cx.assert_excerpts_with_selections(indoc! {"
18525        [EXCERPT]
18526        ˇ[FOLDED]
18527        [EXCERPT]
18528        a1
18529        b1
18530        [EXCERPT]
18531        [FOLDED]
18532        [EXCERPT]
18533        [FOLDED]
18534        "
18535    });
18536    cx.simulate_keystroke("down");
18537    cx.assert_excerpts_with_selections(indoc! {"
18538        [EXCERPT]
18539        [FOLDED]
18540        [EXCERPT]
18541        ˇa1
18542        b1
18543        [EXCERPT]
18544        [FOLDED]
18545        [EXCERPT]
18546        [FOLDED]
18547        "
18548    });
18549    cx.simulate_keystroke("down");
18550    cx.assert_excerpts_with_selections(indoc! {"
18551        [EXCERPT]
18552        [FOLDED]
18553        [EXCERPT]
18554        a1
18555        ˇb1
18556        [EXCERPT]
18557        [FOLDED]
18558        [EXCERPT]
18559        [FOLDED]
18560        "
18561    });
18562    cx.simulate_keystroke("down");
18563    cx.assert_excerpts_with_selections(indoc! {"
18564        [EXCERPT]
18565        [FOLDED]
18566        [EXCERPT]
18567        a1
18568        b1
18569        ˇ[EXCERPT]
18570        [FOLDED]
18571        [EXCERPT]
18572        [FOLDED]
18573        "
18574    });
18575    cx.simulate_keystroke("down");
18576    cx.assert_excerpts_with_selections(indoc! {"
18577        [EXCERPT]
18578        [FOLDED]
18579        [EXCERPT]
18580        a1
18581        b1
18582        [EXCERPT]
18583        ˇ[FOLDED]
18584        [EXCERPT]
18585        [FOLDED]
18586        "
18587    });
18588    for _ in 0..5 {
18589        cx.simulate_keystroke("down");
18590        cx.assert_excerpts_with_selections(indoc! {"
18591            [EXCERPT]
18592            [FOLDED]
18593            [EXCERPT]
18594            a1
18595            b1
18596            [EXCERPT]
18597            [FOLDED]
18598            [EXCERPT]
18599            ˇ[FOLDED]
18600            "
18601        });
18602    }
18603
18604    cx.simulate_keystroke("up");
18605    cx.assert_excerpts_with_selections(indoc! {"
18606        [EXCERPT]
18607        [FOLDED]
18608        [EXCERPT]
18609        a1
18610        b1
18611        [EXCERPT]
18612        ˇ[FOLDED]
18613        [EXCERPT]
18614        [FOLDED]
18615        "
18616    });
18617    cx.simulate_keystroke("up");
18618    cx.assert_excerpts_with_selections(indoc! {"
18619        [EXCERPT]
18620        [FOLDED]
18621        [EXCERPT]
18622        a1
18623        b1
18624        ˇ[EXCERPT]
18625        [FOLDED]
18626        [EXCERPT]
18627        [FOLDED]
18628        "
18629    });
18630    cx.simulate_keystroke("up");
18631    cx.assert_excerpts_with_selections(indoc! {"
18632        [EXCERPT]
18633        [FOLDED]
18634        [EXCERPT]
18635        a1
18636        ˇb1
18637        [EXCERPT]
18638        [FOLDED]
18639        [EXCERPT]
18640        [FOLDED]
18641        "
18642    });
18643    cx.simulate_keystroke("up");
18644    cx.assert_excerpts_with_selections(indoc! {"
18645        [EXCERPT]
18646        [FOLDED]
18647        [EXCERPT]
18648        ˇa1
18649        b1
18650        [EXCERPT]
18651        [FOLDED]
18652        [EXCERPT]
18653        [FOLDED]
18654        "
18655    });
18656    for _ in 0..5 {
18657        cx.simulate_keystroke("up");
18658        cx.assert_excerpts_with_selections(indoc! {"
18659            [EXCERPT]
18660            ˇ[FOLDED]
18661            [EXCERPT]
18662            a1
18663            b1
18664            [EXCERPT]
18665            [FOLDED]
18666            [EXCERPT]
18667            [FOLDED]
18668            "
18669        });
18670    }
18671}
18672
18673#[gpui::test]
18674async fn test_inline_completion_text(cx: &mut TestAppContext) {
18675    init_test(cx, |_| {});
18676
18677    // Simple insertion
18678    assert_highlighted_edits(
18679        "Hello, world!",
18680        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18681        true,
18682        cx,
18683        |highlighted_edits, cx| {
18684            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18685            assert_eq!(highlighted_edits.highlights.len(), 1);
18686            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18687            assert_eq!(
18688                highlighted_edits.highlights[0].1.background_color,
18689                Some(cx.theme().status().created_background)
18690            );
18691        },
18692    )
18693    .await;
18694
18695    // Replacement
18696    assert_highlighted_edits(
18697        "This is a test.",
18698        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18699        false,
18700        cx,
18701        |highlighted_edits, cx| {
18702            assert_eq!(highlighted_edits.text, "That is a test.");
18703            assert_eq!(highlighted_edits.highlights.len(), 1);
18704            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18705            assert_eq!(
18706                highlighted_edits.highlights[0].1.background_color,
18707                Some(cx.theme().status().created_background)
18708            );
18709        },
18710    )
18711    .await;
18712
18713    // Multiple edits
18714    assert_highlighted_edits(
18715        "Hello, world!",
18716        vec![
18717            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18718            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18719        ],
18720        false,
18721        cx,
18722        |highlighted_edits, cx| {
18723            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18724            assert_eq!(highlighted_edits.highlights.len(), 2);
18725            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18726            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18727            assert_eq!(
18728                highlighted_edits.highlights[0].1.background_color,
18729                Some(cx.theme().status().created_background)
18730            );
18731            assert_eq!(
18732                highlighted_edits.highlights[1].1.background_color,
18733                Some(cx.theme().status().created_background)
18734            );
18735        },
18736    )
18737    .await;
18738
18739    // Multiple lines with edits
18740    assert_highlighted_edits(
18741        "First line\nSecond line\nThird line\nFourth line",
18742        vec![
18743            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18744            (
18745                Point::new(2, 0)..Point::new(2, 10),
18746                "New third line".to_string(),
18747            ),
18748            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18749        ],
18750        false,
18751        cx,
18752        |highlighted_edits, cx| {
18753            assert_eq!(
18754                highlighted_edits.text,
18755                "Second modified\nNew third line\nFourth updated line"
18756            );
18757            assert_eq!(highlighted_edits.highlights.len(), 3);
18758            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18759            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18760            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18761            for highlight in &highlighted_edits.highlights {
18762                assert_eq!(
18763                    highlight.1.background_color,
18764                    Some(cx.theme().status().created_background)
18765                );
18766            }
18767        },
18768    )
18769    .await;
18770}
18771
18772#[gpui::test]
18773async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18774    init_test(cx, |_| {});
18775
18776    // Deletion
18777    assert_highlighted_edits(
18778        "Hello, world!",
18779        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18780        true,
18781        cx,
18782        |highlighted_edits, cx| {
18783            assert_eq!(highlighted_edits.text, "Hello, world!");
18784            assert_eq!(highlighted_edits.highlights.len(), 1);
18785            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18786            assert_eq!(
18787                highlighted_edits.highlights[0].1.background_color,
18788                Some(cx.theme().status().deleted_background)
18789            );
18790        },
18791    )
18792    .await;
18793
18794    // Insertion
18795    assert_highlighted_edits(
18796        "Hello, world!",
18797        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18798        true,
18799        cx,
18800        |highlighted_edits, cx| {
18801            assert_eq!(highlighted_edits.highlights.len(), 1);
18802            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18803            assert_eq!(
18804                highlighted_edits.highlights[0].1.background_color,
18805                Some(cx.theme().status().created_background)
18806            );
18807        },
18808    )
18809    .await;
18810}
18811
18812async fn assert_highlighted_edits(
18813    text: &str,
18814    edits: Vec<(Range<Point>, String)>,
18815    include_deletions: bool,
18816    cx: &mut TestAppContext,
18817    assertion_fn: impl Fn(HighlightedText, &App),
18818) {
18819    let window = cx.add_window(|window, cx| {
18820        let buffer = MultiBuffer::build_simple(text, cx);
18821        Editor::new(EditorMode::full(), buffer, None, window, cx)
18822    });
18823    let cx = &mut VisualTestContext::from_window(*window, cx);
18824
18825    let (buffer, snapshot) = window
18826        .update(cx, |editor, _window, cx| {
18827            (
18828                editor.buffer().clone(),
18829                editor.buffer().read(cx).snapshot(cx),
18830            )
18831        })
18832        .unwrap();
18833
18834    let edits = edits
18835        .into_iter()
18836        .map(|(range, edit)| {
18837            (
18838                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18839                edit,
18840            )
18841        })
18842        .collect::<Vec<_>>();
18843
18844    let text_anchor_edits = edits
18845        .clone()
18846        .into_iter()
18847        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18848        .collect::<Vec<_>>();
18849
18850    let edit_preview = window
18851        .update(cx, |_, _window, cx| {
18852            buffer
18853                .read(cx)
18854                .as_singleton()
18855                .unwrap()
18856                .read(cx)
18857                .preview_edits(text_anchor_edits.into(), cx)
18858        })
18859        .unwrap()
18860        .await;
18861
18862    cx.update(|_window, cx| {
18863        let highlighted_edits = inline_completion_edit_text(
18864            &snapshot.as_singleton().unwrap().2,
18865            &edits,
18866            &edit_preview,
18867            include_deletions,
18868            cx,
18869        );
18870        assertion_fn(highlighted_edits, cx)
18871    });
18872}
18873
18874#[track_caller]
18875fn assert_breakpoint(
18876    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18877    path: &Arc<Path>,
18878    expected: Vec<(u32, Breakpoint)>,
18879) {
18880    if expected.len() == 0usize {
18881        assert!(!breakpoints.contains_key(path), "{}", path.display());
18882    } else {
18883        let mut breakpoint = breakpoints
18884            .get(path)
18885            .unwrap()
18886            .into_iter()
18887            .map(|breakpoint| {
18888                (
18889                    breakpoint.row,
18890                    Breakpoint {
18891                        message: breakpoint.message.clone(),
18892                        state: breakpoint.state,
18893                        condition: breakpoint.condition.clone(),
18894                        hit_condition: breakpoint.hit_condition.clone(),
18895                    },
18896                )
18897            })
18898            .collect::<Vec<_>>();
18899
18900        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18901
18902        assert_eq!(expected, breakpoint);
18903    }
18904}
18905
18906fn add_log_breakpoint_at_cursor(
18907    editor: &mut Editor,
18908    log_message: &str,
18909    window: &mut Window,
18910    cx: &mut Context<Editor>,
18911) {
18912    let (anchor, bp) = editor
18913        .breakpoints_at_cursors(window, cx)
18914        .first()
18915        .and_then(|(anchor, bp)| {
18916            if let Some(bp) = bp {
18917                Some((*anchor, bp.clone()))
18918            } else {
18919                None
18920            }
18921        })
18922        .unwrap_or_else(|| {
18923            let cursor_position: Point = editor.selections.newest(cx).head();
18924
18925            let breakpoint_position = editor
18926                .snapshot(window, cx)
18927                .display_snapshot
18928                .buffer_snapshot
18929                .anchor_before(Point::new(cursor_position.row, 0));
18930
18931            (breakpoint_position, Breakpoint::new_log(&log_message))
18932        });
18933
18934    editor.edit_breakpoint_at_anchor(
18935        anchor,
18936        bp,
18937        BreakpointEditAction::EditLogMessage(log_message.into()),
18938        cx,
18939    );
18940}
18941
18942#[gpui::test]
18943async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18944    init_test(cx, |_| {});
18945
18946    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18947    let fs = FakeFs::new(cx.executor());
18948    fs.insert_tree(
18949        path!("/a"),
18950        json!({
18951            "main.rs": sample_text,
18952        }),
18953    )
18954    .await;
18955    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18956    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18957    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18958
18959    let fs = FakeFs::new(cx.executor());
18960    fs.insert_tree(
18961        path!("/a"),
18962        json!({
18963            "main.rs": sample_text,
18964        }),
18965    )
18966    .await;
18967    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18968    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18969    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18970    let worktree_id = workspace
18971        .update(cx, |workspace, _window, cx| {
18972            workspace.project().update(cx, |project, cx| {
18973                project.worktrees(cx).next().unwrap().read(cx).id()
18974            })
18975        })
18976        .unwrap();
18977
18978    let buffer = project
18979        .update(cx, |project, cx| {
18980            project.open_buffer((worktree_id, "main.rs"), cx)
18981        })
18982        .await
18983        .unwrap();
18984
18985    let (editor, cx) = cx.add_window_view(|window, cx| {
18986        Editor::new(
18987            EditorMode::full(),
18988            MultiBuffer::build_from_buffer(buffer, cx),
18989            Some(project.clone()),
18990            window,
18991            cx,
18992        )
18993    });
18994
18995    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18996    let abs_path = project.read_with(cx, |project, cx| {
18997        project
18998            .absolute_path(&project_path, cx)
18999            .map(|path_buf| Arc::from(path_buf.to_owned()))
19000            .unwrap()
19001    });
19002
19003    // assert we can add breakpoint on the first line
19004    editor.update_in(cx, |editor, window, cx| {
19005        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19006        editor.move_to_end(&MoveToEnd, window, cx);
19007        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19008    });
19009
19010    let breakpoints = editor.update(cx, |editor, cx| {
19011        editor
19012            .breakpoint_store()
19013            .as_ref()
19014            .unwrap()
19015            .read(cx)
19016            .all_source_breakpoints(cx)
19017            .clone()
19018    });
19019
19020    assert_eq!(1, breakpoints.len());
19021    assert_breakpoint(
19022        &breakpoints,
19023        &abs_path,
19024        vec![
19025            (0, Breakpoint::new_standard()),
19026            (3, Breakpoint::new_standard()),
19027        ],
19028    );
19029
19030    editor.update_in(cx, |editor, window, cx| {
19031        editor.move_to_beginning(&MoveToBeginning, window, cx);
19032        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19033    });
19034
19035    let breakpoints = editor.update(cx, |editor, cx| {
19036        editor
19037            .breakpoint_store()
19038            .as_ref()
19039            .unwrap()
19040            .read(cx)
19041            .all_source_breakpoints(cx)
19042            .clone()
19043    });
19044
19045    assert_eq!(1, breakpoints.len());
19046    assert_breakpoint(
19047        &breakpoints,
19048        &abs_path,
19049        vec![(3, Breakpoint::new_standard())],
19050    );
19051
19052    editor.update_in(cx, |editor, window, cx| {
19053        editor.move_to_end(&MoveToEnd, window, cx);
19054        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19055    });
19056
19057    let breakpoints = editor.update(cx, |editor, cx| {
19058        editor
19059            .breakpoint_store()
19060            .as_ref()
19061            .unwrap()
19062            .read(cx)
19063            .all_source_breakpoints(cx)
19064            .clone()
19065    });
19066
19067    assert_eq!(0, breakpoints.len());
19068    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19069}
19070
19071#[gpui::test]
19072async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
19073    init_test(cx, |_| {});
19074
19075    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19076
19077    let fs = FakeFs::new(cx.executor());
19078    fs.insert_tree(
19079        path!("/a"),
19080        json!({
19081            "main.rs": sample_text,
19082        }),
19083    )
19084    .await;
19085    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19086    let (workspace, cx) =
19087        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19088
19089    let worktree_id = workspace.update(cx, |workspace, cx| {
19090        workspace.project().update(cx, |project, cx| {
19091            project.worktrees(cx).next().unwrap().read(cx).id()
19092        })
19093    });
19094
19095    let buffer = project
19096        .update(cx, |project, cx| {
19097            project.open_buffer((worktree_id, "main.rs"), cx)
19098        })
19099        .await
19100        .unwrap();
19101
19102    let (editor, cx) = cx.add_window_view(|window, cx| {
19103        Editor::new(
19104            EditorMode::full(),
19105            MultiBuffer::build_from_buffer(buffer, cx),
19106            Some(project.clone()),
19107            window,
19108            cx,
19109        )
19110    });
19111
19112    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19113    let abs_path = project.read_with(cx, |project, cx| {
19114        project
19115            .absolute_path(&project_path, cx)
19116            .map(|path_buf| Arc::from(path_buf.to_owned()))
19117            .unwrap()
19118    });
19119
19120    editor.update_in(cx, |editor, window, cx| {
19121        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19122    });
19123
19124    let breakpoints = editor.update(cx, |editor, cx| {
19125        editor
19126            .breakpoint_store()
19127            .as_ref()
19128            .unwrap()
19129            .read(cx)
19130            .all_source_breakpoints(cx)
19131            .clone()
19132    });
19133
19134    assert_breakpoint(
19135        &breakpoints,
19136        &abs_path,
19137        vec![(0, Breakpoint::new_log("hello world"))],
19138    );
19139
19140    // Removing a log message from a log breakpoint should remove it
19141    editor.update_in(cx, |editor, window, cx| {
19142        add_log_breakpoint_at_cursor(editor, "", window, cx);
19143    });
19144
19145    let breakpoints = editor.update(cx, |editor, cx| {
19146        editor
19147            .breakpoint_store()
19148            .as_ref()
19149            .unwrap()
19150            .read(cx)
19151            .all_source_breakpoints(cx)
19152            .clone()
19153    });
19154
19155    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19156
19157    editor.update_in(cx, |editor, window, cx| {
19158        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19159        editor.move_to_end(&MoveToEnd, window, cx);
19160        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19161        // Not adding a log message to a standard breakpoint shouldn't remove it
19162        add_log_breakpoint_at_cursor(editor, "", window, cx);
19163    });
19164
19165    let breakpoints = editor.update(cx, |editor, cx| {
19166        editor
19167            .breakpoint_store()
19168            .as_ref()
19169            .unwrap()
19170            .read(cx)
19171            .all_source_breakpoints(cx)
19172            .clone()
19173    });
19174
19175    assert_breakpoint(
19176        &breakpoints,
19177        &abs_path,
19178        vec![
19179            (0, Breakpoint::new_standard()),
19180            (3, Breakpoint::new_standard()),
19181        ],
19182    );
19183
19184    editor.update_in(cx, |editor, window, cx| {
19185        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19186    });
19187
19188    let breakpoints = editor.update(cx, |editor, cx| {
19189        editor
19190            .breakpoint_store()
19191            .as_ref()
19192            .unwrap()
19193            .read(cx)
19194            .all_source_breakpoints(cx)
19195            .clone()
19196    });
19197
19198    assert_breakpoint(
19199        &breakpoints,
19200        &abs_path,
19201        vec![
19202            (0, Breakpoint::new_standard()),
19203            (3, Breakpoint::new_log("hello world")),
19204        ],
19205    );
19206
19207    editor.update_in(cx, |editor, window, cx| {
19208        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19209    });
19210
19211    let breakpoints = editor.update(cx, |editor, cx| {
19212        editor
19213            .breakpoint_store()
19214            .as_ref()
19215            .unwrap()
19216            .read(cx)
19217            .all_source_breakpoints(cx)
19218            .clone()
19219    });
19220
19221    assert_breakpoint(
19222        &breakpoints,
19223        &abs_path,
19224        vec![
19225            (0, Breakpoint::new_standard()),
19226            (3, Breakpoint::new_log("hello Earth!!")),
19227        ],
19228    );
19229}
19230
19231/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19232/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19233/// or when breakpoints were placed out of order. This tests for a regression too
19234#[gpui::test]
19235async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19236    init_test(cx, |_| {});
19237
19238    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19239    let fs = FakeFs::new(cx.executor());
19240    fs.insert_tree(
19241        path!("/a"),
19242        json!({
19243            "main.rs": sample_text,
19244        }),
19245    )
19246    .await;
19247    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19248    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19249    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19250
19251    let fs = FakeFs::new(cx.executor());
19252    fs.insert_tree(
19253        path!("/a"),
19254        json!({
19255            "main.rs": sample_text,
19256        }),
19257    )
19258    .await;
19259    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19260    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19261    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19262    let worktree_id = workspace
19263        .update(cx, |workspace, _window, cx| {
19264            workspace.project().update(cx, |project, cx| {
19265                project.worktrees(cx).next().unwrap().read(cx).id()
19266            })
19267        })
19268        .unwrap();
19269
19270    let buffer = project
19271        .update(cx, |project, cx| {
19272            project.open_buffer((worktree_id, "main.rs"), cx)
19273        })
19274        .await
19275        .unwrap();
19276
19277    let (editor, cx) = cx.add_window_view(|window, cx| {
19278        Editor::new(
19279            EditorMode::full(),
19280            MultiBuffer::build_from_buffer(buffer, cx),
19281            Some(project.clone()),
19282            window,
19283            cx,
19284        )
19285    });
19286
19287    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19288    let abs_path = project.read_with(cx, |project, cx| {
19289        project
19290            .absolute_path(&project_path, cx)
19291            .map(|path_buf| Arc::from(path_buf.to_owned()))
19292            .unwrap()
19293    });
19294
19295    // assert we can add breakpoint on the first line
19296    editor.update_in(cx, |editor, window, cx| {
19297        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19298        editor.move_to_end(&MoveToEnd, window, cx);
19299        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19300        editor.move_up(&MoveUp, window, cx);
19301        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19302    });
19303
19304    let breakpoints = editor.update(cx, |editor, cx| {
19305        editor
19306            .breakpoint_store()
19307            .as_ref()
19308            .unwrap()
19309            .read(cx)
19310            .all_source_breakpoints(cx)
19311            .clone()
19312    });
19313
19314    assert_eq!(1, breakpoints.len());
19315    assert_breakpoint(
19316        &breakpoints,
19317        &abs_path,
19318        vec![
19319            (0, Breakpoint::new_standard()),
19320            (2, Breakpoint::new_standard()),
19321            (3, Breakpoint::new_standard()),
19322        ],
19323    );
19324
19325    editor.update_in(cx, |editor, window, cx| {
19326        editor.move_to_beginning(&MoveToBeginning, window, cx);
19327        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19328        editor.move_to_end(&MoveToEnd, window, cx);
19329        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19330        // Disabling a breakpoint that doesn't exist should do nothing
19331        editor.move_up(&MoveUp, window, cx);
19332        editor.move_up(&MoveUp, window, cx);
19333        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19334    });
19335
19336    let breakpoints = editor.update(cx, |editor, cx| {
19337        editor
19338            .breakpoint_store()
19339            .as_ref()
19340            .unwrap()
19341            .read(cx)
19342            .all_source_breakpoints(cx)
19343            .clone()
19344    });
19345
19346    let disable_breakpoint = {
19347        let mut bp = Breakpoint::new_standard();
19348        bp.state = BreakpointState::Disabled;
19349        bp
19350    };
19351
19352    assert_eq!(1, breakpoints.len());
19353    assert_breakpoint(
19354        &breakpoints,
19355        &abs_path,
19356        vec![
19357            (0, disable_breakpoint.clone()),
19358            (2, Breakpoint::new_standard()),
19359            (3, disable_breakpoint.clone()),
19360        ],
19361    );
19362
19363    editor.update_in(cx, |editor, window, cx| {
19364        editor.move_to_beginning(&MoveToBeginning, window, cx);
19365        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19366        editor.move_to_end(&MoveToEnd, window, cx);
19367        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19368        editor.move_up(&MoveUp, window, cx);
19369        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19370    });
19371
19372    let breakpoints = editor.update(cx, |editor, cx| {
19373        editor
19374            .breakpoint_store()
19375            .as_ref()
19376            .unwrap()
19377            .read(cx)
19378            .all_source_breakpoints(cx)
19379            .clone()
19380    });
19381
19382    assert_eq!(1, breakpoints.len());
19383    assert_breakpoint(
19384        &breakpoints,
19385        &abs_path,
19386        vec![
19387            (0, Breakpoint::new_standard()),
19388            (2, disable_breakpoint),
19389            (3, Breakpoint::new_standard()),
19390        ],
19391    );
19392}
19393
19394#[gpui::test]
19395async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19396    init_test(cx, |_| {});
19397    let capabilities = lsp::ServerCapabilities {
19398        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19399            prepare_provider: Some(true),
19400            work_done_progress_options: Default::default(),
19401        })),
19402        ..Default::default()
19403    };
19404    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19405
19406    cx.set_state(indoc! {"
19407        struct Fˇoo {}
19408    "});
19409
19410    cx.update_editor(|editor, _, cx| {
19411        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19412        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19413        editor.highlight_background::<DocumentHighlightRead>(
19414            &[highlight_range],
19415            |c| c.editor_document_highlight_read_background,
19416            cx,
19417        );
19418    });
19419
19420    let mut prepare_rename_handler = cx
19421        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19422            move |_, _, _| async move {
19423                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19424                    start: lsp::Position {
19425                        line: 0,
19426                        character: 7,
19427                    },
19428                    end: lsp::Position {
19429                        line: 0,
19430                        character: 10,
19431                    },
19432                })))
19433            },
19434        );
19435    let prepare_rename_task = cx
19436        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19437        .expect("Prepare rename was not started");
19438    prepare_rename_handler.next().await.unwrap();
19439    prepare_rename_task.await.expect("Prepare rename failed");
19440
19441    let mut rename_handler =
19442        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19443            let edit = lsp::TextEdit {
19444                range: lsp::Range {
19445                    start: lsp::Position {
19446                        line: 0,
19447                        character: 7,
19448                    },
19449                    end: lsp::Position {
19450                        line: 0,
19451                        character: 10,
19452                    },
19453                },
19454                new_text: "FooRenamed".to_string(),
19455            };
19456            Ok(Some(lsp::WorkspaceEdit::new(
19457                // Specify the same edit twice
19458                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19459            )))
19460        });
19461    let rename_task = cx
19462        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19463        .expect("Confirm rename was not started");
19464    rename_handler.next().await.unwrap();
19465    rename_task.await.expect("Confirm rename failed");
19466    cx.run_until_parked();
19467
19468    // Despite two edits, only one is actually applied as those are identical
19469    cx.assert_editor_state(indoc! {"
19470        struct FooRenamedˇ {}
19471    "});
19472}
19473
19474#[gpui::test]
19475async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19476    init_test(cx, |_| {});
19477    // These capabilities indicate that the server does not support prepare rename.
19478    let capabilities = lsp::ServerCapabilities {
19479        rename_provider: Some(lsp::OneOf::Left(true)),
19480        ..Default::default()
19481    };
19482    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19483
19484    cx.set_state(indoc! {"
19485        struct Fˇoo {}
19486    "});
19487
19488    cx.update_editor(|editor, _window, cx| {
19489        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19490        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19491        editor.highlight_background::<DocumentHighlightRead>(
19492            &[highlight_range],
19493            |c| c.editor_document_highlight_read_background,
19494            cx,
19495        );
19496    });
19497
19498    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19499        .expect("Prepare rename was not started")
19500        .await
19501        .expect("Prepare rename failed");
19502
19503    let mut rename_handler =
19504        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19505            let edit = lsp::TextEdit {
19506                range: lsp::Range {
19507                    start: lsp::Position {
19508                        line: 0,
19509                        character: 7,
19510                    },
19511                    end: lsp::Position {
19512                        line: 0,
19513                        character: 10,
19514                    },
19515                },
19516                new_text: "FooRenamed".to_string(),
19517            };
19518            Ok(Some(lsp::WorkspaceEdit::new(
19519                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19520            )))
19521        });
19522    let rename_task = cx
19523        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19524        .expect("Confirm rename was not started");
19525    rename_handler.next().await.unwrap();
19526    rename_task.await.expect("Confirm rename failed");
19527    cx.run_until_parked();
19528
19529    // Correct range is renamed, as `surrounding_word` is used to find it.
19530    cx.assert_editor_state(indoc! {"
19531        struct FooRenamedˇ {}
19532    "});
19533}
19534
19535#[gpui::test]
19536async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19537    init_test(cx, |_| {});
19538    let mut cx = EditorTestContext::new(cx).await;
19539
19540    let language = Arc::new(
19541        Language::new(
19542            LanguageConfig::default(),
19543            Some(tree_sitter_html::LANGUAGE.into()),
19544        )
19545        .with_brackets_query(
19546            r#"
19547            ("<" @open "/>" @close)
19548            ("</" @open ">" @close)
19549            ("<" @open ">" @close)
19550            ("\"" @open "\"" @close)
19551            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19552        "#,
19553        )
19554        .unwrap(),
19555    );
19556    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19557
19558    cx.set_state(indoc! {"
19559        <span>ˇ</span>
19560    "});
19561    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19562    cx.assert_editor_state(indoc! {"
19563        <span>
19564        ˇ
19565        </span>
19566    "});
19567
19568    cx.set_state(indoc! {"
19569        <span><span></span>ˇ</span>
19570    "});
19571    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19572    cx.assert_editor_state(indoc! {"
19573        <span><span></span>
19574        ˇ</span>
19575    "});
19576
19577    cx.set_state(indoc! {"
19578        <span>ˇ
19579        </span>
19580    "});
19581    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19582    cx.assert_editor_state(indoc! {"
19583        <span>
19584        ˇ
19585        </span>
19586    "});
19587}
19588
19589#[gpui::test(iterations = 10)]
19590async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19591    init_test(cx, |_| {});
19592
19593    let fs = FakeFs::new(cx.executor());
19594    fs.insert_tree(
19595        path!("/dir"),
19596        json!({
19597            "a.ts": "a",
19598        }),
19599    )
19600    .await;
19601
19602    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19603    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19604    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19605
19606    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19607    language_registry.add(Arc::new(Language::new(
19608        LanguageConfig {
19609            name: "TypeScript".into(),
19610            matcher: LanguageMatcher {
19611                path_suffixes: vec!["ts".to_string()],
19612                ..Default::default()
19613            },
19614            ..Default::default()
19615        },
19616        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19617    )));
19618    let mut fake_language_servers = language_registry.register_fake_lsp(
19619        "TypeScript",
19620        FakeLspAdapter {
19621            capabilities: lsp::ServerCapabilities {
19622                code_lens_provider: Some(lsp::CodeLensOptions {
19623                    resolve_provider: Some(true),
19624                }),
19625                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19626                    commands: vec!["_the/command".to_string()],
19627                    ..lsp::ExecuteCommandOptions::default()
19628                }),
19629                ..lsp::ServerCapabilities::default()
19630            },
19631            ..FakeLspAdapter::default()
19632        },
19633    );
19634
19635    let (buffer, _handle) = project
19636        .update(cx, |p, cx| {
19637            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19638        })
19639        .await
19640        .unwrap();
19641    cx.executor().run_until_parked();
19642
19643    let fake_server = fake_language_servers.next().await.unwrap();
19644
19645    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19646    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19647    drop(buffer_snapshot);
19648    let actions = cx
19649        .update_window(*workspace, |_, window, cx| {
19650            project.code_actions(&buffer, anchor..anchor, window, cx)
19651        })
19652        .unwrap();
19653
19654    fake_server
19655        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19656            Ok(Some(vec![
19657                lsp::CodeLens {
19658                    range: lsp::Range::default(),
19659                    command: Some(lsp::Command {
19660                        title: "Code lens command".to_owned(),
19661                        command: "_the/command".to_owned(),
19662                        arguments: None,
19663                    }),
19664                    data: None,
19665                },
19666                lsp::CodeLens {
19667                    range: lsp::Range::default(),
19668                    command: Some(lsp::Command {
19669                        title: "Command not in capabilities".to_owned(),
19670                        command: "not in capabilities".to_owned(),
19671                        arguments: None,
19672                    }),
19673                    data: None,
19674                },
19675                lsp::CodeLens {
19676                    range: lsp::Range {
19677                        start: lsp::Position {
19678                            line: 1,
19679                            character: 1,
19680                        },
19681                        end: lsp::Position {
19682                            line: 1,
19683                            character: 1,
19684                        },
19685                    },
19686                    command: Some(lsp::Command {
19687                        title: "Command not in range".to_owned(),
19688                        command: "_the/command".to_owned(),
19689                        arguments: None,
19690                    }),
19691                    data: None,
19692                },
19693            ]))
19694        })
19695        .next()
19696        .await;
19697
19698    let actions = actions.await.unwrap();
19699    assert_eq!(
19700        actions.len(),
19701        1,
19702        "Should have only one valid action for the 0..0 range"
19703    );
19704    let action = actions[0].clone();
19705    let apply = project.update(cx, |project, cx| {
19706        project.apply_code_action(buffer.clone(), action, true, cx)
19707    });
19708
19709    // Resolving the code action does not populate its edits. In absence of
19710    // edits, we must execute the given command.
19711    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19712        |mut lens, _| async move {
19713            let lens_command = lens.command.as_mut().expect("should have a command");
19714            assert_eq!(lens_command.title, "Code lens command");
19715            lens_command.arguments = Some(vec![json!("the-argument")]);
19716            Ok(lens)
19717        },
19718    );
19719
19720    // While executing the command, the language server sends the editor
19721    // a `workspaceEdit` request.
19722    fake_server
19723        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19724            let fake = fake_server.clone();
19725            move |params, _| {
19726                assert_eq!(params.command, "_the/command");
19727                let fake = fake.clone();
19728                async move {
19729                    fake.server
19730                        .request::<lsp::request::ApplyWorkspaceEdit>(
19731                            lsp::ApplyWorkspaceEditParams {
19732                                label: None,
19733                                edit: lsp::WorkspaceEdit {
19734                                    changes: Some(
19735                                        [(
19736                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19737                                            vec![lsp::TextEdit {
19738                                                range: lsp::Range::new(
19739                                                    lsp::Position::new(0, 0),
19740                                                    lsp::Position::new(0, 0),
19741                                                ),
19742                                                new_text: "X".into(),
19743                                            }],
19744                                        )]
19745                                        .into_iter()
19746                                        .collect(),
19747                                    ),
19748                                    ..Default::default()
19749                                },
19750                            },
19751                        )
19752                        .await
19753                        .into_response()
19754                        .unwrap();
19755                    Ok(Some(json!(null)))
19756                }
19757            }
19758        })
19759        .next()
19760        .await;
19761
19762    // Applying the code lens command returns a project transaction containing the edits
19763    // sent by the language server in its `workspaceEdit` request.
19764    let transaction = apply.await.unwrap();
19765    assert!(transaction.0.contains_key(&buffer));
19766    buffer.update(cx, |buffer, cx| {
19767        assert_eq!(buffer.text(), "Xa");
19768        buffer.undo(cx);
19769        assert_eq!(buffer.text(), "a");
19770    });
19771}
19772
19773#[gpui::test]
19774async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19775    init_test(cx, |_| {});
19776
19777    let fs = FakeFs::new(cx.executor());
19778    let main_text = r#"fn main() {
19779println!("1");
19780println!("2");
19781println!("3");
19782println!("4");
19783println!("5");
19784}"#;
19785    let lib_text = "mod foo {}";
19786    fs.insert_tree(
19787        path!("/a"),
19788        json!({
19789            "lib.rs": lib_text,
19790            "main.rs": main_text,
19791        }),
19792    )
19793    .await;
19794
19795    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19796    let (workspace, cx) =
19797        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19798    let worktree_id = workspace.update(cx, |workspace, cx| {
19799        workspace.project().update(cx, |project, cx| {
19800            project.worktrees(cx).next().unwrap().read(cx).id()
19801        })
19802    });
19803
19804    let expected_ranges = vec![
19805        Point::new(0, 0)..Point::new(0, 0),
19806        Point::new(1, 0)..Point::new(1, 1),
19807        Point::new(2, 0)..Point::new(2, 2),
19808        Point::new(3, 0)..Point::new(3, 3),
19809    ];
19810
19811    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19812    let editor_1 = workspace
19813        .update_in(cx, |workspace, window, cx| {
19814            workspace.open_path(
19815                (worktree_id, "main.rs"),
19816                Some(pane_1.downgrade()),
19817                true,
19818                window,
19819                cx,
19820            )
19821        })
19822        .unwrap()
19823        .await
19824        .downcast::<Editor>()
19825        .unwrap();
19826    pane_1.update(cx, |pane, cx| {
19827        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19828        open_editor.update(cx, |editor, cx| {
19829            assert_eq!(
19830                editor.display_text(cx),
19831                main_text,
19832                "Original main.rs text on initial open",
19833            );
19834            assert_eq!(
19835                editor
19836                    .selections
19837                    .all::<Point>(cx)
19838                    .into_iter()
19839                    .map(|s| s.range())
19840                    .collect::<Vec<_>>(),
19841                vec![Point::zero()..Point::zero()],
19842                "Default selections on initial open",
19843            );
19844        })
19845    });
19846    editor_1.update_in(cx, |editor, window, cx| {
19847        editor.change_selections(None, window, cx, |s| {
19848            s.select_ranges(expected_ranges.clone());
19849        });
19850    });
19851
19852    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19853        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19854    });
19855    let editor_2 = workspace
19856        .update_in(cx, |workspace, window, cx| {
19857            workspace.open_path(
19858                (worktree_id, "main.rs"),
19859                Some(pane_2.downgrade()),
19860                true,
19861                window,
19862                cx,
19863            )
19864        })
19865        .unwrap()
19866        .await
19867        .downcast::<Editor>()
19868        .unwrap();
19869    pane_2.update(cx, |pane, cx| {
19870        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19871        open_editor.update(cx, |editor, cx| {
19872            assert_eq!(
19873                editor.display_text(cx),
19874                main_text,
19875                "Original main.rs text on initial open in another panel",
19876            );
19877            assert_eq!(
19878                editor
19879                    .selections
19880                    .all::<Point>(cx)
19881                    .into_iter()
19882                    .map(|s| s.range())
19883                    .collect::<Vec<_>>(),
19884                vec![Point::zero()..Point::zero()],
19885                "Default selections on initial open in another panel",
19886            );
19887        })
19888    });
19889
19890    editor_2.update_in(cx, |editor, window, cx| {
19891        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19892    });
19893
19894    let _other_editor_1 = workspace
19895        .update_in(cx, |workspace, window, cx| {
19896            workspace.open_path(
19897                (worktree_id, "lib.rs"),
19898                Some(pane_1.downgrade()),
19899                true,
19900                window,
19901                cx,
19902            )
19903        })
19904        .unwrap()
19905        .await
19906        .downcast::<Editor>()
19907        .unwrap();
19908    pane_1
19909        .update_in(cx, |pane, window, cx| {
19910            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19911                .unwrap()
19912        })
19913        .await
19914        .unwrap();
19915    drop(editor_1);
19916    pane_1.update(cx, |pane, cx| {
19917        pane.active_item()
19918            .unwrap()
19919            .downcast::<Editor>()
19920            .unwrap()
19921            .update(cx, |editor, cx| {
19922                assert_eq!(
19923                    editor.display_text(cx),
19924                    lib_text,
19925                    "Other file should be open and active",
19926                );
19927            });
19928        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19929    });
19930
19931    let _other_editor_2 = workspace
19932        .update_in(cx, |workspace, window, cx| {
19933            workspace.open_path(
19934                (worktree_id, "lib.rs"),
19935                Some(pane_2.downgrade()),
19936                true,
19937                window,
19938                cx,
19939            )
19940        })
19941        .unwrap()
19942        .await
19943        .downcast::<Editor>()
19944        .unwrap();
19945    pane_2
19946        .update_in(cx, |pane, window, cx| {
19947            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19948                .unwrap()
19949        })
19950        .await
19951        .unwrap();
19952    drop(editor_2);
19953    pane_2.update(cx, |pane, cx| {
19954        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19955        open_editor.update(cx, |editor, cx| {
19956            assert_eq!(
19957                editor.display_text(cx),
19958                lib_text,
19959                "Other file should be open and active in another panel too",
19960            );
19961        });
19962        assert_eq!(
19963            pane.items().count(),
19964            1,
19965            "No other editors should be open in another pane",
19966        );
19967    });
19968
19969    let _editor_1_reopened = workspace
19970        .update_in(cx, |workspace, window, cx| {
19971            workspace.open_path(
19972                (worktree_id, "main.rs"),
19973                Some(pane_1.downgrade()),
19974                true,
19975                window,
19976                cx,
19977            )
19978        })
19979        .unwrap()
19980        .await
19981        .downcast::<Editor>()
19982        .unwrap();
19983    let _editor_2_reopened = workspace
19984        .update_in(cx, |workspace, window, cx| {
19985            workspace.open_path(
19986                (worktree_id, "main.rs"),
19987                Some(pane_2.downgrade()),
19988                true,
19989                window,
19990                cx,
19991            )
19992        })
19993        .unwrap()
19994        .await
19995        .downcast::<Editor>()
19996        .unwrap();
19997    pane_1.update(cx, |pane, cx| {
19998        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19999        open_editor.update(cx, |editor, cx| {
20000            assert_eq!(
20001                editor.display_text(cx),
20002                main_text,
20003                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
20004            );
20005            assert_eq!(
20006                editor
20007                    .selections
20008                    .all::<Point>(cx)
20009                    .into_iter()
20010                    .map(|s| s.range())
20011                    .collect::<Vec<_>>(),
20012                expected_ranges,
20013                "Previous editor in the 1st panel had selections and should get them restored on reopen",
20014            );
20015        })
20016    });
20017    pane_2.update(cx, |pane, cx| {
20018        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20019        open_editor.update(cx, |editor, cx| {
20020            assert_eq!(
20021                editor.display_text(cx),
20022                r#"fn main() {
20023⋯rintln!("1");
20024⋯intln!("2");
20025⋯ntln!("3");
20026println!("4");
20027println!("5");
20028}"#,
20029                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
20030            );
20031            assert_eq!(
20032                editor
20033                    .selections
20034                    .all::<Point>(cx)
20035                    .into_iter()
20036                    .map(|s| s.range())
20037                    .collect::<Vec<_>>(),
20038                vec![Point::zero()..Point::zero()],
20039                "Previous editor in the 2nd pane had no selections changed hence should restore none",
20040            );
20041        })
20042    });
20043}
20044
20045#[gpui::test]
20046async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
20047    init_test(cx, |_| {});
20048
20049    let fs = FakeFs::new(cx.executor());
20050    let main_text = r#"fn main() {
20051println!("1");
20052println!("2");
20053println!("3");
20054println!("4");
20055println!("5");
20056}"#;
20057    let lib_text = "mod foo {}";
20058    fs.insert_tree(
20059        path!("/a"),
20060        json!({
20061            "lib.rs": lib_text,
20062            "main.rs": main_text,
20063        }),
20064    )
20065    .await;
20066
20067    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
20068    let (workspace, cx) =
20069        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20070    let worktree_id = workspace.update(cx, |workspace, cx| {
20071        workspace.project().update(cx, |project, cx| {
20072            project.worktrees(cx).next().unwrap().read(cx).id()
20073        })
20074    });
20075
20076    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20077    let editor = workspace
20078        .update_in(cx, |workspace, window, cx| {
20079            workspace.open_path(
20080                (worktree_id, "main.rs"),
20081                Some(pane.downgrade()),
20082                true,
20083                window,
20084                cx,
20085            )
20086        })
20087        .unwrap()
20088        .await
20089        .downcast::<Editor>()
20090        .unwrap();
20091    pane.update(cx, |pane, cx| {
20092        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20093        open_editor.update(cx, |editor, cx| {
20094            assert_eq!(
20095                editor.display_text(cx),
20096                main_text,
20097                "Original main.rs text on initial open",
20098            );
20099        })
20100    });
20101    editor.update_in(cx, |editor, window, cx| {
20102        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
20103    });
20104
20105    cx.update_global(|store: &mut SettingsStore, cx| {
20106        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20107            s.restore_on_file_reopen = Some(false);
20108        });
20109    });
20110    editor.update_in(cx, |editor, window, cx| {
20111        editor.fold_ranges(
20112            vec![
20113                Point::new(1, 0)..Point::new(1, 1),
20114                Point::new(2, 0)..Point::new(2, 2),
20115                Point::new(3, 0)..Point::new(3, 3),
20116            ],
20117            false,
20118            window,
20119            cx,
20120        );
20121    });
20122    pane.update_in(cx, |pane, window, cx| {
20123        pane.close_all_items(&CloseAllItems::default(), window, cx)
20124            .unwrap()
20125    })
20126    .await
20127    .unwrap();
20128    pane.update(cx, |pane, _| {
20129        assert!(pane.active_item().is_none());
20130    });
20131    cx.update_global(|store: &mut SettingsStore, cx| {
20132        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20133            s.restore_on_file_reopen = Some(true);
20134        });
20135    });
20136
20137    let _editor_reopened = workspace
20138        .update_in(cx, |workspace, window, cx| {
20139            workspace.open_path(
20140                (worktree_id, "main.rs"),
20141                Some(pane.downgrade()),
20142                true,
20143                window,
20144                cx,
20145            )
20146        })
20147        .unwrap()
20148        .await
20149        .downcast::<Editor>()
20150        .unwrap();
20151    pane.update(cx, |pane, cx| {
20152        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20153        open_editor.update(cx, |editor, cx| {
20154            assert_eq!(
20155                editor.display_text(cx),
20156                main_text,
20157                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20158            );
20159        })
20160    });
20161}
20162
20163#[gpui::test]
20164async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20165    struct EmptyModalView {
20166        focus_handle: gpui::FocusHandle,
20167    }
20168    impl EventEmitter<DismissEvent> for EmptyModalView {}
20169    impl Render for EmptyModalView {
20170        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20171            div()
20172        }
20173    }
20174    impl Focusable for EmptyModalView {
20175        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20176            self.focus_handle.clone()
20177        }
20178    }
20179    impl workspace::ModalView for EmptyModalView {}
20180    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20181        EmptyModalView {
20182            focus_handle: cx.focus_handle(),
20183        }
20184    }
20185
20186    init_test(cx, |_| {});
20187
20188    let fs = FakeFs::new(cx.executor());
20189    let project = Project::test(fs, [], cx).await;
20190    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20191    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20192    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20193    let editor = cx.new_window_entity(|window, cx| {
20194        Editor::new(
20195            EditorMode::full(),
20196            buffer,
20197            Some(project.clone()),
20198            window,
20199            cx,
20200        )
20201    });
20202    workspace
20203        .update(cx, |workspace, window, cx| {
20204            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20205        })
20206        .unwrap();
20207    editor.update_in(cx, |editor, window, cx| {
20208        editor.open_context_menu(&OpenContextMenu, window, cx);
20209        assert!(editor.mouse_context_menu.is_some());
20210    });
20211    workspace
20212        .update(cx, |workspace, window, cx| {
20213            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20214        })
20215        .unwrap();
20216    cx.read(|cx| {
20217        assert!(editor.read(cx).mouse_context_menu.is_none());
20218    });
20219}
20220
20221#[gpui::test]
20222async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20223    init_test(cx, |_| {});
20224
20225    let fs = FakeFs::new(cx.executor());
20226    fs.insert_file(path!("/file.html"), Default::default())
20227        .await;
20228
20229    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20230
20231    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20232    let html_language = Arc::new(Language::new(
20233        LanguageConfig {
20234            name: "HTML".into(),
20235            matcher: LanguageMatcher {
20236                path_suffixes: vec!["html".to_string()],
20237                ..LanguageMatcher::default()
20238            },
20239            brackets: BracketPairConfig {
20240                pairs: vec![BracketPair {
20241                    start: "<".into(),
20242                    end: ">".into(),
20243                    close: true,
20244                    ..Default::default()
20245                }],
20246                ..Default::default()
20247            },
20248            ..Default::default()
20249        },
20250        Some(tree_sitter_html::LANGUAGE.into()),
20251    ));
20252    language_registry.add(html_language);
20253    let mut fake_servers = language_registry.register_fake_lsp(
20254        "HTML",
20255        FakeLspAdapter {
20256            capabilities: lsp::ServerCapabilities {
20257                completion_provider: Some(lsp::CompletionOptions {
20258                    resolve_provider: Some(true),
20259                    ..Default::default()
20260                }),
20261                ..Default::default()
20262            },
20263            ..Default::default()
20264        },
20265    );
20266
20267    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20268    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20269
20270    let worktree_id = workspace
20271        .update(cx, |workspace, _window, cx| {
20272            workspace.project().update(cx, |project, cx| {
20273                project.worktrees(cx).next().unwrap().read(cx).id()
20274            })
20275        })
20276        .unwrap();
20277    project
20278        .update(cx, |project, cx| {
20279            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20280        })
20281        .await
20282        .unwrap();
20283    let editor = workspace
20284        .update(cx, |workspace, window, cx| {
20285            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20286        })
20287        .unwrap()
20288        .await
20289        .unwrap()
20290        .downcast::<Editor>()
20291        .unwrap();
20292
20293    let fake_server = fake_servers.next().await.unwrap();
20294    editor.update_in(cx, |editor, window, cx| {
20295        editor.set_text("<ad></ad>", window, cx);
20296        editor.change_selections(None, window, cx, |selections| {
20297            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20298        });
20299        let Some((buffer, _)) = editor
20300            .buffer
20301            .read(cx)
20302            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20303        else {
20304            panic!("Failed to get buffer for selection position");
20305        };
20306        let buffer = buffer.read(cx);
20307        let buffer_id = buffer.remote_id();
20308        let opening_range =
20309            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20310        let closing_range =
20311            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20312        let mut linked_ranges = HashMap::default();
20313        linked_ranges.insert(
20314            buffer_id,
20315            vec![(opening_range.clone(), vec![closing_range.clone()])],
20316        );
20317        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20318    });
20319    let mut completion_handle =
20320        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20321            Ok(Some(lsp::CompletionResponse::Array(vec![
20322                lsp::CompletionItem {
20323                    label: "head".to_string(),
20324                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20325                        lsp::InsertReplaceEdit {
20326                            new_text: "head".to_string(),
20327                            insert: lsp::Range::new(
20328                                lsp::Position::new(0, 1),
20329                                lsp::Position::new(0, 3),
20330                            ),
20331                            replace: lsp::Range::new(
20332                                lsp::Position::new(0, 1),
20333                                lsp::Position::new(0, 3),
20334                            ),
20335                        },
20336                    )),
20337                    ..Default::default()
20338                },
20339            ])))
20340        });
20341    editor.update_in(cx, |editor, window, cx| {
20342        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20343    });
20344    cx.run_until_parked();
20345    completion_handle.next().await.unwrap();
20346    editor.update(cx, |editor, _| {
20347        assert!(
20348            editor.context_menu_visible(),
20349            "Completion menu should be visible"
20350        );
20351    });
20352    editor.update_in(cx, |editor, window, cx| {
20353        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20354    });
20355    cx.executor().run_until_parked();
20356    editor.update(cx, |editor, cx| {
20357        assert_eq!(editor.text(cx), "<head></head>");
20358    });
20359}
20360
20361#[gpui::test]
20362async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20363    init_test(cx, |_| {});
20364
20365    let fs = FakeFs::new(cx.executor());
20366    fs.insert_tree(
20367        path!("/root"),
20368        json!({
20369            "a": {
20370                "main.rs": "fn main() {}",
20371            },
20372            "foo": {
20373                "bar": {
20374                    "external_file.rs": "pub mod external {}",
20375                }
20376            }
20377        }),
20378    )
20379    .await;
20380
20381    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20382    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20383    language_registry.add(rust_lang());
20384    let _fake_servers = language_registry.register_fake_lsp(
20385        "Rust",
20386        FakeLspAdapter {
20387            ..FakeLspAdapter::default()
20388        },
20389    );
20390    let (workspace, cx) =
20391        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20392    let worktree_id = workspace.update(cx, |workspace, cx| {
20393        workspace.project().update(cx, |project, cx| {
20394            project.worktrees(cx).next().unwrap().read(cx).id()
20395        })
20396    });
20397
20398    let assert_language_servers_count =
20399        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20400            project.update(cx, |project, cx| {
20401                let current = project
20402                    .lsp_store()
20403                    .read(cx)
20404                    .as_local()
20405                    .unwrap()
20406                    .language_servers
20407                    .len();
20408                assert_eq!(expected, current, "{context}");
20409            });
20410        };
20411
20412    assert_language_servers_count(
20413        0,
20414        "No servers should be running before any file is open",
20415        cx,
20416    );
20417    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20418    let main_editor = workspace
20419        .update_in(cx, |workspace, window, cx| {
20420            workspace.open_path(
20421                (worktree_id, "main.rs"),
20422                Some(pane.downgrade()),
20423                true,
20424                window,
20425                cx,
20426            )
20427        })
20428        .unwrap()
20429        .await
20430        .downcast::<Editor>()
20431        .unwrap();
20432    pane.update(cx, |pane, cx| {
20433        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20434        open_editor.update(cx, |editor, cx| {
20435            assert_eq!(
20436                editor.display_text(cx),
20437                "fn main() {}",
20438                "Original main.rs text on initial open",
20439            );
20440        });
20441        assert_eq!(open_editor, main_editor);
20442    });
20443    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20444
20445    let external_editor = workspace
20446        .update_in(cx, |workspace, window, cx| {
20447            workspace.open_abs_path(
20448                PathBuf::from("/root/foo/bar/external_file.rs"),
20449                OpenOptions::default(),
20450                window,
20451                cx,
20452            )
20453        })
20454        .await
20455        .expect("opening external file")
20456        .downcast::<Editor>()
20457        .expect("downcasted external file's open element to editor");
20458    pane.update(cx, |pane, cx| {
20459        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20460        open_editor.update(cx, |editor, cx| {
20461            assert_eq!(
20462                editor.display_text(cx),
20463                "pub mod external {}",
20464                "External file is open now",
20465            );
20466        });
20467        assert_eq!(open_editor, external_editor);
20468    });
20469    assert_language_servers_count(
20470        1,
20471        "Second, external, *.rs file should join the existing server",
20472        cx,
20473    );
20474
20475    pane.update_in(cx, |pane, window, cx| {
20476        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20477    })
20478    .unwrap()
20479    .await
20480    .unwrap();
20481    pane.update_in(cx, |pane, window, cx| {
20482        pane.navigate_backward(window, cx);
20483    });
20484    cx.run_until_parked();
20485    pane.update(cx, |pane, cx| {
20486        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20487        open_editor.update(cx, |editor, cx| {
20488            assert_eq!(
20489                editor.display_text(cx),
20490                "pub mod external {}",
20491                "External file is open now",
20492            );
20493        });
20494    });
20495    assert_language_servers_count(
20496        1,
20497        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20498        cx,
20499    );
20500
20501    cx.update(|_, cx| {
20502        workspace::reload(&workspace::Reload::default(), cx);
20503    });
20504    assert_language_servers_count(
20505        1,
20506        "After reloading the worktree with local and external files opened, only one project should be started",
20507        cx,
20508    );
20509}
20510
20511#[gpui::test]
20512async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20513    init_test(cx, |_| {});
20514
20515    let mut cx = EditorTestContext::new(cx).await;
20516    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20517    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20518
20519    // test cursor move to start of each line on tab
20520    // for `if`, `elif`, `else`, `while`, `with` and `for`
20521    cx.set_state(indoc! {"
20522        def main():
20523        ˇ    for item in items:
20524        ˇ        while item.active:
20525        ˇ            if item.value > 10:
20526        ˇ                continue
20527        ˇ            elif item.value < 0:
20528        ˇ                break
20529        ˇ            else:
20530        ˇ                with item.context() as ctx:
20531        ˇ                    yield count
20532        ˇ        else:
20533        ˇ            log('while else')
20534        ˇ    else:
20535        ˇ        log('for else')
20536    "});
20537    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20538    cx.assert_editor_state(indoc! {"
20539        def main():
20540            ˇfor item in items:
20541                ˇwhile item.active:
20542                    ˇif item.value > 10:
20543                        ˇcontinue
20544                    ˇelif item.value < 0:
20545                        ˇbreak
20546                    ˇelse:
20547                        ˇwith item.context() as ctx:
20548                            ˇyield count
20549                ˇelse:
20550                    ˇlog('while else')
20551            ˇelse:
20552                ˇlog('for else')
20553    "});
20554    // test relative indent is preserved when tab
20555    // for `if`, `elif`, `else`, `while`, `with` and `for`
20556    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20557    cx.assert_editor_state(indoc! {"
20558        def main():
20559                ˇfor item in items:
20560                    ˇwhile item.active:
20561                        ˇif item.value > 10:
20562                            ˇcontinue
20563                        ˇelif item.value < 0:
20564                            ˇbreak
20565                        ˇelse:
20566                            ˇwith item.context() as ctx:
20567                                ˇyield count
20568                    ˇelse:
20569                        ˇlog('while else')
20570                ˇelse:
20571                    ˇlog('for else')
20572    "});
20573
20574    // test cursor move to start of each line on tab
20575    // for `try`, `except`, `else`, `finally`, `match` and `def`
20576    cx.set_state(indoc! {"
20577        def main():
20578        ˇ    try:
20579        ˇ       fetch()
20580        ˇ    except ValueError:
20581        ˇ       handle_error()
20582        ˇ    else:
20583        ˇ        match value:
20584        ˇ            case _:
20585        ˇ    finally:
20586        ˇ        def status():
20587        ˇ            return 0
20588    "});
20589    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20590    cx.assert_editor_state(indoc! {"
20591        def main():
20592            ˇtry:
20593                ˇfetch()
20594            ˇexcept ValueError:
20595                ˇhandle_error()
20596            ˇelse:
20597                ˇmatch value:
20598                    ˇcase _:
20599            ˇfinally:
20600                ˇdef status():
20601                    ˇreturn 0
20602    "});
20603    // test relative indent is preserved when tab
20604    // for `try`, `except`, `else`, `finally`, `match` and `def`
20605    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20606    cx.assert_editor_state(indoc! {"
20607        def main():
20608                ˇtry:
20609                    ˇfetch()
20610                ˇexcept ValueError:
20611                    ˇhandle_error()
20612                ˇelse:
20613                    ˇmatch value:
20614                        ˇcase _:
20615                ˇfinally:
20616                    ˇdef status():
20617                        ˇreturn 0
20618    "});
20619}
20620
20621#[gpui::test]
20622async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20623    init_test(cx, |_| {});
20624
20625    let mut cx = EditorTestContext::new(cx).await;
20626    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20627    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20628
20629    // test `else` auto outdents when typed inside `if` block
20630    cx.set_state(indoc! {"
20631        def main():
20632            if i == 2:
20633                return
20634                ˇ
20635    "});
20636    cx.update_editor(|editor, window, cx| {
20637        editor.handle_input("else:", window, cx);
20638    });
20639    cx.assert_editor_state(indoc! {"
20640        def main():
20641            if i == 2:
20642                return
20643            else:ˇ
20644    "});
20645
20646    // test `except` auto outdents when typed inside `try` block
20647    cx.set_state(indoc! {"
20648        def main():
20649            try:
20650                i = 2
20651                ˇ
20652    "});
20653    cx.update_editor(|editor, window, cx| {
20654        editor.handle_input("except:", window, cx);
20655    });
20656    cx.assert_editor_state(indoc! {"
20657        def main():
20658            try:
20659                i = 2
20660            except:ˇ
20661    "});
20662
20663    // test `else` auto outdents when typed inside `except` block
20664    cx.set_state(indoc! {"
20665        def main():
20666            try:
20667                i = 2
20668            except:
20669                j = 2
20670                ˇ
20671    "});
20672    cx.update_editor(|editor, window, cx| {
20673        editor.handle_input("else:", window, cx);
20674    });
20675    cx.assert_editor_state(indoc! {"
20676        def main():
20677            try:
20678                i = 2
20679            except:
20680                j = 2
20681            else:ˇ
20682    "});
20683
20684    // test `finally` auto outdents when typed inside `else` block
20685    cx.set_state(indoc! {"
20686        def main():
20687            try:
20688                i = 2
20689            except:
20690                j = 2
20691            else:
20692                k = 2
20693                ˇ
20694    "});
20695    cx.update_editor(|editor, window, cx| {
20696        editor.handle_input("finally:", window, cx);
20697    });
20698    cx.assert_editor_state(indoc! {"
20699        def main():
20700            try:
20701                i = 2
20702            except:
20703                j = 2
20704            else:
20705                k = 2
20706            finally:ˇ
20707    "});
20708
20709    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20710    // cx.set_state(indoc! {"
20711    //     def main():
20712    //         try:
20713    //             for i in range(n):
20714    //                 pass
20715    //             ˇ
20716    // "});
20717    // cx.update_editor(|editor, window, cx| {
20718    //     editor.handle_input("except:", window, cx);
20719    // });
20720    // cx.assert_editor_state(indoc! {"
20721    //     def main():
20722    //         try:
20723    //             for i in range(n):
20724    //                 pass
20725    //         except:ˇ
20726    // "});
20727
20728    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20729    // cx.set_state(indoc! {"
20730    //     def main():
20731    //         try:
20732    //             i = 2
20733    //         except:
20734    //             for i in range(n):
20735    //                 pass
20736    //             ˇ
20737    // "});
20738    // cx.update_editor(|editor, window, cx| {
20739    //     editor.handle_input("else:", window, cx);
20740    // });
20741    // cx.assert_editor_state(indoc! {"
20742    //     def main():
20743    //         try:
20744    //             i = 2
20745    //         except:
20746    //             for i in range(n):
20747    //                 pass
20748    //         else:ˇ
20749    // "});
20750
20751    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20752    // cx.set_state(indoc! {"
20753    //     def main():
20754    //         try:
20755    //             i = 2
20756    //         except:
20757    //             j = 2
20758    //         else:
20759    //             for i in range(n):
20760    //                 pass
20761    //             ˇ
20762    // "});
20763    // cx.update_editor(|editor, window, cx| {
20764    //     editor.handle_input("finally:", window, cx);
20765    // });
20766    // cx.assert_editor_state(indoc! {"
20767    //     def main():
20768    //         try:
20769    //             i = 2
20770    //         except:
20771    //             j = 2
20772    //         else:
20773    //             for i in range(n):
20774    //                 pass
20775    //         finally:ˇ
20776    // "});
20777
20778    // test `else` stays at correct indent when typed after `for` block
20779    cx.set_state(indoc! {"
20780        def main():
20781            for i in range(10):
20782                if i == 3:
20783                    break
20784            ˇ
20785    "});
20786    cx.update_editor(|editor, window, cx| {
20787        editor.handle_input("else:", window, cx);
20788    });
20789    cx.assert_editor_state(indoc! {"
20790        def main():
20791            for i in range(10):
20792                if i == 3:
20793                    break
20794            else:ˇ
20795    "});
20796}
20797
20798#[gpui::test]
20799async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
20800    init_test(cx, |_| {});
20801    update_test_language_settings(cx, |settings| {
20802        settings.defaults.extend_comment_on_newline = Some(false);
20803    });
20804    let mut cx = EditorTestContext::new(cx).await;
20805    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20806    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20807
20808    // test correct indent after newline on comment
20809    cx.set_state(indoc! {"
20810        # COMMENT:ˇ
20811    "});
20812    cx.update_editor(|editor, window, cx| {
20813        editor.newline(&Newline, window, cx);
20814    });
20815    cx.assert_editor_state(indoc! {"
20816        # COMMENT:
20817        ˇ
20818    "});
20819
20820    // test correct indent after newline in curly brackets
20821    cx.set_state(indoc! {"
20822        {ˇ}
20823    "});
20824    cx.update_editor(|editor, window, cx| {
20825        editor.newline(&Newline, window, cx);
20826    });
20827    cx.run_until_parked();
20828    cx.assert_editor_state(indoc! {"
20829        {
20830            ˇ
20831        }
20832    "});
20833}
20834
20835fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20836    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20837    point..point
20838}
20839
20840fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20841    let (text, ranges) = marked_text_ranges(marked_text, true);
20842    assert_eq!(editor.text(cx), text);
20843    assert_eq!(
20844        editor.selections.ranges(cx),
20845        ranges,
20846        "Assert selections are {}",
20847        marked_text
20848    );
20849}
20850
20851pub fn handle_signature_help_request(
20852    cx: &mut EditorLspTestContext,
20853    mocked_response: lsp::SignatureHelp,
20854) -> impl Future<Output = ()> + use<> {
20855    let mut request =
20856        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20857            let mocked_response = mocked_response.clone();
20858            async move { Ok(Some(mocked_response)) }
20859        });
20860
20861    async move {
20862        request.next().await;
20863    }
20864}
20865
20866/// Handle completion request passing a marked string specifying where the completion
20867/// should be triggered from using '|' character, what range should be replaced, and what completions
20868/// should be returned using '<' and '>' to delimit the range.
20869///
20870/// Also see `handle_completion_request_with_insert_and_replace`.
20871#[track_caller]
20872pub fn handle_completion_request(
20873    cx: &mut EditorLspTestContext,
20874    marked_string: &str,
20875    completions: Vec<&'static str>,
20876    counter: Arc<AtomicUsize>,
20877) -> impl Future<Output = ()> {
20878    let complete_from_marker: TextRangeMarker = '|'.into();
20879    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20880    let (_, mut marked_ranges) = marked_text_ranges_by(
20881        marked_string,
20882        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20883    );
20884
20885    let complete_from_position =
20886        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20887    let replace_range =
20888        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20889
20890    let mut request =
20891        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20892            let completions = completions.clone();
20893            counter.fetch_add(1, atomic::Ordering::Release);
20894            async move {
20895                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20896                assert_eq!(
20897                    params.text_document_position.position,
20898                    complete_from_position
20899                );
20900                Ok(Some(lsp::CompletionResponse::Array(
20901                    completions
20902                        .iter()
20903                        .map(|completion_text| lsp::CompletionItem {
20904                            label: completion_text.to_string(),
20905                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20906                                range: replace_range,
20907                                new_text: completion_text.to_string(),
20908                            })),
20909                            ..Default::default()
20910                        })
20911                        .collect(),
20912                )))
20913            }
20914        });
20915
20916    async move {
20917        request.next().await;
20918    }
20919}
20920
20921/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20922/// given instead, which also contains an `insert` range.
20923///
20924/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20925/// that is, `replace_range.start..cursor_pos`.
20926pub fn handle_completion_request_with_insert_and_replace(
20927    cx: &mut EditorLspTestContext,
20928    marked_string: &str,
20929    completions: Vec<&'static str>,
20930    counter: Arc<AtomicUsize>,
20931) -> impl Future<Output = ()> {
20932    let complete_from_marker: TextRangeMarker = '|'.into();
20933    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20934    let (_, mut marked_ranges) = marked_text_ranges_by(
20935        marked_string,
20936        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20937    );
20938
20939    let complete_from_position =
20940        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20941    let replace_range =
20942        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20943
20944    let mut request =
20945        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20946            let completions = completions.clone();
20947            counter.fetch_add(1, atomic::Ordering::Release);
20948            async move {
20949                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20950                assert_eq!(
20951                    params.text_document_position.position, complete_from_position,
20952                    "marker `|` position doesn't match",
20953                );
20954                Ok(Some(lsp::CompletionResponse::Array(
20955                    completions
20956                        .iter()
20957                        .map(|completion_text| lsp::CompletionItem {
20958                            label: completion_text.to_string(),
20959                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20960                                lsp::InsertReplaceEdit {
20961                                    insert: lsp::Range {
20962                                        start: replace_range.start,
20963                                        end: complete_from_position,
20964                                    },
20965                                    replace: replace_range,
20966                                    new_text: completion_text.to_string(),
20967                                },
20968                            )),
20969                            ..Default::default()
20970                        })
20971                        .collect(),
20972                )))
20973            }
20974        });
20975
20976    async move {
20977        request.next().await;
20978    }
20979}
20980
20981fn handle_resolve_completion_request(
20982    cx: &mut EditorLspTestContext,
20983    edits: Option<Vec<(&'static str, &'static str)>>,
20984) -> impl Future<Output = ()> {
20985    let edits = edits.map(|edits| {
20986        edits
20987            .iter()
20988            .map(|(marked_string, new_text)| {
20989                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20990                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20991                lsp::TextEdit::new(replace_range, new_text.to_string())
20992            })
20993            .collect::<Vec<_>>()
20994    });
20995
20996    let mut request =
20997        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20998            let edits = edits.clone();
20999            async move {
21000                Ok(lsp::CompletionItem {
21001                    additional_text_edits: edits,
21002                    ..Default::default()
21003                })
21004            }
21005        });
21006
21007    async move {
21008        request.next().await;
21009    }
21010}
21011
21012pub(crate) fn update_test_language_settings(
21013    cx: &mut TestAppContext,
21014    f: impl Fn(&mut AllLanguageSettingsContent),
21015) {
21016    cx.update(|cx| {
21017        SettingsStore::update_global(cx, |store, cx| {
21018            store.update_user_settings::<AllLanguageSettings>(cx, f);
21019        });
21020    });
21021}
21022
21023pub(crate) fn update_test_project_settings(
21024    cx: &mut TestAppContext,
21025    f: impl Fn(&mut ProjectSettings),
21026) {
21027    cx.update(|cx| {
21028        SettingsStore::update_global(cx, |store, cx| {
21029            store.update_user_settings::<ProjectSettings>(cx, f);
21030        });
21031    });
21032}
21033
21034pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
21035    cx.update(|cx| {
21036        assets::Assets.load_test_fonts(cx);
21037        let store = SettingsStore::test(cx);
21038        cx.set_global(store);
21039        theme::init(theme::LoadThemes::JustBase, cx);
21040        release_channel::init(SemanticVersion::default(), cx);
21041        client::init_settings(cx);
21042        language::init(cx);
21043        Project::init_settings(cx);
21044        workspace::init_settings(cx);
21045        crate::init(cx);
21046    });
21047
21048    update_test_language_settings(cx, f);
21049}
21050
21051#[track_caller]
21052fn assert_hunk_revert(
21053    not_reverted_text_with_selections: &str,
21054    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
21055    expected_reverted_text_with_selections: &str,
21056    base_text: &str,
21057    cx: &mut EditorLspTestContext,
21058) {
21059    cx.set_state(not_reverted_text_with_selections);
21060    cx.set_head_text(base_text);
21061    cx.executor().run_until_parked();
21062
21063    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
21064        let snapshot = editor.snapshot(window, cx);
21065        let reverted_hunk_statuses = snapshot
21066            .buffer_snapshot
21067            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
21068            .map(|hunk| hunk.status().kind)
21069            .collect::<Vec<_>>();
21070
21071        editor.git_restore(&Default::default(), window, cx);
21072        reverted_hunk_statuses
21073    });
21074    cx.executor().run_until_parked();
21075    cx.assert_editor_state(expected_reverted_text_with_selections);
21076    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
21077}