editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    linked_editing_ranges::LinkedEditingRanges,
    5    scroll::scroll_amount::ScrollAmount,
    6    test::{
    7        assert_text_with_selections, build_editor,
    8        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    9        editor_test_context::EditorTestContext,
   10        select_ranges,
   11    },
   12};
   13use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   14use futures::StreamExt;
   15use gpui::{
   16    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   17    VisualTestContext, WindowBounds, WindowOptions, div,
   18};
   19use indoc::indoc;
   20use language::{
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25    language_settings::{
   26        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   27        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   28    },
   29    tree_sitter_python,
   30};
   31use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   32use lsp::CompletionParams;
   33use multi_buffer::{IndentGuide, PathKey};
   34use parking_lot::Mutex;
   35use pretty_assertions::{assert_eq, assert_ne};
   36use project::{
   37    FakeFs,
   38    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   39    project_settings::{LspSettings, ProjectSettings},
   40};
   41use serde_json::{self, json};
   42use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   43use std::{
   44    iter,
   45    sync::atomic::{self, AtomicUsize},
   46};
   47use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   48use text::ToPoint as _;
   49use unindent::Unindent;
   50use util::{
   51    assert_set_eq, path,
   52    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   53    uri,
   54};
   55use workspace::{
   56    CloseActiveItem, CloseAllItems, CloseInactiveItems, NavigationEntry, OpenOptions, ViewId,
   57    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   58};
   59
   60#[gpui::test]
   61fn test_edit_events(cx: &mut TestAppContext) {
   62    init_test(cx, |_| {});
   63
   64    let buffer = cx.new(|cx| {
   65        let mut buffer = language::Buffer::local("123456", cx);
   66        buffer.set_group_interval(Duration::from_secs(1));
   67        buffer
   68    });
   69
   70    let events = Rc::new(RefCell::new(Vec::new()));
   71    let editor1 = cx.add_window({
   72        let events = events.clone();
   73        |window, cx| {
   74            let entity = cx.entity().clone();
   75            cx.subscribe_in(
   76                &entity,
   77                window,
   78                move |_, _, event: &EditorEvent, _, _| match event {
   79                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   80                    EditorEvent::BufferEdited => {
   81                        events.borrow_mut().push(("editor1", "buffer edited"))
   82                    }
   83                    _ => {}
   84                },
   85            )
   86            .detach();
   87            Editor::for_buffer(buffer.clone(), None, window, cx)
   88        }
   89    });
   90
   91    let editor2 = cx.add_window({
   92        let events = events.clone();
   93        |window, cx| {
   94            cx.subscribe_in(
   95                &cx.entity().clone(),
   96                window,
   97                move |_, _, event: &EditorEvent, _, _| match event {
   98                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   99                    EditorEvent::BufferEdited => {
  100                        events.borrow_mut().push(("editor2", "buffer edited"))
  101                    }
  102                    _ => {}
  103                },
  104            )
  105            .detach();
  106            Editor::for_buffer(buffer.clone(), None, window, cx)
  107        }
  108    });
  109
  110    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  111
  112    // Mutating editor 1 will emit an `Edited` event only for that editor.
  113    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  114    assert_eq!(
  115        mem::take(&mut *events.borrow_mut()),
  116        [
  117            ("editor1", "edited"),
  118            ("editor1", "buffer edited"),
  119            ("editor2", "buffer edited"),
  120        ]
  121    );
  122
  123    // Mutating editor 2 will emit an `Edited` event only for that editor.
  124    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  125    assert_eq!(
  126        mem::take(&mut *events.borrow_mut()),
  127        [
  128            ("editor2", "edited"),
  129            ("editor1", "buffer edited"),
  130            ("editor2", "buffer edited"),
  131        ]
  132    );
  133
  134    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  135    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  136    assert_eq!(
  137        mem::take(&mut *events.borrow_mut()),
  138        [
  139            ("editor1", "edited"),
  140            ("editor1", "buffer edited"),
  141            ("editor2", "buffer edited"),
  142        ]
  143    );
  144
  145    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  146    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  147    assert_eq!(
  148        mem::take(&mut *events.borrow_mut()),
  149        [
  150            ("editor1", "edited"),
  151            ("editor1", "buffer edited"),
  152            ("editor2", "buffer edited"),
  153        ]
  154    );
  155
  156    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  157    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  158    assert_eq!(
  159        mem::take(&mut *events.borrow_mut()),
  160        [
  161            ("editor2", "edited"),
  162            ("editor1", "buffer edited"),
  163            ("editor2", "buffer edited"),
  164        ]
  165    );
  166
  167    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  168    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  169    assert_eq!(
  170        mem::take(&mut *events.borrow_mut()),
  171        [
  172            ("editor2", "edited"),
  173            ("editor1", "buffer edited"),
  174            ("editor2", "buffer edited"),
  175        ]
  176    );
  177
  178    // No event is emitted when the mutation is a no-op.
  179    _ = editor2.update(cx, |editor, window, cx| {
  180        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  181
  182        editor.backspace(&Backspace, window, cx);
  183    });
  184    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  185}
  186
  187#[gpui::test]
  188fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  189    init_test(cx, |_| {});
  190
  191    let mut now = Instant::now();
  192    let group_interval = Duration::from_millis(1);
  193    let buffer = cx.new(|cx| {
  194        let mut buf = language::Buffer::local("123456", cx);
  195        buf.set_group_interval(group_interval);
  196        buf
  197    });
  198    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  199    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  200
  201    _ = editor.update(cx, |editor, window, cx| {
  202        editor.start_transaction_at(now, window, cx);
  203        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  204
  205        editor.insert("cd", window, cx);
  206        editor.end_transaction_at(now, cx);
  207        assert_eq!(editor.text(cx), "12cd56");
  208        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  209
  210        editor.start_transaction_at(now, window, cx);
  211        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  212        editor.insert("e", window, cx);
  213        editor.end_transaction_at(now, cx);
  214        assert_eq!(editor.text(cx), "12cde6");
  215        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  216
  217        now += group_interval + Duration::from_millis(1);
  218        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  219
  220        // Simulate an edit in another editor
  221        buffer.update(cx, |buffer, cx| {
  222            buffer.start_transaction_at(now, cx);
  223            buffer.edit([(0..1, "a")], None, cx);
  224            buffer.edit([(1..1, "b")], None, cx);
  225            buffer.end_transaction_at(now, cx);
  226        });
  227
  228        assert_eq!(editor.text(cx), "ab2cde6");
  229        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  230
  231        // Last transaction happened past the group interval in a different editor.
  232        // Undo it individually and don't restore selections.
  233        editor.undo(&Undo, window, cx);
  234        assert_eq!(editor.text(cx), "12cde6");
  235        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  236
  237        // First two transactions happened within the group interval in this editor.
  238        // Undo them together and restore selections.
  239        editor.undo(&Undo, window, cx);
  240        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  241        assert_eq!(editor.text(cx), "123456");
  242        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  243
  244        // Redo the first two transactions together.
  245        editor.redo(&Redo, window, cx);
  246        assert_eq!(editor.text(cx), "12cde6");
  247        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  248
  249        // Redo the last transaction on its own.
  250        editor.redo(&Redo, window, cx);
  251        assert_eq!(editor.text(cx), "ab2cde6");
  252        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  253
  254        // Test empty transactions.
  255        editor.start_transaction_at(now, window, cx);
  256        editor.end_transaction_at(now, cx);
  257        editor.undo(&Undo, window, cx);
  258        assert_eq!(editor.text(cx), "12cde6");
  259    });
  260}
  261
  262#[gpui::test]
  263fn test_ime_composition(cx: &mut TestAppContext) {
  264    init_test(cx, |_| {});
  265
  266    let buffer = cx.new(|cx| {
  267        let mut buffer = language::Buffer::local("abcde", cx);
  268        // Ensure automatic grouping doesn't occur.
  269        buffer.set_group_interval(Duration::ZERO);
  270        buffer
  271    });
  272
  273    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  274    cx.add_window(|window, cx| {
  275        let mut editor = build_editor(buffer.clone(), window, cx);
  276
  277        // Start a new IME composition.
  278        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  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        assert_eq!(editor.text(cx), "äbcde");
  282        assert_eq!(
  283            editor.marked_text_ranges(cx),
  284            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  285        );
  286
  287        // Finalize IME composition.
  288        editor.replace_text_in_range(None, "ā", window, cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // IME composition edits are grouped and are undone/redone at once.
  293        editor.undo(&Default::default(), window, cx);
  294        assert_eq!(editor.text(cx), "abcde");
  295        assert_eq!(editor.marked_text_ranges(cx), None);
  296        editor.redo(&Default::default(), window, cx);
  297        assert_eq!(editor.text(cx), "ābcde");
  298        assert_eq!(editor.marked_text_ranges(cx), None);
  299
  300        // Start a new IME composition.
  301        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  302        assert_eq!(
  303            editor.marked_text_ranges(cx),
  304            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  305        );
  306
  307        // Undoing during an IME composition cancels it.
  308        editor.undo(&Default::default(), window, cx);
  309        assert_eq!(editor.text(cx), "ābcde");
  310        assert_eq!(editor.marked_text_ranges(cx), None);
  311
  312        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  313        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  314        assert_eq!(editor.text(cx), "ābcdè");
  315        assert_eq!(
  316            editor.marked_text_ranges(cx),
  317            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  318        );
  319
  320        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  321        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  322        assert_eq!(editor.text(cx), "ābcdę");
  323        assert_eq!(editor.marked_text_ranges(cx), None);
  324
  325        // Start a new IME composition with multiple cursors.
  326        editor.change_selections(None, window, cx, |s| {
  327            s.select_ranges([
  328                OffsetUtf16(1)..OffsetUtf16(1),
  329                OffsetUtf16(3)..OffsetUtf16(3),
  330                OffsetUtf16(5)..OffsetUtf16(5),
  331            ])
  332        });
  333        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  334        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  335        assert_eq!(
  336            editor.marked_text_ranges(cx),
  337            Some(vec![
  338                OffsetUtf16(0)..OffsetUtf16(3),
  339                OffsetUtf16(4)..OffsetUtf16(7),
  340                OffsetUtf16(8)..OffsetUtf16(11)
  341            ])
  342        );
  343
  344        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  345        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  346        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  347        assert_eq!(
  348            editor.marked_text_ranges(cx),
  349            Some(vec![
  350                OffsetUtf16(1)..OffsetUtf16(2),
  351                OffsetUtf16(5)..OffsetUtf16(6),
  352                OffsetUtf16(9)..OffsetUtf16(10)
  353            ])
  354        );
  355
  356        // Finalize IME composition with multiple cursors.
  357        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  358        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  359        assert_eq!(editor.marked_text_ranges(cx), None);
  360
  361        editor
  362    });
  363}
  364
  365#[gpui::test]
  366fn test_selection_with_mouse(cx: &mut TestAppContext) {
  367    init_test(cx, |_| {});
  368
  369    let editor = cx.add_window(|window, cx| {
  370        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  371        build_editor(buffer, window, cx)
  372    });
  373
  374    _ = editor.update(cx, |editor, window, cx| {
  375        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  376    });
  377    assert_eq!(
  378        editor
  379            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  380            .unwrap(),
  381        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  382    );
  383
  384    _ = editor.update(cx, |editor, window, cx| {
  385        editor.update_selection(
  386            DisplayPoint::new(DisplayRow(3), 3),
  387            0,
  388            gpui::Point::<f32>::default(),
  389            window,
  390            cx,
  391        );
  392    });
  393
  394    assert_eq!(
  395        editor
  396            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  397            .unwrap(),
  398        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  399    );
  400
  401    _ = editor.update(cx, |editor, window, cx| {
  402        editor.update_selection(
  403            DisplayPoint::new(DisplayRow(1), 1),
  404            0,
  405            gpui::Point::<f32>::default(),
  406            window,
  407            cx,
  408        );
  409    });
  410
  411    assert_eq!(
  412        editor
  413            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  414            .unwrap(),
  415        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  416    );
  417
  418    _ = editor.update(cx, |editor, window, cx| {
  419        editor.end_selection(window, cx);
  420        editor.update_selection(
  421            DisplayPoint::new(DisplayRow(3), 3),
  422            0,
  423            gpui::Point::<f32>::default(),
  424            window,
  425            cx,
  426        );
  427    });
  428
  429    assert_eq!(
  430        editor
  431            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  432            .unwrap(),
  433        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  434    );
  435
  436    _ = editor.update(cx, |editor, window, cx| {
  437        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  438        editor.update_selection(
  439            DisplayPoint::new(DisplayRow(0), 0),
  440            0,
  441            gpui::Point::<f32>::default(),
  442            window,
  443            cx,
  444        );
  445    });
  446
  447    assert_eq!(
  448        editor
  449            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  450            .unwrap(),
  451        [
  452            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  453            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  454        ]
  455    );
  456
  457    _ = editor.update(cx, |editor, window, cx| {
  458        editor.end_selection(window, cx);
  459    });
  460
  461    assert_eq!(
  462        editor
  463            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  464            .unwrap(),
  465        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  466    );
  467}
  468
  469#[gpui::test]
  470fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  471    init_test(cx, |_| {});
  472
  473    let editor = cx.add_window(|window, cx| {
  474        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  475        build_editor(buffer, window, cx)
  476    });
  477
  478    _ = editor.update(cx, |editor, window, cx| {
  479        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  480    });
  481
  482    _ = editor.update(cx, |editor, window, cx| {
  483        editor.end_selection(window, cx);
  484    });
  485
  486    _ = editor.update(cx, |editor, window, cx| {
  487        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  488    });
  489
  490    _ = editor.update(cx, |editor, window, cx| {
  491        editor.end_selection(window, cx);
  492    });
  493
  494    assert_eq!(
  495        editor
  496            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  497            .unwrap(),
  498        [
  499            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  500            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  501        ]
  502    );
  503
  504    _ = editor.update(cx, |editor, window, cx| {
  505        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  506    });
  507
  508    _ = editor.update(cx, |editor, window, cx| {
  509        editor.end_selection(window, cx);
  510    });
  511
  512    assert_eq!(
  513        editor
  514            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  515            .unwrap(),
  516        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  517    );
  518}
  519
  520#[gpui::test]
  521fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  522    init_test(cx, |_| {});
  523
  524    let editor = cx.add_window(|window, cx| {
  525        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  526        build_editor(buffer, window, cx)
  527    });
  528
  529    _ = editor.update(cx, |editor, window, cx| {
  530        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  531        assert_eq!(
  532            editor.selections.display_ranges(cx),
  533            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  534        );
  535    });
  536
  537    _ = editor.update(cx, |editor, window, cx| {
  538        editor.update_selection(
  539            DisplayPoint::new(DisplayRow(3), 3),
  540            0,
  541            gpui::Point::<f32>::default(),
  542            window,
  543            cx,
  544        );
  545        assert_eq!(
  546            editor.selections.display_ranges(cx),
  547            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  548        );
  549    });
  550
  551    _ = editor.update(cx, |editor, window, cx| {
  552        editor.cancel(&Cancel, window, cx);
  553        editor.update_selection(
  554            DisplayPoint::new(DisplayRow(1), 1),
  555            0,
  556            gpui::Point::<f32>::default(),
  557            window,
  558            cx,
  559        );
  560        assert_eq!(
  561            editor.selections.display_ranges(cx),
  562            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  563        );
  564    });
  565}
  566
  567#[gpui::test]
  568fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  569    init_test(cx, |_| {});
  570
  571    let editor = cx.add_window(|window, cx| {
  572        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  573        build_editor(buffer, window, cx)
  574    });
  575
  576    _ = editor.update(cx, |editor, window, cx| {
  577        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  578        assert_eq!(
  579            editor.selections.display_ranges(cx),
  580            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  581        );
  582
  583        editor.move_down(&Default::default(), window, cx);
  584        assert_eq!(
  585            editor.selections.display_ranges(cx),
  586            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  587        );
  588
  589        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  590        assert_eq!(
  591            editor.selections.display_ranges(cx),
  592            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  593        );
  594
  595        editor.move_up(&Default::default(), window, cx);
  596        assert_eq!(
  597            editor.selections.display_ranges(cx),
  598            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  599        );
  600    });
  601}
  602
  603#[gpui::test]
  604fn test_clone(cx: &mut TestAppContext) {
  605    init_test(cx, |_| {});
  606
  607    let (text, selection_ranges) = marked_text_ranges(
  608        indoc! {"
  609            one
  610            two
  611            threeˇ
  612            four
  613            fiveˇ
  614        "},
  615        true,
  616    );
  617
  618    let editor = cx.add_window(|window, cx| {
  619        let buffer = MultiBuffer::build_simple(&text, cx);
  620        build_editor(buffer, window, cx)
  621    });
  622
  623    _ = editor.update(cx, |editor, window, cx| {
  624        editor.change_selections(None, window, cx, |s| {
  625            s.select_ranges(selection_ranges.clone())
  626        });
  627        editor.fold_creases(
  628            vec![
  629                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  630                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  631            ],
  632            true,
  633            window,
  634            cx,
  635        );
  636    });
  637
  638    let cloned_editor = editor
  639        .update(cx, |editor, _, cx| {
  640            cx.open_window(Default::default(), |window, cx| {
  641                cx.new(|cx| editor.clone(window, cx))
  642            })
  643        })
  644        .unwrap()
  645        .unwrap();
  646
  647    let snapshot = editor
  648        .update(cx, |e, window, cx| e.snapshot(window, cx))
  649        .unwrap();
  650    let cloned_snapshot = cloned_editor
  651        .update(cx, |e, window, cx| e.snapshot(window, cx))
  652        .unwrap();
  653
  654    assert_eq!(
  655        cloned_editor
  656            .update(cx, |e, _, cx| e.display_text(cx))
  657            .unwrap(),
  658        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  659    );
  660    assert_eq!(
  661        cloned_snapshot
  662            .folds_in_range(0..text.len())
  663            .collect::<Vec<_>>(),
  664        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  665    );
  666    assert_set_eq!(
  667        cloned_editor
  668            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  669            .unwrap(),
  670        editor
  671            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  672            .unwrap()
  673    );
  674    assert_set_eq!(
  675        cloned_editor
  676            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  677            .unwrap(),
  678        editor
  679            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  680            .unwrap()
  681    );
  682}
  683
  684#[gpui::test]
  685async fn test_navigation_history(cx: &mut TestAppContext) {
  686    init_test(cx, |_| {});
  687
  688    use workspace::item::Item;
  689
  690    let fs = FakeFs::new(cx.executor());
  691    let project = Project::test(fs, [], cx).await;
  692    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  693    let pane = workspace
  694        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  695        .unwrap();
  696
  697    _ = workspace.update(cx, |_v, window, cx| {
  698        cx.new(|cx| {
  699            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  700            let mut editor = build_editor(buffer.clone(), window, cx);
  701            let handle = cx.entity();
  702            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  703
  704            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  705                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  706            }
  707
  708            // Move the cursor a small distance.
  709            // Nothing is added to the navigation history.
  710            editor.change_selections(None, window, cx, |s| {
  711                s.select_display_ranges([
  712                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  713                ])
  714            });
  715            editor.change_selections(None, window, cx, |s| {
  716                s.select_display_ranges([
  717                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  718                ])
  719            });
  720            assert!(pop_history(&mut editor, cx).is_none());
  721
  722            // Move the cursor a large distance.
  723            // The history can jump back to the previous position.
  724            editor.change_selections(None, window, cx, |s| {
  725                s.select_display_ranges([
  726                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  727                ])
  728            });
  729            let nav_entry = pop_history(&mut editor, cx).unwrap();
  730            editor.navigate(nav_entry.data.unwrap(), window, cx);
  731            assert_eq!(nav_entry.item.id(), cx.entity_id());
  732            assert_eq!(
  733                editor.selections.display_ranges(cx),
  734                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  735            );
  736            assert!(pop_history(&mut editor, cx).is_none());
  737
  738            // Move the cursor a small distance via the mouse.
  739            // Nothing is added to the navigation history.
  740            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  741            editor.end_selection(window, cx);
  742            assert_eq!(
  743                editor.selections.display_ranges(cx),
  744                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  745            );
  746            assert!(pop_history(&mut editor, cx).is_none());
  747
  748            // Move the cursor a large distance via the mouse.
  749            // The history can jump back to the previous position.
  750            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  751            editor.end_selection(window, cx);
  752            assert_eq!(
  753                editor.selections.display_ranges(cx),
  754                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  755            );
  756            let nav_entry = pop_history(&mut editor, cx).unwrap();
  757            editor.navigate(nav_entry.data.unwrap(), window, cx);
  758            assert_eq!(nav_entry.item.id(), cx.entity_id());
  759            assert_eq!(
  760                editor.selections.display_ranges(cx),
  761                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  762            );
  763            assert!(pop_history(&mut editor, cx).is_none());
  764
  765            // Set scroll position to check later
  766            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  767            let original_scroll_position = editor.scroll_manager.anchor();
  768
  769            // Jump to the end of the document and adjust scroll
  770            editor.move_to_end(&MoveToEnd, window, cx);
  771            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  772            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  773
  774            let nav_entry = pop_history(&mut editor, cx).unwrap();
  775            editor.navigate(nav_entry.data.unwrap(), window, cx);
  776            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  777
  778            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  779            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  780            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  781            let invalid_point = Point::new(9999, 0);
  782            editor.navigate(
  783                Box::new(NavigationData {
  784                    cursor_anchor: invalid_anchor,
  785                    cursor_position: invalid_point,
  786                    scroll_anchor: ScrollAnchor {
  787                        anchor: invalid_anchor,
  788                        offset: Default::default(),
  789                    },
  790                    scroll_top_row: invalid_point.row,
  791                }),
  792                window,
  793                cx,
  794            );
  795            assert_eq!(
  796                editor.selections.display_ranges(cx),
  797                &[editor.max_point(cx)..editor.max_point(cx)]
  798            );
  799            assert_eq!(
  800                editor.scroll_position(cx),
  801                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  802            );
  803
  804            editor
  805        })
  806    });
  807}
  808
  809#[gpui::test]
  810fn test_cancel(cx: &mut TestAppContext) {
  811    init_test(cx, |_| {});
  812
  813    let editor = cx.add_window(|window, cx| {
  814        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  815        build_editor(buffer, window, cx)
  816    });
  817
  818    _ = editor.update(cx, |editor, window, cx| {
  819        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  820        editor.update_selection(
  821            DisplayPoint::new(DisplayRow(1), 1),
  822            0,
  823            gpui::Point::<f32>::default(),
  824            window,
  825            cx,
  826        );
  827        editor.end_selection(window, cx);
  828
  829        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  830        editor.update_selection(
  831            DisplayPoint::new(DisplayRow(0), 3),
  832            0,
  833            gpui::Point::<f32>::default(),
  834            window,
  835            cx,
  836        );
  837        editor.end_selection(window, cx);
  838        assert_eq!(
  839            editor.selections.display_ranges(cx),
  840            [
  841                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  842                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  843            ]
  844        );
  845    });
  846
  847    _ = editor.update(cx, |editor, window, cx| {
  848        editor.cancel(&Cancel, window, cx);
  849        assert_eq!(
  850            editor.selections.display_ranges(cx),
  851            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  852        );
  853    });
  854
  855    _ = editor.update(cx, |editor, window, cx| {
  856        editor.cancel(&Cancel, window, cx);
  857        assert_eq!(
  858            editor.selections.display_ranges(cx),
  859            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  860        );
  861    });
  862}
  863
  864#[gpui::test]
  865fn test_fold_action(cx: &mut TestAppContext) {
  866    init_test(cx, |_| {});
  867
  868    let editor = cx.add_window(|window, cx| {
  869        let buffer = MultiBuffer::build_simple(
  870            &"
  871                impl Foo {
  872                    // Hello!
  873
  874                    fn a() {
  875                        1
  876                    }
  877
  878                    fn b() {
  879                        2
  880                    }
  881
  882                    fn c() {
  883                        3
  884                    }
  885                }
  886            "
  887            .unindent(),
  888            cx,
  889        );
  890        build_editor(buffer.clone(), window, cx)
  891    });
  892
  893    _ = editor.update(cx, |editor, window, cx| {
  894        editor.change_selections(None, window, cx, |s| {
  895            s.select_display_ranges([
  896                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  897            ]);
  898        });
  899        editor.fold(&Fold, window, cx);
  900        assert_eq!(
  901            editor.display_text(cx),
  902            "
  903                impl Foo {
  904                    // Hello!
  905
  906                    fn a() {
  907                        1
  908                    }
  909
  910                    fn b() {⋯
  911                    }
  912
  913                    fn c() {⋯
  914                    }
  915                }
  916            "
  917            .unindent(),
  918        );
  919
  920        editor.fold(&Fold, window, cx);
  921        assert_eq!(
  922            editor.display_text(cx),
  923            "
  924                impl Foo {⋯
  925                }
  926            "
  927            .unindent(),
  928        );
  929
  930        editor.unfold_lines(&UnfoldLines, window, cx);
  931        assert_eq!(
  932            editor.display_text(cx),
  933            "
  934                impl Foo {
  935                    // Hello!
  936
  937                    fn a() {
  938                        1
  939                    }
  940
  941                    fn b() {⋯
  942                    }
  943
  944                    fn c() {⋯
  945                    }
  946                }
  947            "
  948            .unindent(),
  949        );
  950
  951        editor.unfold_lines(&UnfoldLines, window, cx);
  952        assert_eq!(
  953            editor.display_text(cx),
  954            editor.buffer.read(cx).read(cx).text()
  955        );
  956    });
  957}
  958
  959#[gpui::test]
  960fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  961    init_test(cx, |_| {});
  962
  963    let editor = cx.add_window(|window, cx| {
  964        let buffer = MultiBuffer::build_simple(
  965            &"
  966                class Foo:
  967                    # Hello!
  968
  969                    def a():
  970                        print(1)
  971
  972                    def b():
  973                        print(2)
  974
  975                    def c():
  976                        print(3)
  977            "
  978            .unindent(),
  979            cx,
  980        );
  981        build_editor(buffer.clone(), window, cx)
  982    });
  983
  984    _ = editor.update(cx, |editor, window, cx| {
  985        editor.change_selections(None, window, cx, |s| {
  986            s.select_display_ranges([
  987                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  988            ]);
  989        });
  990        editor.fold(&Fold, window, cx);
  991        assert_eq!(
  992            editor.display_text(cx),
  993            "
  994                class Foo:
  995                    # Hello!
  996
  997                    def a():
  998                        print(1)
  999
 1000                    def b():⋯
 1001
 1002                    def c():⋯
 1003            "
 1004            .unindent(),
 1005        );
 1006
 1007        editor.fold(&Fold, window, cx);
 1008        assert_eq!(
 1009            editor.display_text(cx),
 1010            "
 1011                class Foo:⋯
 1012            "
 1013            .unindent(),
 1014        );
 1015
 1016        editor.unfold_lines(&UnfoldLines, window, cx);
 1017        assert_eq!(
 1018            editor.display_text(cx),
 1019            "
 1020                class Foo:
 1021                    # Hello!
 1022
 1023                    def a():
 1024                        print(1)
 1025
 1026                    def b():⋯
 1027
 1028                    def c():⋯
 1029            "
 1030            .unindent(),
 1031        );
 1032
 1033        editor.unfold_lines(&UnfoldLines, window, cx);
 1034        assert_eq!(
 1035            editor.display_text(cx),
 1036            editor.buffer.read(cx).read(cx).text()
 1037        );
 1038    });
 1039}
 1040
 1041#[gpui::test]
 1042fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1043    init_test(cx, |_| {});
 1044
 1045    let editor = cx.add_window(|window, cx| {
 1046        let buffer = MultiBuffer::build_simple(
 1047            &"
 1048                class Foo:
 1049                    # Hello!
 1050
 1051                    def a():
 1052                        print(1)
 1053
 1054                    def b():
 1055                        print(2)
 1056
 1057
 1058                    def c():
 1059                        print(3)
 1060
 1061
 1062            "
 1063            .unindent(),
 1064            cx,
 1065        );
 1066        build_editor(buffer.clone(), window, cx)
 1067    });
 1068
 1069    _ = editor.update(cx, |editor, window, cx| {
 1070        editor.change_selections(None, window, cx, |s| {
 1071            s.select_display_ranges([
 1072                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1073            ]);
 1074        });
 1075        editor.fold(&Fold, window, cx);
 1076        assert_eq!(
 1077            editor.display_text(cx),
 1078            "
 1079                class Foo:
 1080                    # Hello!
 1081
 1082                    def a():
 1083                        print(1)
 1084
 1085                    def b():⋯
 1086
 1087
 1088                    def c():⋯
 1089
 1090
 1091            "
 1092            .unindent(),
 1093        );
 1094
 1095        editor.fold(&Fold, window, cx);
 1096        assert_eq!(
 1097            editor.display_text(cx),
 1098            "
 1099                class Foo:⋯
 1100
 1101
 1102            "
 1103            .unindent(),
 1104        );
 1105
 1106        editor.unfold_lines(&UnfoldLines, window, cx);
 1107        assert_eq!(
 1108            editor.display_text(cx),
 1109            "
 1110                class Foo:
 1111                    # Hello!
 1112
 1113                    def a():
 1114                        print(1)
 1115
 1116                    def b():⋯
 1117
 1118
 1119                    def c():⋯
 1120
 1121
 1122            "
 1123            .unindent(),
 1124        );
 1125
 1126        editor.unfold_lines(&UnfoldLines, window, cx);
 1127        assert_eq!(
 1128            editor.display_text(cx),
 1129            editor.buffer.read(cx).read(cx).text()
 1130        );
 1131    });
 1132}
 1133
 1134#[gpui::test]
 1135fn test_fold_at_level(cx: &mut TestAppContext) {
 1136    init_test(cx, |_| {});
 1137
 1138    let editor = cx.add_window(|window, cx| {
 1139        let buffer = MultiBuffer::build_simple(
 1140            &"
 1141                class Foo:
 1142                    # Hello!
 1143
 1144                    def a():
 1145                        print(1)
 1146
 1147                    def b():
 1148                        print(2)
 1149
 1150
 1151                class Bar:
 1152                    # World!
 1153
 1154                    def a():
 1155                        print(1)
 1156
 1157                    def b():
 1158                        print(2)
 1159
 1160
 1161            "
 1162            .unindent(),
 1163            cx,
 1164        );
 1165        build_editor(buffer.clone(), window, cx)
 1166    });
 1167
 1168    _ = editor.update(cx, |editor, window, cx| {
 1169        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1170        assert_eq!(
 1171            editor.display_text(cx),
 1172            "
 1173                class Foo:
 1174                    # Hello!
 1175
 1176                    def a():⋯
 1177
 1178                    def b():⋯
 1179
 1180
 1181                class Bar:
 1182                    # World!
 1183
 1184                    def a():⋯
 1185
 1186                    def b():⋯
 1187
 1188
 1189            "
 1190            .unindent(),
 1191        );
 1192
 1193        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1194        assert_eq!(
 1195            editor.display_text(cx),
 1196            "
 1197                class Foo:⋯
 1198
 1199
 1200                class Bar:⋯
 1201
 1202
 1203            "
 1204            .unindent(),
 1205        );
 1206
 1207        editor.unfold_all(&UnfoldAll, window, cx);
 1208        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1209        assert_eq!(
 1210            editor.display_text(cx),
 1211            "
 1212                class Foo:
 1213                    # Hello!
 1214
 1215                    def a():
 1216                        print(1)
 1217
 1218                    def b():
 1219                        print(2)
 1220
 1221
 1222                class Bar:
 1223                    # World!
 1224
 1225                    def a():
 1226                        print(1)
 1227
 1228                    def b():
 1229                        print(2)
 1230
 1231
 1232            "
 1233            .unindent(),
 1234        );
 1235
 1236        assert_eq!(
 1237            editor.display_text(cx),
 1238            editor.buffer.read(cx).read(cx).text()
 1239        );
 1240    });
 1241}
 1242
 1243#[gpui::test]
 1244fn test_move_cursor(cx: &mut TestAppContext) {
 1245    init_test(cx, |_| {});
 1246
 1247    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1248    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1249
 1250    buffer.update(cx, |buffer, cx| {
 1251        buffer.edit(
 1252            vec![
 1253                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1254                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1255            ],
 1256            None,
 1257            cx,
 1258        );
 1259    });
 1260    _ = editor.update(cx, |editor, window, cx| {
 1261        assert_eq!(
 1262            editor.selections.display_ranges(cx),
 1263            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1264        );
 1265
 1266        editor.move_down(&MoveDown, window, cx);
 1267        assert_eq!(
 1268            editor.selections.display_ranges(cx),
 1269            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1270        );
 1271
 1272        editor.move_right(&MoveRight, window, cx);
 1273        assert_eq!(
 1274            editor.selections.display_ranges(cx),
 1275            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1276        );
 1277
 1278        editor.move_left(&MoveLeft, window, cx);
 1279        assert_eq!(
 1280            editor.selections.display_ranges(cx),
 1281            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1282        );
 1283
 1284        editor.move_up(&MoveUp, window, cx);
 1285        assert_eq!(
 1286            editor.selections.display_ranges(cx),
 1287            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1288        );
 1289
 1290        editor.move_to_end(&MoveToEnd, window, cx);
 1291        assert_eq!(
 1292            editor.selections.display_ranges(cx),
 1293            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1294        );
 1295
 1296        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1297        assert_eq!(
 1298            editor.selections.display_ranges(cx),
 1299            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1300        );
 1301
 1302        editor.change_selections(None, window, cx, |s| {
 1303            s.select_display_ranges([
 1304                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1305            ]);
 1306        });
 1307        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1308        assert_eq!(
 1309            editor.selections.display_ranges(cx),
 1310            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1311        );
 1312
 1313        editor.select_to_end(&SelectToEnd, window, cx);
 1314        assert_eq!(
 1315            editor.selections.display_ranges(cx),
 1316            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1317        );
 1318    });
 1319}
 1320
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["// ".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        // ˇ
 2775    "});
 2776        // Ensure that we add comment prefix when existing line contains space
 2777        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2778        cx.assert_editor_state(
 2779            indoc! {"
 2780        // Foo
 2781        //s
 2782        // ˇ
 2783    "}
 2784            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2785            .as_str(),
 2786        );
 2787        // Ensure that we add comment prefix when existing line does not contain space
 2788        cx.set_state(indoc! {"
 2789        // Foo
 2790        //ˇ
 2791    "});
 2792        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793        cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        //
 2796        // ˇ
 2797    "});
 2798        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2799        cx.set_state(indoc! {"
 2800        ˇ// Foo
 2801    "});
 2802        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2803        cx.assert_editor_state(indoc! {"
 2804
 2805        ˇ// Foo
 2806    "});
 2807    }
 2808    // Ensure that comment continuations can be disabled.
 2809    update_test_language_settings(cx, |settings| {
 2810        settings.defaults.extend_comment_on_newline = Some(false);
 2811    });
 2812    let mut cx = EditorTestContext::new(cx).await;
 2813    cx.set_state(indoc! {"
 2814        // Fooˇ
 2815    "});
 2816    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2817    cx.assert_editor_state(indoc! {"
 2818        // Foo
 2819        ˇ
 2820    "});
 2821}
 2822
 2823#[gpui::test]
 2824async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext) {
 2825    init_test(cx, |settings| {
 2826        settings.defaults.tab_size = NonZeroU32::new(4)
 2827    });
 2828
 2829    let language = Arc::new(Language::new(
 2830        LanguageConfig {
 2831            line_comments: vec!["// ".into(), "/// ".into()],
 2832            ..LanguageConfig::default()
 2833        },
 2834        None,
 2835    ));
 2836    {
 2837        let mut cx = EditorTestContext::new(cx).await;
 2838        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2839        cx.set_state(indoc! {"
 2840        //ˇ
 2841    "});
 2842        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2843        cx.assert_editor_state(indoc! {"
 2844        //
 2845        // ˇ
 2846    "});
 2847
 2848        cx.set_state(indoc! {"
 2849        ///ˇ
 2850    "});
 2851        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2852        cx.assert_editor_state(indoc! {"
 2853        ///
 2854        /// ˇ
 2855    "});
 2856    }
 2857}
 2858
 2859#[gpui::test]
 2860async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 2861    init_test(cx, |settings| {
 2862        settings.defaults.tab_size = NonZeroU32::new(4)
 2863    });
 2864
 2865    let language = Arc::new(
 2866        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
 2875                ..LanguageConfig::default()
 2876            },
 2877            Some(tree_sitter_rust::LANGUAGE.into()),
 2878        )
 2879        .with_override_query("[(line_comment)(block_comment)] @comment.inclusive")
 2880        .unwrap(),
 2881    );
 2882
 2883    {
 2884        let mut cx = EditorTestContext::new(cx).await;
 2885        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2886        cx.set_state(indoc! {"
 2887        /**ˇ
 2888    "});
 2889
 2890        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2891        cx.assert_editor_state(indoc! {"
 2892        /**
 2893         * ˇ
 2894    "});
 2895        // Ensure that if cursor is before the comment start,
 2896        // we do not actually insert a comment prefix.
 2897        cx.set_state(indoc! {"
 2898        ˇ/**
 2899    "});
 2900        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2901        cx.assert_editor_state(indoc! {"
 2902
 2903        ˇ/**
 2904    "});
 2905        // Ensure that if cursor is between it doesn't add comment prefix.
 2906        cx.set_state(indoc! {"
 2907        /*ˇ*
 2908    "});
 2909        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2910        cx.assert_editor_state(indoc! {"
 2911        /*
 2912        ˇ*
 2913    "});
 2914        // Ensure that if suffix exists on same line after cursor it adds new line.
 2915        cx.set_state(indoc! {"
 2916        /**ˇ*/
 2917    "});
 2918        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2919        cx.assert_editor_state(indoc! {"
 2920        /**
 2921         * ˇ
 2922         */
 2923    "});
 2924        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2925        cx.set_state(indoc! {"
 2926        /**ˇ */
 2927    "});
 2928        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2929        cx.assert_editor_state(indoc! {"
 2930        /**
 2931         * ˇ
 2932         */
 2933    "});
 2934        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2935        cx.set_state(indoc! {"
 2936        /** ˇ*/
 2937    "});
 2938        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2939        cx.assert_editor_state(
 2940            indoc! {"
 2941        /**s
 2942         * ˇ
 2943         */
 2944    "}
 2945            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2946            .as_str(),
 2947        );
 2948        // Ensure that delimiter space is preserved when newline on already
 2949        // spaced delimiter.
 2950        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2951        cx.assert_editor_state(
 2952            indoc! {"
 2953        /**s
 2954         *s
 2955         * ˇ
 2956         */
 2957    "}
 2958            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2959            .as_str(),
 2960        );
 2961        // Ensure that delimiter space is preserved when space is not
 2962        // on existing delimiter.
 2963        cx.set_state(indoc! {"
 2964        /**
 2965 2966         */
 2967    "});
 2968        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2969        cx.assert_editor_state(indoc! {"
 2970        /**
 2971         *
 2972         * ˇ
 2973         */
 2974    "});
 2975        // Ensure that if suffix exists on same line after cursor it
 2976        // doesn't add extra new line if prefix is not on same line.
 2977        cx.set_state(indoc! {"
 2978        /**
 2979        ˇ*/
 2980    "});
 2981        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2982        cx.assert_editor_state(indoc! {"
 2983        /**
 2984
 2985        ˇ*/
 2986    "});
 2987        // Ensure that it detects suffix after existing prefix.
 2988        cx.set_state(indoc! {"
 2989        /**ˇ/
 2990    "});
 2991        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2992        cx.assert_editor_state(indoc! {"
 2993        /**
 2994        ˇ/
 2995    "});
 2996        // Ensure that if suffix exists on same line before
 2997        // cursor it does not add comment prefix.
 2998        cx.set_state(indoc! {"
 2999        /** */ˇ
 3000    "});
 3001        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3002        cx.assert_editor_state(indoc! {"
 3003        /** */
 3004        ˇ
 3005    "});
 3006        // Ensure that if suffix exists on same line before
 3007        // cursor it does not add comment prefix.
 3008        cx.set_state(indoc! {"
 3009        /**
 3010         *
 3011         */ˇ
 3012    "});
 3013        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3014        cx.assert_editor_state(indoc! {"
 3015        /**
 3016         *
 3017         */
 3018         ˇ
 3019    "});
 3020
 3021        // Ensure that inline comment followed by code
 3022        // doesn't add comment prefix on newline
 3023        cx.set_state(indoc! {"
 3024        /** */ textˇ
 3025    "});
 3026        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3027        cx.assert_editor_state(indoc! {"
 3028        /** */ text
 3029        ˇ
 3030    "});
 3031
 3032        // Ensure that text after comment end tag
 3033        // doesn't add comment prefix on newline
 3034        cx.set_state(indoc! {"
 3035        /**
 3036         *
 3037         */ˇtext
 3038    "});
 3039        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3040        cx.assert_editor_state(indoc! {"
 3041        /**
 3042         *
 3043         */
 3044         ˇtext
 3045    "});
 3046
 3047        // Ensure if not comment block it doesn't
 3048        // add comment prefix on newline
 3049        cx.set_state(indoc! {"
 3050        * textˇ
 3051    "});
 3052        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3053        cx.assert_editor_state(indoc! {"
 3054        * text
 3055        ˇ
 3056    "});
 3057    }
 3058    // Ensure that comment continuations can be disabled.
 3059    update_test_language_settings(cx, |settings| {
 3060        settings.defaults.extend_comment_on_newline = Some(false);
 3061    });
 3062    let mut cx = EditorTestContext::new(cx).await;
 3063    cx.set_state(indoc! {"
 3064        /**ˇ
 3065    "});
 3066    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 3067    cx.assert_editor_state(indoc! {"
 3068        /**
 3069        ˇ
 3070    "});
 3071}
 3072
 3073#[gpui::test]
 3074fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 3075    init_test(cx, |_| {});
 3076
 3077    let editor = cx.add_window(|window, cx| {
 3078        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3079        let mut editor = build_editor(buffer.clone(), window, cx);
 3080        editor.change_selections(None, window, cx, |s| {
 3081            s.select_ranges([3..4, 11..12, 19..20])
 3082        });
 3083        editor
 3084    });
 3085
 3086    _ = editor.update(cx, |editor, window, cx| {
 3087        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3088        editor.buffer.update(cx, |buffer, cx| {
 3089            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3090            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3091        });
 3092        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3093
 3094        editor.insert("Z", window, cx);
 3095        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3096
 3097        // The selections are moved after the inserted characters
 3098        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3099    });
 3100}
 3101
 3102#[gpui::test]
 3103async fn test_tab(cx: &mut TestAppContext) {
 3104    init_test(cx, |settings| {
 3105        settings.defaults.tab_size = NonZeroU32::new(3)
 3106    });
 3107
 3108    let mut cx = EditorTestContext::new(cx).await;
 3109    cx.set_state(indoc! {"
 3110        ˇabˇc
 3111        ˇ🏀ˇ🏀ˇefg
 3112 3113    "});
 3114    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116           ˇab ˇc
 3117           ˇ🏀  ˇ🏀  ˇefg
 3118        d  ˇ
 3119    "});
 3120
 3121    cx.set_state(indoc! {"
 3122        a
 3123        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3124    "});
 3125    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3126    cx.assert_editor_state(indoc! {"
 3127        a
 3128           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3129    "});
 3130}
 3131
 3132#[gpui::test]
 3133async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3134    init_test(cx, |_| {});
 3135
 3136    let mut cx = EditorTestContext::new(cx).await;
 3137    let language = Arc::new(
 3138        Language::new(
 3139            LanguageConfig::default(),
 3140            Some(tree_sitter_rust::LANGUAGE.into()),
 3141        )
 3142        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3143        .unwrap(),
 3144    );
 3145    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3146
 3147    // test when all cursors are not at suggested indent
 3148    // then simply move to their suggested indent location
 3149    cx.set_state(indoc! {"
 3150        const a: B = (
 3151            c(
 3152        ˇ
 3153        ˇ    )
 3154        );
 3155    "});
 3156    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3157    cx.assert_editor_state(indoc! {"
 3158        const a: B = (
 3159            c(
 3160                ˇ
 3161            ˇ)
 3162        );
 3163    "});
 3164
 3165    // test cursor already at suggested indent not moving when
 3166    // other cursors are yet to reach their suggested indents
 3167    cx.set_state(indoc! {"
 3168        ˇ
 3169        const a: B = (
 3170            c(
 3171                d(
 3172        ˇ
 3173                )
 3174        ˇ
 3175        ˇ    )
 3176        );
 3177    "});
 3178    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3179    cx.assert_editor_state(indoc! {"
 3180        ˇ
 3181        const a: B = (
 3182            c(
 3183                d(
 3184                    ˇ
 3185                )
 3186                ˇ
 3187            ˇ)
 3188        );
 3189    "});
 3190    // test when all cursors are at suggested indent then tab is inserted
 3191    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3192    cx.assert_editor_state(indoc! {"
 3193            ˇ
 3194        const a: B = (
 3195            c(
 3196                d(
 3197                        ˇ
 3198                )
 3199                    ˇ
 3200                ˇ)
 3201        );
 3202    "});
 3203
 3204    // test when current indent is less than suggested indent,
 3205    // we adjust line to match suggested indent and move cursor to it
 3206    //
 3207    // when no other cursor is at word boundary, all of them should move
 3208    cx.set_state(indoc! {"
 3209        const a: B = (
 3210            c(
 3211                d(
 3212        ˇ
 3213        ˇ   )
 3214        ˇ   )
 3215        );
 3216    "});
 3217    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3218    cx.assert_editor_state(indoc! {"
 3219        const a: B = (
 3220            c(
 3221                d(
 3222                    ˇ
 3223                ˇ)
 3224            ˇ)
 3225        );
 3226    "});
 3227
 3228    // test when current indent is less than suggested indent,
 3229    // we adjust line to match suggested indent and move cursor to it
 3230    //
 3231    // when some other cursor is at word boundary, it should not move
 3232    cx.set_state(indoc! {"
 3233        const a: B = (
 3234            c(
 3235                d(
 3236        ˇ
 3237        ˇ   )
 3238           ˇ)
 3239        );
 3240    "});
 3241    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3242    cx.assert_editor_state(indoc! {"
 3243        const a: B = (
 3244            c(
 3245                d(
 3246                    ˇ
 3247                ˇ)
 3248            ˇ)
 3249        );
 3250    "});
 3251
 3252    // test when current indent is more than suggested indent,
 3253    // we just move cursor to current indent instead of suggested indent
 3254    //
 3255    // when no other cursor is at word boundary, all of them should move
 3256    cx.set_state(indoc! {"
 3257        const a: B = (
 3258            c(
 3259                d(
 3260        ˇ
 3261        ˇ                )
 3262        ˇ   )
 3263        );
 3264    "});
 3265    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3266    cx.assert_editor_state(indoc! {"
 3267        const a: B = (
 3268            c(
 3269                d(
 3270                    ˇ
 3271                        ˇ)
 3272            ˇ)
 3273        );
 3274    "});
 3275    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3276    cx.assert_editor_state(indoc! {"
 3277        const a: B = (
 3278            c(
 3279                d(
 3280                        ˇ
 3281                            ˇ)
 3282                ˇ)
 3283        );
 3284    "});
 3285
 3286    // test when current indent is more than suggested indent,
 3287    // we just move cursor to current indent instead of suggested indent
 3288    //
 3289    // when some other cursor is at word boundary, it doesn't move
 3290    cx.set_state(indoc! {"
 3291        const a: B = (
 3292            c(
 3293                d(
 3294        ˇ
 3295        ˇ                )
 3296            ˇ)
 3297        );
 3298    "});
 3299    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3300    cx.assert_editor_state(indoc! {"
 3301        const a: B = (
 3302            c(
 3303                d(
 3304                    ˇ
 3305                        ˇ)
 3306            ˇ)
 3307        );
 3308    "});
 3309
 3310    // handle auto-indent when there are multiple cursors on the same line
 3311    cx.set_state(indoc! {"
 3312        const a: B = (
 3313            c(
 3314        ˇ    ˇ
 3315        ˇ    )
 3316        );
 3317    "});
 3318    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3319    cx.assert_editor_state(indoc! {"
 3320        const a: B = (
 3321            c(
 3322                ˇ
 3323            ˇ)
 3324        );
 3325    "});
 3326}
 3327
 3328#[gpui::test]
 3329async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3330    init_test(cx, |settings| {
 3331        settings.defaults.tab_size = NonZeroU32::new(3)
 3332    });
 3333
 3334    let mut cx = EditorTestContext::new(cx).await;
 3335    cx.set_state(indoc! {"
 3336         ˇ
 3337        \t ˇ
 3338        \t  ˇ
 3339        \t   ˇ
 3340         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3341    "});
 3342
 3343    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3344    cx.assert_editor_state(indoc! {"
 3345           ˇ
 3346        \t   ˇ
 3347        \t   ˇ
 3348        \t      ˇ
 3349         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3350    "});
 3351}
 3352
 3353#[gpui::test]
 3354async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3355    init_test(cx, |settings| {
 3356        settings.defaults.tab_size = NonZeroU32::new(4)
 3357    });
 3358
 3359    let language = Arc::new(
 3360        Language::new(
 3361            LanguageConfig::default(),
 3362            Some(tree_sitter_rust::LANGUAGE.into()),
 3363        )
 3364        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3365        .unwrap(),
 3366    );
 3367
 3368    let mut cx = EditorTestContext::new(cx).await;
 3369    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3370    cx.set_state(indoc! {"
 3371        fn a() {
 3372            if b {
 3373        \t ˇc
 3374            }
 3375        }
 3376    "});
 3377
 3378    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3379    cx.assert_editor_state(indoc! {"
 3380        fn a() {
 3381            if b {
 3382                ˇc
 3383            }
 3384        }
 3385    "});
 3386}
 3387
 3388#[gpui::test]
 3389async fn test_indent_outdent(cx: &mut TestAppContext) {
 3390    init_test(cx, |settings| {
 3391        settings.defaults.tab_size = NonZeroU32::new(4);
 3392    });
 3393
 3394    let mut cx = EditorTestContext::new(cx).await;
 3395
 3396    cx.set_state(indoc! {"
 3397          «oneˇ» «twoˇ»
 3398        three
 3399         four
 3400    "});
 3401    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3402    cx.assert_editor_state(indoc! {"
 3403            «oneˇ» «twoˇ»
 3404        three
 3405         four
 3406    "});
 3407
 3408    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3409    cx.assert_editor_state(indoc! {"
 3410        «oneˇ» «twoˇ»
 3411        three
 3412         four
 3413    "});
 3414
 3415    // select across line ending
 3416    cx.set_state(indoc! {"
 3417        one two
 3418        t«hree
 3419        ˇ» four
 3420    "});
 3421    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3422    cx.assert_editor_state(indoc! {"
 3423        one two
 3424            t«hree
 3425        ˇ» four
 3426    "});
 3427
 3428    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3429    cx.assert_editor_state(indoc! {"
 3430        one two
 3431        t«hree
 3432        ˇ» four
 3433    "});
 3434
 3435    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3436    cx.set_state(indoc! {"
 3437        one two
 3438        ˇthree
 3439            four
 3440    "});
 3441    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3442    cx.assert_editor_state(indoc! {"
 3443        one two
 3444            ˇthree
 3445            four
 3446    "});
 3447
 3448    cx.set_state(indoc! {"
 3449        one two
 3450        ˇ    three
 3451            four
 3452    "});
 3453    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3454    cx.assert_editor_state(indoc! {"
 3455        one two
 3456        ˇthree
 3457            four
 3458    "});
 3459}
 3460
 3461#[gpui::test]
 3462async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3463    init_test(cx, |settings| {
 3464        settings.defaults.hard_tabs = Some(true);
 3465    });
 3466
 3467    let mut cx = EditorTestContext::new(cx).await;
 3468
 3469    // select two ranges on one line
 3470    cx.set_state(indoc! {"
 3471        «oneˇ» «twoˇ»
 3472        three
 3473        four
 3474    "});
 3475    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3476    cx.assert_editor_state(indoc! {"
 3477        \t«oneˇ» «twoˇ»
 3478        three
 3479        four
 3480    "});
 3481    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3482    cx.assert_editor_state(indoc! {"
 3483        \t\t«oneˇ» «twoˇ»
 3484        three
 3485        four
 3486    "});
 3487    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3488    cx.assert_editor_state(indoc! {"
 3489        \t«oneˇ» «twoˇ»
 3490        three
 3491        four
 3492    "});
 3493    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3494    cx.assert_editor_state(indoc! {"
 3495        «oneˇ» «twoˇ»
 3496        three
 3497        four
 3498    "});
 3499
 3500    // select across a line ending
 3501    cx.set_state(indoc! {"
 3502        one two
 3503        t«hree
 3504        ˇ»four
 3505    "});
 3506    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3507    cx.assert_editor_state(indoc! {"
 3508        one two
 3509        \tt«hree
 3510        ˇ»four
 3511    "});
 3512    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3513    cx.assert_editor_state(indoc! {"
 3514        one two
 3515        \t\tt«hree
 3516        ˇ»four
 3517    "});
 3518    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3519    cx.assert_editor_state(indoc! {"
 3520        one two
 3521        \tt«hree
 3522        ˇ»four
 3523    "});
 3524    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3525    cx.assert_editor_state(indoc! {"
 3526        one two
 3527        t«hree
 3528        ˇ»four
 3529    "});
 3530
 3531    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3532    cx.set_state(indoc! {"
 3533        one two
 3534        ˇthree
 3535        four
 3536    "});
 3537    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3538    cx.assert_editor_state(indoc! {"
 3539        one two
 3540        ˇthree
 3541        four
 3542    "});
 3543    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3544    cx.assert_editor_state(indoc! {"
 3545        one two
 3546        \tˇthree
 3547        four
 3548    "});
 3549    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3550    cx.assert_editor_state(indoc! {"
 3551        one two
 3552        ˇthree
 3553        four
 3554    "});
 3555}
 3556
 3557#[gpui::test]
 3558fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3559    init_test(cx, |settings| {
 3560        settings.languages.extend([
 3561            (
 3562                "TOML".into(),
 3563                LanguageSettingsContent {
 3564                    tab_size: NonZeroU32::new(2),
 3565                    ..Default::default()
 3566                },
 3567            ),
 3568            (
 3569                "Rust".into(),
 3570                LanguageSettingsContent {
 3571                    tab_size: NonZeroU32::new(4),
 3572                    ..Default::default()
 3573                },
 3574            ),
 3575        ]);
 3576    });
 3577
 3578    let toml_language = Arc::new(Language::new(
 3579        LanguageConfig {
 3580            name: "TOML".into(),
 3581            ..Default::default()
 3582        },
 3583        None,
 3584    ));
 3585    let rust_language = Arc::new(Language::new(
 3586        LanguageConfig {
 3587            name: "Rust".into(),
 3588            ..Default::default()
 3589        },
 3590        None,
 3591    ));
 3592
 3593    let toml_buffer =
 3594        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3595    let rust_buffer =
 3596        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3597    let multibuffer = cx.new(|cx| {
 3598        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3599        multibuffer.push_excerpts(
 3600            toml_buffer.clone(),
 3601            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3602            cx,
 3603        );
 3604        multibuffer.push_excerpts(
 3605            rust_buffer.clone(),
 3606            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3607            cx,
 3608        );
 3609        multibuffer
 3610    });
 3611
 3612    cx.add_window(|window, cx| {
 3613        let mut editor = build_editor(multibuffer, window, cx);
 3614
 3615        assert_eq!(
 3616            editor.text(cx),
 3617            indoc! {"
 3618                a = 1
 3619                b = 2
 3620
 3621                const c: usize = 3;
 3622            "}
 3623        );
 3624
 3625        select_ranges(
 3626            &mut editor,
 3627            indoc! {"
 3628                «aˇ» = 1
 3629                b = 2
 3630
 3631                «const c:ˇ» usize = 3;
 3632            "},
 3633            window,
 3634            cx,
 3635        );
 3636
 3637        editor.tab(&Tab, window, cx);
 3638        assert_text_with_selections(
 3639            &mut editor,
 3640            indoc! {"
 3641                  «aˇ» = 1
 3642                b = 2
 3643
 3644                    «const c:ˇ» usize = 3;
 3645            "},
 3646            cx,
 3647        );
 3648        editor.backtab(&Backtab, window, cx);
 3649        assert_text_with_selections(
 3650            &mut editor,
 3651            indoc! {"
 3652                «aˇ» = 1
 3653                b = 2
 3654
 3655                «const c:ˇ» usize = 3;
 3656            "},
 3657            cx,
 3658        );
 3659
 3660        editor
 3661    });
 3662}
 3663
 3664#[gpui::test]
 3665async fn test_backspace(cx: &mut TestAppContext) {
 3666    init_test(cx, |_| {});
 3667
 3668    let mut cx = EditorTestContext::new(cx).await;
 3669
 3670    // Basic backspace
 3671    cx.set_state(indoc! {"
 3672        onˇe two three
 3673        fou«rˇ» five six
 3674        seven «ˇeight nine
 3675        »ten
 3676    "});
 3677    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3678    cx.assert_editor_state(indoc! {"
 3679        oˇe two three
 3680        fouˇ five six
 3681        seven ˇten
 3682    "});
 3683
 3684    // Test backspace inside and around indents
 3685    cx.set_state(indoc! {"
 3686        zero
 3687            ˇone
 3688                ˇtwo
 3689            ˇ ˇ ˇ  three
 3690        ˇ  ˇ  four
 3691    "});
 3692    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3693    cx.assert_editor_state(indoc! {"
 3694        zero
 3695        ˇone
 3696            ˇtwo
 3697        ˇ  threeˇ  four
 3698    "});
 3699}
 3700
 3701#[gpui::test]
 3702async fn test_delete(cx: &mut TestAppContext) {
 3703    init_test(cx, |_| {});
 3704
 3705    let mut cx = EditorTestContext::new(cx).await;
 3706    cx.set_state(indoc! {"
 3707        onˇe two three
 3708        fou«rˇ» five six
 3709        seven «ˇeight nine
 3710        »ten
 3711    "});
 3712    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3713    cx.assert_editor_state(indoc! {"
 3714        onˇ two three
 3715        fouˇ five six
 3716        seven ˇten
 3717    "});
 3718}
 3719
 3720#[gpui::test]
 3721fn test_delete_line(cx: &mut TestAppContext) {
 3722    init_test(cx, |_| {});
 3723
 3724    let editor = cx.add_window(|window, cx| {
 3725        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3726        build_editor(buffer, window, cx)
 3727    });
 3728    _ = editor.update(cx, |editor, window, cx| {
 3729        editor.change_selections(None, window, cx, |s| {
 3730            s.select_display_ranges([
 3731                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3732                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3733                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3734            ])
 3735        });
 3736        editor.delete_line(&DeleteLine, window, cx);
 3737        assert_eq!(editor.display_text(cx), "ghi");
 3738        assert_eq!(
 3739            editor.selections.display_ranges(cx),
 3740            vec![
 3741                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3742                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3743            ]
 3744        );
 3745    });
 3746
 3747    let editor = cx.add_window(|window, cx| {
 3748        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3749        build_editor(buffer, window, cx)
 3750    });
 3751    _ = editor.update(cx, |editor, window, cx| {
 3752        editor.change_selections(None, window, cx, |s| {
 3753            s.select_display_ranges([
 3754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3755            ])
 3756        });
 3757        editor.delete_line(&DeleteLine, window, cx);
 3758        assert_eq!(editor.display_text(cx), "ghi\n");
 3759        assert_eq!(
 3760            editor.selections.display_ranges(cx),
 3761            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3762        );
 3763    });
 3764}
 3765
 3766#[gpui::test]
 3767fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3768    init_test(cx, |_| {});
 3769
 3770    cx.add_window(|window, cx| {
 3771        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3772        let mut editor = build_editor(buffer.clone(), window, cx);
 3773        let buffer = buffer.read(cx).as_singleton().unwrap();
 3774
 3775        assert_eq!(
 3776            editor.selections.ranges::<Point>(cx),
 3777            &[Point::new(0, 0)..Point::new(0, 0)]
 3778        );
 3779
 3780        // When on single line, replace newline at end by space
 3781        editor.join_lines(&JoinLines, window, cx);
 3782        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3783        assert_eq!(
 3784            editor.selections.ranges::<Point>(cx),
 3785            &[Point::new(0, 3)..Point::new(0, 3)]
 3786        );
 3787
 3788        // When multiple lines are selected, remove newlines that are spanned by the selection
 3789        editor.change_selections(None, window, cx, |s| {
 3790            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3791        });
 3792        editor.join_lines(&JoinLines, window, cx);
 3793        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3794        assert_eq!(
 3795            editor.selections.ranges::<Point>(cx),
 3796            &[Point::new(0, 11)..Point::new(0, 11)]
 3797        );
 3798
 3799        // Undo should be transactional
 3800        editor.undo(&Undo, window, cx);
 3801        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3802        assert_eq!(
 3803            editor.selections.ranges::<Point>(cx),
 3804            &[Point::new(0, 5)..Point::new(2, 2)]
 3805        );
 3806
 3807        // When joining an empty line don't insert a space
 3808        editor.change_selections(None, window, cx, |s| {
 3809            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3810        });
 3811        editor.join_lines(&JoinLines, window, cx);
 3812        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3813        assert_eq!(
 3814            editor.selections.ranges::<Point>(cx),
 3815            [Point::new(2, 3)..Point::new(2, 3)]
 3816        );
 3817
 3818        // We can remove trailing newlines
 3819        editor.join_lines(&JoinLines, window, cx);
 3820        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3821        assert_eq!(
 3822            editor.selections.ranges::<Point>(cx),
 3823            [Point::new(2, 3)..Point::new(2, 3)]
 3824        );
 3825
 3826        // We don't blow up on the last line
 3827        editor.join_lines(&JoinLines, window, cx);
 3828        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3829        assert_eq!(
 3830            editor.selections.ranges::<Point>(cx),
 3831            [Point::new(2, 3)..Point::new(2, 3)]
 3832        );
 3833
 3834        // reset to test indentation
 3835        editor.buffer.update(cx, |buffer, cx| {
 3836            buffer.edit(
 3837                [
 3838                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3839                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3840                ],
 3841                None,
 3842                cx,
 3843            )
 3844        });
 3845
 3846        // We remove any leading spaces
 3847        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3848        editor.change_selections(None, window, cx, |s| {
 3849            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3850        });
 3851        editor.join_lines(&JoinLines, window, cx);
 3852        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3853
 3854        // We don't insert a space for a line containing only spaces
 3855        editor.join_lines(&JoinLines, window, cx);
 3856        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3857
 3858        // We ignore any leading tabs
 3859        editor.join_lines(&JoinLines, window, cx);
 3860        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3861
 3862        editor
 3863    });
 3864}
 3865
 3866#[gpui::test]
 3867fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3868    init_test(cx, |_| {});
 3869
 3870    cx.add_window(|window, cx| {
 3871        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3872        let mut editor = build_editor(buffer.clone(), window, cx);
 3873        let buffer = buffer.read(cx).as_singleton().unwrap();
 3874
 3875        editor.change_selections(None, window, cx, |s| {
 3876            s.select_ranges([
 3877                Point::new(0, 2)..Point::new(1, 1),
 3878                Point::new(1, 2)..Point::new(1, 2),
 3879                Point::new(3, 1)..Point::new(3, 2),
 3880            ])
 3881        });
 3882
 3883        editor.join_lines(&JoinLines, window, cx);
 3884        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3885
 3886        assert_eq!(
 3887            editor.selections.ranges::<Point>(cx),
 3888            [
 3889                Point::new(0, 7)..Point::new(0, 7),
 3890                Point::new(1, 3)..Point::new(1, 3)
 3891            ]
 3892        );
 3893        editor
 3894    });
 3895}
 3896
 3897#[gpui::test]
 3898async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3899    init_test(cx, |_| {});
 3900
 3901    let mut cx = EditorTestContext::new(cx).await;
 3902
 3903    let diff_base = r#"
 3904        Line 0
 3905        Line 1
 3906        Line 2
 3907        Line 3
 3908        "#
 3909    .unindent();
 3910
 3911    cx.set_state(
 3912        &r#"
 3913        ˇLine 0
 3914        Line 1
 3915        Line 2
 3916        Line 3
 3917        "#
 3918        .unindent(),
 3919    );
 3920
 3921    cx.set_head_text(&diff_base);
 3922    executor.run_until_parked();
 3923
 3924    // Join lines
 3925    cx.update_editor(|editor, window, cx| {
 3926        editor.join_lines(&JoinLines, window, cx);
 3927    });
 3928    executor.run_until_parked();
 3929
 3930    cx.assert_editor_state(
 3931        &r#"
 3932        Line 0ˇ Line 1
 3933        Line 2
 3934        Line 3
 3935        "#
 3936        .unindent(),
 3937    );
 3938    // Join again
 3939    cx.update_editor(|editor, window, cx| {
 3940        editor.join_lines(&JoinLines, window, cx);
 3941    });
 3942    executor.run_until_parked();
 3943
 3944    cx.assert_editor_state(
 3945        &r#"
 3946        Line 0 Line 1ˇ Line 2
 3947        Line 3
 3948        "#
 3949        .unindent(),
 3950    );
 3951}
 3952
 3953#[gpui::test]
 3954async fn test_custom_newlines_cause_no_false_positive_diffs(
 3955    executor: BackgroundExecutor,
 3956    cx: &mut TestAppContext,
 3957) {
 3958    init_test(cx, |_| {});
 3959    let mut cx = EditorTestContext::new(cx).await;
 3960    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3961    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3962    executor.run_until_parked();
 3963
 3964    cx.update_editor(|editor, window, cx| {
 3965        let snapshot = editor.snapshot(window, cx);
 3966        assert_eq!(
 3967            snapshot
 3968                .buffer_snapshot
 3969                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3970                .collect::<Vec<_>>(),
 3971            Vec::new(),
 3972            "Should not have any diffs for files with custom newlines"
 3973        );
 3974    });
 3975}
 3976
 3977#[gpui::test]
 3978async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3979    init_test(cx, |_| {});
 3980
 3981    let mut cx = EditorTestContext::new(cx).await;
 3982
 3983    // Test sort_lines_case_insensitive()
 3984    cx.set_state(indoc! {"
 3985        «z
 3986        y
 3987        x
 3988        Z
 3989        Y
 3990        Xˇ»
 3991    "});
 3992    cx.update_editor(|e, window, cx| {
 3993        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3994    });
 3995    cx.assert_editor_state(indoc! {"
 3996        «x
 3997        X
 3998        y
 3999        Y
 4000        z
 4001        Zˇ»
 4002    "});
 4003
 4004    // Test reverse_lines()
 4005    cx.set_state(indoc! {"
 4006        «5
 4007        4
 4008        3
 4009        2
 4010        1ˇ»
 4011    "});
 4012    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 4013    cx.assert_editor_state(indoc! {"
 4014        «1
 4015        2
 4016        3
 4017        4
 4018        5ˇ»
 4019    "});
 4020
 4021    // Skip testing shuffle_line()
 4022
 4023    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 4024    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 4025
 4026    // Don't manipulate when cursor is on single line, but expand the selection
 4027    cx.set_state(indoc! {"
 4028        ddˇdd
 4029        ccc
 4030        bb
 4031        a
 4032    "});
 4033    cx.update_editor(|e, window, cx| {
 4034        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4035    });
 4036    cx.assert_editor_state(indoc! {"
 4037        «ddddˇ»
 4038        ccc
 4039        bb
 4040        a
 4041    "});
 4042
 4043    // Basic manipulate case
 4044    // Start selection moves to column 0
 4045    // End of selection shrinks to fit shorter line
 4046    cx.set_state(indoc! {"
 4047        dd«d
 4048        ccc
 4049        bb
 4050        aaaaaˇ»
 4051    "});
 4052    cx.update_editor(|e, window, cx| {
 4053        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4054    });
 4055    cx.assert_editor_state(indoc! {"
 4056        «aaaaa
 4057        bb
 4058        ccc
 4059        dddˇ»
 4060    "});
 4061
 4062    // Manipulate case with newlines
 4063    cx.set_state(indoc! {"
 4064        dd«d
 4065        ccc
 4066
 4067        bb
 4068        aaaaa
 4069
 4070        ˇ»
 4071    "});
 4072    cx.update_editor(|e, window, cx| {
 4073        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4074    });
 4075    cx.assert_editor_state(indoc! {"
 4076        «
 4077
 4078        aaaaa
 4079        bb
 4080        ccc
 4081        dddˇ»
 4082
 4083    "});
 4084
 4085    // Adding new line
 4086    cx.set_state(indoc! {"
 4087        aa«a
 4088        bbˇ»b
 4089    "});
 4090    cx.update_editor(|e, window, cx| {
 4091        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4092    });
 4093    cx.assert_editor_state(indoc! {"
 4094        «aaa
 4095        bbb
 4096        added_lineˇ»
 4097    "});
 4098
 4099    // Removing line
 4100    cx.set_state(indoc! {"
 4101        aa«a
 4102        bbbˇ»
 4103    "});
 4104    cx.update_editor(|e, window, cx| {
 4105        e.manipulate_lines(window, cx, |lines| {
 4106            lines.pop();
 4107        })
 4108    });
 4109    cx.assert_editor_state(indoc! {"
 4110        «aaaˇ»
 4111    "});
 4112
 4113    // Removing all lines
 4114    cx.set_state(indoc! {"
 4115        aa«a
 4116        bbbˇ»
 4117    "});
 4118    cx.update_editor(|e, window, cx| {
 4119        e.manipulate_lines(window, cx, |lines| {
 4120            lines.drain(..);
 4121        })
 4122    });
 4123    cx.assert_editor_state(indoc! {"
 4124        ˇ
 4125    "});
 4126}
 4127
 4128#[gpui::test]
 4129async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4130    init_test(cx, |_| {});
 4131
 4132    let mut cx = EditorTestContext::new(cx).await;
 4133
 4134    // Consider continuous selection as single selection
 4135    cx.set_state(indoc! {"
 4136        Aaa«aa
 4137        cˇ»c«c
 4138        bb
 4139        aaaˇ»aa
 4140    "});
 4141    cx.update_editor(|e, window, cx| {
 4142        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4143    });
 4144    cx.assert_editor_state(indoc! {"
 4145        «Aaaaa
 4146        ccc
 4147        bb
 4148        aaaaaˇ»
 4149    "});
 4150
 4151    cx.set_state(indoc! {"
 4152        Aaa«aa
 4153        cˇ»c«c
 4154        bb
 4155        aaaˇ»aa
 4156    "});
 4157    cx.update_editor(|e, window, cx| {
 4158        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4159    });
 4160    cx.assert_editor_state(indoc! {"
 4161        «Aaaaa
 4162        ccc
 4163        bbˇ»
 4164    "});
 4165
 4166    // Consider non continuous selection as distinct dedup operations
 4167    cx.set_state(indoc! {"
 4168        «aaaaa
 4169        bb
 4170        aaaaa
 4171        aaaaaˇ»
 4172
 4173        aaa«aaˇ»
 4174    "});
 4175    cx.update_editor(|e, window, cx| {
 4176        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4177    });
 4178    cx.assert_editor_state(indoc! {"
 4179        «aaaaa
 4180        bbˇ»
 4181
 4182        «aaaaaˇ»
 4183    "});
 4184}
 4185
 4186#[gpui::test]
 4187async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4188    init_test(cx, |_| {});
 4189
 4190    let mut cx = EditorTestContext::new(cx).await;
 4191
 4192    cx.set_state(indoc! {"
 4193        «Aaa
 4194        aAa
 4195        Aaaˇ»
 4196    "});
 4197    cx.update_editor(|e, window, cx| {
 4198        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4199    });
 4200    cx.assert_editor_state(indoc! {"
 4201        «Aaa
 4202        aAaˇ»
 4203    "});
 4204
 4205    cx.set_state(indoc! {"
 4206        «Aaa
 4207        aAa
 4208        aaAˇ»
 4209    "});
 4210    cx.update_editor(|e, window, cx| {
 4211        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4212    });
 4213    cx.assert_editor_state(indoc! {"
 4214        «Aaaˇ»
 4215    "});
 4216}
 4217
 4218#[gpui::test]
 4219async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4220    init_test(cx, |_| {});
 4221
 4222    let mut cx = EditorTestContext::new(cx).await;
 4223
 4224    // Manipulate with multiple selections on a single line
 4225    cx.set_state(indoc! {"
 4226        dd«dd
 4227        cˇ»c«c
 4228        bb
 4229        aaaˇ»aa
 4230    "});
 4231    cx.update_editor(|e, window, cx| {
 4232        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4233    });
 4234    cx.assert_editor_state(indoc! {"
 4235        «aaaaa
 4236        bb
 4237        ccc
 4238        ddddˇ»
 4239    "});
 4240
 4241    // Manipulate with multiple disjoin selections
 4242    cx.set_state(indoc! {"
 4243 4244        4
 4245        3
 4246        2
 4247        1ˇ»
 4248
 4249        dd«dd
 4250        ccc
 4251        bb
 4252        aaaˇ»aa
 4253    "});
 4254    cx.update_editor(|e, window, cx| {
 4255        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4256    });
 4257    cx.assert_editor_state(indoc! {"
 4258        «1
 4259        2
 4260        3
 4261        4
 4262        5ˇ»
 4263
 4264        «aaaaa
 4265        bb
 4266        ccc
 4267        ddddˇ»
 4268    "});
 4269
 4270    // Adding lines on each selection
 4271    cx.set_state(indoc! {"
 4272 4273        1ˇ»
 4274
 4275        bb«bb
 4276        aaaˇ»aa
 4277    "});
 4278    cx.update_editor(|e, window, cx| {
 4279        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4280    });
 4281    cx.assert_editor_state(indoc! {"
 4282        «2
 4283        1
 4284        added lineˇ»
 4285
 4286        «bbbb
 4287        aaaaa
 4288        added lineˇ»
 4289    "});
 4290
 4291    // Removing lines on each selection
 4292    cx.set_state(indoc! {"
 4293 4294        1ˇ»
 4295
 4296        bb«bb
 4297        aaaˇ»aa
 4298    "});
 4299    cx.update_editor(|e, window, cx| {
 4300        e.manipulate_lines(window, cx, |lines| {
 4301            lines.pop();
 4302        })
 4303    });
 4304    cx.assert_editor_state(indoc! {"
 4305        «2ˇ»
 4306
 4307        «bbbbˇ»
 4308    "});
 4309}
 4310
 4311#[gpui::test]
 4312async fn test_toggle_case(cx: &mut TestAppContext) {
 4313    init_test(cx, |_| {});
 4314
 4315    let mut cx = EditorTestContext::new(cx).await;
 4316
 4317    // If all lower case -> upper case
 4318    cx.set_state(indoc! {"
 4319        «hello worldˇ»
 4320    "});
 4321    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4322    cx.assert_editor_state(indoc! {"
 4323        «HELLO WORLDˇ»
 4324    "});
 4325
 4326    // If all upper case -> lower case
 4327    cx.set_state(indoc! {"
 4328        «HELLO WORLDˇ»
 4329    "});
 4330    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4331    cx.assert_editor_state(indoc! {"
 4332        «hello worldˇ»
 4333    "});
 4334
 4335    // If any upper case characters are identified -> lower case
 4336    // This matches JetBrains IDEs
 4337    cx.set_state(indoc! {"
 4338        «hEllo worldˇ»
 4339    "});
 4340    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4341    cx.assert_editor_state(indoc! {"
 4342        «hello worldˇ»
 4343    "});
 4344}
 4345
 4346#[gpui::test]
 4347async fn test_manipulate_text(cx: &mut TestAppContext) {
 4348    init_test(cx, |_| {});
 4349
 4350    let mut cx = EditorTestContext::new(cx).await;
 4351
 4352    // Test convert_to_upper_case()
 4353    cx.set_state(indoc! {"
 4354        «hello worldˇ»
 4355    "});
 4356    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4357    cx.assert_editor_state(indoc! {"
 4358        «HELLO WORLDˇ»
 4359    "});
 4360
 4361    // Test convert_to_lower_case()
 4362    cx.set_state(indoc! {"
 4363        «HELLO WORLDˇ»
 4364    "});
 4365    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4366    cx.assert_editor_state(indoc! {"
 4367        «hello worldˇ»
 4368    "});
 4369
 4370    // Test multiple line, single selection case
 4371    cx.set_state(indoc! {"
 4372        «The quick brown
 4373        fox jumps over
 4374        the lazy dogˇ»
 4375    "});
 4376    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4377    cx.assert_editor_state(indoc! {"
 4378        «The Quick Brown
 4379        Fox Jumps Over
 4380        The Lazy Dogˇ»
 4381    "});
 4382
 4383    // Test multiple line, single selection case
 4384    cx.set_state(indoc! {"
 4385        «The quick brown
 4386        fox jumps over
 4387        the lazy dogˇ»
 4388    "});
 4389    cx.update_editor(|e, window, cx| {
 4390        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4391    });
 4392    cx.assert_editor_state(indoc! {"
 4393        «TheQuickBrown
 4394        FoxJumpsOver
 4395        TheLazyDogˇ»
 4396    "});
 4397
 4398    // From here on out, test more complex cases of manipulate_text()
 4399
 4400    // Test no selection case - should affect words cursors are in
 4401    // Cursor at beginning, middle, and end of word
 4402    cx.set_state(indoc! {"
 4403        ˇhello big beauˇtiful worldˇ
 4404    "});
 4405    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4406    cx.assert_editor_state(indoc! {"
 4407        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4408    "});
 4409
 4410    // Test multiple selections on a single line and across multiple lines
 4411    cx.set_state(indoc! {"
 4412        «Theˇ» quick «brown
 4413        foxˇ» jumps «overˇ»
 4414        the «lazyˇ» dog
 4415    "});
 4416    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4417    cx.assert_editor_state(indoc! {"
 4418        «THEˇ» quick «BROWN
 4419        FOXˇ» jumps «OVERˇ»
 4420        the «LAZYˇ» dog
 4421    "});
 4422
 4423    // Test case where text length grows
 4424    cx.set_state(indoc! {"
 4425        «tschüߡ»
 4426    "});
 4427    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4428    cx.assert_editor_state(indoc! {"
 4429        «TSCHÜSSˇ»
 4430    "});
 4431
 4432    // Test to make sure we don't crash when text shrinks
 4433    cx.set_state(indoc! {"
 4434        aaa_bbbˇ
 4435    "});
 4436    cx.update_editor(|e, window, cx| {
 4437        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4438    });
 4439    cx.assert_editor_state(indoc! {"
 4440        «aaaBbbˇ»
 4441    "});
 4442
 4443    // Test to make sure we all aware of the fact that each word can grow and shrink
 4444    // Final selections should be aware of this fact
 4445    cx.set_state(indoc! {"
 4446        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4447    "});
 4448    cx.update_editor(|e, window, cx| {
 4449        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4450    });
 4451    cx.assert_editor_state(indoc! {"
 4452        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4453    "});
 4454
 4455    cx.set_state(indoc! {"
 4456        «hElLo, WoRld!ˇ»
 4457    "});
 4458    cx.update_editor(|e, window, cx| {
 4459        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4460    });
 4461    cx.assert_editor_state(indoc! {"
 4462        «HeLlO, wOrLD!ˇ»
 4463    "});
 4464}
 4465
 4466#[gpui::test]
 4467fn test_duplicate_line(cx: &mut TestAppContext) {
 4468    init_test(cx, |_| {});
 4469
 4470    let editor = cx.add_window(|window, cx| {
 4471        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4472        build_editor(buffer, window, cx)
 4473    });
 4474    _ = editor.update(cx, |editor, window, cx| {
 4475        editor.change_selections(None, window, cx, |s| {
 4476            s.select_display_ranges([
 4477                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4478                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4479                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4480                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4481            ])
 4482        });
 4483        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4484        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4485        assert_eq!(
 4486            editor.selections.display_ranges(cx),
 4487            vec![
 4488                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4489                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4490                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4491                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4492            ]
 4493        );
 4494    });
 4495
 4496    let editor = cx.add_window(|window, cx| {
 4497        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4498        build_editor(buffer, window, cx)
 4499    });
 4500    _ = editor.update(cx, |editor, window, cx| {
 4501        editor.change_selections(None, window, cx, |s| {
 4502            s.select_display_ranges([
 4503                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4504                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4505            ])
 4506        });
 4507        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4508        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4509        assert_eq!(
 4510            editor.selections.display_ranges(cx),
 4511            vec![
 4512                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4513                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4514            ]
 4515        );
 4516    });
 4517
 4518    // With `move_upwards` the selections stay in place, except for
 4519    // the lines inserted above them
 4520    let editor = cx.add_window(|window, cx| {
 4521        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4522        build_editor(buffer, window, cx)
 4523    });
 4524    _ = editor.update(cx, |editor, window, cx| {
 4525        editor.change_selections(None, window, cx, |s| {
 4526            s.select_display_ranges([
 4527                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4528                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4529                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4530                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4531            ])
 4532        });
 4533        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4534        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4535        assert_eq!(
 4536            editor.selections.display_ranges(cx),
 4537            vec![
 4538                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4539                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4540                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4541                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4542            ]
 4543        );
 4544    });
 4545
 4546    let editor = cx.add_window(|window, cx| {
 4547        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4548        build_editor(buffer, window, cx)
 4549    });
 4550    _ = editor.update(cx, |editor, window, cx| {
 4551        editor.change_selections(None, window, cx, |s| {
 4552            s.select_display_ranges([
 4553                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4554                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4555            ])
 4556        });
 4557        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4558        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4559        assert_eq!(
 4560            editor.selections.display_ranges(cx),
 4561            vec![
 4562                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4564            ]
 4565        );
 4566    });
 4567
 4568    let editor = cx.add_window(|window, cx| {
 4569        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4570        build_editor(buffer, window, cx)
 4571    });
 4572    _ = editor.update(cx, |editor, window, cx| {
 4573        editor.change_selections(None, window, cx, |s| {
 4574            s.select_display_ranges([
 4575                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4576                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4577            ])
 4578        });
 4579        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4580        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4581        assert_eq!(
 4582            editor.selections.display_ranges(cx),
 4583            vec![
 4584                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4585                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4586            ]
 4587        );
 4588    });
 4589}
 4590
 4591#[gpui::test]
 4592fn test_move_line_up_down(cx: &mut TestAppContext) {
 4593    init_test(cx, |_| {});
 4594
 4595    let editor = cx.add_window(|window, cx| {
 4596        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4597        build_editor(buffer, window, cx)
 4598    });
 4599    _ = editor.update(cx, |editor, window, cx| {
 4600        editor.fold_creases(
 4601            vec![
 4602                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4603                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4604                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4605            ],
 4606            true,
 4607            window,
 4608            cx,
 4609        );
 4610        editor.change_selections(None, window, cx, |s| {
 4611            s.select_display_ranges([
 4612                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4613                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4614                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4615                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4616            ])
 4617        });
 4618        assert_eq!(
 4619            editor.display_text(cx),
 4620            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4621        );
 4622
 4623        editor.move_line_up(&MoveLineUp, window, cx);
 4624        assert_eq!(
 4625            editor.display_text(cx),
 4626            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4627        );
 4628        assert_eq!(
 4629            editor.selections.display_ranges(cx),
 4630            vec![
 4631                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4632                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4633                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4634                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4635            ]
 4636        );
 4637    });
 4638
 4639    _ = editor.update(cx, |editor, window, cx| {
 4640        editor.move_line_down(&MoveLineDown, window, cx);
 4641        assert_eq!(
 4642            editor.display_text(cx),
 4643            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4644        );
 4645        assert_eq!(
 4646            editor.selections.display_ranges(cx),
 4647            vec![
 4648                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4649                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4650                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4651                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4652            ]
 4653        );
 4654    });
 4655
 4656    _ = editor.update(cx, |editor, window, cx| {
 4657        editor.move_line_down(&MoveLineDown, window, cx);
 4658        assert_eq!(
 4659            editor.display_text(cx),
 4660            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4661        );
 4662        assert_eq!(
 4663            editor.selections.display_ranges(cx),
 4664            vec![
 4665                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4666                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4667                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4668                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4669            ]
 4670        );
 4671    });
 4672
 4673    _ = editor.update(cx, |editor, window, cx| {
 4674        editor.move_line_up(&MoveLineUp, window, cx);
 4675        assert_eq!(
 4676            editor.display_text(cx),
 4677            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4678        );
 4679        assert_eq!(
 4680            editor.selections.display_ranges(cx),
 4681            vec![
 4682                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4683                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4684                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4685                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4686            ]
 4687        );
 4688    });
 4689}
 4690
 4691#[gpui::test]
 4692fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4693    init_test(cx, |_| {});
 4694
 4695    let editor = cx.add_window(|window, cx| {
 4696        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4697        build_editor(buffer, window, cx)
 4698    });
 4699    _ = editor.update(cx, |editor, window, cx| {
 4700        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4701        editor.insert_blocks(
 4702            [BlockProperties {
 4703                style: BlockStyle::Fixed,
 4704                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4705                height: Some(1),
 4706                render: Arc::new(|_| div().into_any()),
 4707                priority: 0,
 4708                render_in_minimap: true,
 4709            }],
 4710            Some(Autoscroll::fit()),
 4711            cx,
 4712        );
 4713        editor.change_selections(None, window, cx, |s| {
 4714            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4715        });
 4716        editor.move_line_down(&MoveLineDown, window, cx);
 4717    });
 4718}
 4719
 4720#[gpui::test]
 4721async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4722    init_test(cx, |_| {});
 4723
 4724    let mut cx = EditorTestContext::new(cx).await;
 4725    cx.set_state(
 4726        &"
 4727            ˇzero
 4728            one
 4729            two
 4730            three
 4731            four
 4732            five
 4733        "
 4734        .unindent(),
 4735    );
 4736
 4737    // Create a four-line block that replaces three lines of text.
 4738    cx.update_editor(|editor, window, cx| {
 4739        let snapshot = editor.snapshot(window, cx);
 4740        let snapshot = &snapshot.buffer_snapshot;
 4741        let placement = BlockPlacement::Replace(
 4742            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4743        );
 4744        editor.insert_blocks(
 4745            [BlockProperties {
 4746                placement,
 4747                height: Some(4),
 4748                style: BlockStyle::Sticky,
 4749                render: Arc::new(|_| gpui::div().into_any_element()),
 4750                priority: 0,
 4751                render_in_minimap: true,
 4752            }],
 4753            None,
 4754            cx,
 4755        );
 4756    });
 4757
 4758    // Move down so that the cursor touches the block.
 4759    cx.update_editor(|editor, window, cx| {
 4760        editor.move_down(&Default::default(), window, cx);
 4761    });
 4762    cx.assert_editor_state(
 4763        &"
 4764            zero
 4765            «one
 4766            two
 4767            threeˇ»
 4768            four
 4769            five
 4770        "
 4771        .unindent(),
 4772    );
 4773
 4774    // Move down past the block.
 4775    cx.update_editor(|editor, window, cx| {
 4776        editor.move_down(&Default::default(), window, cx);
 4777    });
 4778    cx.assert_editor_state(
 4779        &"
 4780            zero
 4781            one
 4782            two
 4783            three
 4784            ˇfour
 4785            five
 4786        "
 4787        .unindent(),
 4788    );
 4789}
 4790
 4791#[gpui::test]
 4792fn test_transpose(cx: &mut TestAppContext) {
 4793    init_test(cx, |_| {});
 4794
 4795    _ = cx.add_window(|window, cx| {
 4796        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4797        editor.set_style(EditorStyle::default(), window, cx);
 4798        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4799        editor.transpose(&Default::default(), window, cx);
 4800        assert_eq!(editor.text(cx), "bac");
 4801        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4802
 4803        editor.transpose(&Default::default(), window, cx);
 4804        assert_eq!(editor.text(cx), "bca");
 4805        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4806
 4807        editor.transpose(&Default::default(), window, cx);
 4808        assert_eq!(editor.text(cx), "bac");
 4809        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4810
 4811        editor
 4812    });
 4813
 4814    _ = cx.add_window(|window, cx| {
 4815        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4816        editor.set_style(EditorStyle::default(), window, cx);
 4817        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4818        editor.transpose(&Default::default(), window, cx);
 4819        assert_eq!(editor.text(cx), "acb\nde");
 4820        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4821
 4822        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4823        editor.transpose(&Default::default(), window, cx);
 4824        assert_eq!(editor.text(cx), "acbd\ne");
 4825        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4826
 4827        editor.transpose(&Default::default(), window, cx);
 4828        assert_eq!(editor.text(cx), "acbde\n");
 4829        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4830
 4831        editor.transpose(&Default::default(), window, cx);
 4832        assert_eq!(editor.text(cx), "acbd\ne");
 4833        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4834
 4835        editor
 4836    });
 4837
 4838    _ = cx.add_window(|window, cx| {
 4839        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4840        editor.set_style(EditorStyle::default(), window, cx);
 4841        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4842        editor.transpose(&Default::default(), window, cx);
 4843        assert_eq!(editor.text(cx), "bacd\ne");
 4844        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4845
 4846        editor.transpose(&Default::default(), window, cx);
 4847        assert_eq!(editor.text(cx), "bcade\n");
 4848        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4849
 4850        editor.transpose(&Default::default(), window, cx);
 4851        assert_eq!(editor.text(cx), "bcda\ne");
 4852        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4853
 4854        editor.transpose(&Default::default(), window, cx);
 4855        assert_eq!(editor.text(cx), "bcade\n");
 4856        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4857
 4858        editor.transpose(&Default::default(), window, cx);
 4859        assert_eq!(editor.text(cx), "bcaed\n");
 4860        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4861
 4862        editor
 4863    });
 4864
 4865    _ = cx.add_window(|window, cx| {
 4866        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4867        editor.set_style(EditorStyle::default(), window, cx);
 4868        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4869        editor.transpose(&Default::default(), window, cx);
 4870        assert_eq!(editor.text(cx), "🏀🍐✋");
 4871        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4872
 4873        editor.transpose(&Default::default(), window, cx);
 4874        assert_eq!(editor.text(cx), "🏀✋🍐");
 4875        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4876
 4877        editor.transpose(&Default::default(), window, cx);
 4878        assert_eq!(editor.text(cx), "🏀🍐✋");
 4879        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4880
 4881        editor
 4882    });
 4883}
 4884
 4885#[gpui::test]
 4886async fn test_rewrap(cx: &mut TestAppContext) {
 4887    init_test(cx, |settings| {
 4888        settings.languages.extend([
 4889            (
 4890                "Markdown".into(),
 4891                LanguageSettingsContent {
 4892                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4893                    ..Default::default()
 4894                },
 4895            ),
 4896            (
 4897                "Plain Text".into(),
 4898                LanguageSettingsContent {
 4899                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4900                    ..Default::default()
 4901                },
 4902            ),
 4903        ])
 4904    });
 4905
 4906    let mut cx = EditorTestContext::new(cx).await;
 4907
 4908    let language_with_c_comments = Arc::new(Language::new(
 4909        LanguageConfig {
 4910            line_comments: vec!["// ".into()],
 4911            ..LanguageConfig::default()
 4912        },
 4913        None,
 4914    ));
 4915    let language_with_pound_comments = Arc::new(Language::new(
 4916        LanguageConfig {
 4917            line_comments: vec!["# ".into()],
 4918            ..LanguageConfig::default()
 4919        },
 4920        None,
 4921    ));
 4922    let markdown_language = Arc::new(Language::new(
 4923        LanguageConfig {
 4924            name: "Markdown".into(),
 4925            ..LanguageConfig::default()
 4926        },
 4927        None,
 4928    ));
 4929    let language_with_doc_comments = Arc::new(Language::new(
 4930        LanguageConfig {
 4931            line_comments: vec!["// ".into(), "/// ".into()],
 4932            ..LanguageConfig::default()
 4933        },
 4934        Some(tree_sitter_rust::LANGUAGE.into()),
 4935    ));
 4936
 4937    let plaintext_language = Arc::new(Language::new(
 4938        LanguageConfig {
 4939            name: "Plain Text".into(),
 4940            ..LanguageConfig::default()
 4941        },
 4942        None,
 4943    ));
 4944
 4945    assert_rewrap(
 4946        indoc! {"
 4947            // ˇ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.
 4948        "},
 4949        indoc! {"
 4950            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4951            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4952            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4953            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4954            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4955            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4956            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4957            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4958            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4959            // porttitor id. Aliquam id accumsan eros.
 4960        "},
 4961        language_with_c_comments.clone(),
 4962        &mut cx,
 4963    );
 4964
 4965    // Test that rewrapping works inside of a selection
 4966    assert_rewrap(
 4967        indoc! {"
 4968            «// 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.ˇ»
 4969        "},
 4970        indoc! {"
 4971            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4972            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4973            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4974            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4975            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4976            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4977            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4978            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4979            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4980            // porttitor id. Aliquam id accumsan eros.ˇ»
 4981        "},
 4982        language_with_c_comments.clone(),
 4983        &mut cx,
 4984    );
 4985
 4986    // Test that cursors that expand to the same region are collapsed.
 4987    assert_rewrap(
 4988        indoc! {"
 4989            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4990            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4991            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4992            // ˇ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.
 4993        "},
 4994        indoc! {"
 4995            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4996            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4997            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4998            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4999            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 5000            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 5001            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 5002            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 5003            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 5004            // porttitor id. Aliquam id accumsan eros.
 5005        "},
 5006        language_with_c_comments.clone(),
 5007        &mut cx,
 5008    );
 5009
 5010    // Test that non-contiguous selections are treated separately.
 5011    assert_rewrap(
 5012        indoc! {"
 5013            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 5014            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 5015            //
 5016            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5017            // ˇ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.
 5018        "},
 5019        indoc! {"
 5020            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 5021            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 5022            // auctor, eu lacinia sapien scelerisque.
 5023            //
 5024            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 5025            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 5026            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 5027            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 5028            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 5029            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 5030            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 5031        "},
 5032        language_with_c_comments.clone(),
 5033        &mut cx,
 5034    );
 5035
 5036    // Test that different comment prefixes are supported.
 5037    assert_rewrap(
 5038        indoc! {"
 5039            # ˇ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.
 5040        "},
 5041        indoc! {"
 5042            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 5043            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5044            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5045            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5046            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 5047            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 5048            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 5049            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 5050            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 5051            # accumsan eros.
 5052        "},
 5053        language_with_pound_comments.clone(),
 5054        &mut cx,
 5055    );
 5056
 5057    // Test that rewrapping is ignored outside of comments in most languages.
 5058    assert_rewrap(
 5059        indoc! {"
 5060            /// Adds two numbers.
 5061            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5062            fn add(a: u32, b: u32) -> u32 {
 5063                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ˇ
 5064            }
 5065        "},
 5066        indoc! {"
 5067            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 5068            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 5069            fn add(a: u32, b: u32) -> u32 {
 5070                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ˇ
 5071            }
 5072        "},
 5073        language_with_doc_comments.clone(),
 5074        &mut cx,
 5075    );
 5076
 5077    // Test that rewrapping works in Markdown and Plain Text languages.
 5078    assert_rewrap(
 5079        indoc! {"
 5080            # Hello
 5081
 5082            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.
 5083        "},
 5084        indoc! {"
 5085            # Hello
 5086
 5087            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5088            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5089            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5090            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5091            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5092            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5093            Integer sit amet scelerisque nisi.
 5094        "},
 5095        markdown_language,
 5096        &mut cx,
 5097    );
 5098
 5099    assert_rewrap(
 5100        indoc! {"
 5101            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.
 5102        "},
 5103        indoc! {"
 5104            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5105            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5106            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5107            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5108            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5109            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5110            Integer sit amet scelerisque nisi.
 5111        "},
 5112        plaintext_language,
 5113        &mut cx,
 5114    );
 5115
 5116    // Test rewrapping unaligned comments in a selection.
 5117    assert_rewrap(
 5118        indoc! {"
 5119            fn foo() {
 5120                if true {
 5121            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5122            // Praesent semper egestas tellus id dignissim.ˇ»
 5123                    do_something();
 5124                } else {
 5125                    //
 5126                }
 5127            }
 5128        "},
 5129        indoc! {"
 5130            fn foo() {
 5131                if true {
 5132            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5133                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5134                    // egestas tellus id dignissim.ˇ»
 5135                    do_something();
 5136                } else {
 5137                    //
 5138                }
 5139            }
 5140        "},
 5141        language_with_doc_comments.clone(),
 5142        &mut cx,
 5143    );
 5144
 5145    assert_rewrap(
 5146        indoc! {"
 5147            fn foo() {
 5148                if true {
 5149            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5150            // Praesent semper egestas tellus id dignissim.»
 5151                    do_something();
 5152                } else {
 5153                    //
 5154                }
 5155
 5156            }
 5157        "},
 5158        indoc! {"
 5159            fn foo() {
 5160                if true {
 5161            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5162                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5163                    // egestas tellus id dignissim.»
 5164                    do_something();
 5165                } else {
 5166                    //
 5167                }
 5168
 5169            }
 5170        "},
 5171        language_with_doc_comments.clone(),
 5172        &mut cx,
 5173    );
 5174
 5175    #[track_caller]
 5176    fn assert_rewrap(
 5177        unwrapped_text: &str,
 5178        wrapped_text: &str,
 5179        language: Arc<Language>,
 5180        cx: &mut EditorTestContext,
 5181    ) {
 5182        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5183        cx.set_state(unwrapped_text);
 5184        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5185        cx.assert_editor_state(wrapped_text);
 5186    }
 5187}
 5188
 5189#[gpui::test]
 5190async fn test_hard_wrap(cx: &mut TestAppContext) {
 5191    init_test(cx, |_| {});
 5192    let mut cx = EditorTestContext::new(cx).await;
 5193
 5194    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5195    cx.update_editor(|editor, _, cx| {
 5196        editor.set_hard_wrap(Some(14), cx);
 5197    });
 5198
 5199    cx.set_state(indoc!(
 5200        "
 5201        one two three ˇ
 5202        "
 5203    ));
 5204    cx.simulate_input("four");
 5205    cx.run_until_parked();
 5206
 5207    cx.assert_editor_state(indoc!(
 5208        "
 5209        one two three
 5210        fourˇ
 5211        "
 5212    ));
 5213
 5214    cx.update_editor(|editor, window, cx| {
 5215        editor.newline(&Default::default(), window, cx);
 5216    });
 5217    cx.run_until_parked();
 5218    cx.assert_editor_state(indoc!(
 5219        "
 5220        one two three
 5221        four
 5222        ˇ
 5223        "
 5224    ));
 5225
 5226    cx.simulate_input("five");
 5227    cx.run_until_parked();
 5228    cx.assert_editor_state(indoc!(
 5229        "
 5230        one two three
 5231        four
 5232        fiveˇ
 5233        "
 5234    ));
 5235
 5236    cx.update_editor(|editor, window, cx| {
 5237        editor.newline(&Default::default(), window, cx);
 5238    });
 5239    cx.run_until_parked();
 5240    cx.simulate_input("# ");
 5241    cx.run_until_parked();
 5242    cx.assert_editor_state(indoc!(
 5243        "
 5244        one two three
 5245        four
 5246        five
 5247        # ˇ
 5248        "
 5249    ));
 5250
 5251    cx.update_editor(|editor, window, cx| {
 5252        editor.newline(&Default::default(), window, cx);
 5253    });
 5254    cx.run_until_parked();
 5255    cx.assert_editor_state(indoc!(
 5256        "
 5257        one two three
 5258        four
 5259        five
 5260        #\x20
 5261 5262        "
 5263    ));
 5264
 5265    cx.simulate_input(" 6");
 5266    cx.run_until_parked();
 5267    cx.assert_editor_state(indoc!(
 5268        "
 5269        one two three
 5270        four
 5271        five
 5272        #
 5273        # 6ˇ
 5274        "
 5275    ));
 5276}
 5277
 5278#[gpui::test]
 5279async fn test_clipboard(cx: &mut TestAppContext) {
 5280    init_test(cx, |_| {});
 5281
 5282    let mut cx = EditorTestContext::new(cx).await;
 5283
 5284    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5285    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5286    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5287
 5288    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5289    cx.set_state("two ˇfour ˇsix ˇ");
 5290    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5291    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5292
 5293    // Paste again but with only two cursors. Since the number of cursors doesn't
 5294    // match the number of slices in the clipboard, the entire clipboard text
 5295    // is pasted at each cursor.
 5296    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5297    cx.update_editor(|e, window, cx| {
 5298        e.handle_input("( ", window, cx);
 5299        e.paste(&Paste, window, cx);
 5300        e.handle_input(") ", window, cx);
 5301    });
 5302    cx.assert_editor_state(
 5303        &([
 5304            "( one✅ ",
 5305            "three ",
 5306            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5307            "three ",
 5308            "five ) ˇ",
 5309        ]
 5310        .join("\n")),
 5311    );
 5312
 5313    // Cut with three selections, one of which is full-line.
 5314    cx.set_state(indoc! {"
 5315        1«2ˇ»3
 5316        4ˇ567
 5317        «8ˇ»9"});
 5318    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5319    cx.assert_editor_state(indoc! {"
 5320        1ˇ3
 5321        ˇ9"});
 5322
 5323    // Paste with three selections, noticing how the copied selection that was full-line
 5324    // gets inserted before the second cursor.
 5325    cx.set_state(indoc! {"
 5326        1ˇ3
 5327 5328        «oˇ»ne"});
 5329    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5330    cx.assert_editor_state(indoc! {"
 5331        12ˇ3
 5332        4567
 5333 5334        8ˇne"});
 5335
 5336    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5337    cx.set_state(indoc! {"
 5338        The quick brown
 5339        fox juˇmps over
 5340        the lazy dog"});
 5341    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5342    assert_eq!(
 5343        cx.read_from_clipboard()
 5344            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5345        Some("fox jumps over\n".to_string())
 5346    );
 5347
 5348    // Paste with three selections, noticing how the copied full-line selection is inserted
 5349    // before the empty selections but replaces the selection that is non-empty.
 5350    cx.set_state(indoc! {"
 5351        Tˇhe quick brown
 5352        «foˇ»x jumps over
 5353        tˇhe lazy dog"});
 5354    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5355    cx.assert_editor_state(indoc! {"
 5356        fox jumps over
 5357        Tˇhe quick brown
 5358        fox jumps over
 5359        ˇx jumps over
 5360        fox jumps over
 5361        tˇhe lazy dog"});
 5362}
 5363
 5364#[gpui::test]
 5365async fn test_copy_trim(cx: &mut TestAppContext) {
 5366    init_test(cx, |_| {});
 5367
 5368    let mut cx = EditorTestContext::new(cx).await;
 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"
 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 preserves all indentation selected",
 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 should strip all leading whitespaces, even if some of it was selected"
 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            "     for selection 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        "Regular copying for reverse selection works the same",
 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            "for selection in selections.iter() {
 5485let mut start = selection.start;
 5486let mut end = selection.end;
 5487let is_entire_line = selection.is_empty();
 5488if is_entire_line {
 5489    start = Point::new(start.row, 0);"
 5490                .to_string()
 5491        ),
 5492        "Copying with stripping for reverse selection works the same"
 5493    );
 5494
 5495    cx.set_state(
 5496        r#"            for selection «in selections.iter() {
 5497            let mut start = selection.start;
 5498            let mut end = selection.end;
 5499            let is_entire_line = selection.is_empty();
 5500            if is_entire_line {
 5501                start = Point::new(start.row, 0);ˇ»
 5502                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5503            }
 5504        "#,
 5505    );
 5506    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5507    assert_eq!(
 5508        cx.read_from_clipboard()
 5509            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5510        Some(
 5511            "in selections.iter() {
 5512            let mut start = selection.start;
 5513            let mut end = selection.end;
 5514            let is_entire_line = selection.is_empty();
 5515            if is_entire_line {
 5516                start = Point::new(start.row, 0);"
 5517                .to_string()
 5518        ),
 5519        "When selecting past the indent, the copying works as usual",
 5520    );
 5521    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5522    assert_eq!(
 5523        cx.read_from_clipboard()
 5524            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5525        Some(
 5526            "in selections.iter() {
 5527            let mut start = selection.start;
 5528            let mut end = selection.end;
 5529            let is_entire_line = selection.is_empty();
 5530            if is_entire_line {
 5531                start = Point::new(start.row, 0);"
 5532                .to_string()
 5533        ),
 5534        "When selecting past the indent, nothing is trimmed"
 5535    );
 5536
 5537    cx.set_state(
 5538        r#"            «for selection in selections.iter() {
 5539            let mut start = selection.start;
 5540
 5541            let mut end = selection.end;
 5542            let is_entire_line = selection.is_empty();
 5543            if is_entire_line {
 5544                start = Point::new(start.row, 0);
 5545ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5546            }
 5547        "#,
 5548    );
 5549    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5550    assert_eq!(
 5551        cx.read_from_clipboard()
 5552            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5553        Some(
 5554            "for selection in selections.iter() {
 5555let mut start = selection.start;
 5556
 5557let mut end = selection.end;
 5558let is_entire_line = selection.is_empty();
 5559if is_entire_line {
 5560    start = Point::new(start.row, 0);
 5561"
 5562            .to_string()
 5563        ),
 5564        "Copying with stripping should ignore empty lines"
 5565    );
 5566}
 5567
 5568#[gpui::test]
 5569async fn test_paste_multiline(cx: &mut TestAppContext) {
 5570    init_test(cx, |_| {});
 5571
 5572    let mut cx = EditorTestContext::new(cx).await;
 5573    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5574
 5575    // Cut an indented block, without the leading whitespace.
 5576    cx.set_state(indoc! {"
 5577        const a: B = (
 5578            c(),
 5579            «d(
 5580                e,
 5581                f
 5582            )ˇ»
 5583        );
 5584    "});
 5585    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5586    cx.assert_editor_state(indoc! {"
 5587        const a: B = (
 5588            c(),
 5589            ˇ
 5590        );
 5591    "});
 5592
 5593    // Paste it at the same position.
 5594    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5595    cx.assert_editor_state(indoc! {"
 5596        const a: B = (
 5597            c(),
 5598            d(
 5599                e,
 5600                f
 5601 5602        );
 5603    "});
 5604
 5605    // Paste it at a line with a lower indent level.
 5606    cx.set_state(indoc! {"
 5607        ˇ
 5608        const a: B = (
 5609            c(),
 5610        );
 5611    "});
 5612    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5613    cx.assert_editor_state(indoc! {"
 5614        d(
 5615            e,
 5616            f
 5617 5618        const a: B = (
 5619            c(),
 5620        );
 5621    "});
 5622
 5623    // Cut an indented block, with the leading whitespace.
 5624    cx.set_state(indoc! {"
 5625        const a: B = (
 5626            c(),
 5627        «    d(
 5628                e,
 5629                f
 5630            )
 5631        ˇ»);
 5632    "});
 5633    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5634    cx.assert_editor_state(indoc! {"
 5635        const a: B = (
 5636            c(),
 5637        ˇ);
 5638    "});
 5639
 5640    // Paste it at the same position.
 5641    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5642    cx.assert_editor_state(indoc! {"
 5643        const a: B = (
 5644            c(),
 5645            d(
 5646                e,
 5647                f
 5648            )
 5649        ˇ);
 5650    "});
 5651
 5652    // Paste it at a line with a higher indent level.
 5653    cx.set_state(indoc! {"
 5654        const a: B = (
 5655            c(),
 5656            d(
 5657                e,
 5658 5659            )
 5660        );
 5661    "});
 5662    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5663    cx.assert_editor_state(indoc! {"
 5664        const a: B = (
 5665            c(),
 5666            d(
 5667                e,
 5668                f    d(
 5669                    e,
 5670                    f
 5671                )
 5672        ˇ
 5673            )
 5674        );
 5675    "});
 5676
 5677    // Copy an indented block, starting mid-line
 5678    cx.set_state(indoc! {"
 5679        const a: B = (
 5680            c(),
 5681            somethin«g(
 5682                e,
 5683                f
 5684            )ˇ»
 5685        );
 5686    "});
 5687    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5688
 5689    // Paste it on a line with a lower indent level
 5690    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5691    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5692    cx.assert_editor_state(indoc! {"
 5693        const a: B = (
 5694            c(),
 5695            something(
 5696                e,
 5697                f
 5698            )
 5699        );
 5700        g(
 5701            e,
 5702            f
 5703"});
 5704}
 5705
 5706#[gpui::test]
 5707async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5708    init_test(cx, |_| {});
 5709
 5710    cx.write_to_clipboard(ClipboardItem::new_string(
 5711        "    d(\n        e\n    );\n".into(),
 5712    ));
 5713
 5714    let mut cx = EditorTestContext::new(cx).await;
 5715    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5716
 5717    cx.set_state(indoc! {"
 5718        fn a() {
 5719            b();
 5720            if c() {
 5721                ˇ
 5722            }
 5723        }
 5724    "});
 5725
 5726    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5727    cx.assert_editor_state(indoc! {"
 5728        fn a() {
 5729            b();
 5730            if c() {
 5731                d(
 5732                    e
 5733                );
 5734        ˇ
 5735            }
 5736        }
 5737    "});
 5738
 5739    cx.set_state(indoc! {"
 5740        fn a() {
 5741            b();
 5742            ˇ
 5743        }
 5744    "});
 5745
 5746    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5747    cx.assert_editor_state(indoc! {"
 5748        fn a() {
 5749            b();
 5750            d(
 5751                e
 5752            );
 5753        ˇ
 5754        }
 5755    "});
 5756}
 5757
 5758#[gpui::test]
 5759fn test_select_all(cx: &mut TestAppContext) {
 5760    init_test(cx, |_| {});
 5761
 5762    let editor = cx.add_window(|window, cx| {
 5763        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5764        build_editor(buffer, window, cx)
 5765    });
 5766    _ = editor.update(cx, |editor, window, cx| {
 5767        editor.select_all(&SelectAll, window, cx);
 5768        assert_eq!(
 5769            editor.selections.display_ranges(cx),
 5770            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5771        );
 5772    });
 5773}
 5774
 5775#[gpui::test]
 5776fn test_select_line(cx: &mut TestAppContext) {
 5777    init_test(cx, |_| {});
 5778
 5779    let editor = cx.add_window(|window, cx| {
 5780        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5781        build_editor(buffer, window, cx)
 5782    });
 5783    _ = editor.update(cx, |editor, window, cx| {
 5784        editor.change_selections(None, window, cx, |s| {
 5785            s.select_display_ranges([
 5786                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5787                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5788                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5789                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5790            ])
 5791        });
 5792        editor.select_line(&SelectLine, window, cx);
 5793        assert_eq!(
 5794            editor.selections.display_ranges(cx),
 5795            vec![
 5796                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5797                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5798            ]
 5799        );
 5800    });
 5801
 5802    _ = editor.update(cx, |editor, window, cx| {
 5803        editor.select_line(&SelectLine, window, cx);
 5804        assert_eq!(
 5805            editor.selections.display_ranges(cx),
 5806            vec![
 5807                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5808                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5809            ]
 5810        );
 5811    });
 5812
 5813    _ = editor.update(cx, |editor, window, cx| {
 5814        editor.select_line(&SelectLine, window, cx);
 5815        assert_eq!(
 5816            editor.selections.display_ranges(cx),
 5817            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5818        );
 5819    });
 5820}
 5821
 5822#[gpui::test]
 5823async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5824    init_test(cx, |_| {});
 5825    let mut cx = EditorTestContext::new(cx).await;
 5826
 5827    #[track_caller]
 5828    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5829        cx.set_state(initial_state);
 5830        cx.update_editor(|e, window, cx| {
 5831            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5832        });
 5833        cx.assert_editor_state(expected_state);
 5834    }
 5835
 5836    // Selection starts and ends at the middle of lines, left-to-right
 5837    test(
 5838        &mut cx,
 5839        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5840        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5841    );
 5842    // Same thing, right-to-left
 5843    test(
 5844        &mut cx,
 5845        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5846        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5847    );
 5848
 5849    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5850    test(
 5851        &mut cx,
 5852        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5853        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5854    );
 5855    // Same thing, right-to-left
 5856    test(
 5857        &mut cx,
 5858        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5859        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5860    );
 5861
 5862    // Whole buffer, left-to-right, last line ends with newline
 5863    test(
 5864        &mut cx,
 5865        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5866        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5867    );
 5868    // Same thing, right-to-left
 5869    test(
 5870        &mut cx,
 5871        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5872        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5873    );
 5874
 5875    // Starts at the end of a line, ends at the start of another
 5876    test(
 5877        &mut cx,
 5878        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5879        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5880    );
 5881}
 5882
 5883#[gpui::test]
 5884async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5885    init_test(cx, |_| {});
 5886
 5887    let editor = cx.add_window(|window, cx| {
 5888        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5889        build_editor(buffer, window, cx)
 5890    });
 5891
 5892    // setup
 5893    _ = editor.update(cx, |editor, window, cx| {
 5894        editor.fold_creases(
 5895            vec![
 5896                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5897                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5898                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5899            ],
 5900            true,
 5901            window,
 5902            cx,
 5903        );
 5904        assert_eq!(
 5905            editor.display_text(cx),
 5906            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5907        );
 5908    });
 5909
 5910    _ = editor.update(cx, |editor, window, cx| {
 5911        editor.change_selections(None, window, cx, |s| {
 5912            s.select_display_ranges([
 5913                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5914                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5915                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5916                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5917            ])
 5918        });
 5919        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5920        assert_eq!(
 5921            editor.display_text(cx),
 5922            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5923        );
 5924    });
 5925    EditorTestContext::for_editor(editor, cx)
 5926        .await
 5927        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5928
 5929    _ = editor.update(cx, |editor, window, cx| {
 5930        editor.change_selections(None, window, cx, |s| {
 5931            s.select_display_ranges([
 5932                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5933            ])
 5934        });
 5935        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5936        assert_eq!(
 5937            editor.display_text(cx),
 5938            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5939        );
 5940        assert_eq!(
 5941            editor.selections.display_ranges(cx),
 5942            [
 5943                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5944                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5945                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5946                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5947                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5948                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5949                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5950            ]
 5951        );
 5952    });
 5953    EditorTestContext::for_editor(editor, cx)
 5954        .await
 5955        .assert_editor_state(
 5956            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5957        );
 5958}
 5959
 5960#[gpui::test]
 5961async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5962    init_test(cx, |_| {});
 5963
 5964    let mut cx = EditorTestContext::new(cx).await;
 5965
 5966    cx.set_state(indoc!(
 5967        r#"abc
 5968           defˇghi
 5969
 5970           jk
 5971           nlmo
 5972           "#
 5973    ));
 5974
 5975    cx.update_editor(|editor, window, cx| {
 5976        editor.add_selection_above(&Default::default(), window, cx);
 5977    });
 5978
 5979    cx.assert_editor_state(indoc!(
 5980        r#"abcˇ
 5981           defˇghi
 5982
 5983           jk
 5984           nlmo
 5985           "#
 5986    ));
 5987
 5988    cx.update_editor(|editor, window, cx| {
 5989        editor.add_selection_above(&Default::default(), window, cx);
 5990    });
 5991
 5992    cx.assert_editor_state(indoc!(
 5993        r#"abcˇ
 5994            defˇghi
 5995
 5996            jk
 5997            nlmo
 5998            "#
 5999    ));
 6000
 6001    cx.update_editor(|editor, window, cx| {
 6002        editor.add_selection_below(&Default::default(), window, cx);
 6003    });
 6004
 6005    cx.assert_editor_state(indoc!(
 6006        r#"abc
 6007           defˇghi
 6008
 6009           jk
 6010           nlmo
 6011           "#
 6012    ));
 6013
 6014    cx.update_editor(|editor, window, cx| {
 6015        editor.undo_selection(&Default::default(), window, cx);
 6016    });
 6017
 6018    cx.assert_editor_state(indoc!(
 6019        r#"abcˇ
 6020           defˇghi
 6021
 6022           jk
 6023           nlmo
 6024           "#
 6025    ));
 6026
 6027    cx.update_editor(|editor, window, cx| {
 6028        editor.redo_selection(&Default::default(), window, cx);
 6029    });
 6030
 6031    cx.assert_editor_state(indoc!(
 6032        r#"abc
 6033           defˇghi
 6034
 6035           jk
 6036           nlmo
 6037           "#
 6038    ));
 6039
 6040    cx.update_editor(|editor, window, cx| {
 6041        editor.add_selection_below(&Default::default(), window, cx);
 6042    });
 6043
 6044    cx.assert_editor_state(indoc!(
 6045        r#"abc
 6046           defˇghi
 6047
 6048           jk
 6049           nlmˇo
 6050           "#
 6051    ));
 6052
 6053    cx.update_editor(|editor, window, cx| {
 6054        editor.add_selection_below(&Default::default(), window, cx);
 6055    });
 6056
 6057    cx.assert_editor_state(indoc!(
 6058        r#"abc
 6059           defˇghi
 6060
 6061           jk
 6062           nlmˇo
 6063           "#
 6064    ));
 6065
 6066    // change selections
 6067    cx.set_state(indoc!(
 6068        r#"abc
 6069           def«ˇg»hi
 6070
 6071           jk
 6072           nlmo
 6073           "#
 6074    ));
 6075
 6076    cx.update_editor(|editor, window, cx| {
 6077        editor.add_selection_below(&Default::default(), window, cx);
 6078    });
 6079
 6080    cx.assert_editor_state(indoc!(
 6081        r#"abc
 6082           def«ˇg»hi
 6083
 6084           jk
 6085           nlm«ˇo»
 6086           "#
 6087    ));
 6088
 6089    cx.update_editor(|editor, window, cx| {
 6090        editor.add_selection_below(&Default::default(), window, cx);
 6091    });
 6092
 6093    cx.assert_editor_state(indoc!(
 6094        r#"abc
 6095           def«ˇg»hi
 6096
 6097           jk
 6098           nlm«ˇo»
 6099           "#
 6100    ));
 6101
 6102    cx.update_editor(|editor, window, cx| {
 6103        editor.add_selection_above(&Default::default(), window, cx);
 6104    });
 6105
 6106    cx.assert_editor_state(indoc!(
 6107        r#"abc
 6108           def«ˇg»hi
 6109
 6110           jk
 6111           nlmo
 6112           "#
 6113    ));
 6114
 6115    cx.update_editor(|editor, window, cx| {
 6116        editor.add_selection_above(&Default::default(), window, cx);
 6117    });
 6118
 6119    cx.assert_editor_state(indoc!(
 6120        r#"abc
 6121           def«ˇg»hi
 6122
 6123           jk
 6124           nlmo
 6125           "#
 6126    ));
 6127
 6128    // Change selections again
 6129    cx.set_state(indoc!(
 6130        r#"a«bc
 6131           defgˇ»hi
 6132
 6133           jk
 6134           nlmo
 6135           "#
 6136    ));
 6137
 6138    cx.update_editor(|editor, window, cx| {
 6139        editor.add_selection_below(&Default::default(), window, cx);
 6140    });
 6141
 6142    cx.assert_editor_state(indoc!(
 6143        r#"a«bcˇ»
 6144           d«efgˇ»hi
 6145
 6146           j«kˇ»
 6147           nlmo
 6148           "#
 6149    ));
 6150
 6151    cx.update_editor(|editor, window, cx| {
 6152        editor.add_selection_below(&Default::default(), window, cx);
 6153    });
 6154    cx.assert_editor_state(indoc!(
 6155        r#"a«bcˇ»
 6156           d«efgˇ»hi
 6157
 6158           j«kˇ»
 6159           n«lmoˇ»
 6160           "#
 6161    ));
 6162    cx.update_editor(|editor, window, cx| {
 6163        editor.add_selection_above(&Default::default(), window, cx);
 6164    });
 6165
 6166    cx.assert_editor_state(indoc!(
 6167        r#"a«bcˇ»
 6168           d«efgˇ»hi
 6169
 6170           j«kˇ»
 6171           nlmo
 6172           "#
 6173    ));
 6174
 6175    // Change selections again
 6176    cx.set_state(indoc!(
 6177        r#"abc
 6178           d«ˇefghi
 6179
 6180           jk
 6181           nlm»o
 6182           "#
 6183    ));
 6184
 6185    cx.update_editor(|editor, window, cx| {
 6186        editor.add_selection_above(&Default::default(), window, cx);
 6187    });
 6188
 6189    cx.assert_editor_state(indoc!(
 6190        r#"a«ˇbc»
 6191           d«ˇef»ghi
 6192
 6193           j«ˇk»
 6194           n«ˇlm»o
 6195           "#
 6196    ));
 6197
 6198    cx.update_editor(|editor, window, cx| {
 6199        editor.add_selection_below(&Default::default(), window, cx);
 6200    });
 6201
 6202    cx.assert_editor_state(indoc!(
 6203        r#"abc
 6204           d«ˇef»ghi
 6205
 6206           j«ˇk»
 6207           n«ˇlm»o
 6208           "#
 6209    ));
 6210}
 6211
 6212#[gpui::test]
 6213async fn test_select_next(cx: &mut TestAppContext) {
 6214    init_test(cx, |_| {});
 6215
 6216    let mut cx = EditorTestContext::new(cx).await;
 6217    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6218
 6219    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6220        .unwrap();
 6221    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6222
 6223    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6224        .unwrap();
 6225    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6226
 6227    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6228    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6229
 6230    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6231    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6232
 6233    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6234        .unwrap();
 6235    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6236
 6237    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6238        .unwrap();
 6239    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6240
 6241    // Test selection direction should be preserved
 6242    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6243
 6244    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6245        .unwrap();
 6246    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6247}
 6248
 6249#[gpui::test]
 6250async fn test_select_all_matches(cx: &mut TestAppContext) {
 6251    init_test(cx, |_| {});
 6252
 6253    let mut cx = EditorTestContext::new(cx).await;
 6254
 6255    // Test caret-only selections
 6256    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6257    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6258        .unwrap();
 6259    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6260
 6261    // Test left-to-right selections
 6262    cx.set_state("abc\n«abcˇ»\nabc");
 6263    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6264        .unwrap();
 6265    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6266
 6267    // Test right-to-left selections
 6268    cx.set_state("abc\n«ˇabc»\nabc");
 6269    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6270        .unwrap();
 6271    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6272
 6273    // Test selecting whitespace with caret selection
 6274    cx.set_state("abc\nˇ   abc\nabc");
 6275    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6276        .unwrap();
 6277    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6278
 6279    // Test selecting whitespace with left-to-right selection
 6280    cx.set_state("abc\n«ˇ  »abc\nabc");
 6281    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6282        .unwrap();
 6283    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6284
 6285    // Test no matches with right-to-left selection
 6286    cx.set_state("abc\n«  ˇ»abc\nabc");
 6287    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6288        .unwrap();
 6289    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6290}
 6291
 6292#[gpui::test]
 6293async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6294    init_test(cx, |_| {});
 6295
 6296    let mut cx = EditorTestContext::new(cx).await;
 6297
 6298    let large_body_1 = "\nd".repeat(200);
 6299    let large_body_2 = "\ne".repeat(200);
 6300
 6301    cx.set_state(&format!(
 6302        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6303    ));
 6304    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6305        let scroll_position = editor.scroll_position(cx);
 6306        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6307        scroll_position
 6308    });
 6309
 6310    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6311        .unwrap();
 6312    cx.assert_editor_state(&format!(
 6313        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6314    ));
 6315    let scroll_position_after_selection =
 6316        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6317    assert_eq!(
 6318        initial_scroll_position, scroll_position_after_selection,
 6319        "Scroll position should not change after selecting all matches"
 6320    );
 6321}
 6322
 6323#[gpui::test]
 6324async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6325    init_test(cx, |_| {});
 6326
 6327    let mut cx = EditorLspTestContext::new_rust(
 6328        lsp::ServerCapabilities {
 6329            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6330            ..Default::default()
 6331        },
 6332        cx,
 6333    )
 6334    .await;
 6335
 6336    cx.set_state(indoc! {"
 6337        line 1
 6338        line 2
 6339        linˇe 3
 6340        line 4
 6341        line 5
 6342    "});
 6343
 6344    // Make an edit
 6345    cx.update_editor(|editor, window, cx| {
 6346        editor.handle_input("X", window, cx);
 6347    });
 6348
 6349    // Move cursor to a different position
 6350    cx.update_editor(|editor, window, cx| {
 6351        editor.change_selections(None, window, cx, |s| {
 6352            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6353        });
 6354    });
 6355
 6356    cx.assert_editor_state(indoc! {"
 6357        line 1
 6358        line 2
 6359        linXe 3
 6360        line 4
 6361        liˇne 5
 6362    "});
 6363
 6364    cx.lsp
 6365        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6366            Ok(Some(vec![lsp::TextEdit::new(
 6367                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6368                "PREFIX ".to_string(),
 6369            )]))
 6370        });
 6371
 6372    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6373        .unwrap()
 6374        .await
 6375        .unwrap();
 6376
 6377    cx.assert_editor_state(indoc! {"
 6378        PREFIX line 1
 6379        line 2
 6380        linXe 3
 6381        line 4
 6382        liˇne 5
 6383    "});
 6384
 6385    // Undo formatting
 6386    cx.update_editor(|editor, window, cx| {
 6387        editor.undo(&Default::default(), window, cx);
 6388    });
 6389
 6390    // Verify cursor moved back to position after edit
 6391    cx.assert_editor_state(indoc! {"
 6392        line 1
 6393        line 2
 6394        linXˇe 3
 6395        line 4
 6396        line 5
 6397    "});
 6398}
 6399
 6400#[gpui::test]
 6401async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6402    init_test(cx, |_| {});
 6403
 6404    let mut cx = EditorTestContext::new(cx).await;
 6405    cx.set_state(
 6406        r#"let foo = 2;
 6407lˇet foo = 2;
 6408let fooˇ = 2;
 6409let foo = 2;
 6410let foo = ˇ2;"#,
 6411    );
 6412
 6413    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6414        .unwrap();
 6415    cx.assert_editor_state(
 6416        r#"let foo = 2;
 6417«letˇ» foo = 2;
 6418let «fooˇ» = 2;
 6419let foo = 2;
 6420let foo = «2ˇ»;"#,
 6421    );
 6422
 6423    // noop for multiple selections with different contents
 6424    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6425        .unwrap();
 6426    cx.assert_editor_state(
 6427        r#"let foo = 2;
 6428«letˇ» foo = 2;
 6429let «fooˇ» = 2;
 6430let foo = 2;
 6431let foo = «2ˇ»;"#,
 6432    );
 6433
 6434    // Test last selection direction should be preserved
 6435    cx.set_state(
 6436        r#"let foo = 2;
 6437let foo = 2;
 6438let «fooˇ» = 2;
 6439let «ˇfoo» = 2;
 6440let foo = 2;"#,
 6441    );
 6442
 6443    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6444        .unwrap();
 6445    cx.assert_editor_state(
 6446        r#"let foo = 2;
 6447let foo = 2;
 6448let «fooˇ» = 2;
 6449let «ˇfoo» = 2;
 6450let «ˇfoo» = 2;"#,
 6451    );
 6452}
 6453
 6454#[gpui::test]
 6455async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6456    init_test(cx, |_| {});
 6457
 6458    let mut cx =
 6459        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6460
 6461    cx.assert_editor_state(indoc! {"
 6462        ˇbbb
 6463        ccc
 6464
 6465        bbb
 6466        ccc
 6467        "});
 6468    cx.dispatch_action(SelectPrevious::default());
 6469    cx.assert_editor_state(indoc! {"
 6470                «bbbˇ»
 6471                ccc
 6472
 6473                bbb
 6474                ccc
 6475                "});
 6476    cx.dispatch_action(SelectPrevious::default());
 6477    cx.assert_editor_state(indoc! {"
 6478                «bbbˇ»
 6479                ccc
 6480
 6481                «bbbˇ»
 6482                ccc
 6483                "});
 6484}
 6485
 6486#[gpui::test]
 6487async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6488    init_test(cx, |_| {});
 6489
 6490    let mut cx = EditorTestContext::new(cx).await;
 6491    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6492
 6493    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6494        .unwrap();
 6495    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6496
 6497    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6498        .unwrap();
 6499    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6500
 6501    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6502    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6503
 6504    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6505    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6506
 6507    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6508        .unwrap();
 6509    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6510
 6511    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6512        .unwrap();
 6513    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6514}
 6515
 6516#[gpui::test]
 6517async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6518    init_test(cx, |_| {});
 6519
 6520    let mut cx = EditorTestContext::new(cx).await;
 6521    cx.set_state("");
 6522
 6523    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6524        .unwrap();
 6525    cx.assert_editor_state("«aˇ»");
 6526    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6527        .unwrap();
 6528    cx.assert_editor_state("«aˇ»");
 6529}
 6530
 6531#[gpui::test]
 6532async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6533    init_test(cx, |_| {});
 6534
 6535    let mut cx = EditorTestContext::new(cx).await;
 6536    cx.set_state(
 6537        r#"let foo = 2;
 6538lˇet foo = 2;
 6539let fooˇ = 2;
 6540let foo = 2;
 6541let foo = ˇ2;"#,
 6542    );
 6543
 6544    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6545        .unwrap();
 6546    cx.assert_editor_state(
 6547        r#"let foo = 2;
 6548«letˇ» foo = 2;
 6549let «fooˇ» = 2;
 6550let foo = 2;
 6551let foo = «2ˇ»;"#,
 6552    );
 6553
 6554    // noop for multiple selections with different contents
 6555    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6556        .unwrap();
 6557    cx.assert_editor_state(
 6558        r#"let foo = 2;
 6559«letˇ» foo = 2;
 6560let «fooˇ» = 2;
 6561let foo = 2;
 6562let foo = «2ˇ»;"#,
 6563    );
 6564}
 6565
 6566#[gpui::test]
 6567async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6568    init_test(cx, |_| {});
 6569
 6570    let mut cx = EditorTestContext::new(cx).await;
 6571    cx.set_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    // selection direction is preserved
 6576    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6577
 6578    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6579        .unwrap();
 6580    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6581
 6582    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6583    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6584
 6585    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6586    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6587
 6588    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6589        .unwrap();
 6590    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6591
 6592    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6593        .unwrap();
 6594    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6595}
 6596
 6597#[gpui::test]
 6598async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6599    init_test(cx, |_| {});
 6600
 6601    let language = Arc::new(Language::new(
 6602        LanguageConfig::default(),
 6603        Some(tree_sitter_rust::LANGUAGE.into()),
 6604    ));
 6605
 6606    let text = r#"
 6607        use mod1::mod2::{mod3, mod4};
 6608
 6609        fn fn_1(param1: bool, param2: &str) {
 6610            let var1 = "text";
 6611        }
 6612    "#
 6613    .unindent();
 6614
 6615    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6616    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6617    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6618
 6619    editor
 6620        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6621        .await;
 6622
 6623    editor.update_in(cx, |editor, window, cx| {
 6624        editor.change_selections(None, window, cx, |s| {
 6625            s.select_display_ranges([
 6626                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6627                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6628                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6629            ]);
 6630        });
 6631        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6632    });
 6633    editor.update(cx, |editor, cx| {
 6634        assert_text_with_selections(
 6635            editor,
 6636            indoc! {r#"
 6637                use mod1::mod2::{mod3, «mod4ˇ»};
 6638
 6639                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6640                    let var1 = "«ˇtext»";
 6641                }
 6642            "#},
 6643            cx,
 6644        );
 6645    });
 6646
 6647    editor.update_in(cx, |editor, window, cx| {
 6648        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6649    });
 6650    editor.update(cx, |editor, cx| {
 6651        assert_text_with_selections(
 6652            editor,
 6653            indoc! {r#"
 6654                use mod1::mod2::«{mod3, mod4}ˇ»;
 6655
 6656                «ˇfn fn_1(param1: bool, param2: &str) {
 6657                    let var1 = "text";
 6658 6659            "#},
 6660            cx,
 6661        );
 6662    });
 6663
 6664    editor.update_in(cx, |editor, window, cx| {
 6665        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6666    });
 6667    assert_eq!(
 6668        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6669        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6670    );
 6671
 6672    // Trying to expand the selected syntax node one more time has no effect.
 6673    editor.update_in(cx, |editor, window, cx| {
 6674        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6675    });
 6676    assert_eq!(
 6677        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6678        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6679    );
 6680
 6681    editor.update_in(cx, |editor, window, cx| {
 6682        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6683    });
 6684    editor.update(cx, |editor, cx| {
 6685        assert_text_with_selections(
 6686            editor,
 6687            indoc! {r#"
 6688                use mod1::mod2::«{mod3, mod4}ˇ»;
 6689
 6690                «ˇfn fn_1(param1: bool, param2: &str) {
 6691                    let var1 = "text";
 6692 6693            "#},
 6694            cx,
 6695        );
 6696    });
 6697
 6698    editor.update_in(cx, |editor, window, cx| {
 6699        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6700    });
 6701    editor.update(cx, |editor, cx| {
 6702        assert_text_with_selections(
 6703            editor,
 6704            indoc! {r#"
 6705                use mod1::mod2::{mod3, «mod4ˇ»};
 6706
 6707                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6708                    let var1 = "«ˇtext»";
 6709                }
 6710            "#},
 6711            cx,
 6712        );
 6713    });
 6714
 6715    editor.update_in(cx, |editor, window, cx| {
 6716        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6717    });
 6718    editor.update(cx, |editor, cx| {
 6719        assert_text_with_selections(
 6720            editor,
 6721            indoc! {r#"
 6722                use mod1::mod2::{mod3, mo«ˇ»d4};
 6723
 6724                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6725                    let var1 = "te«ˇ»xt";
 6726                }
 6727            "#},
 6728            cx,
 6729        );
 6730    });
 6731
 6732    // Trying to shrink the selected syntax node one more time has no effect.
 6733    editor.update_in(cx, |editor, window, cx| {
 6734        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6735    });
 6736    editor.update_in(cx, |editor, _, cx| {
 6737        assert_text_with_selections(
 6738            editor,
 6739            indoc! {r#"
 6740                use mod1::mod2::{mod3, mo«ˇ»d4};
 6741
 6742                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6743                    let var1 = "te«ˇ»xt";
 6744                }
 6745            "#},
 6746            cx,
 6747        );
 6748    });
 6749
 6750    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6751    // a fold.
 6752    editor.update_in(cx, |editor, window, cx| {
 6753        editor.fold_creases(
 6754            vec![
 6755                Crease::simple(
 6756                    Point::new(0, 21)..Point::new(0, 24),
 6757                    FoldPlaceholder::test(),
 6758                ),
 6759                Crease::simple(
 6760                    Point::new(3, 20)..Point::new(3, 22),
 6761                    FoldPlaceholder::test(),
 6762                ),
 6763            ],
 6764            true,
 6765            window,
 6766            cx,
 6767        );
 6768        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6769    });
 6770    editor.update(cx, |editor, cx| {
 6771        assert_text_with_selections(
 6772            editor,
 6773            indoc! {r#"
 6774                use mod1::mod2::«{mod3, mod4}ˇ»;
 6775
 6776                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6777                    let var1 = "«ˇtext»";
 6778                }
 6779            "#},
 6780            cx,
 6781        );
 6782    });
 6783}
 6784
 6785#[gpui::test]
 6786async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6787    init_test(cx, |_| {});
 6788
 6789    let language = Arc::new(Language::new(
 6790        LanguageConfig::default(),
 6791        Some(tree_sitter_rust::LANGUAGE.into()),
 6792    ));
 6793
 6794    let text = "let a = 2;";
 6795
 6796    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6797    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6798    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6799
 6800    editor
 6801        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6802        .await;
 6803
 6804    // Test case 1: Cursor at end of word
 6805    editor.update_in(cx, |editor, window, cx| {
 6806        editor.change_selections(None, window, cx, |s| {
 6807            s.select_display_ranges([
 6808                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6809            ]);
 6810        });
 6811    });
 6812    editor.update(cx, |editor, cx| {
 6813        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6814    });
 6815    editor.update_in(cx, |editor, window, cx| {
 6816        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6817    });
 6818    editor.update(cx, |editor, cx| {
 6819        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6820    });
 6821    editor.update_in(cx, |editor, window, cx| {
 6822        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6823    });
 6824    editor.update(cx, |editor, cx| {
 6825        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6826    });
 6827
 6828    // Test case 2: Cursor at end of statement
 6829    editor.update_in(cx, |editor, window, cx| {
 6830        editor.change_selections(None, window, cx, |s| {
 6831            s.select_display_ranges([
 6832                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6833            ]);
 6834        });
 6835    });
 6836    editor.update(cx, |editor, cx| {
 6837        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6838    });
 6839    editor.update_in(cx, |editor, window, cx| {
 6840        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6841    });
 6842    editor.update(cx, |editor, cx| {
 6843        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6844    });
 6845}
 6846
 6847#[gpui::test]
 6848async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6849    init_test(cx, |_| {});
 6850
 6851    let language = Arc::new(Language::new(
 6852        LanguageConfig::default(),
 6853        Some(tree_sitter_rust::LANGUAGE.into()),
 6854    ));
 6855
 6856    let text = r#"
 6857        use mod1::mod2::{mod3, mod4};
 6858
 6859        fn fn_1(param1: bool, param2: &str) {
 6860            let var1 = "hello world";
 6861        }
 6862    "#
 6863    .unindent();
 6864
 6865    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6866    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6867    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6868
 6869    editor
 6870        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6871        .await;
 6872
 6873    // Test 1: Cursor on a letter of a string word
 6874    editor.update_in(cx, |editor, window, cx| {
 6875        editor.change_selections(None, window, cx, |s| {
 6876            s.select_display_ranges([
 6877                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6878            ]);
 6879        });
 6880    });
 6881    editor.update_in(cx, |editor, window, cx| {
 6882        assert_text_with_selections(
 6883            editor,
 6884            indoc! {r#"
 6885                use mod1::mod2::{mod3, mod4};
 6886
 6887                fn fn_1(param1: bool, param2: &str) {
 6888                    let var1 = "hˇello world";
 6889                }
 6890            "#},
 6891            cx,
 6892        );
 6893        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6894        assert_text_with_selections(
 6895            editor,
 6896            indoc! {r#"
 6897                use mod1::mod2::{mod3, mod4};
 6898
 6899                fn fn_1(param1: bool, param2: &str) {
 6900                    let var1 = "«ˇhello» world";
 6901                }
 6902            "#},
 6903            cx,
 6904        );
 6905    });
 6906
 6907    // Test 2: Partial selection within a word
 6908    editor.update_in(cx, |editor, window, cx| {
 6909        editor.change_selections(None, window, cx, |s| {
 6910            s.select_display_ranges([
 6911                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6912            ]);
 6913        });
 6914    });
 6915    editor.update_in(cx, |editor, window, cx| {
 6916        assert_text_with_selections(
 6917            editor,
 6918            indoc! {r#"
 6919                use mod1::mod2::{mod3, mod4};
 6920
 6921                fn fn_1(param1: bool, param2: &str) {
 6922                    let var1 = "h«elˇ»lo world";
 6923                }
 6924            "#},
 6925            cx,
 6926        );
 6927        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6928        assert_text_with_selections(
 6929            editor,
 6930            indoc! {r#"
 6931                use mod1::mod2::{mod3, mod4};
 6932
 6933                fn fn_1(param1: bool, param2: &str) {
 6934                    let var1 = "«ˇhello» world";
 6935                }
 6936            "#},
 6937            cx,
 6938        );
 6939    });
 6940
 6941    // Test 3: Complete word already selected
 6942    editor.update_in(cx, |editor, window, cx| {
 6943        editor.change_selections(None, window, cx, |s| {
 6944            s.select_display_ranges([
 6945                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6946            ]);
 6947        });
 6948    });
 6949    editor.update_in(cx, |editor, window, cx| {
 6950        assert_text_with_selections(
 6951            editor,
 6952            indoc! {r#"
 6953                use mod1::mod2::{mod3, mod4};
 6954
 6955                fn fn_1(param1: bool, param2: &str) {
 6956                    let var1 = "«helloˇ» world";
 6957                }
 6958            "#},
 6959            cx,
 6960        );
 6961        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6962        assert_text_with_selections(
 6963            editor,
 6964            indoc! {r#"
 6965                use mod1::mod2::{mod3, mod4};
 6966
 6967                fn fn_1(param1: bool, param2: &str) {
 6968                    let var1 = "«hello worldˇ»";
 6969                }
 6970            "#},
 6971            cx,
 6972        );
 6973    });
 6974
 6975    // Test 4: Selection spanning across words
 6976    editor.update_in(cx, |editor, window, cx| {
 6977        editor.change_selections(None, window, cx, |s| {
 6978            s.select_display_ranges([
 6979                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6980            ]);
 6981        });
 6982    });
 6983    editor.update_in(cx, |editor, window, cx| {
 6984        assert_text_with_selections(
 6985            editor,
 6986            indoc! {r#"
 6987                use mod1::mod2::{mod3, mod4};
 6988
 6989                fn fn_1(param1: bool, param2: &str) {
 6990                    let var1 = "hel«lo woˇ»rld";
 6991                }
 6992            "#},
 6993            cx,
 6994        );
 6995        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6996        assert_text_with_selections(
 6997            editor,
 6998            indoc! {r#"
 6999                use mod1::mod2::{mod3, mod4};
 7000
 7001                fn fn_1(param1: bool, param2: &str) {
 7002                    let var1 = "«ˇhello world»";
 7003                }
 7004            "#},
 7005            cx,
 7006        );
 7007    });
 7008
 7009    // Test 5: Expansion beyond string
 7010    editor.update_in(cx, |editor, window, cx| {
 7011        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7012        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 7013        assert_text_with_selections(
 7014            editor,
 7015            indoc! {r#"
 7016                use mod1::mod2::{mod3, mod4};
 7017
 7018                fn fn_1(param1: bool, param2: &str) {
 7019                    «ˇlet var1 = "hello world";»
 7020                }
 7021            "#},
 7022            cx,
 7023        );
 7024    });
 7025}
 7026
 7027#[gpui::test]
 7028async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 7029    init_test(cx, |_| {});
 7030
 7031    let base_text = r#"
 7032        impl A {
 7033            // this is an uncommitted comment
 7034
 7035            fn b() {
 7036                c();
 7037            }
 7038
 7039            // this is another uncommitted comment
 7040
 7041            fn d() {
 7042                // e
 7043                // f
 7044            }
 7045        }
 7046
 7047        fn g() {
 7048            // h
 7049        }
 7050    "#
 7051    .unindent();
 7052
 7053    let text = r#"
 7054        ˇimpl A {
 7055
 7056            fn b() {
 7057                c();
 7058            }
 7059
 7060            fn d() {
 7061                // e
 7062                // f
 7063            }
 7064        }
 7065
 7066        fn g() {
 7067            // h
 7068        }
 7069    "#
 7070    .unindent();
 7071
 7072    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7073    cx.set_state(&text);
 7074    cx.set_head_text(&base_text);
 7075    cx.update_editor(|editor, window, cx| {
 7076        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7077    });
 7078
 7079    cx.assert_state_with_diff(
 7080        "
 7081        ˇimpl A {
 7082      -     // this is an uncommitted comment
 7083
 7084            fn b() {
 7085                c();
 7086            }
 7087
 7088      -     // this is another uncommitted comment
 7089      -
 7090            fn d() {
 7091                // e
 7092                // f
 7093            }
 7094        }
 7095
 7096        fn g() {
 7097            // h
 7098        }
 7099    "
 7100        .unindent(),
 7101    );
 7102
 7103    let expected_display_text = "
 7104        impl A {
 7105            // this is an uncommitted comment
 7106
 7107            fn b() {
 7108 7109            }
 7110
 7111            // this is another uncommitted comment
 7112
 7113            fn d() {
 7114 7115            }
 7116        }
 7117
 7118        fn g() {
 7119 7120        }
 7121        "
 7122    .unindent();
 7123
 7124    cx.update_editor(|editor, window, cx| {
 7125        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7126        assert_eq!(editor.display_text(cx), expected_display_text);
 7127    });
 7128}
 7129
 7130#[gpui::test]
 7131async fn test_autoindent(cx: &mut TestAppContext) {
 7132    init_test(cx, |_| {});
 7133
 7134    let language = Arc::new(
 7135        Language::new(
 7136            LanguageConfig {
 7137                brackets: BracketPairConfig {
 7138                    pairs: vec![
 7139                        BracketPair {
 7140                            start: "{".to_string(),
 7141                            end: "}".to_string(),
 7142                            close: false,
 7143                            surround: false,
 7144                            newline: true,
 7145                        },
 7146                        BracketPair {
 7147                            start: "(".to_string(),
 7148                            end: ")".to_string(),
 7149                            close: false,
 7150                            surround: false,
 7151                            newline: true,
 7152                        },
 7153                    ],
 7154                    ..Default::default()
 7155                },
 7156                ..Default::default()
 7157            },
 7158            Some(tree_sitter_rust::LANGUAGE.into()),
 7159        )
 7160        .with_indents_query(
 7161            r#"
 7162                (_ "(" ")" @end) @indent
 7163                (_ "{" "}" @end) @indent
 7164            "#,
 7165        )
 7166        .unwrap(),
 7167    );
 7168
 7169    let text = "fn a() {}";
 7170
 7171    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7172    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7173    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7174    editor
 7175        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7176        .await;
 7177
 7178    editor.update_in(cx, |editor, window, cx| {
 7179        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7180        editor.newline(&Newline, window, cx);
 7181        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7182        assert_eq!(
 7183            editor.selections.ranges(cx),
 7184            &[
 7185                Point::new(1, 4)..Point::new(1, 4),
 7186                Point::new(3, 4)..Point::new(3, 4),
 7187                Point::new(5, 0)..Point::new(5, 0)
 7188            ]
 7189        );
 7190    });
 7191}
 7192
 7193#[gpui::test]
 7194async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7195    init_test(cx, |_| {});
 7196
 7197    {
 7198        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7199        cx.set_state(indoc! {"
 7200            impl A {
 7201
 7202                fn b() {}
 7203
 7204            «fn c() {
 7205
 7206            }ˇ»
 7207            }
 7208        "});
 7209
 7210        cx.update_editor(|editor, window, cx| {
 7211            editor.autoindent(&Default::default(), window, cx);
 7212        });
 7213
 7214        cx.assert_editor_state(indoc! {"
 7215            impl A {
 7216
 7217                fn b() {}
 7218
 7219                «fn c() {
 7220
 7221                }ˇ»
 7222            }
 7223        "});
 7224    }
 7225
 7226    {
 7227        let mut cx = EditorTestContext::new_multibuffer(
 7228            cx,
 7229            [indoc! { "
 7230                impl A {
 7231                «
 7232                // a
 7233                fn b(){}
 7234                »
 7235                «
 7236                    }
 7237                    fn c(){}
 7238                »
 7239            "}],
 7240        );
 7241
 7242        let buffer = cx.update_editor(|editor, _, cx| {
 7243            let buffer = editor.buffer().update(cx, |buffer, _| {
 7244                buffer.all_buffers().iter().next().unwrap().clone()
 7245            });
 7246            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7247            buffer
 7248        });
 7249
 7250        cx.run_until_parked();
 7251        cx.update_editor(|editor, window, cx| {
 7252            editor.select_all(&Default::default(), window, cx);
 7253            editor.autoindent(&Default::default(), window, cx)
 7254        });
 7255        cx.run_until_parked();
 7256
 7257        cx.update(|_, cx| {
 7258            assert_eq!(
 7259                buffer.read(cx).text(),
 7260                indoc! { "
 7261                    impl A {
 7262
 7263                        // a
 7264                        fn b(){}
 7265
 7266
 7267                    }
 7268                    fn c(){}
 7269
 7270                " }
 7271            )
 7272        });
 7273    }
 7274}
 7275
 7276#[gpui::test]
 7277async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7278    init_test(cx, |_| {});
 7279
 7280    let mut cx = EditorTestContext::new(cx).await;
 7281
 7282    let language = Arc::new(Language::new(
 7283        LanguageConfig {
 7284            brackets: BracketPairConfig {
 7285                pairs: vec![
 7286                    BracketPair {
 7287                        start: "{".to_string(),
 7288                        end: "}".to_string(),
 7289                        close: true,
 7290                        surround: true,
 7291                        newline: true,
 7292                    },
 7293                    BracketPair {
 7294                        start: "(".to_string(),
 7295                        end: ")".to_string(),
 7296                        close: true,
 7297                        surround: true,
 7298                        newline: true,
 7299                    },
 7300                    BracketPair {
 7301                        start: "/*".to_string(),
 7302                        end: " */".to_string(),
 7303                        close: true,
 7304                        surround: true,
 7305                        newline: true,
 7306                    },
 7307                    BracketPair {
 7308                        start: "[".to_string(),
 7309                        end: "]".to_string(),
 7310                        close: false,
 7311                        surround: false,
 7312                        newline: true,
 7313                    },
 7314                    BracketPair {
 7315                        start: "\"".to_string(),
 7316                        end: "\"".to_string(),
 7317                        close: true,
 7318                        surround: true,
 7319                        newline: false,
 7320                    },
 7321                    BracketPair {
 7322                        start: "<".to_string(),
 7323                        end: ">".to_string(),
 7324                        close: false,
 7325                        surround: true,
 7326                        newline: true,
 7327                    },
 7328                ],
 7329                ..Default::default()
 7330            },
 7331            autoclose_before: "})]".to_string(),
 7332            ..Default::default()
 7333        },
 7334        Some(tree_sitter_rust::LANGUAGE.into()),
 7335    ));
 7336
 7337    cx.language_registry().add(language.clone());
 7338    cx.update_buffer(|buffer, cx| {
 7339        buffer.set_language(Some(language), cx);
 7340    });
 7341
 7342    cx.set_state(
 7343        &r#"
 7344            🏀ˇ
 7345            εˇ
 7346            ❤️ˇ
 7347        "#
 7348        .unindent(),
 7349    );
 7350
 7351    // autoclose multiple nested brackets at multiple cursors
 7352    cx.update_editor(|editor, window, cx| {
 7353        editor.handle_input("{", window, cx);
 7354        editor.handle_input("{", window, cx);
 7355        editor.handle_input("{", window, cx);
 7356    });
 7357    cx.assert_editor_state(
 7358        &"
 7359            🏀{{{ˇ}}}
 7360            ε{{{ˇ}}}
 7361            ❤️{{{ˇ}}}
 7362        "
 7363        .unindent(),
 7364    );
 7365
 7366    // insert a different closing bracket
 7367    cx.update_editor(|editor, window, cx| {
 7368        editor.handle_input(")", window, cx);
 7369    });
 7370    cx.assert_editor_state(
 7371        &"
 7372            🏀{{{)ˇ}}}
 7373            ε{{{)ˇ}}}
 7374            ❤️{{{)ˇ}}}
 7375        "
 7376        .unindent(),
 7377    );
 7378
 7379    // skip over the auto-closed brackets when typing a closing bracket
 7380    cx.update_editor(|editor, window, cx| {
 7381        editor.move_right(&MoveRight, window, cx);
 7382        editor.handle_input("}", window, cx);
 7383        editor.handle_input("}", window, cx);
 7384        editor.handle_input("}", window, cx);
 7385    });
 7386    cx.assert_editor_state(
 7387        &"
 7388            🏀{{{)}}}}ˇ
 7389            ε{{{)}}}}ˇ
 7390            ❤️{{{)}}}}ˇ
 7391        "
 7392        .unindent(),
 7393    );
 7394
 7395    // autoclose multi-character pairs
 7396    cx.set_state(
 7397        &"
 7398            ˇ
 7399            ˇ
 7400        "
 7401        .unindent(),
 7402    );
 7403    cx.update_editor(|editor, window, cx| {
 7404        editor.handle_input("/", window, cx);
 7405        editor.handle_input("*", window, cx);
 7406    });
 7407    cx.assert_editor_state(
 7408        &"
 7409            /*ˇ */
 7410            /*ˇ */
 7411        "
 7412        .unindent(),
 7413    );
 7414
 7415    // one cursor autocloses a multi-character pair, one cursor
 7416    // does not autoclose.
 7417    cx.set_state(
 7418        &"
 7419 7420            ˇ
 7421        "
 7422        .unindent(),
 7423    );
 7424    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7425    cx.assert_editor_state(
 7426        &"
 7427            /*ˇ */
 7428 7429        "
 7430        .unindent(),
 7431    );
 7432
 7433    // Don't autoclose if the next character isn't whitespace and isn't
 7434    // listed in the language's "autoclose_before" section.
 7435    cx.set_state("ˇa b");
 7436    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7437    cx.assert_editor_state("{ˇa b");
 7438
 7439    // Don't autoclose if `close` is false for the bracket pair
 7440    cx.set_state("ˇ");
 7441    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7442    cx.assert_editor_state("");
 7443
 7444    // Surround with brackets if text is selected
 7445    cx.set_state("«aˇ» b");
 7446    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7447    cx.assert_editor_state("{«aˇ»} b");
 7448
 7449    // Autoclose when not immediately after a word character
 7450    cx.set_state("a ˇ");
 7451    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7452    cx.assert_editor_state("a \"ˇ\"");
 7453
 7454    // Autoclose pair where the start and end characters are the same
 7455    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7456    cx.assert_editor_state("a \"\"ˇ");
 7457
 7458    // Don't autoclose when immediately after a word character
 7459    cx.set_state("");
 7460    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7461    cx.assert_editor_state("a\"ˇ");
 7462
 7463    // Do autoclose when after a non-word character
 7464    cx.set_state("");
 7465    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7466    cx.assert_editor_state("{\"ˇ\"");
 7467
 7468    // Non identical pairs autoclose regardless of preceding character
 7469    cx.set_state("");
 7470    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7471    cx.assert_editor_state("a{ˇ}");
 7472
 7473    // Don't autoclose pair if autoclose is disabled
 7474    cx.set_state("ˇ");
 7475    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7476    cx.assert_editor_state("");
 7477
 7478    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7479    cx.set_state("«aˇ» b");
 7480    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7481    cx.assert_editor_state("<«aˇ»> b");
 7482}
 7483
 7484#[gpui::test]
 7485async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7486    init_test(cx, |settings| {
 7487        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7488    });
 7489
 7490    let mut cx = EditorTestContext::new(cx).await;
 7491
 7492    let language = Arc::new(Language::new(
 7493        LanguageConfig {
 7494            brackets: BracketPairConfig {
 7495                pairs: vec![
 7496                    BracketPair {
 7497                        start: "{".to_string(),
 7498                        end: "}".to_string(),
 7499                        close: true,
 7500                        surround: true,
 7501                        newline: true,
 7502                    },
 7503                    BracketPair {
 7504                        start: "(".to_string(),
 7505                        end: ")".to_string(),
 7506                        close: true,
 7507                        surround: true,
 7508                        newline: true,
 7509                    },
 7510                    BracketPair {
 7511                        start: "[".to_string(),
 7512                        end: "]".to_string(),
 7513                        close: false,
 7514                        surround: false,
 7515                        newline: true,
 7516                    },
 7517                ],
 7518                ..Default::default()
 7519            },
 7520            autoclose_before: "})]".to_string(),
 7521            ..Default::default()
 7522        },
 7523        Some(tree_sitter_rust::LANGUAGE.into()),
 7524    ));
 7525
 7526    cx.language_registry().add(language.clone());
 7527    cx.update_buffer(|buffer, cx| {
 7528        buffer.set_language(Some(language), cx);
 7529    });
 7530
 7531    cx.set_state(
 7532        &"
 7533            ˇ
 7534            ˇ
 7535            ˇ
 7536        "
 7537        .unindent(),
 7538    );
 7539
 7540    // ensure only matching closing brackets are skipped over
 7541    cx.update_editor(|editor, window, cx| {
 7542        editor.handle_input("}", window, cx);
 7543        editor.move_left(&MoveLeft, window, cx);
 7544        editor.handle_input(")", window, cx);
 7545        editor.move_left(&MoveLeft, window, cx);
 7546    });
 7547    cx.assert_editor_state(
 7548        &"
 7549            ˇ)}
 7550            ˇ)}
 7551            ˇ)}
 7552        "
 7553        .unindent(),
 7554    );
 7555
 7556    // skip-over closing brackets at multiple cursors
 7557    cx.update_editor(|editor, window, cx| {
 7558        editor.handle_input(")", window, cx);
 7559        editor.handle_input("}", window, cx);
 7560    });
 7561    cx.assert_editor_state(
 7562        &"
 7563            )}ˇ
 7564            )}ˇ
 7565            )}ˇ
 7566        "
 7567        .unindent(),
 7568    );
 7569
 7570    // ignore non-close brackets
 7571    cx.update_editor(|editor, window, cx| {
 7572        editor.handle_input("]", window, cx);
 7573        editor.move_left(&MoveLeft, window, cx);
 7574        editor.handle_input("]", window, cx);
 7575    });
 7576    cx.assert_editor_state(
 7577        &"
 7578            )}]ˇ]
 7579            )}]ˇ]
 7580            )}]ˇ]
 7581        "
 7582        .unindent(),
 7583    );
 7584}
 7585
 7586#[gpui::test]
 7587async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7588    init_test(cx, |_| {});
 7589
 7590    let mut cx = EditorTestContext::new(cx).await;
 7591
 7592    let html_language = Arc::new(
 7593        Language::new(
 7594            LanguageConfig {
 7595                name: "HTML".into(),
 7596                brackets: BracketPairConfig {
 7597                    pairs: vec![
 7598                        BracketPair {
 7599                            start: "<".into(),
 7600                            end: ">".into(),
 7601                            close: true,
 7602                            ..Default::default()
 7603                        },
 7604                        BracketPair {
 7605                            start: "{".into(),
 7606                            end: "}".into(),
 7607                            close: true,
 7608                            ..Default::default()
 7609                        },
 7610                        BracketPair {
 7611                            start: "(".into(),
 7612                            end: ")".into(),
 7613                            close: true,
 7614                            ..Default::default()
 7615                        },
 7616                    ],
 7617                    ..Default::default()
 7618                },
 7619                autoclose_before: "})]>".into(),
 7620                ..Default::default()
 7621            },
 7622            Some(tree_sitter_html::LANGUAGE.into()),
 7623        )
 7624        .with_injection_query(
 7625            r#"
 7626            (script_element
 7627                (raw_text) @injection.content
 7628                (#set! injection.language "javascript"))
 7629            "#,
 7630        )
 7631        .unwrap(),
 7632    );
 7633
 7634    let javascript_language = Arc::new(Language::new(
 7635        LanguageConfig {
 7636            name: "JavaScript".into(),
 7637            brackets: BracketPairConfig {
 7638                pairs: vec![
 7639                    BracketPair {
 7640                        start: "/*".into(),
 7641                        end: " */".into(),
 7642                        close: true,
 7643                        ..Default::default()
 7644                    },
 7645                    BracketPair {
 7646                        start: "{".into(),
 7647                        end: "}".into(),
 7648                        close: true,
 7649                        ..Default::default()
 7650                    },
 7651                    BracketPair {
 7652                        start: "(".into(),
 7653                        end: ")".into(),
 7654                        close: true,
 7655                        ..Default::default()
 7656                    },
 7657                ],
 7658                ..Default::default()
 7659            },
 7660            autoclose_before: "})]>".into(),
 7661            ..Default::default()
 7662        },
 7663        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7664    ));
 7665
 7666    cx.language_registry().add(html_language.clone());
 7667    cx.language_registry().add(javascript_language.clone());
 7668
 7669    cx.update_buffer(|buffer, cx| {
 7670        buffer.set_language(Some(html_language), cx);
 7671    });
 7672
 7673    cx.set_state(
 7674        &r#"
 7675            <body>ˇ
 7676                <script>
 7677                    var x = 1;ˇ
 7678                </script>
 7679            </body>ˇ
 7680        "#
 7681        .unindent(),
 7682    );
 7683
 7684    // Precondition: different languages are active at different locations.
 7685    cx.update_editor(|editor, window, cx| {
 7686        let snapshot = editor.snapshot(window, cx);
 7687        let cursors = editor.selections.ranges::<usize>(cx);
 7688        let languages = cursors
 7689            .iter()
 7690            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7691            .collect::<Vec<_>>();
 7692        assert_eq!(
 7693            languages,
 7694            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7695        );
 7696    });
 7697
 7698    // Angle brackets autoclose in HTML, but not JavaScript.
 7699    cx.update_editor(|editor, window, cx| {
 7700        editor.handle_input("<", window, cx);
 7701        editor.handle_input("a", window, cx);
 7702    });
 7703    cx.assert_editor_state(
 7704        &r#"
 7705            <body><aˇ>
 7706                <script>
 7707                    var x = 1;<aˇ
 7708                </script>
 7709            </body><aˇ>
 7710        "#
 7711        .unindent(),
 7712    );
 7713
 7714    // Curly braces and parens autoclose in both HTML and JavaScript.
 7715    cx.update_editor(|editor, window, cx| {
 7716        editor.handle_input(" b=", window, cx);
 7717        editor.handle_input("{", window, cx);
 7718        editor.handle_input("c", window, cx);
 7719        editor.handle_input("(", window, cx);
 7720    });
 7721    cx.assert_editor_state(
 7722        &r#"
 7723            <body><a b={c(ˇ)}>
 7724                <script>
 7725                    var x = 1;<a b={c(ˇ)}
 7726                </script>
 7727            </body><a b={c(ˇ)}>
 7728        "#
 7729        .unindent(),
 7730    );
 7731
 7732    // Brackets that were already autoclosed are skipped.
 7733    cx.update_editor(|editor, window, cx| {
 7734        editor.handle_input(")", window, cx);
 7735        editor.handle_input("d", window, cx);
 7736        editor.handle_input("}", window, cx);
 7737    });
 7738    cx.assert_editor_state(
 7739        &r#"
 7740            <body><a b={c()d}ˇ>
 7741                <script>
 7742                    var x = 1;<a b={c()d}ˇ
 7743                </script>
 7744            </body><a b={c()d}ˇ>
 7745        "#
 7746        .unindent(),
 7747    );
 7748    cx.update_editor(|editor, window, cx| {
 7749        editor.handle_input(">", window, cx);
 7750    });
 7751    cx.assert_editor_state(
 7752        &r#"
 7753            <body><a b={c()d}>ˇ
 7754                <script>
 7755                    var x = 1;<a b={c()d}>ˇ
 7756                </script>
 7757            </body><a b={c()d}>ˇ
 7758        "#
 7759        .unindent(),
 7760    );
 7761
 7762    // Reset
 7763    cx.set_state(
 7764        &r#"
 7765            <body>ˇ
 7766                <script>
 7767                    var x = 1;ˇ
 7768                </script>
 7769            </body>ˇ
 7770        "#
 7771        .unindent(),
 7772    );
 7773
 7774    cx.update_editor(|editor, window, cx| {
 7775        editor.handle_input("<", window, cx);
 7776    });
 7777    cx.assert_editor_state(
 7778        &r#"
 7779            <body><ˇ>
 7780                <script>
 7781                    var x = 1;<ˇ
 7782                </script>
 7783            </body><ˇ>
 7784        "#
 7785        .unindent(),
 7786    );
 7787
 7788    // When backspacing, the closing angle brackets are removed.
 7789    cx.update_editor(|editor, window, cx| {
 7790        editor.backspace(&Backspace, window, cx);
 7791    });
 7792    cx.assert_editor_state(
 7793        &r#"
 7794            <body>ˇ
 7795                <script>
 7796                    var x = 1;ˇ
 7797                </script>
 7798            </body>ˇ
 7799        "#
 7800        .unindent(),
 7801    );
 7802
 7803    // Block comments autoclose in JavaScript, but not HTML.
 7804    cx.update_editor(|editor, window, cx| {
 7805        editor.handle_input("/", window, cx);
 7806        editor.handle_input("*", window, cx);
 7807    });
 7808    cx.assert_editor_state(
 7809        &r#"
 7810            <body>/*ˇ
 7811                <script>
 7812                    var x = 1;/*ˇ */
 7813                </script>
 7814            </body>/*ˇ
 7815        "#
 7816        .unindent(),
 7817    );
 7818}
 7819
 7820#[gpui::test]
 7821async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7822    init_test(cx, |_| {});
 7823
 7824    let mut cx = EditorTestContext::new(cx).await;
 7825
 7826    let rust_language = Arc::new(
 7827        Language::new(
 7828            LanguageConfig {
 7829                name: "Rust".into(),
 7830                brackets: serde_json::from_value(json!([
 7831                    { "start": "{", "end": "}", "close": true, "newline": true },
 7832                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7833                ]))
 7834                .unwrap(),
 7835                autoclose_before: "})]>".into(),
 7836                ..Default::default()
 7837            },
 7838            Some(tree_sitter_rust::LANGUAGE.into()),
 7839        )
 7840        .with_override_query("(string_literal) @string")
 7841        .unwrap(),
 7842    );
 7843
 7844    cx.language_registry().add(rust_language.clone());
 7845    cx.update_buffer(|buffer, cx| {
 7846        buffer.set_language(Some(rust_language), cx);
 7847    });
 7848
 7849    cx.set_state(
 7850        &r#"
 7851            let x = ˇ
 7852        "#
 7853        .unindent(),
 7854    );
 7855
 7856    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7857    cx.update_editor(|editor, window, cx| {
 7858        editor.handle_input("\"", window, cx);
 7859    });
 7860    cx.assert_editor_state(
 7861        &r#"
 7862            let x = "ˇ"
 7863        "#
 7864        .unindent(),
 7865    );
 7866
 7867    // Inserting another quotation mark. The cursor moves across the existing
 7868    // automatically-inserted quotation mark.
 7869    cx.update_editor(|editor, window, cx| {
 7870        editor.handle_input("\"", window, cx);
 7871    });
 7872    cx.assert_editor_state(
 7873        &r#"
 7874            let x = ""ˇ
 7875        "#
 7876        .unindent(),
 7877    );
 7878
 7879    // Reset
 7880    cx.set_state(
 7881        &r#"
 7882            let x = ˇ
 7883        "#
 7884        .unindent(),
 7885    );
 7886
 7887    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7888    cx.update_editor(|editor, window, cx| {
 7889        editor.handle_input("\"", window, cx);
 7890        editor.handle_input(" ", window, cx);
 7891        editor.move_left(&Default::default(), window, cx);
 7892        editor.handle_input("\\", window, cx);
 7893        editor.handle_input("\"", window, cx);
 7894    });
 7895    cx.assert_editor_state(
 7896        &r#"
 7897            let x = "\"ˇ "
 7898        "#
 7899        .unindent(),
 7900    );
 7901
 7902    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7903    // mark. Nothing is inserted.
 7904    cx.update_editor(|editor, window, cx| {
 7905        editor.move_right(&Default::default(), window, cx);
 7906        editor.handle_input("\"", window, cx);
 7907    });
 7908    cx.assert_editor_state(
 7909        &r#"
 7910            let x = "\" "ˇ
 7911        "#
 7912        .unindent(),
 7913    );
 7914}
 7915
 7916#[gpui::test]
 7917async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7918    init_test(cx, |_| {});
 7919
 7920    let language = Arc::new(Language::new(
 7921        LanguageConfig {
 7922            brackets: BracketPairConfig {
 7923                pairs: vec![
 7924                    BracketPair {
 7925                        start: "{".to_string(),
 7926                        end: "}".to_string(),
 7927                        close: true,
 7928                        surround: true,
 7929                        newline: true,
 7930                    },
 7931                    BracketPair {
 7932                        start: "/* ".to_string(),
 7933                        end: "*/".to_string(),
 7934                        close: true,
 7935                        surround: true,
 7936                        ..Default::default()
 7937                    },
 7938                ],
 7939                ..Default::default()
 7940            },
 7941            ..Default::default()
 7942        },
 7943        Some(tree_sitter_rust::LANGUAGE.into()),
 7944    ));
 7945
 7946    let text = r#"
 7947        a
 7948        b
 7949        c
 7950    "#
 7951    .unindent();
 7952
 7953    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7954    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7955    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7956    editor
 7957        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7958        .await;
 7959
 7960    editor.update_in(cx, |editor, window, cx| {
 7961        editor.change_selections(None, window, cx, |s| {
 7962            s.select_display_ranges([
 7963                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7964                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7965                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7966            ])
 7967        });
 7968
 7969        editor.handle_input("{", window, cx);
 7970        editor.handle_input("{", window, cx);
 7971        editor.handle_input("{", window, cx);
 7972        assert_eq!(
 7973            editor.text(cx),
 7974            "
 7975                {{{a}}}
 7976                {{{b}}}
 7977                {{{c}}}
 7978            "
 7979            .unindent()
 7980        );
 7981        assert_eq!(
 7982            editor.selections.display_ranges(cx),
 7983            [
 7984                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7985                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7986                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7987            ]
 7988        );
 7989
 7990        editor.undo(&Undo, window, cx);
 7991        editor.undo(&Undo, window, cx);
 7992        editor.undo(&Undo, window, cx);
 7993        assert_eq!(
 7994            editor.text(cx),
 7995            "
 7996                a
 7997                b
 7998                c
 7999            "
 8000            .unindent()
 8001        );
 8002        assert_eq!(
 8003            editor.selections.display_ranges(cx),
 8004            [
 8005                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8006                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8007                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8008            ]
 8009        );
 8010
 8011        // Ensure inserting the first character of a multi-byte bracket pair
 8012        // doesn't surround the selections with the bracket.
 8013        editor.handle_input("/", window, cx);
 8014        assert_eq!(
 8015            editor.text(cx),
 8016            "
 8017                /
 8018                /
 8019                /
 8020            "
 8021            .unindent()
 8022        );
 8023        assert_eq!(
 8024            editor.selections.display_ranges(cx),
 8025            [
 8026                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8027                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8028                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8029            ]
 8030        );
 8031
 8032        editor.undo(&Undo, window, cx);
 8033        assert_eq!(
 8034            editor.text(cx),
 8035            "
 8036                a
 8037                b
 8038                c
 8039            "
 8040            .unindent()
 8041        );
 8042        assert_eq!(
 8043            editor.selections.display_ranges(cx),
 8044            [
 8045                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 8046                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 8047                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 8048            ]
 8049        );
 8050
 8051        // Ensure inserting the last character of a multi-byte bracket pair
 8052        // doesn't surround the selections with the bracket.
 8053        editor.handle_input("*", window, cx);
 8054        assert_eq!(
 8055            editor.text(cx),
 8056            "
 8057                *
 8058                *
 8059                *
 8060            "
 8061            .unindent()
 8062        );
 8063        assert_eq!(
 8064            editor.selections.display_ranges(cx),
 8065            [
 8066                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8067                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8068                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8069            ]
 8070        );
 8071    });
 8072}
 8073
 8074#[gpui::test]
 8075async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8076    init_test(cx, |_| {});
 8077
 8078    let language = Arc::new(Language::new(
 8079        LanguageConfig {
 8080            brackets: BracketPairConfig {
 8081                pairs: vec![BracketPair {
 8082                    start: "{".to_string(),
 8083                    end: "}".to_string(),
 8084                    close: true,
 8085                    surround: true,
 8086                    newline: true,
 8087                }],
 8088                ..Default::default()
 8089            },
 8090            autoclose_before: "}".to_string(),
 8091            ..Default::default()
 8092        },
 8093        Some(tree_sitter_rust::LANGUAGE.into()),
 8094    ));
 8095
 8096    let text = r#"
 8097        a
 8098        b
 8099        c
 8100    "#
 8101    .unindent();
 8102
 8103    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8104    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8105    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8106    editor
 8107        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8108        .await;
 8109
 8110    editor.update_in(cx, |editor, window, cx| {
 8111        editor.change_selections(None, window, cx, |s| {
 8112            s.select_ranges([
 8113                Point::new(0, 1)..Point::new(0, 1),
 8114                Point::new(1, 1)..Point::new(1, 1),
 8115                Point::new(2, 1)..Point::new(2, 1),
 8116            ])
 8117        });
 8118
 8119        editor.handle_input("{", window, cx);
 8120        editor.handle_input("{", window, cx);
 8121        editor.handle_input("_", window, cx);
 8122        assert_eq!(
 8123            editor.text(cx),
 8124            "
 8125                a{{_}}
 8126                b{{_}}
 8127                c{{_}}
 8128            "
 8129            .unindent()
 8130        );
 8131        assert_eq!(
 8132            editor.selections.ranges::<Point>(cx),
 8133            [
 8134                Point::new(0, 4)..Point::new(0, 4),
 8135                Point::new(1, 4)..Point::new(1, 4),
 8136                Point::new(2, 4)..Point::new(2, 4)
 8137            ]
 8138        );
 8139
 8140        editor.backspace(&Default::default(), window, cx);
 8141        editor.backspace(&Default::default(), window, cx);
 8142        assert_eq!(
 8143            editor.text(cx),
 8144            "
 8145                a{}
 8146                b{}
 8147                c{}
 8148            "
 8149            .unindent()
 8150        );
 8151        assert_eq!(
 8152            editor.selections.ranges::<Point>(cx),
 8153            [
 8154                Point::new(0, 2)..Point::new(0, 2),
 8155                Point::new(1, 2)..Point::new(1, 2),
 8156                Point::new(2, 2)..Point::new(2, 2)
 8157            ]
 8158        );
 8159
 8160        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8161        assert_eq!(
 8162            editor.text(cx),
 8163            "
 8164                a
 8165                b
 8166                c
 8167            "
 8168            .unindent()
 8169        );
 8170        assert_eq!(
 8171            editor.selections.ranges::<Point>(cx),
 8172            [
 8173                Point::new(0, 1)..Point::new(0, 1),
 8174                Point::new(1, 1)..Point::new(1, 1),
 8175                Point::new(2, 1)..Point::new(2, 1)
 8176            ]
 8177        );
 8178    });
 8179}
 8180
 8181#[gpui::test]
 8182async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8183    init_test(cx, |settings| {
 8184        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8185    });
 8186
 8187    let mut cx = EditorTestContext::new(cx).await;
 8188
 8189    let language = Arc::new(Language::new(
 8190        LanguageConfig {
 8191            brackets: BracketPairConfig {
 8192                pairs: vec![
 8193                    BracketPair {
 8194                        start: "{".to_string(),
 8195                        end: "}".to_string(),
 8196                        close: true,
 8197                        surround: true,
 8198                        newline: true,
 8199                    },
 8200                    BracketPair {
 8201                        start: "(".to_string(),
 8202                        end: ")".to_string(),
 8203                        close: true,
 8204                        surround: true,
 8205                        newline: true,
 8206                    },
 8207                    BracketPair {
 8208                        start: "[".to_string(),
 8209                        end: "]".to_string(),
 8210                        close: false,
 8211                        surround: true,
 8212                        newline: true,
 8213                    },
 8214                ],
 8215                ..Default::default()
 8216            },
 8217            autoclose_before: "})]".to_string(),
 8218            ..Default::default()
 8219        },
 8220        Some(tree_sitter_rust::LANGUAGE.into()),
 8221    ));
 8222
 8223    cx.language_registry().add(language.clone());
 8224    cx.update_buffer(|buffer, cx| {
 8225        buffer.set_language(Some(language), cx);
 8226    });
 8227
 8228    cx.set_state(
 8229        &"
 8230            {(ˇ)}
 8231            [[ˇ]]
 8232            {(ˇ)}
 8233        "
 8234        .unindent(),
 8235    );
 8236
 8237    cx.update_editor(|editor, window, cx| {
 8238        editor.backspace(&Default::default(), window, cx);
 8239        editor.backspace(&Default::default(), window, cx);
 8240    });
 8241
 8242    cx.assert_editor_state(
 8243        &"
 8244            ˇ
 8245            ˇ]]
 8246            ˇ
 8247        "
 8248        .unindent(),
 8249    );
 8250
 8251    cx.update_editor(|editor, window, cx| {
 8252        editor.handle_input("{", window, cx);
 8253        editor.handle_input("{", window, cx);
 8254        editor.move_right(&MoveRight, window, cx);
 8255        editor.move_right(&MoveRight, window, cx);
 8256        editor.move_left(&MoveLeft, window, cx);
 8257        editor.move_left(&MoveLeft, window, cx);
 8258        editor.backspace(&Default::default(), window, cx);
 8259    });
 8260
 8261    cx.assert_editor_state(
 8262        &"
 8263            {ˇ}
 8264            {ˇ}]]
 8265            {ˇ}
 8266        "
 8267        .unindent(),
 8268    );
 8269
 8270    cx.update_editor(|editor, window, cx| {
 8271        editor.backspace(&Default::default(), window, cx);
 8272    });
 8273
 8274    cx.assert_editor_state(
 8275        &"
 8276            ˇ
 8277            ˇ]]
 8278            ˇ
 8279        "
 8280        .unindent(),
 8281    );
 8282}
 8283
 8284#[gpui::test]
 8285async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8286    init_test(cx, |_| {});
 8287
 8288    let language = Arc::new(Language::new(
 8289        LanguageConfig::default(),
 8290        Some(tree_sitter_rust::LANGUAGE.into()),
 8291    ));
 8292
 8293    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8294    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8295    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8296    editor
 8297        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8298        .await;
 8299
 8300    editor.update_in(cx, |editor, window, cx| {
 8301        editor.set_auto_replace_emoji_shortcode(true);
 8302
 8303        editor.handle_input("Hello ", window, cx);
 8304        editor.handle_input(":wave", window, cx);
 8305        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8306
 8307        editor.handle_input(":", window, cx);
 8308        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8309
 8310        editor.handle_input(" :smile", window, cx);
 8311        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8312
 8313        editor.handle_input(":", window, cx);
 8314        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8315
 8316        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8317        editor.handle_input(":wave", window, cx);
 8318        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8319
 8320        editor.handle_input(":", window, cx);
 8321        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8322
 8323        editor.handle_input(":1", window, cx);
 8324        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8325
 8326        editor.handle_input(":", window, cx);
 8327        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8328
 8329        // Ensure shortcode does not get replaced when it is part of a word
 8330        editor.handle_input(" Test:wave", window, cx);
 8331        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8332
 8333        editor.handle_input(":", window, cx);
 8334        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8335
 8336        editor.set_auto_replace_emoji_shortcode(false);
 8337
 8338        // Ensure shortcode does not get replaced when auto replace is off
 8339        editor.handle_input(" :wave", window, cx);
 8340        assert_eq!(
 8341            editor.text(cx),
 8342            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8343        );
 8344
 8345        editor.handle_input(":", window, cx);
 8346        assert_eq!(
 8347            editor.text(cx),
 8348            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8349        );
 8350    });
 8351}
 8352
 8353#[gpui::test]
 8354async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8355    init_test(cx, |_| {});
 8356
 8357    let (text, insertion_ranges) = marked_text_ranges(
 8358        indoc! {"
 8359            ˇ
 8360        "},
 8361        false,
 8362    );
 8363
 8364    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8365    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8366
 8367    _ = editor.update_in(cx, |editor, window, cx| {
 8368        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8369
 8370        editor
 8371            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8372            .unwrap();
 8373
 8374        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8375            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8376            assert_eq!(editor.text(cx), expected_text);
 8377            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8378        }
 8379
 8380        assert(
 8381            editor,
 8382            cx,
 8383            indoc! {"
 8384            type «» =•
 8385            "},
 8386        );
 8387
 8388        assert!(editor.context_menu_visible(), "There should be a matches");
 8389    });
 8390}
 8391
 8392#[gpui::test]
 8393async fn test_snippets(cx: &mut TestAppContext) {
 8394    init_test(cx, |_| {});
 8395
 8396    let (text, insertion_ranges) = marked_text_ranges(
 8397        indoc! {"
 8398            a.ˇ b
 8399            a.ˇ b
 8400            a.ˇ b
 8401        "},
 8402        false,
 8403    );
 8404
 8405    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8406    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8407
 8408    editor.update_in(cx, |editor, window, cx| {
 8409        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8410
 8411        editor
 8412            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8413            .unwrap();
 8414
 8415        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8416            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8417            assert_eq!(editor.text(cx), expected_text);
 8418            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8419        }
 8420
 8421        assert(
 8422            editor,
 8423            cx,
 8424            indoc! {"
 8425                a.f(«one», two, «three») b
 8426                a.f(«one», two, «three») b
 8427                a.f(«one», two, «three») b
 8428            "},
 8429        );
 8430
 8431        // Can't move earlier than the first tab stop
 8432        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8433        assert(
 8434            editor,
 8435            cx,
 8436            indoc! {"
 8437                a.f(«one», two, «three») b
 8438                a.f(«one», two, «three») b
 8439                a.f(«one», two, «three») b
 8440            "},
 8441        );
 8442
 8443        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8444        assert(
 8445            editor,
 8446            cx,
 8447            indoc! {"
 8448                a.f(one, «two», three) b
 8449                a.f(one, «two», three) b
 8450                a.f(one, «two», three) b
 8451            "},
 8452        );
 8453
 8454        editor.move_to_prev_snippet_tabstop(window, cx);
 8455        assert(
 8456            editor,
 8457            cx,
 8458            indoc! {"
 8459                a.f(«one», two, «three») b
 8460                a.f(«one», two, «three») b
 8461                a.f(«one», two, «three») b
 8462            "},
 8463        );
 8464
 8465        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8466        assert(
 8467            editor,
 8468            cx,
 8469            indoc! {"
 8470                a.f(one, «two», three) b
 8471                a.f(one, «two», three) b
 8472                a.f(one, «two», three) b
 8473            "},
 8474        );
 8475        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8476        assert(
 8477            editor,
 8478            cx,
 8479            indoc! {"
 8480                a.f(one, two, three)ˇ b
 8481                a.f(one, two, three)ˇ b
 8482                a.f(one, two, three)ˇ b
 8483            "},
 8484        );
 8485
 8486        // As soon as the last tab stop is reached, snippet state is gone
 8487        editor.move_to_prev_snippet_tabstop(window, cx);
 8488        assert(
 8489            editor,
 8490            cx,
 8491            indoc! {"
 8492                a.f(one, two, three)ˇ b
 8493                a.f(one, two, three)ˇ b
 8494                a.f(one, two, three)ˇ b
 8495            "},
 8496        );
 8497    });
 8498}
 8499
 8500#[gpui::test]
 8501async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8502    init_test(cx, |_| {});
 8503
 8504    let fs = FakeFs::new(cx.executor());
 8505    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8506
 8507    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8508
 8509    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8510    language_registry.add(rust_lang());
 8511    let mut fake_servers = language_registry.register_fake_lsp(
 8512        "Rust",
 8513        FakeLspAdapter {
 8514            capabilities: lsp::ServerCapabilities {
 8515                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8516                ..Default::default()
 8517            },
 8518            ..Default::default()
 8519        },
 8520    );
 8521
 8522    let buffer = project
 8523        .update(cx, |project, cx| {
 8524            project.open_local_buffer(path!("/file.rs"), cx)
 8525        })
 8526        .await
 8527        .unwrap();
 8528
 8529    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8530    let (editor, cx) = cx.add_window_view(|window, cx| {
 8531        build_editor_with_project(project.clone(), buffer, window, cx)
 8532    });
 8533    editor.update_in(cx, |editor, window, cx| {
 8534        editor.set_text("one\ntwo\nthree\n", window, cx)
 8535    });
 8536    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8537
 8538    cx.executor().start_waiting();
 8539    let fake_server = fake_servers.next().await.unwrap();
 8540
 8541    {
 8542        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8543            move |params, _| async move {
 8544                assert_eq!(
 8545                    params.text_document.uri,
 8546                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8547                );
 8548                assert_eq!(params.options.tab_size, 4);
 8549                Ok(Some(vec![lsp::TextEdit::new(
 8550                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8551                    ", ".to_string(),
 8552                )]))
 8553            },
 8554        );
 8555        let save = editor
 8556            .update_in(cx, |editor, window, cx| {
 8557                editor.save(true, project.clone(), window, cx)
 8558            })
 8559            .unwrap();
 8560        cx.executor().start_waiting();
 8561        save.await;
 8562
 8563        assert_eq!(
 8564            editor.update(cx, |editor, cx| editor.text(cx)),
 8565            "one, two\nthree\n"
 8566        );
 8567        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8568    }
 8569
 8570    {
 8571        editor.update_in(cx, |editor, window, cx| {
 8572            editor.set_text("one\ntwo\nthree\n", window, cx)
 8573        });
 8574        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8575
 8576        // Ensure we can still save even if formatting hangs.
 8577        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8578            move |params, _| async move {
 8579                assert_eq!(
 8580                    params.text_document.uri,
 8581                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8582                );
 8583                futures::future::pending::<()>().await;
 8584                unreachable!()
 8585            },
 8586        );
 8587        let save = editor
 8588            .update_in(cx, |editor, window, cx| {
 8589                editor.save(true, project.clone(), window, cx)
 8590            })
 8591            .unwrap();
 8592        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8593        cx.executor().start_waiting();
 8594        save.await;
 8595        assert_eq!(
 8596            editor.update(cx, |editor, cx| editor.text(cx)),
 8597            "one\ntwo\nthree\n"
 8598        );
 8599    }
 8600
 8601    // For non-dirty buffer, no formatting request should be sent
 8602    {
 8603        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8604
 8605        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8606            panic!("Should not be invoked on non-dirty buffer");
 8607        });
 8608        let save = editor
 8609            .update_in(cx, |editor, window, cx| {
 8610                editor.save(true, project.clone(), window, cx)
 8611            })
 8612            .unwrap();
 8613        cx.executor().start_waiting();
 8614        save.await;
 8615    }
 8616
 8617    // Set rust language override and assert overridden tabsize is sent to language server
 8618    update_test_language_settings(cx, |settings| {
 8619        settings.languages.insert(
 8620            "Rust".into(),
 8621            LanguageSettingsContent {
 8622                tab_size: NonZeroU32::new(8),
 8623                ..Default::default()
 8624            },
 8625        );
 8626    });
 8627
 8628    {
 8629        editor.update_in(cx, |editor, window, cx| {
 8630            editor.set_text("somehting_new\n", window, cx)
 8631        });
 8632        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8633        let _formatting_request_signal = fake_server
 8634            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8635                assert_eq!(
 8636                    params.text_document.uri,
 8637                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8638                );
 8639                assert_eq!(params.options.tab_size, 8);
 8640                Ok(Some(vec![]))
 8641            });
 8642        let save = editor
 8643            .update_in(cx, |editor, window, cx| {
 8644                editor.save(true, project.clone(), window, cx)
 8645            })
 8646            .unwrap();
 8647        cx.executor().start_waiting();
 8648        save.await;
 8649    }
 8650}
 8651
 8652#[gpui::test]
 8653async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8654    init_test(cx, |_| {});
 8655
 8656    let cols = 4;
 8657    let rows = 10;
 8658    let sample_text_1 = sample_text(rows, cols, 'a');
 8659    assert_eq!(
 8660        sample_text_1,
 8661        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8662    );
 8663    let sample_text_2 = sample_text(rows, cols, 'l');
 8664    assert_eq!(
 8665        sample_text_2,
 8666        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8667    );
 8668    let sample_text_3 = sample_text(rows, cols, 'v');
 8669    assert_eq!(
 8670        sample_text_3,
 8671        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8672    );
 8673
 8674    let fs = FakeFs::new(cx.executor());
 8675    fs.insert_tree(
 8676        path!("/a"),
 8677        json!({
 8678            "main.rs": sample_text_1,
 8679            "other.rs": sample_text_2,
 8680            "lib.rs": sample_text_3,
 8681        }),
 8682    )
 8683    .await;
 8684
 8685    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8686    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8687    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8688
 8689    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8690    language_registry.add(rust_lang());
 8691    let mut fake_servers = language_registry.register_fake_lsp(
 8692        "Rust",
 8693        FakeLspAdapter {
 8694            capabilities: lsp::ServerCapabilities {
 8695                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8696                ..Default::default()
 8697            },
 8698            ..Default::default()
 8699        },
 8700    );
 8701
 8702    let worktree = project.update(cx, |project, cx| {
 8703        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8704        assert_eq!(worktrees.len(), 1);
 8705        worktrees.pop().unwrap()
 8706    });
 8707    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8708
 8709    let buffer_1 = project
 8710        .update(cx, |project, cx| {
 8711            project.open_buffer((worktree_id, "main.rs"), cx)
 8712        })
 8713        .await
 8714        .unwrap();
 8715    let buffer_2 = project
 8716        .update(cx, |project, cx| {
 8717            project.open_buffer((worktree_id, "other.rs"), cx)
 8718        })
 8719        .await
 8720        .unwrap();
 8721    let buffer_3 = project
 8722        .update(cx, |project, cx| {
 8723            project.open_buffer((worktree_id, "lib.rs"), cx)
 8724        })
 8725        .await
 8726        .unwrap();
 8727
 8728    let multi_buffer = cx.new(|cx| {
 8729        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8730        multi_buffer.push_excerpts(
 8731            buffer_1.clone(),
 8732            [
 8733                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8734                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8735                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8736            ],
 8737            cx,
 8738        );
 8739        multi_buffer.push_excerpts(
 8740            buffer_2.clone(),
 8741            [
 8742                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8743                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8744                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8745            ],
 8746            cx,
 8747        );
 8748        multi_buffer.push_excerpts(
 8749            buffer_3.clone(),
 8750            [
 8751                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8752                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8753                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8754            ],
 8755            cx,
 8756        );
 8757        multi_buffer
 8758    });
 8759    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8760        Editor::new(
 8761            EditorMode::full(),
 8762            multi_buffer,
 8763            Some(project.clone()),
 8764            window,
 8765            cx,
 8766        )
 8767    });
 8768
 8769    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8770        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8771            s.select_ranges(Some(1..2))
 8772        });
 8773        editor.insert("|one|two|three|", window, cx);
 8774    });
 8775    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8776    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8777        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8778            s.select_ranges(Some(60..70))
 8779        });
 8780        editor.insert("|four|five|six|", window, cx);
 8781    });
 8782    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8783
 8784    // First two buffers should be edited, but not the third one.
 8785    assert_eq!(
 8786        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8787        "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}",
 8788    );
 8789    buffer_1.update(cx, |buffer, _| {
 8790        assert!(buffer.is_dirty());
 8791        assert_eq!(
 8792            buffer.text(),
 8793            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8794        )
 8795    });
 8796    buffer_2.update(cx, |buffer, _| {
 8797        assert!(buffer.is_dirty());
 8798        assert_eq!(
 8799            buffer.text(),
 8800            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8801        )
 8802    });
 8803    buffer_3.update(cx, |buffer, _| {
 8804        assert!(!buffer.is_dirty());
 8805        assert_eq!(buffer.text(), sample_text_3,)
 8806    });
 8807    cx.executor().run_until_parked();
 8808
 8809    cx.executor().start_waiting();
 8810    let save = multi_buffer_editor
 8811        .update_in(cx, |editor, window, cx| {
 8812            editor.save(true, project.clone(), window, cx)
 8813        })
 8814        .unwrap();
 8815
 8816    let fake_server = fake_servers.next().await.unwrap();
 8817    fake_server
 8818        .server
 8819        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8820            Ok(Some(vec![lsp::TextEdit::new(
 8821                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8822                format!("[{} formatted]", params.text_document.uri),
 8823            )]))
 8824        })
 8825        .detach();
 8826    save.await;
 8827
 8828    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8829    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8830    assert_eq!(
 8831        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8832        uri!(
 8833            "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}"
 8834        ),
 8835    );
 8836    buffer_1.update(cx, |buffer, _| {
 8837        assert!(!buffer.is_dirty());
 8838        assert_eq!(
 8839            buffer.text(),
 8840            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8841        )
 8842    });
 8843    buffer_2.update(cx, |buffer, _| {
 8844        assert!(!buffer.is_dirty());
 8845        assert_eq!(
 8846            buffer.text(),
 8847            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8848        )
 8849    });
 8850    buffer_3.update(cx, |buffer, _| {
 8851        assert!(!buffer.is_dirty());
 8852        assert_eq!(buffer.text(), sample_text_3,)
 8853    });
 8854}
 8855
 8856#[gpui::test]
 8857async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8858    init_test(cx, |_| {});
 8859
 8860    let fs = FakeFs::new(cx.executor());
 8861    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8862
 8863    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8864
 8865    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8866    language_registry.add(rust_lang());
 8867    let mut fake_servers = language_registry.register_fake_lsp(
 8868        "Rust",
 8869        FakeLspAdapter {
 8870            capabilities: lsp::ServerCapabilities {
 8871                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8872                ..Default::default()
 8873            },
 8874            ..Default::default()
 8875        },
 8876    );
 8877
 8878    let buffer = project
 8879        .update(cx, |project, cx| {
 8880            project.open_local_buffer(path!("/file.rs"), cx)
 8881        })
 8882        .await
 8883        .unwrap();
 8884
 8885    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8886    let (editor, cx) = cx.add_window_view(|window, cx| {
 8887        build_editor_with_project(project.clone(), buffer, window, cx)
 8888    });
 8889    editor.update_in(cx, |editor, window, cx| {
 8890        editor.set_text("one\ntwo\nthree\n", window, cx)
 8891    });
 8892    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8893
 8894    cx.executor().start_waiting();
 8895    let fake_server = fake_servers.next().await.unwrap();
 8896
 8897    let save = editor
 8898        .update_in(cx, |editor, window, cx| {
 8899            editor.save(true, project.clone(), window, cx)
 8900        })
 8901        .unwrap();
 8902    fake_server
 8903        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8904            assert_eq!(
 8905                params.text_document.uri,
 8906                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8907            );
 8908            assert_eq!(params.options.tab_size, 4);
 8909            Ok(Some(vec![lsp::TextEdit::new(
 8910                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8911                ", ".to_string(),
 8912            )]))
 8913        })
 8914        .next()
 8915        .await;
 8916    cx.executor().start_waiting();
 8917    save.await;
 8918    assert_eq!(
 8919        editor.update(cx, |editor, cx| editor.text(cx)),
 8920        "one, two\nthree\n"
 8921    );
 8922    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8923
 8924    editor.update_in(cx, |editor, window, cx| {
 8925        editor.set_text("one\ntwo\nthree\n", window, cx)
 8926    });
 8927    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8928
 8929    // Ensure we can still save even if formatting hangs.
 8930    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8931        move |params, _| async move {
 8932            assert_eq!(
 8933                params.text_document.uri,
 8934                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8935            );
 8936            futures::future::pending::<()>().await;
 8937            unreachable!()
 8938        },
 8939    );
 8940    let save = editor
 8941        .update_in(cx, |editor, window, cx| {
 8942            editor.save(true, project.clone(), window, cx)
 8943        })
 8944        .unwrap();
 8945    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8946    cx.executor().start_waiting();
 8947    save.await;
 8948    assert_eq!(
 8949        editor.update(cx, |editor, cx| editor.text(cx)),
 8950        "one\ntwo\nthree\n"
 8951    );
 8952    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8953
 8954    // For non-dirty buffer, no formatting request should be sent
 8955    let save = editor
 8956        .update_in(cx, |editor, window, cx| {
 8957            editor.save(true, project.clone(), window, cx)
 8958        })
 8959        .unwrap();
 8960    let _pending_format_request = fake_server
 8961        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8962            panic!("Should not be invoked on non-dirty buffer");
 8963        })
 8964        .next();
 8965    cx.executor().start_waiting();
 8966    save.await;
 8967
 8968    // Set Rust language override and assert overridden tabsize is sent to language server
 8969    update_test_language_settings(cx, |settings| {
 8970        settings.languages.insert(
 8971            "Rust".into(),
 8972            LanguageSettingsContent {
 8973                tab_size: NonZeroU32::new(8),
 8974                ..Default::default()
 8975            },
 8976        );
 8977    });
 8978
 8979    editor.update_in(cx, |editor, window, cx| {
 8980        editor.set_text("somehting_new\n", window, cx)
 8981    });
 8982    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8983    let save = editor
 8984        .update_in(cx, |editor, window, cx| {
 8985            editor.save(true, project.clone(), window, cx)
 8986        })
 8987        .unwrap();
 8988    fake_server
 8989        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8990            assert_eq!(
 8991                params.text_document.uri,
 8992                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8993            );
 8994            assert_eq!(params.options.tab_size, 8);
 8995            Ok(Some(vec![]))
 8996        })
 8997        .next()
 8998        .await;
 8999    cx.executor().start_waiting();
 9000    save.await;
 9001}
 9002
 9003#[gpui::test]
 9004async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 9005    init_test(cx, |settings| {
 9006        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9007            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9008        ))
 9009    });
 9010
 9011    let fs = FakeFs::new(cx.executor());
 9012    fs.insert_file(path!("/file.rs"), Default::default()).await;
 9013
 9014    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9015
 9016    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9017    language_registry.add(Arc::new(Language::new(
 9018        LanguageConfig {
 9019            name: "Rust".into(),
 9020            matcher: LanguageMatcher {
 9021                path_suffixes: vec!["rs".to_string()],
 9022                ..Default::default()
 9023            },
 9024            ..LanguageConfig::default()
 9025        },
 9026        Some(tree_sitter_rust::LANGUAGE.into()),
 9027    )));
 9028    update_test_language_settings(cx, |settings| {
 9029        // Enable Prettier formatting for the same buffer, and ensure
 9030        // LSP is called instead of Prettier.
 9031        settings.defaults.prettier = Some(PrettierSettings {
 9032            allowed: true,
 9033            ..PrettierSettings::default()
 9034        });
 9035    });
 9036    let mut fake_servers = language_registry.register_fake_lsp(
 9037        "Rust",
 9038        FakeLspAdapter {
 9039            capabilities: lsp::ServerCapabilities {
 9040                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9041                ..Default::default()
 9042            },
 9043            ..Default::default()
 9044        },
 9045    );
 9046
 9047    let buffer = project
 9048        .update(cx, |project, cx| {
 9049            project.open_local_buffer(path!("/file.rs"), cx)
 9050        })
 9051        .await
 9052        .unwrap();
 9053
 9054    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9055    let (editor, cx) = cx.add_window_view(|window, cx| {
 9056        build_editor_with_project(project.clone(), buffer, window, cx)
 9057    });
 9058    editor.update_in(cx, |editor, window, cx| {
 9059        editor.set_text("one\ntwo\nthree\n", window, cx)
 9060    });
 9061
 9062    cx.executor().start_waiting();
 9063    let fake_server = fake_servers.next().await.unwrap();
 9064
 9065    let format = editor
 9066        .update_in(cx, |editor, window, cx| {
 9067            editor.perform_format(
 9068                project.clone(),
 9069                FormatTrigger::Manual,
 9070                FormatTarget::Buffers,
 9071                window,
 9072                cx,
 9073            )
 9074        })
 9075        .unwrap();
 9076    fake_server
 9077        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9078            assert_eq!(
 9079                params.text_document.uri,
 9080                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9081            );
 9082            assert_eq!(params.options.tab_size, 4);
 9083            Ok(Some(vec![lsp::TextEdit::new(
 9084                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9085                ", ".to_string(),
 9086            )]))
 9087        })
 9088        .next()
 9089        .await;
 9090    cx.executor().start_waiting();
 9091    format.await;
 9092    assert_eq!(
 9093        editor.update(cx, |editor, cx| editor.text(cx)),
 9094        "one, two\nthree\n"
 9095    );
 9096
 9097    editor.update_in(cx, |editor, window, cx| {
 9098        editor.set_text("one\ntwo\nthree\n", window, cx)
 9099    });
 9100    // Ensure we don't lock if formatting hangs.
 9101    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9102        move |params, _| async move {
 9103            assert_eq!(
 9104                params.text_document.uri,
 9105                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9106            );
 9107            futures::future::pending::<()>().await;
 9108            unreachable!()
 9109        },
 9110    );
 9111    let format = editor
 9112        .update_in(cx, |editor, window, cx| {
 9113            editor.perform_format(
 9114                project,
 9115                FormatTrigger::Manual,
 9116                FormatTarget::Buffers,
 9117                window,
 9118                cx,
 9119            )
 9120        })
 9121        .unwrap();
 9122    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9123    cx.executor().start_waiting();
 9124    format.await;
 9125    assert_eq!(
 9126        editor.update(cx, |editor, cx| editor.text(cx)),
 9127        "one\ntwo\nthree\n"
 9128    );
 9129}
 9130
 9131#[gpui::test]
 9132async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9133    init_test(cx, |settings| {
 9134        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9135        settings.defaults.formatter =
 9136            Some(language_settings::SelectedFormatter::List(FormatterList(
 9137                vec![
 9138                    Formatter::LanguageServer { name: None },
 9139                    Formatter::CodeActions(
 9140                        [
 9141                            ("code-action-1".into(), true),
 9142                            ("code-action-2".into(), true),
 9143                        ]
 9144                        .into_iter()
 9145                        .collect(),
 9146                    ),
 9147                ]
 9148                .into(),
 9149            )))
 9150    });
 9151
 9152    let fs = FakeFs::new(cx.executor());
 9153    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9154        .await;
 9155
 9156    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9157    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9158    language_registry.add(rust_lang());
 9159
 9160    let mut fake_servers = language_registry.register_fake_lsp(
 9161        "Rust",
 9162        FakeLspAdapter {
 9163            capabilities: lsp::ServerCapabilities {
 9164                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9165                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9166                    commands: vec!["the-command-for-code-action-1".into()],
 9167                    ..Default::default()
 9168                }),
 9169                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9170                ..Default::default()
 9171            },
 9172            ..Default::default()
 9173        },
 9174    );
 9175
 9176    let buffer = project
 9177        .update(cx, |project, cx| {
 9178            project.open_local_buffer(path!("/file.rs"), cx)
 9179        })
 9180        .await
 9181        .unwrap();
 9182
 9183    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9184    let (editor, cx) = cx.add_window_view(|window, cx| {
 9185        build_editor_with_project(project.clone(), buffer, window, cx)
 9186    });
 9187
 9188    cx.executor().start_waiting();
 9189
 9190    let fake_server = fake_servers.next().await.unwrap();
 9191    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9192        move |_params, _| async move {
 9193            Ok(Some(vec![lsp::TextEdit::new(
 9194                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9195                "applied-formatting\n".to_string(),
 9196            )]))
 9197        },
 9198    );
 9199    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9200        move |params, _| async move {
 9201            assert_eq!(
 9202                params.context.only,
 9203                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9204            );
 9205            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9206            Ok(Some(vec![
 9207                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9208                    kind: Some("code-action-1".into()),
 9209                    edit: Some(lsp::WorkspaceEdit::new(
 9210                        [(
 9211                            uri.clone(),
 9212                            vec![lsp::TextEdit::new(
 9213                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9214                                "applied-code-action-1-edit\n".to_string(),
 9215                            )],
 9216                        )]
 9217                        .into_iter()
 9218                        .collect(),
 9219                    )),
 9220                    command: Some(lsp::Command {
 9221                        command: "the-command-for-code-action-1".into(),
 9222                        ..Default::default()
 9223                    }),
 9224                    ..Default::default()
 9225                }),
 9226                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9227                    kind: Some("code-action-2".into()),
 9228                    edit: Some(lsp::WorkspaceEdit::new(
 9229                        [(
 9230                            uri.clone(),
 9231                            vec![lsp::TextEdit::new(
 9232                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9233                                "applied-code-action-2-edit\n".to_string(),
 9234                            )],
 9235                        )]
 9236                        .into_iter()
 9237                        .collect(),
 9238                    )),
 9239                    ..Default::default()
 9240                }),
 9241            ]))
 9242        },
 9243    );
 9244
 9245    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9246        move |params, _| async move { Ok(params) }
 9247    });
 9248
 9249    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9250    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9251        let fake = fake_server.clone();
 9252        let lock = command_lock.clone();
 9253        move |params, _| {
 9254            assert_eq!(params.command, "the-command-for-code-action-1");
 9255            let fake = fake.clone();
 9256            let lock = lock.clone();
 9257            async move {
 9258                lock.lock().await;
 9259                fake.server
 9260                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9261                        label: None,
 9262                        edit: lsp::WorkspaceEdit {
 9263                            changes: Some(
 9264                                [(
 9265                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9266                                    vec![lsp::TextEdit {
 9267                                        range: lsp::Range::new(
 9268                                            lsp::Position::new(0, 0),
 9269                                            lsp::Position::new(0, 0),
 9270                                        ),
 9271                                        new_text: "applied-code-action-1-command\n".into(),
 9272                                    }],
 9273                                )]
 9274                                .into_iter()
 9275                                .collect(),
 9276                            ),
 9277                            ..Default::default()
 9278                        },
 9279                    })
 9280                    .await
 9281                    .into_response()
 9282                    .unwrap();
 9283                Ok(Some(json!(null)))
 9284            }
 9285        }
 9286    });
 9287
 9288    cx.executor().start_waiting();
 9289    editor
 9290        .update_in(cx, |editor, window, cx| {
 9291            editor.perform_format(
 9292                project.clone(),
 9293                FormatTrigger::Manual,
 9294                FormatTarget::Buffers,
 9295                window,
 9296                cx,
 9297            )
 9298        })
 9299        .unwrap()
 9300        .await;
 9301    editor.update(cx, |editor, cx| {
 9302        assert_eq!(
 9303            editor.text(cx),
 9304            r#"
 9305                applied-code-action-2-edit
 9306                applied-code-action-1-command
 9307                applied-code-action-1-edit
 9308                applied-formatting
 9309                one
 9310                two
 9311                three
 9312            "#
 9313            .unindent()
 9314        );
 9315    });
 9316
 9317    editor.update_in(cx, |editor, window, cx| {
 9318        editor.undo(&Default::default(), window, cx);
 9319        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9320    });
 9321
 9322    // Perform a manual edit while waiting for an LSP command
 9323    // that's being run as part of a formatting code action.
 9324    let lock_guard = command_lock.lock().await;
 9325    let format = editor
 9326        .update_in(cx, |editor, window, cx| {
 9327            editor.perform_format(
 9328                project.clone(),
 9329                FormatTrigger::Manual,
 9330                FormatTarget::Buffers,
 9331                window,
 9332                cx,
 9333            )
 9334        })
 9335        .unwrap();
 9336    cx.run_until_parked();
 9337    editor.update(cx, |editor, cx| {
 9338        assert_eq!(
 9339            editor.text(cx),
 9340            r#"
 9341                applied-code-action-1-edit
 9342                applied-formatting
 9343                one
 9344                two
 9345                three
 9346            "#
 9347            .unindent()
 9348        );
 9349
 9350        editor.buffer.update(cx, |buffer, cx| {
 9351            let ix = buffer.len(cx);
 9352            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9353        });
 9354    });
 9355
 9356    // Allow the LSP command to proceed. Because the buffer was edited,
 9357    // the second code action will not be run.
 9358    drop(lock_guard);
 9359    format.await;
 9360    editor.update_in(cx, |editor, window, cx| {
 9361        assert_eq!(
 9362            editor.text(cx),
 9363            r#"
 9364                applied-code-action-1-command
 9365                applied-code-action-1-edit
 9366                applied-formatting
 9367                one
 9368                two
 9369                three
 9370                edited
 9371            "#
 9372            .unindent()
 9373        );
 9374
 9375        // The manual edit is undone first, because it is the last thing the user did
 9376        // (even though the command completed afterwards).
 9377        editor.undo(&Default::default(), window, cx);
 9378        assert_eq!(
 9379            editor.text(cx),
 9380            r#"
 9381                applied-code-action-1-command
 9382                applied-code-action-1-edit
 9383                applied-formatting
 9384                one
 9385                two
 9386                three
 9387            "#
 9388            .unindent()
 9389        );
 9390
 9391        // All the formatting (including the command, which completed after the manual edit)
 9392        // is undone together.
 9393        editor.undo(&Default::default(), window, cx);
 9394        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9395    });
 9396}
 9397
 9398#[gpui::test]
 9399async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9400    init_test(cx, |settings| {
 9401        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9402            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9403        ))
 9404    });
 9405
 9406    let fs = FakeFs::new(cx.executor());
 9407    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9408
 9409    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9410
 9411    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9412    language_registry.add(Arc::new(Language::new(
 9413        LanguageConfig {
 9414            name: "TypeScript".into(),
 9415            matcher: LanguageMatcher {
 9416                path_suffixes: vec!["ts".to_string()],
 9417                ..Default::default()
 9418            },
 9419            ..LanguageConfig::default()
 9420        },
 9421        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9422    )));
 9423    update_test_language_settings(cx, |settings| {
 9424        settings.defaults.prettier = Some(PrettierSettings {
 9425            allowed: true,
 9426            ..PrettierSettings::default()
 9427        });
 9428    });
 9429    let mut fake_servers = language_registry.register_fake_lsp(
 9430        "TypeScript",
 9431        FakeLspAdapter {
 9432            capabilities: lsp::ServerCapabilities {
 9433                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9434                ..Default::default()
 9435            },
 9436            ..Default::default()
 9437        },
 9438    );
 9439
 9440    let buffer = project
 9441        .update(cx, |project, cx| {
 9442            project.open_local_buffer(path!("/file.ts"), cx)
 9443        })
 9444        .await
 9445        .unwrap();
 9446
 9447    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9448    let (editor, cx) = cx.add_window_view(|window, cx| {
 9449        build_editor_with_project(project.clone(), buffer, window, cx)
 9450    });
 9451    editor.update_in(cx, |editor, window, cx| {
 9452        editor.set_text(
 9453            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9454            window,
 9455            cx,
 9456        )
 9457    });
 9458
 9459    cx.executor().start_waiting();
 9460    let fake_server = fake_servers.next().await.unwrap();
 9461
 9462    let format = editor
 9463        .update_in(cx, |editor, window, cx| {
 9464            editor.perform_code_action_kind(
 9465                project.clone(),
 9466                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9467                window,
 9468                cx,
 9469            )
 9470        })
 9471        .unwrap();
 9472    fake_server
 9473        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9474            assert_eq!(
 9475                params.text_document.uri,
 9476                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9477            );
 9478            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9479                lsp::CodeAction {
 9480                    title: "Organize Imports".to_string(),
 9481                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9482                    edit: Some(lsp::WorkspaceEdit {
 9483                        changes: Some(
 9484                            [(
 9485                                params.text_document.uri.clone(),
 9486                                vec![lsp::TextEdit::new(
 9487                                    lsp::Range::new(
 9488                                        lsp::Position::new(1, 0),
 9489                                        lsp::Position::new(2, 0),
 9490                                    ),
 9491                                    "".to_string(),
 9492                                )],
 9493                            )]
 9494                            .into_iter()
 9495                            .collect(),
 9496                        ),
 9497                        ..Default::default()
 9498                    }),
 9499                    ..Default::default()
 9500                },
 9501            )]))
 9502        })
 9503        .next()
 9504        .await;
 9505    cx.executor().start_waiting();
 9506    format.await;
 9507    assert_eq!(
 9508        editor.update(cx, |editor, cx| editor.text(cx)),
 9509        "import { a } from 'module';\n\nconst x = a;\n"
 9510    );
 9511
 9512    editor.update_in(cx, |editor, window, cx| {
 9513        editor.set_text(
 9514            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9515            window,
 9516            cx,
 9517        )
 9518    });
 9519    // Ensure we don't lock if code action hangs.
 9520    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9521        move |params, _| async move {
 9522            assert_eq!(
 9523                params.text_document.uri,
 9524                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9525            );
 9526            futures::future::pending::<()>().await;
 9527            unreachable!()
 9528        },
 9529    );
 9530    let format = editor
 9531        .update_in(cx, |editor, window, cx| {
 9532            editor.perform_code_action_kind(
 9533                project,
 9534                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9535                window,
 9536                cx,
 9537            )
 9538        })
 9539        .unwrap();
 9540    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9541    cx.executor().start_waiting();
 9542    format.await;
 9543    assert_eq!(
 9544        editor.update(cx, |editor, cx| editor.text(cx)),
 9545        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9546    );
 9547}
 9548
 9549#[gpui::test]
 9550async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9551    init_test(cx, |_| {});
 9552
 9553    let mut cx = EditorLspTestContext::new_rust(
 9554        lsp::ServerCapabilities {
 9555            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9556            ..Default::default()
 9557        },
 9558        cx,
 9559    )
 9560    .await;
 9561
 9562    cx.set_state(indoc! {"
 9563        one.twoˇ
 9564    "});
 9565
 9566    // The format request takes a long time. When it completes, it inserts
 9567    // a newline and an indent before the `.`
 9568    cx.lsp
 9569        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9570            let executor = cx.background_executor().clone();
 9571            async move {
 9572                executor.timer(Duration::from_millis(100)).await;
 9573                Ok(Some(vec![lsp::TextEdit {
 9574                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9575                    new_text: "\n    ".into(),
 9576                }]))
 9577            }
 9578        });
 9579
 9580    // Submit a format request.
 9581    let format_1 = cx
 9582        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9583        .unwrap();
 9584    cx.executor().run_until_parked();
 9585
 9586    // Submit a second format request.
 9587    let format_2 = cx
 9588        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9589        .unwrap();
 9590    cx.executor().run_until_parked();
 9591
 9592    // Wait for both format requests to complete
 9593    cx.executor().advance_clock(Duration::from_millis(200));
 9594    cx.executor().start_waiting();
 9595    format_1.await.unwrap();
 9596    cx.executor().start_waiting();
 9597    format_2.await.unwrap();
 9598
 9599    // The formatting edits only happens once.
 9600    cx.assert_editor_state(indoc! {"
 9601        one
 9602            .twoˇ
 9603    "});
 9604}
 9605
 9606#[gpui::test]
 9607async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9608    init_test(cx, |settings| {
 9609        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9610    });
 9611
 9612    let mut cx = EditorLspTestContext::new_rust(
 9613        lsp::ServerCapabilities {
 9614            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9615            ..Default::default()
 9616        },
 9617        cx,
 9618    )
 9619    .await;
 9620
 9621    // Set up a buffer white some trailing whitespace and no trailing newline.
 9622    cx.set_state(
 9623        &[
 9624            "one ",   //
 9625            "twoˇ",   //
 9626            "three ", //
 9627            "four",   //
 9628        ]
 9629        .join("\n"),
 9630    );
 9631
 9632    // Submit a format request.
 9633    let format = cx
 9634        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9635        .unwrap();
 9636
 9637    // Record which buffer changes have been sent to the language server
 9638    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9639    cx.lsp
 9640        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9641            let buffer_changes = buffer_changes.clone();
 9642            move |params, _| {
 9643                buffer_changes.lock().extend(
 9644                    params
 9645                        .content_changes
 9646                        .into_iter()
 9647                        .map(|e| (e.range.unwrap(), e.text)),
 9648                );
 9649            }
 9650        });
 9651
 9652    // Handle formatting requests to the language server.
 9653    cx.lsp
 9654        .set_request_handler::<lsp::request::Formatting, _, _>({
 9655            let buffer_changes = buffer_changes.clone();
 9656            move |_, _| {
 9657                // When formatting is requested, trailing whitespace has already been stripped,
 9658                // and the trailing newline has already been added.
 9659                assert_eq!(
 9660                    &buffer_changes.lock()[1..],
 9661                    &[
 9662                        (
 9663                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9664                            "".into()
 9665                        ),
 9666                        (
 9667                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9668                            "".into()
 9669                        ),
 9670                        (
 9671                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9672                            "\n".into()
 9673                        ),
 9674                    ]
 9675                );
 9676
 9677                // Insert blank lines between each line of the buffer.
 9678                async move {
 9679                    Ok(Some(vec![
 9680                        lsp::TextEdit {
 9681                            range: lsp::Range::new(
 9682                                lsp::Position::new(1, 0),
 9683                                lsp::Position::new(1, 0),
 9684                            ),
 9685                            new_text: "\n".into(),
 9686                        },
 9687                        lsp::TextEdit {
 9688                            range: lsp::Range::new(
 9689                                lsp::Position::new(2, 0),
 9690                                lsp::Position::new(2, 0),
 9691                            ),
 9692                            new_text: "\n".into(),
 9693                        },
 9694                    ]))
 9695                }
 9696            }
 9697        });
 9698
 9699    // After formatting the buffer, the trailing whitespace is stripped,
 9700    // a newline is appended, and the edits provided by the language server
 9701    // have been applied.
 9702    format.await.unwrap();
 9703    cx.assert_editor_state(
 9704        &[
 9705            "one",   //
 9706            "",      //
 9707            "twoˇ",  //
 9708            "",      //
 9709            "three", //
 9710            "four",  //
 9711            "",      //
 9712        ]
 9713        .join("\n"),
 9714    );
 9715
 9716    // Undoing the formatting undoes the trailing whitespace removal, the
 9717    // trailing newline, and the LSP edits.
 9718    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9719    cx.assert_editor_state(
 9720        &[
 9721            "one ",   //
 9722            "twoˇ",   //
 9723            "three ", //
 9724            "four",   //
 9725        ]
 9726        .join("\n"),
 9727    );
 9728}
 9729
 9730#[gpui::test]
 9731async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9732    cx: &mut TestAppContext,
 9733) {
 9734    init_test(cx, |_| {});
 9735
 9736    cx.update(|cx| {
 9737        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9738            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9739                settings.auto_signature_help = Some(true);
 9740            });
 9741        });
 9742    });
 9743
 9744    let mut cx = EditorLspTestContext::new_rust(
 9745        lsp::ServerCapabilities {
 9746            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9747                ..Default::default()
 9748            }),
 9749            ..Default::default()
 9750        },
 9751        cx,
 9752    )
 9753    .await;
 9754
 9755    let language = Language::new(
 9756        LanguageConfig {
 9757            name: "Rust".into(),
 9758            brackets: BracketPairConfig {
 9759                pairs: vec![
 9760                    BracketPair {
 9761                        start: "{".to_string(),
 9762                        end: "}".to_string(),
 9763                        close: true,
 9764                        surround: true,
 9765                        newline: true,
 9766                    },
 9767                    BracketPair {
 9768                        start: "(".to_string(),
 9769                        end: ")".to_string(),
 9770                        close: true,
 9771                        surround: true,
 9772                        newline: true,
 9773                    },
 9774                    BracketPair {
 9775                        start: "/*".to_string(),
 9776                        end: " */".to_string(),
 9777                        close: true,
 9778                        surround: true,
 9779                        newline: true,
 9780                    },
 9781                    BracketPair {
 9782                        start: "[".to_string(),
 9783                        end: "]".to_string(),
 9784                        close: false,
 9785                        surround: false,
 9786                        newline: true,
 9787                    },
 9788                    BracketPair {
 9789                        start: "\"".to_string(),
 9790                        end: "\"".to_string(),
 9791                        close: true,
 9792                        surround: true,
 9793                        newline: false,
 9794                    },
 9795                    BracketPair {
 9796                        start: "<".to_string(),
 9797                        end: ">".to_string(),
 9798                        close: false,
 9799                        surround: true,
 9800                        newline: true,
 9801                    },
 9802                ],
 9803                ..Default::default()
 9804            },
 9805            autoclose_before: "})]".to_string(),
 9806            ..Default::default()
 9807        },
 9808        Some(tree_sitter_rust::LANGUAGE.into()),
 9809    );
 9810    let language = Arc::new(language);
 9811
 9812    cx.language_registry().add(language.clone());
 9813    cx.update_buffer(|buffer, cx| {
 9814        buffer.set_language(Some(language), cx);
 9815    });
 9816
 9817    cx.set_state(
 9818        &r#"
 9819            fn main() {
 9820                sampleˇ
 9821            }
 9822        "#
 9823        .unindent(),
 9824    );
 9825
 9826    cx.update_editor(|editor, window, cx| {
 9827        editor.handle_input("(", window, cx);
 9828    });
 9829    cx.assert_editor_state(
 9830        &"
 9831            fn main() {
 9832                sample(ˇ)
 9833            }
 9834        "
 9835        .unindent(),
 9836    );
 9837
 9838    let mocked_response = lsp::SignatureHelp {
 9839        signatures: vec![lsp::SignatureInformation {
 9840            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9841            documentation: None,
 9842            parameters: Some(vec![
 9843                lsp::ParameterInformation {
 9844                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9845                    documentation: None,
 9846                },
 9847                lsp::ParameterInformation {
 9848                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9849                    documentation: None,
 9850                },
 9851            ]),
 9852            active_parameter: None,
 9853        }],
 9854        active_signature: Some(0),
 9855        active_parameter: Some(0),
 9856    };
 9857    handle_signature_help_request(&mut cx, mocked_response).await;
 9858
 9859    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9860        .await;
 9861
 9862    cx.editor(|editor, _, _| {
 9863        let signature_help_state = editor.signature_help_state.popover().cloned();
 9864        assert_eq!(
 9865            signature_help_state.unwrap().label,
 9866            "param1: u8, param2: u8"
 9867        );
 9868    });
 9869}
 9870
 9871#[gpui::test]
 9872async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9873    init_test(cx, |_| {});
 9874
 9875    cx.update(|cx| {
 9876        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9877            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9878                settings.auto_signature_help = Some(false);
 9879                settings.show_signature_help_after_edits = Some(false);
 9880            });
 9881        });
 9882    });
 9883
 9884    let mut cx = EditorLspTestContext::new_rust(
 9885        lsp::ServerCapabilities {
 9886            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9887                ..Default::default()
 9888            }),
 9889            ..Default::default()
 9890        },
 9891        cx,
 9892    )
 9893    .await;
 9894
 9895    let language = Language::new(
 9896        LanguageConfig {
 9897            name: "Rust".into(),
 9898            brackets: BracketPairConfig {
 9899                pairs: vec![
 9900                    BracketPair {
 9901                        start: "{".to_string(),
 9902                        end: "}".to_string(),
 9903                        close: true,
 9904                        surround: true,
 9905                        newline: true,
 9906                    },
 9907                    BracketPair {
 9908                        start: "(".to_string(),
 9909                        end: ")".to_string(),
 9910                        close: true,
 9911                        surround: true,
 9912                        newline: true,
 9913                    },
 9914                    BracketPair {
 9915                        start: "/*".to_string(),
 9916                        end: " */".to_string(),
 9917                        close: true,
 9918                        surround: true,
 9919                        newline: true,
 9920                    },
 9921                    BracketPair {
 9922                        start: "[".to_string(),
 9923                        end: "]".to_string(),
 9924                        close: false,
 9925                        surround: false,
 9926                        newline: true,
 9927                    },
 9928                    BracketPair {
 9929                        start: "\"".to_string(),
 9930                        end: "\"".to_string(),
 9931                        close: true,
 9932                        surround: true,
 9933                        newline: false,
 9934                    },
 9935                    BracketPair {
 9936                        start: "<".to_string(),
 9937                        end: ">".to_string(),
 9938                        close: false,
 9939                        surround: true,
 9940                        newline: true,
 9941                    },
 9942                ],
 9943                ..Default::default()
 9944            },
 9945            autoclose_before: "})]".to_string(),
 9946            ..Default::default()
 9947        },
 9948        Some(tree_sitter_rust::LANGUAGE.into()),
 9949    );
 9950    let language = Arc::new(language);
 9951
 9952    cx.language_registry().add(language.clone());
 9953    cx.update_buffer(|buffer, cx| {
 9954        buffer.set_language(Some(language), cx);
 9955    });
 9956
 9957    // Ensure that signature_help is not called when no signature help is enabled.
 9958    cx.set_state(
 9959        &r#"
 9960            fn main() {
 9961                sampleˇ
 9962            }
 9963        "#
 9964        .unindent(),
 9965    );
 9966    cx.update_editor(|editor, window, cx| {
 9967        editor.handle_input("(", window, cx);
 9968    });
 9969    cx.assert_editor_state(
 9970        &"
 9971            fn main() {
 9972                sample(ˇ)
 9973            }
 9974        "
 9975        .unindent(),
 9976    );
 9977    cx.editor(|editor, _, _| {
 9978        assert!(editor.signature_help_state.task().is_none());
 9979    });
 9980
 9981    let mocked_response = lsp::SignatureHelp {
 9982        signatures: vec![lsp::SignatureInformation {
 9983            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9984            documentation: None,
 9985            parameters: Some(vec![
 9986                lsp::ParameterInformation {
 9987                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9988                    documentation: None,
 9989                },
 9990                lsp::ParameterInformation {
 9991                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9992                    documentation: None,
 9993                },
 9994            ]),
 9995            active_parameter: None,
 9996        }],
 9997        active_signature: Some(0),
 9998        active_parameter: Some(0),
 9999    };
10000
10001    // Ensure that signature_help is called when enabled afte edits
10002    cx.update(|_, cx| {
10003        cx.update_global::<SettingsStore, _>(|settings, cx| {
10004            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10005                settings.auto_signature_help = Some(false);
10006                settings.show_signature_help_after_edits = Some(true);
10007            });
10008        });
10009    });
10010    cx.set_state(
10011        &r#"
10012            fn main() {
10013                sampleˇ
10014            }
10015        "#
10016        .unindent(),
10017    );
10018    cx.update_editor(|editor, window, cx| {
10019        editor.handle_input("(", window, cx);
10020    });
10021    cx.assert_editor_state(
10022        &"
10023            fn main() {
10024                sample(ˇ)
10025            }
10026        "
10027        .unindent(),
10028    );
10029    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10030    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10031        .await;
10032    cx.update_editor(|editor, _, _| {
10033        let signature_help_state = editor.signature_help_state.popover().cloned();
10034        assert!(signature_help_state.is_some());
10035        assert_eq!(
10036            signature_help_state.unwrap().label,
10037            "param1: u8, param2: u8"
10038        );
10039        editor.signature_help_state = SignatureHelpState::default();
10040    });
10041
10042    // Ensure that signature_help is called when auto signature help override is enabled
10043    cx.update(|_, cx| {
10044        cx.update_global::<SettingsStore, _>(|settings, cx| {
10045            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10046                settings.auto_signature_help = Some(true);
10047                settings.show_signature_help_after_edits = Some(false);
10048            });
10049        });
10050    });
10051    cx.set_state(
10052        &r#"
10053            fn main() {
10054                sampleˇ
10055            }
10056        "#
10057        .unindent(),
10058    );
10059    cx.update_editor(|editor, window, cx| {
10060        editor.handle_input("(", window, cx);
10061    });
10062    cx.assert_editor_state(
10063        &"
10064            fn main() {
10065                sample(ˇ)
10066            }
10067        "
10068        .unindent(),
10069    );
10070    handle_signature_help_request(&mut cx, mocked_response).await;
10071    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10072        .await;
10073    cx.editor(|editor, _, _| {
10074        let signature_help_state = editor.signature_help_state.popover().cloned();
10075        assert!(signature_help_state.is_some());
10076        assert_eq!(
10077            signature_help_state.unwrap().label,
10078            "param1: u8, param2: u8"
10079        );
10080    });
10081}
10082
10083#[gpui::test]
10084async fn test_signature_help(cx: &mut TestAppContext) {
10085    init_test(cx, |_| {});
10086    cx.update(|cx| {
10087        cx.update_global::<SettingsStore, _>(|settings, cx| {
10088            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10089                settings.auto_signature_help = Some(true);
10090            });
10091        });
10092    });
10093
10094    let mut cx = EditorLspTestContext::new_rust(
10095        lsp::ServerCapabilities {
10096            signature_help_provider: Some(lsp::SignatureHelpOptions {
10097                ..Default::default()
10098            }),
10099            ..Default::default()
10100        },
10101        cx,
10102    )
10103    .await;
10104
10105    // A test that directly calls `show_signature_help`
10106    cx.update_editor(|editor, window, cx| {
10107        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10108    });
10109
10110    let mocked_response = lsp::SignatureHelp {
10111        signatures: vec![lsp::SignatureInformation {
10112            label: "fn sample(param1: u8, param2: u8)".to_string(),
10113            documentation: None,
10114            parameters: Some(vec![
10115                lsp::ParameterInformation {
10116                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10117                    documentation: None,
10118                },
10119                lsp::ParameterInformation {
10120                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10121                    documentation: None,
10122                },
10123            ]),
10124            active_parameter: None,
10125        }],
10126        active_signature: Some(0),
10127        active_parameter: Some(0),
10128    };
10129    handle_signature_help_request(&mut cx, mocked_response).await;
10130
10131    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10132        .await;
10133
10134    cx.editor(|editor, _, _| {
10135        let signature_help_state = editor.signature_help_state.popover().cloned();
10136        assert!(signature_help_state.is_some());
10137        assert_eq!(
10138            signature_help_state.unwrap().label,
10139            "param1: u8, param2: u8"
10140        );
10141    });
10142
10143    // When exiting outside from inside the brackets, `signature_help` is closed.
10144    cx.set_state(indoc! {"
10145        fn main() {
10146            sample(ˇ);
10147        }
10148
10149        fn sample(param1: u8, param2: u8) {}
10150    "});
10151
10152    cx.update_editor(|editor, window, cx| {
10153        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10154    });
10155
10156    let mocked_response = lsp::SignatureHelp {
10157        signatures: Vec::new(),
10158        active_signature: None,
10159        active_parameter: None,
10160    };
10161    handle_signature_help_request(&mut cx, mocked_response).await;
10162
10163    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10164        .await;
10165
10166    cx.editor(|editor, _, _| {
10167        assert!(!editor.signature_help_state.is_shown());
10168    });
10169
10170    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10171    cx.set_state(indoc! {"
10172        fn main() {
10173            sample(ˇ);
10174        }
10175
10176        fn sample(param1: u8, param2: u8) {}
10177    "});
10178
10179    let mocked_response = lsp::SignatureHelp {
10180        signatures: vec![lsp::SignatureInformation {
10181            label: "fn sample(param1: u8, param2: u8)".to_string(),
10182            documentation: None,
10183            parameters: Some(vec![
10184                lsp::ParameterInformation {
10185                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10186                    documentation: None,
10187                },
10188                lsp::ParameterInformation {
10189                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10190                    documentation: None,
10191                },
10192            ]),
10193            active_parameter: None,
10194        }],
10195        active_signature: Some(0),
10196        active_parameter: Some(0),
10197    };
10198    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10199    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10200        .await;
10201    cx.editor(|editor, _, _| {
10202        assert!(editor.signature_help_state.is_shown());
10203    });
10204
10205    // Restore the popover with more parameter input
10206    cx.set_state(indoc! {"
10207        fn main() {
10208            sample(param1, param2ˇ);
10209        }
10210
10211        fn sample(param1: u8, param2: u8) {}
10212    "});
10213
10214    let mocked_response = lsp::SignatureHelp {
10215        signatures: vec![lsp::SignatureInformation {
10216            label: "fn sample(param1: u8, param2: u8)".to_string(),
10217            documentation: None,
10218            parameters: Some(vec![
10219                lsp::ParameterInformation {
10220                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10221                    documentation: None,
10222                },
10223                lsp::ParameterInformation {
10224                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10225                    documentation: None,
10226                },
10227            ]),
10228            active_parameter: None,
10229        }],
10230        active_signature: Some(0),
10231        active_parameter: Some(1),
10232    };
10233    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10234    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10235        .await;
10236
10237    // When selecting a range, the popover is gone.
10238    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10239    cx.update_editor(|editor, window, cx| {
10240        editor.change_selections(None, window, cx, |s| {
10241            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10242        })
10243    });
10244    cx.assert_editor_state(indoc! {"
10245        fn main() {
10246            sample(param1, «ˇparam2»);
10247        }
10248
10249        fn sample(param1: u8, param2: u8) {}
10250    "});
10251    cx.editor(|editor, _, _| {
10252        assert!(!editor.signature_help_state.is_shown());
10253    });
10254
10255    // When unselecting again, the popover is back if within the brackets.
10256    cx.update_editor(|editor, window, cx| {
10257        editor.change_selections(None, window, cx, |s| {
10258            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10259        })
10260    });
10261    cx.assert_editor_state(indoc! {"
10262        fn main() {
10263            sample(param1, ˇparam2);
10264        }
10265
10266        fn sample(param1: u8, param2: u8) {}
10267    "});
10268    handle_signature_help_request(&mut cx, mocked_response).await;
10269    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10270        .await;
10271    cx.editor(|editor, _, _| {
10272        assert!(editor.signature_help_state.is_shown());
10273    });
10274
10275    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10276    cx.update_editor(|editor, window, cx| {
10277        editor.change_selections(None, window, cx, |s| {
10278            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10279            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10280        })
10281    });
10282    cx.assert_editor_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    cx.update_editor(|editor, _, cx| {
10313        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10314    });
10315    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10316        .await;
10317    cx.update_editor(|editor, window, cx| {
10318        editor.change_selections(None, window, cx, |s| {
10319            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10320        })
10321    });
10322    cx.assert_editor_state(indoc! {"
10323        fn main() {
10324            sample(param1, «ˇparam2»);
10325        }
10326
10327        fn sample(param1: u8, param2: u8) {}
10328    "});
10329    cx.update_editor(|editor, window, cx| {
10330        editor.change_selections(None, window, cx, |s| {
10331            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10332        })
10333    });
10334    cx.assert_editor_state(indoc! {"
10335        fn main() {
10336            sample(param1, ˇparam2);
10337        }
10338
10339        fn sample(param1: u8, param2: u8) {}
10340    "});
10341    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10342        .await;
10343}
10344
10345#[gpui::test]
10346async fn test_completion_mode(cx: &mut TestAppContext) {
10347    init_test(cx, |_| {});
10348    let mut cx = EditorLspTestContext::new_rust(
10349        lsp::ServerCapabilities {
10350            completion_provider: Some(lsp::CompletionOptions {
10351                resolve_provider: Some(true),
10352                ..Default::default()
10353            }),
10354            ..Default::default()
10355        },
10356        cx,
10357    )
10358    .await;
10359
10360    struct Run {
10361        run_description: &'static str,
10362        initial_state: String,
10363        buffer_marked_text: String,
10364        completion_text: &'static str,
10365        expected_with_insert_mode: String,
10366        expected_with_replace_mode: String,
10367        expected_with_replace_subsequence_mode: String,
10368        expected_with_replace_suffix_mode: String,
10369    }
10370
10371    let runs = [
10372        Run {
10373            run_description: "Start of word matches completion text",
10374            initial_state: "before ediˇ after".into(),
10375            buffer_marked_text: "before <edi|> after".into(),
10376            completion_text: "editor",
10377            expected_with_insert_mode: "before editorˇ after".into(),
10378            expected_with_replace_mode: "before editorˇ after".into(),
10379            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10380            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10381        },
10382        Run {
10383            run_description: "Accept same text at the middle of the word",
10384            initial_state: "before ediˇtor after".into(),
10385            buffer_marked_text: "before <edi|tor> after".into(),
10386            completion_text: "editor",
10387            expected_with_insert_mode: "before editorˇtor after".into(),
10388            expected_with_replace_mode: "before editorˇ after".into(),
10389            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10390            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10391        },
10392        Run {
10393            run_description: "End of word matches completion text -- cursor at end",
10394            initial_state: "before torˇ after".into(),
10395            buffer_marked_text: "before <tor|> after".into(),
10396            completion_text: "editor",
10397            expected_with_insert_mode: "before editorˇ after".into(),
10398            expected_with_replace_mode: "before editorˇ after".into(),
10399            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10400            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10401        },
10402        Run {
10403            run_description: "End of word matches completion text -- cursor at start",
10404            initial_state: "before ˇtor after".into(),
10405            buffer_marked_text: "before <|tor> after".into(),
10406            completion_text: "editor",
10407            expected_with_insert_mode: "before editorˇtor after".into(),
10408            expected_with_replace_mode: "before editorˇ after".into(),
10409            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10410            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10411        },
10412        Run {
10413            run_description: "Prepend text containing whitespace",
10414            initial_state: "pˇfield: bool".into(),
10415            buffer_marked_text: "<p|field>: bool".into(),
10416            completion_text: "pub ",
10417            expected_with_insert_mode: "pub ˇfield: bool".into(),
10418            expected_with_replace_mode: "pub ˇ: bool".into(),
10419            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10420            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10421        },
10422        Run {
10423            run_description: "Add element to start of list",
10424            initial_state: "[element_ˇelement_2]".into(),
10425            buffer_marked_text: "[<element_|element_2>]".into(),
10426            completion_text: "element_1",
10427            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10428            expected_with_replace_mode: "[element_1ˇ]".into(),
10429            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10430            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10431        },
10432        Run {
10433            run_description: "Add element to start of list -- first and second elements are equal",
10434            initial_state: "[elˇelement]".into(),
10435            buffer_marked_text: "[<el|element>]".into(),
10436            completion_text: "element",
10437            expected_with_insert_mode: "[elementˇelement]".into(),
10438            expected_with_replace_mode: "[elementˇ]".into(),
10439            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10440            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10441        },
10442        Run {
10443            run_description: "Ends with matching suffix",
10444            initial_state: "SubˇError".into(),
10445            buffer_marked_text: "<Sub|Error>".into(),
10446            completion_text: "SubscriptionError",
10447            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10448            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10449            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10450            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10451        },
10452        Run {
10453            run_description: "Suffix is a subsequence -- contiguous",
10454            initial_state: "SubˇErr".into(),
10455            buffer_marked_text: "<Sub|Err>".into(),
10456            completion_text: "SubscriptionError",
10457            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10458            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10459            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10460            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10461        },
10462        Run {
10463            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10464            initial_state: "Suˇscrirr".into(),
10465            buffer_marked_text: "<Su|scrirr>".into(),
10466            completion_text: "SubscriptionError",
10467            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10468            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10469            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10470            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10471        },
10472        Run {
10473            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10474            initial_state: "foo(indˇix)".into(),
10475            buffer_marked_text: "foo(<ind|ix>)".into(),
10476            completion_text: "node_index",
10477            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10478            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10479            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10480            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10481        },
10482    ];
10483
10484    for run in runs {
10485        let run_variations = [
10486            (LspInsertMode::Insert, run.expected_with_insert_mode),
10487            (LspInsertMode::Replace, run.expected_with_replace_mode),
10488            (
10489                LspInsertMode::ReplaceSubsequence,
10490                run.expected_with_replace_subsequence_mode,
10491            ),
10492            (
10493                LspInsertMode::ReplaceSuffix,
10494                run.expected_with_replace_suffix_mode,
10495            ),
10496        ];
10497
10498        for (lsp_insert_mode, expected_text) in run_variations {
10499            eprintln!(
10500                "run = {:?}, mode = {lsp_insert_mode:.?}",
10501                run.run_description,
10502            );
10503
10504            update_test_language_settings(&mut cx, |settings| {
10505                settings.defaults.completions = Some(CompletionSettings {
10506                    lsp_insert_mode,
10507                    words: WordsCompletionMode::Disabled,
10508                    lsp: true,
10509                    lsp_fetch_timeout_ms: 0,
10510                });
10511            });
10512
10513            cx.set_state(&run.initial_state);
10514            cx.update_editor(|editor, window, cx| {
10515                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10516            });
10517
10518            let counter = Arc::new(AtomicUsize::new(0));
10519            handle_completion_request_with_insert_and_replace(
10520                &mut cx,
10521                &run.buffer_marked_text,
10522                vec![run.completion_text],
10523                counter.clone(),
10524            )
10525            .await;
10526            cx.condition(|editor, _| editor.context_menu_visible())
10527                .await;
10528            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10529
10530            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10531                editor
10532                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10533                    .unwrap()
10534            });
10535            cx.assert_editor_state(&expected_text);
10536            handle_resolve_completion_request(&mut cx, None).await;
10537            apply_additional_edits.await.unwrap();
10538        }
10539    }
10540}
10541
10542#[gpui::test]
10543async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10544    init_test(cx, |_| {});
10545    let mut cx = EditorLspTestContext::new_rust(
10546        lsp::ServerCapabilities {
10547            completion_provider: Some(lsp::CompletionOptions {
10548                resolve_provider: Some(true),
10549                ..Default::default()
10550            }),
10551            ..Default::default()
10552        },
10553        cx,
10554    )
10555    .await;
10556
10557    let initial_state = "SubˇError";
10558    let buffer_marked_text = "<Sub|Error>";
10559    let completion_text = "SubscriptionError";
10560    let expected_with_insert_mode = "SubscriptionErrorˇError";
10561    let expected_with_replace_mode = "SubscriptionErrorˇ";
10562
10563    update_test_language_settings(&mut cx, |settings| {
10564        settings.defaults.completions = Some(CompletionSettings {
10565            words: WordsCompletionMode::Disabled,
10566            // set the opposite here to ensure that the action is overriding the default behavior
10567            lsp_insert_mode: LspInsertMode::Insert,
10568            lsp: true,
10569            lsp_fetch_timeout_ms: 0,
10570        });
10571    });
10572
10573    cx.set_state(initial_state);
10574    cx.update_editor(|editor, window, cx| {
10575        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10576    });
10577
10578    let counter = Arc::new(AtomicUsize::new(0));
10579    handle_completion_request_with_insert_and_replace(
10580        &mut cx,
10581        &buffer_marked_text,
10582        vec![completion_text],
10583        counter.clone(),
10584    )
10585    .await;
10586    cx.condition(|editor, _| editor.context_menu_visible())
10587        .await;
10588    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10589
10590    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10591        editor
10592            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10593            .unwrap()
10594    });
10595    cx.assert_editor_state(&expected_with_replace_mode);
10596    handle_resolve_completion_request(&mut cx, None).await;
10597    apply_additional_edits.await.unwrap();
10598
10599    update_test_language_settings(&mut cx, |settings| {
10600        settings.defaults.completions = Some(CompletionSettings {
10601            words: WordsCompletionMode::Disabled,
10602            // set the opposite here to ensure that the action is overriding the default behavior
10603            lsp_insert_mode: LspInsertMode::Replace,
10604            lsp: true,
10605            lsp_fetch_timeout_ms: 0,
10606        });
10607    });
10608
10609    cx.set_state(initial_state);
10610    cx.update_editor(|editor, window, cx| {
10611        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10612    });
10613    handle_completion_request_with_insert_and_replace(
10614        &mut cx,
10615        &buffer_marked_text,
10616        vec![completion_text],
10617        counter.clone(),
10618    )
10619    .await;
10620    cx.condition(|editor, _| editor.context_menu_visible())
10621        .await;
10622    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10623
10624    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10625        editor
10626            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10627            .unwrap()
10628    });
10629    cx.assert_editor_state(&expected_with_insert_mode);
10630    handle_resolve_completion_request(&mut cx, None).await;
10631    apply_additional_edits.await.unwrap();
10632}
10633
10634#[gpui::test]
10635async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10636    init_test(cx, |_| {});
10637    let mut cx = EditorLspTestContext::new_rust(
10638        lsp::ServerCapabilities {
10639            completion_provider: Some(lsp::CompletionOptions {
10640                resolve_provider: Some(true),
10641                ..Default::default()
10642            }),
10643            ..Default::default()
10644        },
10645        cx,
10646    )
10647    .await;
10648
10649    // scenario: surrounding text matches completion text
10650    let completion_text = "to_offset";
10651    let initial_state = indoc! {"
10652        1. buf.to_offˇsuffix
10653        2. buf.to_offˇsuf
10654        3. buf.to_offˇfix
10655        4. buf.to_offˇ
10656        5. into_offˇensive
10657        6. ˇsuffix
10658        7. let ˇ //
10659        8. aaˇzz
10660        9. buf.to_off«zzzzzˇ»suffix
10661        10. buf.«ˇzzzzz»suffix
10662        11. to_off«ˇzzzzz»
10663
10664        buf.to_offˇsuffix  // newest cursor
10665    "};
10666    let completion_marked_buffer = indoc! {"
10667        1. buf.to_offsuffix
10668        2. buf.to_offsuf
10669        3. buf.to_offfix
10670        4. buf.to_off
10671        5. into_offensive
10672        6. suffix
10673        7. let  //
10674        8. aazz
10675        9. buf.to_offzzzzzsuffix
10676        10. buf.zzzzzsuffix
10677        11. to_offzzzzz
10678
10679        buf.<to_off|suffix>  // newest cursor
10680    "};
10681    let expected = indoc! {"
10682        1. buf.to_offsetˇ
10683        2. buf.to_offsetˇsuf
10684        3. buf.to_offsetˇfix
10685        4. buf.to_offsetˇ
10686        5. into_offsetˇensive
10687        6. to_offsetˇsuffix
10688        7. let to_offsetˇ //
10689        8. aato_offsetˇzz
10690        9. buf.to_offsetˇ
10691        10. buf.to_offsetˇsuffix
10692        11. to_offsetˇ
10693
10694        buf.to_offsetˇ  // newest cursor
10695    "};
10696    cx.set_state(initial_state);
10697    cx.update_editor(|editor, window, cx| {
10698        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10699    });
10700    handle_completion_request_with_insert_and_replace(
10701        &mut cx,
10702        completion_marked_buffer,
10703        vec![completion_text],
10704        Arc::new(AtomicUsize::new(0)),
10705    )
10706    .await;
10707    cx.condition(|editor, _| editor.context_menu_visible())
10708        .await;
10709    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10710        editor
10711            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10712            .unwrap()
10713    });
10714    cx.assert_editor_state(expected);
10715    handle_resolve_completion_request(&mut cx, None).await;
10716    apply_additional_edits.await.unwrap();
10717
10718    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10719    let completion_text = "foo_and_bar";
10720    let initial_state = indoc! {"
10721        1. ooanbˇ
10722        2. zooanbˇ
10723        3. ooanbˇz
10724        4. zooanbˇz
10725        5. ooanˇ
10726        6. oanbˇ
10727
10728        ooanbˇ
10729    "};
10730    let completion_marked_buffer = indoc! {"
10731        1. ooanb
10732        2. zooanb
10733        3. ooanbz
10734        4. zooanbz
10735        5. ooan
10736        6. oanb
10737
10738        <ooanb|>
10739    "};
10740    let expected = indoc! {"
10741        1. foo_and_barˇ
10742        2. zfoo_and_barˇ
10743        3. foo_and_barˇz
10744        4. zfoo_and_barˇz
10745        5. ooanfoo_and_barˇ
10746        6. oanbfoo_and_barˇ
10747
10748        foo_and_barˇ
10749    "};
10750    cx.set_state(initial_state);
10751    cx.update_editor(|editor, window, cx| {
10752        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10753    });
10754    handle_completion_request_with_insert_and_replace(
10755        &mut cx,
10756        completion_marked_buffer,
10757        vec![completion_text],
10758        Arc::new(AtomicUsize::new(0)),
10759    )
10760    .await;
10761    cx.condition(|editor, _| editor.context_menu_visible())
10762        .await;
10763    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10764        editor
10765            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10766            .unwrap()
10767    });
10768    cx.assert_editor_state(expected);
10769    handle_resolve_completion_request(&mut cx, None).await;
10770    apply_additional_edits.await.unwrap();
10771
10772    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10773    // (expects the same as if it was inserted at the end)
10774    let completion_text = "foo_and_bar";
10775    let initial_state = indoc! {"
10776        1. ooˇanb
10777        2. zooˇanb
10778        3. ooˇanbz
10779        4. zooˇanbz
10780
10781        ooˇanb
10782    "};
10783    let completion_marked_buffer = indoc! {"
10784        1. ooanb
10785        2. zooanb
10786        3. ooanbz
10787        4. zooanbz
10788
10789        <oo|anb>
10790    "};
10791    let expected = indoc! {"
10792        1. foo_and_barˇ
10793        2. zfoo_and_barˇ
10794        3. foo_and_barˇz
10795        4. zfoo_and_barˇz
10796
10797        foo_and_barˇ
10798    "};
10799    cx.set_state(initial_state);
10800    cx.update_editor(|editor, window, cx| {
10801        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10802    });
10803    handle_completion_request_with_insert_and_replace(
10804        &mut cx,
10805        completion_marked_buffer,
10806        vec![completion_text],
10807        Arc::new(AtomicUsize::new(0)),
10808    )
10809    .await;
10810    cx.condition(|editor, _| editor.context_menu_visible())
10811        .await;
10812    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10813        editor
10814            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10815            .unwrap()
10816    });
10817    cx.assert_editor_state(expected);
10818    handle_resolve_completion_request(&mut cx, None).await;
10819    apply_additional_edits.await.unwrap();
10820}
10821
10822// This used to crash
10823#[gpui::test]
10824async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10825    init_test(cx, |_| {});
10826
10827    let buffer_text = indoc! {"
10828        fn main() {
10829            10.satu;
10830
10831            //
10832            // separate cursors so they open in different excerpts (manually reproducible)
10833            //
10834
10835            10.satu20;
10836        }
10837    "};
10838    let multibuffer_text_with_selections = indoc! {"
10839        fn main() {
10840            10.satuˇ;
10841
10842            //
10843
10844            //
10845
10846            10.satuˇ20;
10847        }
10848    "};
10849    let expected_multibuffer = indoc! {"
10850        fn main() {
10851            10.saturating_sub()ˇ;
10852
10853            //
10854
10855            //
10856
10857            10.saturating_sub()ˇ;
10858        }
10859    "};
10860
10861    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10862    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10863
10864    let fs = FakeFs::new(cx.executor());
10865    fs.insert_tree(
10866        path!("/a"),
10867        json!({
10868            "main.rs": buffer_text,
10869        }),
10870    )
10871    .await;
10872
10873    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10874    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10875    language_registry.add(rust_lang());
10876    let mut fake_servers = language_registry.register_fake_lsp(
10877        "Rust",
10878        FakeLspAdapter {
10879            capabilities: lsp::ServerCapabilities {
10880                completion_provider: Some(lsp::CompletionOptions {
10881                    resolve_provider: None,
10882                    ..lsp::CompletionOptions::default()
10883                }),
10884                ..lsp::ServerCapabilities::default()
10885            },
10886            ..FakeLspAdapter::default()
10887        },
10888    );
10889    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10890    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10891    let buffer = project
10892        .update(cx, |project, cx| {
10893            project.open_local_buffer(path!("/a/main.rs"), cx)
10894        })
10895        .await
10896        .unwrap();
10897
10898    let multi_buffer = cx.new(|cx| {
10899        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10900        multi_buffer.push_excerpts(
10901            buffer.clone(),
10902            [ExcerptRange::new(0..first_excerpt_end)],
10903            cx,
10904        );
10905        multi_buffer.push_excerpts(
10906            buffer.clone(),
10907            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10908            cx,
10909        );
10910        multi_buffer
10911    });
10912
10913    let editor = workspace
10914        .update(cx, |_, window, cx| {
10915            cx.new(|cx| {
10916                Editor::new(
10917                    EditorMode::Full {
10918                        scale_ui_elements_with_buffer_font_size: false,
10919                        show_active_line_background: false,
10920                        sized_by_content: false,
10921                    },
10922                    multi_buffer.clone(),
10923                    Some(project.clone()),
10924                    window,
10925                    cx,
10926                )
10927            })
10928        })
10929        .unwrap();
10930
10931    let pane = workspace
10932        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10933        .unwrap();
10934    pane.update_in(cx, |pane, window, cx| {
10935        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10936    });
10937
10938    let fake_server = fake_servers.next().await.unwrap();
10939
10940    editor.update_in(cx, |editor, window, cx| {
10941        editor.change_selections(None, window, cx, |s| {
10942            s.select_ranges([
10943                Point::new(1, 11)..Point::new(1, 11),
10944                Point::new(7, 11)..Point::new(7, 11),
10945            ])
10946        });
10947
10948        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10949    });
10950
10951    editor.update_in(cx, |editor, window, cx| {
10952        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10953    });
10954
10955    fake_server
10956        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10957            let completion_item = lsp::CompletionItem {
10958                label: "saturating_sub()".into(),
10959                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10960                    lsp::InsertReplaceEdit {
10961                        new_text: "saturating_sub()".to_owned(),
10962                        insert: lsp::Range::new(
10963                            lsp::Position::new(7, 7),
10964                            lsp::Position::new(7, 11),
10965                        ),
10966                        replace: lsp::Range::new(
10967                            lsp::Position::new(7, 7),
10968                            lsp::Position::new(7, 13),
10969                        ),
10970                    },
10971                )),
10972                ..lsp::CompletionItem::default()
10973            };
10974
10975            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10976        })
10977        .next()
10978        .await
10979        .unwrap();
10980
10981    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10982        .await;
10983
10984    editor
10985        .update_in(cx, |editor, window, cx| {
10986            editor
10987                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10988                .unwrap()
10989        })
10990        .await
10991        .unwrap();
10992
10993    editor.update(cx, |editor, cx| {
10994        assert_text_with_selections(editor, expected_multibuffer, cx);
10995    })
10996}
10997
10998#[gpui::test]
10999async fn test_completion(cx: &mut TestAppContext) {
11000    init_test(cx, |_| {});
11001
11002    let mut cx = EditorLspTestContext::new_rust(
11003        lsp::ServerCapabilities {
11004            completion_provider: Some(lsp::CompletionOptions {
11005                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11006                resolve_provider: Some(true),
11007                ..Default::default()
11008            }),
11009            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11010            ..Default::default()
11011        },
11012        cx,
11013    )
11014    .await;
11015    let counter = Arc::new(AtomicUsize::new(0));
11016
11017    cx.set_state(indoc! {"
11018        oneˇ
11019        two
11020        three
11021    "});
11022    cx.simulate_keystroke(".");
11023    handle_completion_request(
11024        &mut cx,
11025        indoc! {"
11026            one.|<>
11027            two
11028            three
11029        "},
11030        vec!["first_completion", "second_completion"],
11031        counter.clone(),
11032    )
11033    .await;
11034    cx.condition(|editor, _| editor.context_menu_visible())
11035        .await;
11036    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11037
11038    let _handler = handle_signature_help_request(
11039        &mut cx,
11040        lsp::SignatureHelp {
11041            signatures: vec![lsp::SignatureInformation {
11042                label: "test signature".to_string(),
11043                documentation: None,
11044                parameters: Some(vec![lsp::ParameterInformation {
11045                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
11046                    documentation: None,
11047                }]),
11048                active_parameter: None,
11049            }],
11050            active_signature: None,
11051            active_parameter: None,
11052        },
11053    );
11054    cx.update_editor(|editor, window, cx| {
11055        assert!(
11056            !editor.signature_help_state.is_shown(),
11057            "No signature help was called for"
11058        );
11059        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11060    });
11061    cx.run_until_parked();
11062    cx.update_editor(|editor, _, _| {
11063        assert!(
11064            !editor.signature_help_state.is_shown(),
11065            "No signature help should be shown when completions menu is open"
11066        );
11067    });
11068
11069    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11070        editor.context_menu_next(&Default::default(), window, cx);
11071        editor
11072            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11073            .unwrap()
11074    });
11075    cx.assert_editor_state(indoc! {"
11076        one.second_completionˇ
11077        two
11078        three
11079    "});
11080
11081    handle_resolve_completion_request(
11082        &mut cx,
11083        Some(vec![
11084            (
11085                //This overlaps with the primary completion edit which is
11086                //misbehavior from the LSP spec, test that we filter it out
11087                indoc! {"
11088                    one.second_ˇcompletion
11089                    two
11090                    threeˇ
11091                "},
11092                "overlapping additional edit",
11093            ),
11094            (
11095                indoc! {"
11096                    one.second_completion
11097                    two
11098                    threeˇ
11099                "},
11100                "\nadditional edit",
11101            ),
11102        ]),
11103    )
11104    .await;
11105    apply_additional_edits.await.unwrap();
11106    cx.assert_editor_state(indoc! {"
11107        one.second_completionˇ
11108        two
11109        three
11110        additional edit
11111    "});
11112
11113    cx.set_state(indoc! {"
11114        one.second_completion
11115        twoˇ
11116        threeˇ
11117        additional edit
11118    "});
11119    cx.simulate_keystroke(" ");
11120    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11121    cx.simulate_keystroke("s");
11122    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11123
11124    cx.assert_editor_state(indoc! {"
11125        one.second_completion
11126        two sˇ
11127        three sˇ
11128        additional edit
11129    "});
11130    handle_completion_request(
11131        &mut cx,
11132        indoc! {"
11133            one.second_completion
11134            two s
11135            three <s|>
11136            additional edit
11137        "},
11138        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11139        counter.clone(),
11140    )
11141    .await;
11142    cx.condition(|editor, _| editor.context_menu_visible())
11143        .await;
11144    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11145
11146    cx.simulate_keystroke("i");
11147
11148    handle_completion_request(
11149        &mut cx,
11150        indoc! {"
11151            one.second_completion
11152            two si
11153            three <si|>
11154            additional edit
11155        "},
11156        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11157        counter.clone(),
11158    )
11159    .await;
11160    cx.condition(|editor, _| editor.context_menu_visible())
11161        .await;
11162    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11163
11164    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11165        editor
11166            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11167            .unwrap()
11168    });
11169    cx.assert_editor_state(indoc! {"
11170        one.second_completion
11171        two sixth_completionˇ
11172        three sixth_completionˇ
11173        additional edit
11174    "});
11175
11176    apply_additional_edits.await.unwrap();
11177
11178    update_test_language_settings(&mut cx, |settings| {
11179        settings.defaults.show_completions_on_input = Some(false);
11180    });
11181    cx.set_state("editorˇ");
11182    cx.simulate_keystroke(".");
11183    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11184    cx.simulate_keystrokes("c l o");
11185    cx.assert_editor_state("editor.cloˇ");
11186    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11187    cx.update_editor(|editor, window, cx| {
11188        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11189    });
11190    handle_completion_request(
11191        &mut cx,
11192        "editor.<clo|>",
11193        vec!["close", "clobber"],
11194        counter.clone(),
11195    )
11196    .await;
11197    cx.condition(|editor, _| editor.context_menu_visible())
11198        .await;
11199    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11200
11201    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11202        editor
11203            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11204            .unwrap()
11205    });
11206    cx.assert_editor_state("editor.closeˇ");
11207    handle_resolve_completion_request(&mut cx, None).await;
11208    apply_additional_edits.await.unwrap();
11209}
11210
11211#[gpui::test]
11212async fn test_word_completion(cx: &mut TestAppContext) {
11213    let lsp_fetch_timeout_ms = 10;
11214    init_test(cx, |language_settings| {
11215        language_settings.defaults.completions = Some(CompletionSettings {
11216            words: WordsCompletionMode::Fallback,
11217            lsp: true,
11218            lsp_fetch_timeout_ms: 10,
11219            lsp_insert_mode: LspInsertMode::Insert,
11220        });
11221    });
11222
11223    let mut cx = EditorLspTestContext::new_rust(
11224        lsp::ServerCapabilities {
11225            completion_provider: Some(lsp::CompletionOptions {
11226                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11227                ..lsp::CompletionOptions::default()
11228            }),
11229            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11230            ..lsp::ServerCapabilities::default()
11231        },
11232        cx,
11233    )
11234    .await;
11235
11236    let throttle_completions = Arc::new(AtomicBool::new(false));
11237
11238    let lsp_throttle_completions = throttle_completions.clone();
11239    let _completion_requests_handler =
11240        cx.lsp
11241            .server
11242            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11243                let lsp_throttle_completions = lsp_throttle_completions.clone();
11244                let cx = cx.clone();
11245                async move {
11246                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11247                        cx.background_executor()
11248                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11249                            .await;
11250                    }
11251                    Ok(Some(lsp::CompletionResponse::Array(vec![
11252                        lsp::CompletionItem {
11253                            label: "first".into(),
11254                            ..lsp::CompletionItem::default()
11255                        },
11256                        lsp::CompletionItem {
11257                            label: "last".into(),
11258                            ..lsp::CompletionItem::default()
11259                        },
11260                    ])))
11261                }
11262            });
11263
11264    cx.set_state(indoc! {"
11265        oneˇ
11266        two
11267        three
11268    "});
11269    cx.simulate_keystroke(".");
11270    cx.executor().run_until_parked();
11271    cx.condition(|editor, _| editor.context_menu_visible())
11272        .await;
11273    cx.update_editor(|editor, window, cx| {
11274        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11275        {
11276            assert_eq!(
11277                completion_menu_entries(&menu),
11278                &["first", "last"],
11279                "When LSP server is fast to reply, no fallback word completions are used"
11280            );
11281        } else {
11282            panic!("expected completion menu to be open");
11283        }
11284        editor.cancel(&Cancel, window, cx);
11285    });
11286    cx.executor().run_until_parked();
11287    cx.condition(|editor, _| !editor.context_menu_visible())
11288        .await;
11289
11290    throttle_completions.store(true, atomic::Ordering::Release);
11291    cx.simulate_keystroke(".");
11292    cx.executor()
11293        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11294    cx.executor().run_until_parked();
11295    cx.condition(|editor, _| editor.context_menu_visible())
11296        .await;
11297    cx.update_editor(|editor, _, _| {
11298        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11299        {
11300            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11301                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11302        } else {
11303            panic!("expected completion menu to be open");
11304        }
11305    });
11306}
11307
11308#[gpui::test]
11309async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11310    init_test(cx, |language_settings| {
11311        language_settings.defaults.completions = Some(CompletionSettings {
11312            words: WordsCompletionMode::Enabled,
11313            lsp: true,
11314            lsp_fetch_timeout_ms: 0,
11315            lsp_insert_mode: LspInsertMode::Insert,
11316        });
11317    });
11318
11319    let mut cx = EditorLspTestContext::new_rust(
11320        lsp::ServerCapabilities {
11321            completion_provider: Some(lsp::CompletionOptions {
11322                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11323                ..lsp::CompletionOptions::default()
11324            }),
11325            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11326            ..lsp::ServerCapabilities::default()
11327        },
11328        cx,
11329    )
11330    .await;
11331
11332    let _completion_requests_handler =
11333        cx.lsp
11334            .server
11335            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11336                Ok(Some(lsp::CompletionResponse::Array(vec![
11337                    lsp::CompletionItem {
11338                        label: "first".into(),
11339                        ..lsp::CompletionItem::default()
11340                    },
11341                    lsp::CompletionItem {
11342                        label: "last".into(),
11343                        ..lsp::CompletionItem::default()
11344                    },
11345                ])))
11346            });
11347
11348    cx.set_state(indoc! {"ˇ
11349        first
11350        last
11351        second
11352    "});
11353    cx.simulate_keystroke(".");
11354    cx.executor().run_until_parked();
11355    cx.condition(|editor, _| editor.context_menu_visible())
11356        .await;
11357    cx.update_editor(|editor, _, _| {
11358        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11359        {
11360            assert_eq!(
11361                completion_menu_entries(&menu),
11362                &["first", "last", "second"],
11363                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11364            );
11365        } else {
11366            panic!("expected completion menu to be open");
11367        }
11368    });
11369}
11370
11371#[gpui::test]
11372async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11373    init_test(cx, |language_settings| {
11374        language_settings.defaults.completions = Some(CompletionSettings {
11375            words: WordsCompletionMode::Disabled,
11376            lsp: true,
11377            lsp_fetch_timeout_ms: 0,
11378            lsp_insert_mode: LspInsertMode::Insert,
11379        });
11380    });
11381
11382    let mut cx = EditorLspTestContext::new_rust(
11383        lsp::ServerCapabilities {
11384            completion_provider: Some(lsp::CompletionOptions {
11385                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11386                ..lsp::CompletionOptions::default()
11387            }),
11388            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11389            ..lsp::ServerCapabilities::default()
11390        },
11391        cx,
11392    )
11393    .await;
11394
11395    let _completion_requests_handler =
11396        cx.lsp
11397            .server
11398            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11399                panic!("LSP completions should not be queried when dealing with word completions")
11400            });
11401
11402    cx.set_state(indoc! {"ˇ
11403        first
11404        last
11405        second
11406    "});
11407    cx.update_editor(|editor, window, cx| {
11408        editor.show_word_completions(&ShowWordCompletions, window, cx);
11409    });
11410    cx.executor().run_until_parked();
11411    cx.condition(|editor, _| editor.context_menu_visible())
11412        .await;
11413    cx.update_editor(|editor, _, _| {
11414        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11415        {
11416            assert_eq!(
11417                completion_menu_entries(&menu),
11418                &["first", "last", "second"],
11419                "`ShowWordCompletions` action should show word completions"
11420            );
11421        } else {
11422            panic!("expected completion menu to be open");
11423        }
11424    });
11425
11426    cx.simulate_keystroke("l");
11427    cx.executor().run_until_parked();
11428    cx.condition(|editor, _| editor.context_menu_visible())
11429        .await;
11430    cx.update_editor(|editor, _, _| {
11431        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11432        {
11433            assert_eq!(
11434                completion_menu_entries(&menu),
11435                &["last"],
11436                "After showing word completions, further editing should filter them and not query the LSP"
11437            );
11438        } else {
11439            panic!("expected completion menu to be open");
11440        }
11441    });
11442}
11443
11444#[gpui::test]
11445async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11446    init_test(cx, |language_settings| {
11447        language_settings.defaults.completions = Some(CompletionSettings {
11448            words: WordsCompletionMode::Fallback,
11449            lsp: false,
11450            lsp_fetch_timeout_ms: 0,
11451            lsp_insert_mode: LspInsertMode::Insert,
11452        });
11453    });
11454
11455    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11456
11457    cx.set_state(indoc! {"ˇ
11458        0_usize
11459        let
11460        33
11461        4.5f32
11462    "});
11463    cx.update_editor(|editor, window, cx| {
11464        editor.show_completions(&ShowCompletions::default(), window, cx);
11465    });
11466    cx.executor().run_until_parked();
11467    cx.condition(|editor, _| editor.context_menu_visible())
11468        .await;
11469    cx.update_editor(|editor, window, cx| {
11470        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11471        {
11472            assert_eq!(
11473                completion_menu_entries(&menu),
11474                &["let"],
11475                "With no digits in the completion query, no digits should be in the word completions"
11476            );
11477        } else {
11478            panic!("expected completion menu to be open");
11479        }
11480        editor.cancel(&Cancel, window, cx);
11481    });
11482
11483    cx.set_state(indoc! {"11484        0_usize
11485        let
11486        3
11487        33.35f32
11488    "});
11489    cx.update_editor(|editor, window, cx| {
11490        editor.show_completions(&ShowCompletions::default(), window, cx);
11491    });
11492    cx.executor().run_until_parked();
11493    cx.condition(|editor, _| editor.context_menu_visible())
11494        .await;
11495    cx.update_editor(|editor, _, _| {
11496        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11497        {
11498            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11499                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11500        } else {
11501            panic!("expected completion menu to be open");
11502        }
11503    });
11504}
11505
11506fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11507    let position = || lsp::Position {
11508        line: params.text_document_position.position.line,
11509        character: params.text_document_position.position.character,
11510    };
11511    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11512        range: lsp::Range {
11513            start: position(),
11514            end: position(),
11515        },
11516        new_text: text.to_string(),
11517    }))
11518}
11519
11520#[gpui::test]
11521async fn test_multiline_completion(cx: &mut TestAppContext) {
11522    init_test(cx, |_| {});
11523
11524    let fs = FakeFs::new(cx.executor());
11525    fs.insert_tree(
11526        path!("/a"),
11527        json!({
11528            "main.ts": "a",
11529        }),
11530    )
11531    .await;
11532
11533    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11534    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11535    let typescript_language = Arc::new(Language::new(
11536        LanguageConfig {
11537            name: "TypeScript".into(),
11538            matcher: LanguageMatcher {
11539                path_suffixes: vec!["ts".to_string()],
11540                ..LanguageMatcher::default()
11541            },
11542            line_comments: vec!["// ".into()],
11543            ..LanguageConfig::default()
11544        },
11545        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11546    ));
11547    language_registry.add(typescript_language.clone());
11548    let mut fake_servers = language_registry.register_fake_lsp(
11549        "TypeScript",
11550        FakeLspAdapter {
11551            capabilities: lsp::ServerCapabilities {
11552                completion_provider: Some(lsp::CompletionOptions {
11553                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11554                    ..lsp::CompletionOptions::default()
11555                }),
11556                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11557                ..lsp::ServerCapabilities::default()
11558            },
11559            // Emulate vtsls label generation
11560            label_for_completion: Some(Box::new(|item, _| {
11561                let text = if let Some(description) = item
11562                    .label_details
11563                    .as_ref()
11564                    .and_then(|label_details| label_details.description.as_ref())
11565                {
11566                    format!("{} {}", item.label, description)
11567                } else if let Some(detail) = &item.detail {
11568                    format!("{} {}", item.label, detail)
11569                } else {
11570                    item.label.clone()
11571                };
11572                let len = text.len();
11573                Some(language::CodeLabel {
11574                    text,
11575                    runs: Vec::new(),
11576                    filter_range: 0..len,
11577                })
11578            })),
11579            ..FakeLspAdapter::default()
11580        },
11581    );
11582    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11583    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11584    let worktree_id = workspace
11585        .update(cx, |workspace, _window, cx| {
11586            workspace.project().update(cx, |project, cx| {
11587                project.worktrees(cx).next().unwrap().read(cx).id()
11588            })
11589        })
11590        .unwrap();
11591    let _buffer = project
11592        .update(cx, |project, cx| {
11593            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11594        })
11595        .await
11596        .unwrap();
11597    let editor = workspace
11598        .update(cx, |workspace, window, cx| {
11599            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11600        })
11601        .unwrap()
11602        .await
11603        .unwrap()
11604        .downcast::<Editor>()
11605        .unwrap();
11606    let fake_server = fake_servers.next().await.unwrap();
11607
11608    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11609    let multiline_label_2 = "a\nb\nc\n";
11610    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11611    let multiline_description = "d\ne\nf\n";
11612    let multiline_detail_2 = "g\nh\ni\n";
11613
11614    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11615        move |params, _| async move {
11616            Ok(Some(lsp::CompletionResponse::Array(vec![
11617                lsp::CompletionItem {
11618                    label: multiline_label.to_string(),
11619                    text_edit: gen_text_edit(&params, "new_text_1"),
11620                    ..lsp::CompletionItem::default()
11621                },
11622                lsp::CompletionItem {
11623                    label: "single line label 1".to_string(),
11624                    detail: Some(multiline_detail.to_string()),
11625                    text_edit: gen_text_edit(&params, "new_text_2"),
11626                    ..lsp::CompletionItem::default()
11627                },
11628                lsp::CompletionItem {
11629                    label: "single line label 2".to_string(),
11630                    label_details: Some(lsp::CompletionItemLabelDetails {
11631                        description: Some(multiline_description.to_string()),
11632                        detail: None,
11633                    }),
11634                    text_edit: gen_text_edit(&params, "new_text_2"),
11635                    ..lsp::CompletionItem::default()
11636                },
11637                lsp::CompletionItem {
11638                    label: multiline_label_2.to_string(),
11639                    detail: Some(multiline_detail_2.to_string()),
11640                    text_edit: gen_text_edit(&params, "new_text_3"),
11641                    ..lsp::CompletionItem::default()
11642                },
11643                lsp::CompletionItem {
11644                    label: "Label with many     spaces and \t but without newlines".to_string(),
11645                    detail: Some(
11646                        "Details with many     spaces and \t but without newlines".to_string(),
11647                    ),
11648                    text_edit: gen_text_edit(&params, "new_text_4"),
11649                    ..lsp::CompletionItem::default()
11650                },
11651            ])))
11652        },
11653    );
11654
11655    editor.update_in(cx, |editor, window, cx| {
11656        cx.focus_self(window);
11657        editor.move_to_end(&MoveToEnd, window, cx);
11658        editor.handle_input(".", window, cx);
11659    });
11660    cx.run_until_parked();
11661    completion_handle.next().await.unwrap();
11662
11663    editor.update(cx, |editor, _| {
11664        assert!(editor.context_menu_visible());
11665        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11666        {
11667            let completion_labels = menu
11668                .completions
11669                .borrow()
11670                .iter()
11671                .map(|c| c.label.text.clone())
11672                .collect::<Vec<_>>();
11673            assert_eq!(
11674                completion_labels,
11675                &[
11676                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11677                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11678                    "single line label 2 d e f ",
11679                    "a b c g h i ",
11680                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11681                ],
11682                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11683            );
11684
11685            for completion in menu
11686                .completions
11687                .borrow()
11688                .iter() {
11689                    assert_eq!(
11690                        completion.label.filter_range,
11691                        0..completion.label.text.len(),
11692                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11693                    );
11694                }
11695        } else {
11696            panic!("expected completion menu to be open");
11697        }
11698    });
11699}
11700
11701#[gpui::test]
11702async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11703    init_test(cx, |_| {});
11704    let mut cx = EditorLspTestContext::new_rust(
11705        lsp::ServerCapabilities {
11706            completion_provider: Some(lsp::CompletionOptions {
11707                trigger_characters: Some(vec![".".to_string()]),
11708                ..Default::default()
11709            }),
11710            ..Default::default()
11711        },
11712        cx,
11713    )
11714    .await;
11715    cx.lsp
11716        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11717            Ok(Some(lsp::CompletionResponse::Array(vec![
11718                lsp::CompletionItem {
11719                    label: "first".into(),
11720                    ..Default::default()
11721                },
11722                lsp::CompletionItem {
11723                    label: "last".into(),
11724                    ..Default::default()
11725                },
11726            ])))
11727        });
11728    cx.set_state("variableˇ");
11729    cx.simulate_keystroke(".");
11730    cx.executor().run_until_parked();
11731
11732    cx.update_editor(|editor, _, _| {
11733        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11734        {
11735            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11736        } else {
11737            panic!("expected completion menu to be open");
11738        }
11739    });
11740
11741    cx.update_editor(|editor, window, cx| {
11742        editor.move_page_down(&MovePageDown::default(), window, cx);
11743        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11744        {
11745            assert!(
11746                menu.selected_item == 1,
11747                "expected PageDown to select the last item from the context menu"
11748            );
11749        } else {
11750            panic!("expected completion menu to stay open after PageDown");
11751        }
11752    });
11753
11754    cx.update_editor(|editor, window, cx| {
11755        editor.move_page_up(&MovePageUp::default(), window, cx);
11756        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11757        {
11758            assert!(
11759                menu.selected_item == 0,
11760                "expected PageUp to select the first item from the context menu"
11761            );
11762        } else {
11763            panic!("expected completion menu to stay open after PageUp");
11764        }
11765    });
11766}
11767
11768#[gpui::test]
11769async fn test_as_is_completions(cx: &mut TestAppContext) {
11770    init_test(cx, |_| {});
11771    let mut cx = EditorLspTestContext::new_rust(
11772        lsp::ServerCapabilities {
11773            completion_provider: Some(lsp::CompletionOptions {
11774                ..Default::default()
11775            }),
11776            ..Default::default()
11777        },
11778        cx,
11779    )
11780    .await;
11781    cx.lsp
11782        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11783            Ok(Some(lsp::CompletionResponse::Array(vec![
11784                lsp::CompletionItem {
11785                    label: "unsafe".into(),
11786                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11787                        range: lsp::Range {
11788                            start: lsp::Position {
11789                                line: 1,
11790                                character: 2,
11791                            },
11792                            end: lsp::Position {
11793                                line: 1,
11794                                character: 3,
11795                            },
11796                        },
11797                        new_text: "unsafe".to_string(),
11798                    })),
11799                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11800                    ..Default::default()
11801                },
11802            ])))
11803        });
11804    cx.set_state("fn a() {}\n");
11805    cx.executor().run_until_parked();
11806    cx.update_editor(|editor, window, cx| {
11807        editor.show_completions(
11808            &ShowCompletions {
11809                trigger: Some("\n".into()),
11810            },
11811            window,
11812            cx,
11813        );
11814    });
11815    cx.executor().run_until_parked();
11816
11817    cx.update_editor(|editor, window, cx| {
11818        editor.confirm_completion(&Default::default(), window, cx)
11819    });
11820    cx.executor().run_until_parked();
11821    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11822}
11823
11824#[gpui::test]
11825async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11826    init_test(cx, |_| {});
11827
11828    let mut cx = EditorLspTestContext::new_rust(
11829        lsp::ServerCapabilities {
11830            completion_provider: Some(lsp::CompletionOptions {
11831                trigger_characters: Some(vec![".".to_string()]),
11832                resolve_provider: Some(true),
11833                ..Default::default()
11834            }),
11835            ..Default::default()
11836        },
11837        cx,
11838    )
11839    .await;
11840
11841    cx.set_state("fn main() { let a = 2ˇ; }");
11842    cx.simulate_keystroke(".");
11843    let completion_item = lsp::CompletionItem {
11844        label: "Some".into(),
11845        kind: Some(lsp::CompletionItemKind::SNIPPET),
11846        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11847        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11848            kind: lsp::MarkupKind::Markdown,
11849            value: "```rust\nSome(2)\n```".to_string(),
11850        })),
11851        deprecated: Some(false),
11852        sort_text: Some("Some".to_string()),
11853        filter_text: Some("Some".to_string()),
11854        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11855        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11856            range: lsp::Range {
11857                start: lsp::Position {
11858                    line: 0,
11859                    character: 22,
11860                },
11861                end: lsp::Position {
11862                    line: 0,
11863                    character: 22,
11864                },
11865            },
11866            new_text: "Some(2)".to_string(),
11867        })),
11868        additional_text_edits: Some(vec![lsp::TextEdit {
11869            range: lsp::Range {
11870                start: lsp::Position {
11871                    line: 0,
11872                    character: 20,
11873                },
11874                end: lsp::Position {
11875                    line: 0,
11876                    character: 22,
11877                },
11878            },
11879            new_text: "".to_string(),
11880        }]),
11881        ..Default::default()
11882    };
11883
11884    let closure_completion_item = completion_item.clone();
11885    let counter = Arc::new(AtomicUsize::new(0));
11886    let counter_clone = counter.clone();
11887    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11888        let task_completion_item = closure_completion_item.clone();
11889        counter_clone.fetch_add(1, atomic::Ordering::Release);
11890        async move {
11891            Ok(Some(lsp::CompletionResponse::Array(vec![
11892                task_completion_item,
11893            ])))
11894        }
11895    });
11896
11897    cx.condition(|editor, _| editor.context_menu_visible())
11898        .await;
11899    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11900    assert!(request.next().await.is_some());
11901    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11902
11903    cx.simulate_keystrokes("S o m");
11904    cx.condition(|editor, _| editor.context_menu_visible())
11905        .await;
11906    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11907    assert!(request.next().await.is_some());
11908    assert!(request.next().await.is_some());
11909    assert!(request.next().await.is_some());
11910    request.close();
11911    assert!(request.next().await.is_none());
11912    assert_eq!(
11913        counter.load(atomic::Ordering::Acquire),
11914        4,
11915        "With the completions menu open, only one LSP request should happen per input"
11916    );
11917}
11918
11919#[gpui::test]
11920async fn test_toggle_comment(cx: &mut TestAppContext) {
11921    init_test(cx, |_| {});
11922    let mut cx = EditorTestContext::new(cx).await;
11923    let language = Arc::new(Language::new(
11924        LanguageConfig {
11925            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11926            ..Default::default()
11927        },
11928        Some(tree_sitter_rust::LANGUAGE.into()),
11929    ));
11930    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11931
11932    // If multiple selections intersect a line, the line is only toggled once.
11933    cx.set_state(indoc! {"
11934        fn a() {
11935            «//b();
11936            ˇ»// «c();
11937            //ˇ»  d();
11938        }
11939    "});
11940
11941    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11942
11943    cx.assert_editor_state(indoc! {"
11944        fn a() {
11945            «b();
11946            c();
11947            ˇ» d();
11948        }
11949    "});
11950
11951    // The comment prefix is inserted at the same column for every line in a
11952    // selection.
11953    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11954
11955    cx.assert_editor_state(indoc! {"
11956        fn a() {
11957            // «b();
11958            // c();
11959            ˇ»//  d();
11960        }
11961    "});
11962
11963    // If a selection ends at the beginning of a line, that line is not toggled.
11964    cx.set_selections_state(indoc! {"
11965        fn a() {
11966            // b();
11967            «// c();
11968        ˇ»    //  d();
11969        }
11970    "});
11971
11972    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11973
11974    cx.assert_editor_state(indoc! {"
11975        fn a() {
11976            // b();
11977            «c();
11978        ˇ»    //  d();
11979        }
11980    "});
11981
11982    // If a selection span a single line and is empty, the line is toggled.
11983    cx.set_state(indoc! {"
11984        fn a() {
11985            a();
11986            b();
11987        ˇ
11988        }
11989    "});
11990
11991    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11992
11993    cx.assert_editor_state(indoc! {"
11994        fn a() {
11995            a();
11996            b();
11997        //•ˇ
11998        }
11999    "});
12000
12001    // If a selection span multiple lines, empty lines are not toggled.
12002    cx.set_state(indoc! {"
12003        fn a() {
12004            «a();
12005
12006            c();ˇ»
12007        }
12008    "});
12009
12010    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
12011
12012    cx.assert_editor_state(indoc! {"
12013        fn a() {
12014            // «a();
12015
12016            // c();ˇ»
12017        }
12018    "});
12019
12020    // If a selection includes multiple comment prefixes, all lines are uncommented.
12021    cx.set_state(indoc! {"
12022        fn a() {
12023            «// a();
12024            /// b();
12025            //! c();ˇ»
12026        }
12027    "});
12028
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            «a();
12034            b();
12035            c();ˇ»
12036        }
12037    "});
12038}
12039
12040#[gpui::test]
12041async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
12042    init_test(cx, |_| {});
12043    let mut cx = EditorTestContext::new(cx).await;
12044    let language = Arc::new(Language::new(
12045        LanguageConfig {
12046            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
12047            ..Default::default()
12048        },
12049        Some(tree_sitter_rust::LANGUAGE.into()),
12050    ));
12051    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
12052
12053    let toggle_comments = &ToggleComments {
12054        advance_downwards: false,
12055        ignore_indent: true,
12056    };
12057
12058    // If multiple selections intersect a line, the line is only toggled once.
12059    cx.set_state(indoc! {"
12060        fn a() {
12061        //    «b();
12062        //    c();
12063        //    ˇ» d();
12064        }
12065    "});
12066
12067    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12068
12069    cx.assert_editor_state(indoc! {"
12070        fn a() {
12071            «b();
12072            c();
12073            ˇ» d();
12074        }
12075    "});
12076
12077    // The comment prefix is inserted at the beginning of each line
12078    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12079
12080    cx.assert_editor_state(indoc! {"
12081        fn a() {
12082        //    «b();
12083        //    c();
12084        //    ˇ» d();
12085        }
12086    "});
12087
12088    // If a selection ends at the beginning of a line, that line is not toggled.
12089    cx.set_selections_state(indoc! {"
12090        fn a() {
12091        //    b();
12092        //    «c();
12093        ˇ»//     d();
12094        }
12095    "});
12096
12097    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12098
12099    cx.assert_editor_state(indoc! {"
12100        fn a() {
12101        //    b();
12102            «c();
12103        ˇ»//     d();
12104        }
12105    "});
12106
12107    // If a selection span a single line and is empty, the line is toggled.
12108    cx.set_state(indoc! {"
12109        fn a() {
12110            a();
12111            b();
12112        ˇ
12113        }
12114    "});
12115
12116    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12117
12118    cx.assert_editor_state(indoc! {"
12119        fn a() {
12120            a();
12121            b();
12122        //ˇ
12123        }
12124    "});
12125
12126    // If a selection span multiple lines, empty lines are not toggled.
12127    cx.set_state(indoc! {"
12128        fn a() {
12129            «a();
12130
12131            c();ˇ»
12132        }
12133    "});
12134
12135    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12136
12137    cx.assert_editor_state(indoc! {"
12138        fn a() {
12139        //    «a();
12140
12141        //    c();ˇ»
12142        }
12143    "});
12144
12145    // If a selection includes multiple comment prefixes, all lines are uncommented.
12146    cx.set_state(indoc! {"
12147        fn a() {
12148        //    «a();
12149        ///    b();
12150        //!    c();ˇ»
12151        }
12152    "});
12153
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            «a();
12159            b();
12160            c();ˇ»
12161        }
12162    "});
12163}
12164
12165#[gpui::test]
12166async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12167    init_test(cx, |_| {});
12168
12169    let language = Arc::new(Language::new(
12170        LanguageConfig {
12171            line_comments: vec!["// ".into()],
12172            ..Default::default()
12173        },
12174        Some(tree_sitter_rust::LANGUAGE.into()),
12175    ));
12176
12177    let mut cx = EditorTestContext::new(cx).await;
12178
12179    cx.language_registry().add(language.clone());
12180    cx.update_buffer(|buffer, cx| {
12181        buffer.set_language(Some(language), cx);
12182    });
12183
12184    let toggle_comments = &ToggleComments {
12185        advance_downwards: true,
12186        ignore_indent: false,
12187    };
12188
12189    // Single cursor on one line -> advance
12190    // Cursor moves horizontally 3 characters as well on non-blank line
12191    cx.set_state(indoc!(
12192        "fn a() {
12193             ˇdog();
12194             cat();
12195        }"
12196    ));
12197    cx.update_editor(|editor, window, cx| {
12198        editor.toggle_comments(toggle_comments, window, cx);
12199    });
12200    cx.assert_editor_state(indoc!(
12201        "fn a() {
12202             // dog();
12203             catˇ();
12204        }"
12205    ));
12206
12207    // Single selection on one line -> don't advance
12208    cx.set_state(indoc!(
12209        "fn a() {
12210             «dog()ˇ»;
12211             cat();
12212        }"
12213    ));
12214    cx.update_editor(|editor, window, cx| {
12215        editor.toggle_comments(toggle_comments, window, cx);
12216    });
12217    cx.assert_editor_state(indoc!(
12218        "fn a() {
12219             // «dog()ˇ»;
12220             cat();
12221        }"
12222    ));
12223
12224    // Multiple cursors on one line -> advance
12225    cx.set_state(indoc!(
12226        "fn a() {
12227             ˇdˇog();
12228             cat();
12229        }"
12230    ));
12231    cx.update_editor(|editor, window, cx| {
12232        editor.toggle_comments(toggle_comments, window, cx);
12233    });
12234    cx.assert_editor_state(indoc!(
12235        "fn a() {
12236             // dog();
12237             catˇ(ˇ);
12238        }"
12239    ));
12240
12241    // Multiple cursors on one line, with selection -> don't advance
12242    cx.set_state(indoc!(
12243        "fn a() {
12244             ˇdˇog«()ˇ»;
12245             cat();
12246        }"
12247    ));
12248    cx.update_editor(|editor, window, cx| {
12249        editor.toggle_comments(toggle_comments, window, cx);
12250    });
12251    cx.assert_editor_state(indoc!(
12252        "fn a() {
12253             // ˇdˇog«()ˇ»;
12254             cat();
12255        }"
12256    ));
12257
12258    // Single cursor on one line -> advance
12259    // Cursor moves to column 0 on blank line
12260    cx.set_state(indoc!(
12261        "fn a() {
12262             ˇdog();
12263
12264             cat();
12265        }"
12266    ));
12267    cx.update_editor(|editor, window, cx| {
12268        editor.toggle_comments(toggle_comments, window, cx);
12269    });
12270    cx.assert_editor_state(indoc!(
12271        "fn a() {
12272             // dog();
12273        ˇ
12274             cat();
12275        }"
12276    ));
12277
12278    // Single cursor on one line -> advance
12279    // Cursor starts and ends at column 0
12280    cx.set_state(indoc!(
12281        "fn a() {
12282         ˇ    dog();
12283             cat();
12284        }"
12285    ));
12286    cx.update_editor(|editor, window, cx| {
12287        editor.toggle_comments(toggle_comments, window, cx);
12288    });
12289    cx.assert_editor_state(indoc!(
12290        "fn a() {
12291             // dog();
12292         ˇ    cat();
12293        }"
12294    ));
12295}
12296
12297#[gpui::test]
12298async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12299    init_test(cx, |_| {});
12300
12301    let mut cx = EditorTestContext::new(cx).await;
12302
12303    let html_language = Arc::new(
12304        Language::new(
12305            LanguageConfig {
12306                name: "HTML".into(),
12307                block_comment: Some(("<!-- ".into(), " -->".into())),
12308                ..Default::default()
12309            },
12310            Some(tree_sitter_html::LANGUAGE.into()),
12311        )
12312        .with_injection_query(
12313            r#"
12314            (script_element
12315                (raw_text) @injection.content
12316                (#set! injection.language "javascript"))
12317            "#,
12318        )
12319        .unwrap(),
12320    );
12321
12322    let javascript_language = Arc::new(Language::new(
12323        LanguageConfig {
12324            name: "JavaScript".into(),
12325            line_comments: vec!["// ".into()],
12326            ..Default::default()
12327        },
12328        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12329    ));
12330
12331    cx.language_registry().add(html_language.clone());
12332    cx.language_registry().add(javascript_language.clone());
12333    cx.update_buffer(|buffer, cx| {
12334        buffer.set_language(Some(html_language), cx);
12335    });
12336
12337    // Toggle comments for empty selections
12338    cx.set_state(
12339        &r#"
12340            <p>A</p>ˇ
12341            <p>B</p>ˇ
12342            <p>C</p>ˇ
12343        "#
12344        .unindent(),
12345    );
12346    cx.update_editor(|editor, window, cx| {
12347        editor.toggle_comments(&ToggleComments::default(), window, cx)
12348    });
12349    cx.assert_editor_state(
12350        &r#"
12351            <!-- <p>A</p>ˇ -->
12352            <!-- <p>B</p>ˇ -->
12353            <!-- <p>C</p>ˇ -->
12354        "#
12355        .unindent(),
12356    );
12357    cx.update_editor(|editor, window, cx| {
12358        editor.toggle_comments(&ToggleComments::default(), window, cx)
12359    });
12360    cx.assert_editor_state(
12361        &r#"
12362            <p>A</p>ˇ
12363            <p>B</p>ˇ
12364            <p>C</p>ˇ
12365        "#
12366        .unindent(),
12367    );
12368
12369    // Toggle comments for mixture of empty and non-empty selections, where
12370    // multiple selections occupy a given line.
12371    cx.set_state(
12372        &r#"
12373            <p>A«</p>
12374            <p>ˇ»B</p>ˇ
12375            <p>C«</p>
12376            <p>ˇ»D</p>ˇ
12377        "#
12378        .unindent(),
12379    );
12380
12381    cx.update_editor(|editor, window, cx| {
12382        editor.toggle_comments(&ToggleComments::default(), window, cx)
12383    });
12384    cx.assert_editor_state(
12385        &r#"
12386            <!-- <p>A«</p>
12387            <p>ˇ»B</p>ˇ -->
12388            <!-- <p>C«</p>
12389            <p>ˇ»D</p>ˇ -->
12390        "#
12391        .unindent(),
12392    );
12393    cx.update_editor(|editor, window, cx| {
12394        editor.toggle_comments(&ToggleComments::default(), window, cx)
12395    });
12396    cx.assert_editor_state(
12397        &r#"
12398            <p>A«</p>
12399            <p>ˇ»B</p>ˇ
12400            <p>C«</p>
12401            <p>ˇ»D</p>ˇ
12402        "#
12403        .unindent(),
12404    );
12405
12406    // Toggle comments when different languages are active for different
12407    // selections.
12408    cx.set_state(
12409        &r#"
12410            ˇ<script>
12411                ˇvar x = new Y();
12412            ˇ</script>
12413        "#
12414        .unindent(),
12415    );
12416    cx.executor().run_until_parked();
12417    cx.update_editor(|editor, window, cx| {
12418        editor.toggle_comments(&ToggleComments::default(), window, cx)
12419    });
12420    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12421    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12422    cx.assert_editor_state(
12423        &r#"
12424            <!-- ˇ<script> -->
12425                // ˇvar x = new Y();
12426            <!-- ˇ</script> -->
12427        "#
12428        .unindent(),
12429    );
12430}
12431
12432#[gpui::test]
12433fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12434    init_test(cx, |_| {});
12435
12436    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12437    let multibuffer = cx.new(|cx| {
12438        let mut multibuffer = MultiBuffer::new(ReadWrite);
12439        multibuffer.push_excerpts(
12440            buffer.clone(),
12441            [
12442                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12443                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12444            ],
12445            cx,
12446        );
12447        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12448        multibuffer
12449    });
12450
12451    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12452    editor.update_in(cx, |editor, window, cx| {
12453        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12454        editor.change_selections(None, window, cx, |s| {
12455            s.select_ranges([
12456                Point::new(0, 0)..Point::new(0, 0),
12457                Point::new(1, 0)..Point::new(1, 0),
12458            ])
12459        });
12460
12461        editor.handle_input("X", window, cx);
12462        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12463        assert_eq!(
12464            editor.selections.ranges(cx),
12465            [
12466                Point::new(0, 1)..Point::new(0, 1),
12467                Point::new(1, 1)..Point::new(1, 1),
12468            ]
12469        );
12470
12471        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12472        editor.change_selections(None, window, cx, |s| {
12473            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12474        });
12475        editor.backspace(&Default::default(), window, cx);
12476        assert_eq!(editor.text(cx), "Xa\nbbb");
12477        assert_eq!(
12478            editor.selections.ranges(cx),
12479            [Point::new(1, 0)..Point::new(1, 0)]
12480        );
12481
12482        editor.change_selections(None, window, cx, |s| {
12483            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12484        });
12485        editor.backspace(&Default::default(), window, cx);
12486        assert_eq!(editor.text(cx), "X\nbb");
12487        assert_eq!(
12488            editor.selections.ranges(cx),
12489            [Point::new(0, 1)..Point::new(0, 1)]
12490        );
12491    });
12492}
12493
12494#[gpui::test]
12495fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12496    init_test(cx, |_| {});
12497
12498    let markers = vec![('[', ']').into(), ('(', ')').into()];
12499    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12500        indoc! {"
12501            [aaaa
12502            (bbbb]
12503            cccc)",
12504        },
12505        markers.clone(),
12506    );
12507    let excerpt_ranges = markers.into_iter().map(|marker| {
12508        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12509        ExcerptRange::new(context.clone())
12510    });
12511    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12512    let multibuffer = cx.new(|cx| {
12513        let mut multibuffer = MultiBuffer::new(ReadWrite);
12514        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12515        multibuffer
12516    });
12517
12518    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12519    editor.update_in(cx, |editor, window, cx| {
12520        let (expected_text, selection_ranges) = marked_text_ranges(
12521            indoc! {"
12522                aaaa
12523                bˇbbb
12524                bˇbbˇb
12525                cccc"
12526            },
12527            true,
12528        );
12529        assert_eq!(editor.text(cx), expected_text);
12530        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12531
12532        editor.handle_input("X", window, cx);
12533
12534        let (expected_text, expected_selections) = marked_text_ranges(
12535            indoc! {"
12536                aaaa
12537                bXˇbbXb
12538                bXˇbbXˇb
12539                cccc"
12540            },
12541            false,
12542        );
12543        assert_eq!(editor.text(cx), expected_text);
12544        assert_eq!(editor.selections.ranges(cx), expected_selections);
12545
12546        editor.newline(&Newline, window, cx);
12547        let (expected_text, expected_selections) = marked_text_ranges(
12548            indoc! {"
12549                aaaa
12550                bX
12551                ˇbbX
12552                b
12553                bX
12554                ˇbbX
12555                ˇb
12556                cccc"
12557            },
12558            false,
12559        );
12560        assert_eq!(editor.text(cx), expected_text);
12561        assert_eq!(editor.selections.ranges(cx), expected_selections);
12562    });
12563}
12564
12565#[gpui::test]
12566fn test_refresh_selections(cx: &mut TestAppContext) {
12567    init_test(cx, |_| {});
12568
12569    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12570    let mut excerpt1_id = None;
12571    let multibuffer = cx.new(|cx| {
12572        let mut multibuffer = MultiBuffer::new(ReadWrite);
12573        excerpt1_id = multibuffer
12574            .push_excerpts(
12575                buffer.clone(),
12576                [
12577                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12578                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12579                ],
12580                cx,
12581            )
12582            .into_iter()
12583            .next();
12584        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12585        multibuffer
12586    });
12587
12588    let editor = cx.add_window(|window, cx| {
12589        let mut editor = build_editor(multibuffer.clone(), window, cx);
12590        let snapshot = editor.snapshot(window, cx);
12591        editor.change_selections(None, window, cx, |s| {
12592            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12593        });
12594        editor.begin_selection(
12595            Point::new(2, 1).to_display_point(&snapshot),
12596            true,
12597            1,
12598            window,
12599            cx,
12600        );
12601        assert_eq!(
12602            editor.selections.ranges(cx),
12603            [
12604                Point::new(1, 3)..Point::new(1, 3),
12605                Point::new(2, 1)..Point::new(2, 1),
12606            ]
12607        );
12608        editor
12609    });
12610
12611    // Refreshing selections is a no-op when excerpts haven't changed.
12612    _ = editor.update(cx, |editor, window, cx| {
12613        editor.change_selections(None, window, cx, |s| s.refresh());
12614        assert_eq!(
12615            editor.selections.ranges(cx),
12616            [
12617                Point::new(1, 3)..Point::new(1, 3),
12618                Point::new(2, 1)..Point::new(2, 1),
12619            ]
12620        );
12621    });
12622
12623    multibuffer.update(cx, |multibuffer, cx| {
12624        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12625    });
12626    _ = editor.update(cx, |editor, window, cx| {
12627        // Removing an excerpt causes the first selection to become degenerate.
12628        assert_eq!(
12629            editor.selections.ranges(cx),
12630            [
12631                Point::new(0, 0)..Point::new(0, 0),
12632                Point::new(0, 1)..Point::new(0, 1)
12633            ]
12634        );
12635
12636        // Refreshing selections will relocate the first selection to the original buffer
12637        // location.
12638        editor.change_selections(None, window, cx, |s| s.refresh());
12639        assert_eq!(
12640            editor.selections.ranges(cx),
12641            [
12642                Point::new(0, 1)..Point::new(0, 1),
12643                Point::new(0, 3)..Point::new(0, 3)
12644            ]
12645        );
12646        assert!(editor.selections.pending_anchor().is_some());
12647    });
12648}
12649
12650#[gpui::test]
12651fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12652    init_test(cx, |_| {});
12653
12654    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12655    let mut excerpt1_id = None;
12656    let multibuffer = cx.new(|cx| {
12657        let mut multibuffer = MultiBuffer::new(ReadWrite);
12658        excerpt1_id = multibuffer
12659            .push_excerpts(
12660                buffer.clone(),
12661                [
12662                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12663                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12664                ],
12665                cx,
12666            )
12667            .into_iter()
12668            .next();
12669        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12670        multibuffer
12671    });
12672
12673    let editor = cx.add_window(|window, cx| {
12674        let mut editor = build_editor(multibuffer.clone(), window, cx);
12675        let snapshot = editor.snapshot(window, cx);
12676        editor.begin_selection(
12677            Point::new(1, 3).to_display_point(&snapshot),
12678            false,
12679            1,
12680            window,
12681            cx,
12682        );
12683        assert_eq!(
12684            editor.selections.ranges(cx),
12685            [Point::new(1, 3)..Point::new(1, 3)]
12686        );
12687        editor
12688    });
12689
12690    multibuffer.update(cx, |multibuffer, cx| {
12691        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12692    });
12693    _ = editor.update(cx, |editor, window, cx| {
12694        assert_eq!(
12695            editor.selections.ranges(cx),
12696            [Point::new(0, 0)..Point::new(0, 0)]
12697        );
12698
12699        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12700        editor.change_selections(None, window, cx, |s| s.refresh());
12701        assert_eq!(
12702            editor.selections.ranges(cx),
12703            [Point::new(0, 3)..Point::new(0, 3)]
12704        );
12705        assert!(editor.selections.pending_anchor().is_some());
12706    });
12707}
12708
12709#[gpui::test]
12710async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12711    init_test(cx, |_| {});
12712
12713    let language = Arc::new(
12714        Language::new(
12715            LanguageConfig {
12716                brackets: BracketPairConfig {
12717                    pairs: vec![
12718                        BracketPair {
12719                            start: "{".to_string(),
12720                            end: "}".to_string(),
12721                            close: true,
12722                            surround: true,
12723                            newline: true,
12724                        },
12725                        BracketPair {
12726                            start: "/* ".to_string(),
12727                            end: " */".to_string(),
12728                            close: true,
12729                            surround: true,
12730                            newline: true,
12731                        },
12732                    ],
12733                    ..Default::default()
12734                },
12735                ..Default::default()
12736            },
12737            Some(tree_sitter_rust::LANGUAGE.into()),
12738        )
12739        .with_indents_query("")
12740        .unwrap(),
12741    );
12742
12743    let text = concat!(
12744        "{   }\n",     //
12745        "  x\n",       //
12746        "  /*   */\n", //
12747        "x\n",         //
12748        "{{} }\n",     //
12749    );
12750
12751    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12752    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12753    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12754    editor
12755        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12756        .await;
12757
12758    editor.update_in(cx, |editor, window, cx| {
12759        editor.change_selections(None, window, cx, |s| {
12760            s.select_display_ranges([
12761                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12762                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12763                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12764            ])
12765        });
12766        editor.newline(&Newline, window, cx);
12767
12768        assert_eq!(
12769            editor.buffer().read(cx).read(cx).text(),
12770            concat!(
12771                "{ \n",    // Suppress rustfmt
12772                "\n",      //
12773                "}\n",     //
12774                "  x\n",   //
12775                "  /* \n", //
12776                "  \n",    //
12777                "  */\n",  //
12778                "x\n",     //
12779                "{{} \n",  //
12780                "}\n",     //
12781            )
12782        );
12783    });
12784}
12785
12786#[gpui::test]
12787fn test_highlighted_ranges(cx: &mut TestAppContext) {
12788    init_test(cx, |_| {});
12789
12790    let editor = cx.add_window(|window, cx| {
12791        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12792        build_editor(buffer.clone(), window, cx)
12793    });
12794
12795    _ = editor.update(cx, |editor, window, cx| {
12796        struct Type1;
12797        struct Type2;
12798
12799        let buffer = editor.buffer.read(cx).snapshot(cx);
12800
12801        let anchor_range =
12802            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12803
12804        editor.highlight_background::<Type1>(
12805            &[
12806                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12807                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12808                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12809                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12810            ],
12811            |_| Hsla::red(),
12812            cx,
12813        );
12814        editor.highlight_background::<Type2>(
12815            &[
12816                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12817                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12818                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12819                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12820            ],
12821            |_| Hsla::green(),
12822            cx,
12823        );
12824
12825        let snapshot = editor.snapshot(window, cx);
12826        let mut highlighted_ranges = editor.background_highlights_in_range(
12827            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12828            &snapshot,
12829            cx.theme().colors(),
12830        );
12831        // Enforce a consistent ordering based on color without relying on the ordering of the
12832        // highlight's `TypeId` which is non-executor.
12833        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12834        assert_eq!(
12835            highlighted_ranges,
12836            &[
12837                (
12838                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12839                    Hsla::red(),
12840                ),
12841                (
12842                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12843                    Hsla::red(),
12844                ),
12845                (
12846                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12847                    Hsla::green(),
12848                ),
12849                (
12850                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12851                    Hsla::green(),
12852                ),
12853            ]
12854        );
12855        assert_eq!(
12856            editor.background_highlights_in_range(
12857                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12858                &snapshot,
12859                cx.theme().colors(),
12860            ),
12861            &[(
12862                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12863                Hsla::red(),
12864            )]
12865        );
12866    });
12867}
12868
12869#[gpui::test]
12870async fn test_following(cx: &mut TestAppContext) {
12871    init_test(cx, |_| {});
12872
12873    let fs = FakeFs::new(cx.executor());
12874    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12875
12876    let buffer = project.update(cx, |project, cx| {
12877        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12878        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12879    });
12880    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12881    let follower = cx.update(|cx| {
12882        cx.open_window(
12883            WindowOptions {
12884                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12885                    gpui::Point::new(px(0.), px(0.)),
12886                    gpui::Point::new(px(10.), px(80.)),
12887                ))),
12888                ..Default::default()
12889            },
12890            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12891        )
12892        .unwrap()
12893    });
12894
12895    let is_still_following = Rc::new(RefCell::new(true));
12896    let follower_edit_event_count = Rc::new(RefCell::new(0));
12897    let pending_update = Rc::new(RefCell::new(None));
12898    let leader_entity = leader.root(cx).unwrap();
12899    let follower_entity = follower.root(cx).unwrap();
12900    _ = follower.update(cx, {
12901        let update = pending_update.clone();
12902        let is_still_following = is_still_following.clone();
12903        let follower_edit_event_count = follower_edit_event_count.clone();
12904        |_, window, cx| {
12905            cx.subscribe_in(
12906                &leader_entity,
12907                window,
12908                move |_, leader, event, window, cx| {
12909                    leader.read(cx).add_event_to_update_proto(
12910                        event,
12911                        &mut update.borrow_mut(),
12912                        window,
12913                        cx,
12914                    );
12915                },
12916            )
12917            .detach();
12918
12919            cx.subscribe_in(
12920                &follower_entity,
12921                window,
12922                move |_, _, event: &EditorEvent, _window, _cx| {
12923                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12924                        *is_still_following.borrow_mut() = false;
12925                    }
12926
12927                    if let EditorEvent::BufferEdited = event {
12928                        *follower_edit_event_count.borrow_mut() += 1;
12929                    }
12930                },
12931            )
12932            .detach();
12933        }
12934    });
12935
12936    // Update the selections only
12937    _ = leader.update(cx, |leader, window, cx| {
12938        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12939    });
12940    follower
12941        .update(cx, |follower, window, cx| {
12942            follower.apply_update_proto(
12943                &project,
12944                pending_update.borrow_mut().take().unwrap(),
12945                window,
12946                cx,
12947            )
12948        })
12949        .unwrap()
12950        .await
12951        .unwrap();
12952    _ = follower.update(cx, |follower, _, cx| {
12953        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12954    });
12955    assert!(*is_still_following.borrow());
12956    assert_eq!(*follower_edit_event_count.borrow(), 0);
12957
12958    // Update the scroll position only
12959    _ = leader.update(cx, |leader, window, cx| {
12960        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12961    });
12962    follower
12963        .update(cx, |follower, window, cx| {
12964            follower.apply_update_proto(
12965                &project,
12966                pending_update.borrow_mut().take().unwrap(),
12967                window,
12968                cx,
12969            )
12970        })
12971        .unwrap()
12972        .await
12973        .unwrap();
12974    assert_eq!(
12975        follower
12976            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12977            .unwrap(),
12978        gpui::Point::new(1.5, 3.5)
12979    );
12980    assert!(*is_still_following.borrow());
12981    assert_eq!(*follower_edit_event_count.borrow(), 0);
12982
12983    // Update the selections and scroll position. The follower's scroll position is updated
12984    // via autoscroll, not via the leader's exact scroll position.
12985    _ = leader.update(cx, |leader, window, cx| {
12986        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12987        leader.request_autoscroll(Autoscroll::newest(), cx);
12988        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12989    });
12990    follower
12991        .update(cx, |follower, window, cx| {
12992            follower.apply_update_proto(
12993                &project,
12994                pending_update.borrow_mut().take().unwrap(),
12995                window,
12996                cx,
12997            )
12998        })
12999        .unwrap()
13000        .await
13001        .unwrap();
13002    _ = follower.update(cx, |follower, _, cx| {
13003        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
13004        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
13005    });
13006    assert!(*is_still_following.borrow());
13007
13008    // Creating a pending selection that precedes another selection
13009    _ = leader.update(cx, |leader, window, cx| {
13010        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
13011        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
13012    });
13013    follower
13014        .update(cx, |follower, window, cx| {
13015            follower.apply_update_proto(
13016                &project,
13017                pending_update.borrow_mut().take().unwrap(),
13018                window,
13019                cx,
13020            )
13021        })
13022        .unwrap()
13023        .await
13024        .unwrap();
13025    _ = follower.update(cx, |follower, _, cx| {
13026        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
13027    });
13028    assert!(*is_still_following.borrow());
13029
13030    // Extend the pending selection so that it surrounds another selection
13031    _ = leader.update(cx, |leader, window, cx| {
13032        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
13033    });
13034    follower
13035        .update(cx, |follower, window, cx| {
13036            follower.apply_update_proto(
13037                &project,
13038                pending_update.borrow_mut().take().unwrap(),
13039                window,
13040                cx,
13041            )
13042        })
13043        .unwrap()
13044        .await
13045        .unwrap();
13046    _ = follower.update(cx, |follower, _, cx| {
13047        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
13048    });
13049
13050    // Scrolling locally breaks the follow
13051    _ = follower.update(cx, |follower, window, cx| {
13052        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13053        follower.set_scroll_anchor(
13054            ScrollAnchor {
13055                anchor: top_anchor,
13056                offset: gpui::Point::new(0.0, 0.5),
13057            },
13058            window,
13059            cx,
13060        );
13061    });
13062    assert!(!(*is_still_following.borrow()));
13063}
13064
13065#[gpui::test]
13066async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13067    init_test(cx, |_| {});
13068
13069    let fs = FakeFs::new(cx.executor());
13070    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13071    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13072    let pane = workspace
13073        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13074        .unwrap();
13075
13076    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13077
13078    let leader = pane.update_in(cx, |_, window, cx| {
13079        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13080        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13081    });
13082
13083    // Start following the editor when it has no excerpts.
13084    let mut state_message =
13085        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13086    let workspace_entity = workspace.root(cx).unwrap();
13087    let follower_1 = cx
13088        .update_window(*workspace.deref(), |_, window, cx| {
13089            Editor::from_state_proto(
13090                workspace_entity,
13091                ViewId {
13092                    creator: CollaboratorId::PeerId(PeerId::default()),
13093                    id: 0,
13094                },
13095                &mut state_message,
13096                window,
13097                cx,
13098            )
13099        })
13100        .unwrap()
13101        .unwrap()
13102        .await
13103        .unwrap();
13104
13105    let update_message = Rc::new(RefCell::new(None));
13106    follower_1.update_in(cx, {
13107        let update = update_message.clone();
13108        |_, window, cx| {
13109            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13110                leader.read(cx).add_event_to_update_proto(
13111                    event,
13112                    &mut update.borrow_mut(),
13113                    window,
13114                    cx,
13115                );
13116            })
13117            .detach();
13118        }
13119    });
13120
13121    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13122        (
13123            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13124            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13125        )
13126    });
13127
13128    // Insert some excerpts.
13129    leader.update(cx, |leader, cx| {
13130        leader.buffer.update(cx, |multibuffer, cx| {
13131            multibuffer.set_excerpts_for_path(
13132                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13133                buffer_1.clone(),
13134                vec![
13135                    Point::row_range(0..3),
13136                    Point::row_range(1..6),
13137                    Point::row_range(12..15),
13138                ],
13139                0,
13140                cx,
13141            );
13142            multibuffer.set_excerpts_for_path(
13143                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13144                buffer_2.clone(),
13145                vec![Point::row_range(0..6), Point::row_range(8..12)],
13146                0,
13147                cx,
13148            );
13149        });
13150    });
13151
13152    // Apply the update of adding the excerpts.
13153    follower_1
13154        .update_in(cx, |follower, window, cx| {
13155            follower.apply_update_proto(
13156                &project,
13157                update_message.borrow().clone().unwrap(),
13158                window,
13159                cx,
13160            )
13161        })
13162        .await
13163        .unwrap();
13164    assert_eq!(
13165        follower_1.update(cx, |editor, cx| editor.text(cx)),
13166        leader.update(cx, |editor, cx| editor.text(cx))
13167    );
13168    update_message.borrow_mut().take();
13169
13170    // Start following separately after it already has excerpts.
13171    let mut state_message =
13172        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13173    let workspace_entity = workspace.root(cx).unwrap();
13174    let follower_2 = cx
13175        .update_window(*workspace.deref(), |_, window, cx| {
13176            Editor::from_state_proto(
13177                workspace_entity,
13178                ViewId {
13179                    creator: CollaboratorId::PeerId(PeerId::default()),
13180                    id: 0,
13181                },
13182                &mut state_message,
13183                window,
13184                cx,
13185            )
13186        })
13187        .unwrap()
13188        .unwrap()
13189        .await
13190        .unwrap();
13191    assert_eq!(
13192        follower_2.update(cx, |editor, cx| editor.text(cx)),
13193        leader.update(cx, |editor, cx| editor.text(cx))
13194    );
13195
13196    // Remove some excerpts.
13197    leader.update(cx, |leader, cx| {
13198        leader.buffer.update(cx, |multibuffer, cx| {
13199            let excerpt_ids = multibuffer.excerpt_ids();
13200            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13201            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13202        });
13203    });
13204
13205    // Apply the update of removing the excerpts.
13206    follower_1
13207        .update_in(cx, |follower, window, cx| {
13208            follower.apply_update_proto(
13209                &project,
13210                update_message.borrow().clone().unwrap(),
13211                window,
13212                cx,
13213            )
13214        })
13215        .await
13216        .unwrap();
13217    follower_2
13218        .update_in(cx, |follower, window, cx| {
13219            follower.apply_update_proto(
13220                &project,
13221                update_message.borrow().clone().unwrap(),
13222                window,
13223                cx,
13224            )
13225        })
13226        .await
13227        .unwrap();
13228    update_message.borrow_mut().take();
13229    assert_eq!(
13230        follower_1.update(cx, |editor, cx| editor.text(cx)),
13231        leader.update(cx, |editor, cx| editor.text(cx))
13232    );
13233}
13234
13235#[gpui::test]
13236async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13237    init_test(cx, |_| {});
13238
13239    let mut cx = EditorTestContext::new(cx).await;
13240    let lsp_store =
13241        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13242
13243    cx.set_state(indoc! {"
13244        ˇfn func(abc def: i32) -> u32 {
13245        }
13246    "});
13247
13248    cx.update(|_, cx| {
13249        lsp_store.update(cx, |lsp_store, cx| {
13250            lsp_store
13251                .update_diagnostics(
13252                    LanguageServerId(0),
13253                    lsp::PublishDiagnosticsParams {
13254                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13255                        version: None,
13256                        diagnostics: vec![
13257                            lsp::Diagnostic {
13258                                range: lsp::Range::new(
13259                                    lsp::Position::new(0, 11),
13260                                    lsp::Position::new(0, 12),
13261                                ),
13262                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13263                                ..Default::default()
13264                            },
13265                            lsp::Diagnostic {
13266                                range: lsp::Range::new(
13267                                    lsp::Position::new(0, 12),
13268                                    lsp::Position::new(0, 15),
13269                                ),
13270                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13271                                ..Default::default()
13272                            },
13273                            lsp::Diagnostic {
13274                                range: lsp::Range::new(
13275                                    lsp::Position::new(0, 25),
13276                                    lsp::Position::new(0, 28),
13277                                ),
13278                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13279                                ..Default::default()
13280                            },
13281                        ],
13282                    },
13283                    &[],
13284                    cx,
13285                )
13286                .unwrap()
13287        });
13288    });
13289
13290    executor.run_until_parked();
13291
13292    cx.update_editor(|editor, window, cx| {
13293        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13294    });
13295
13296    cx.assert_editor_state(indoc! {"
13297        fn func(abc def: i32) -> ˇu32 {
13298        }
13299    "});
13300
13301    cx.update_editor(|editor, window, cx| {
13302        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13303    });
13304
13305    cx.assert_editor_state(indoc! {"
13306        fn func(abc ˇdef: i32) -> u32 {
13307        }
13308    "});
13309
13310    cx.update_editor(|editor, window, cx| {
13311        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13312    });
13313
13314    cx.assert_editor_state(indoc! {"
13315        fn func(abcˇ def: i32) -> u32 {
13316        }
13317    "});
13318
13319    cx.update_editor(|editor, window, cx| {
13320        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13321    });
13322
13323    cx.assert_editor_state(indoc! {"
13324        fn func(abc def: i32) -> ˇu32 {
13325        }
13326    "});
13327}
13328
13329#[gpui::test]
13330async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13331    init_test(cx, |_| {});
13332
13333    let mut cx = EditorTestContext::new(cx).await;
13334
13335    let diff_base = r#"
13336        use some::mod;
13337
13338        const A: u32 = 42;
13339
13340        fn main() {
13341            println!("hello");
13342
13343            println!("world");
13344        }
13345        "#
13346    .unindent();
13347
13348    // Edits are modified, removed, modified, added
13349    cx.set_state(
13350        &r#"
13351        use some::modified;
13352
13353        ˇ
13354        fn main() {
13355            println!("hello there");
13356
13357            println!("around the");
13358            println!("world");
13359        }
13360        "#
13361        .unindent(),
13362    );
13363
13364    cx.set_head_text(&diff_base);
13365    executor.run_until_parked();
13366
13367    cx.update_editor(|editor, window, cx| {
13368        //Wrap around the bottom of the buffer
13369        for _ in 0..3 {
13370            editor.go_to_next_hunk(&GoToHunk, window, cx);
13371        }
13372    });
13373
13374    cx.assert_editor_state(
13375        &r#"
13376        ˇuse some::modified;
13377
13378
13379        fn main() {
13380            println!("hello there");
13381
13382            println!("around the");
13383            println!("world");
13384        }
13385        "#
13386        .unindent(),
13387    );
13388
13389    cx.update_editor(|editor, window, cx| {
13390        //Wrap around the top of the buffer
13391        for _ in 0..2 {
13392            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13393        }
13394    });
13395
13396    cx.assert_editor_state(
13397        &r#"
13398        use some::modified;
13399
13400
13401        fn main() {
13402        ˇ    println!("hello there");
13403
13404            println!("around the");
13405            println!("world");
13406        }
13407        "#
13408        .unindent(),
13409    );
13410
13411    cx.update_editor(|editor, window, cx| {
13412        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13413    });
13414
13415    cx.assert_editor_state(
13416        &r#"
13417        use some::modified;
13418
13419        ˇ
13420        fn main() {
13421            println!("hello there");
13422
13423            println!("around the");
13424            println!("world");
13425        }
13426        "#
13427        .unindent(),
13428    );
13429
13430    cx.update_editor(|editor, window, cx| {
13431        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13432    });
13433
13434    cx.assert_editor_state(
13435        &r#"
13436        ˇuse some::modified;
13437
13438
13439        fn main() {
13440            println!("hello there");
13441
13442            println!("around the");
13443            println!("world");
13444        }
13445        "#
13446        .unindent(),
13447    );
13448
13449    cx.update_editor(|editor, window, cx| {
13450        for _ in 0..2 {
13451            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13452        }
13453    });
13454
13455    cx.assert_editor_state(
13456        &r#"
13457        use some::modified;
13458
13459
13460        fn main() {
13461        ˇ    println!("hello there");
13462
13463            println!("around the");
13464            println!("world");
13465        }
13466        "#
13467        .unindent(),
13468    );
13469
13470    cx.update_editor(|editor, window, cx| {
13471        editor.fold(&Fold, window, cx);
13472    });
13473
13474    cx.update_editor(|editor, window, cx| {
13475        editor.go_to_next_hunk(&GoToHunk, window, cx);
13476    });
13477
13478    cx.assert_editor_state(
13479        &r#"
13480        ˇuse some::modified;
13481
13482
13483        fn main() {
13484            println!("hello there");
13485
13486            println!("around the");
13487            println!("world");
13488        }
13489        "#
13490        .unindent(),
13491    );
13492}
13493
13494#[test]
13495fn test_split_words() {
13496    fn split(text: &str) -> Vec<&str> {
13497        split_words(text).collect()
13498    }
13499
13500    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13501    assert_eq!(split("hello_world"), &["hello_", "world"]);
13502    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13503    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13504    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13505    assert_eq!(split("helloworld"), &["helloworld"]);
13506
13507    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13508}
13509
13510#[gpui::test]
13511async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13512    init_test(cx, |_| {});
13513
13514    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13515    let mut assert = |before, after| {
13516        let _state_context = cx.set_state(before);
13517        cx.run_until_parked();
13518        cx.update_editor(|editor, window, cx| {
13519            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13520        });
13521        cx.run_until_parked();
13522        cx.assert_editor_state(after);
13523    };
13524
13525    // Outside bracket jumps to outside of matching bracket
13526    assert("console.logˇ(var);", "console.log(var)ˇ;");
13527    assert("console.log(var)ˇ;", "console.logˇ(var);");
13528
13529    // Inside bracket jumps to inside of matching bracket
13530    assert("console.log(ˇvar);", "console.log(varˇ);");
13531    assert("console.log(varˇ);", "console.log(ˇvar);");
13532
13533    // When outside a bracket and inside, favor jumping to the inside bracket
13534    assert(
13535        "console.log('foo', [1, 2, 3]ˇ);",
13536        "console.log(ˇ'foo', [1, 2, 3]);",
13537    );
13538    assert(
13539        "console.log(ˇ'foo', [1, 2, 3]);",
13540        "console.log('foo', [1, 2, 3]ˇ);",
13541    );
13542
13543    // Bias forward if two options are equally likely
13544    assert(
13545        "let result = curried_fun()ˇ();",
13546        "let result = curried_fun()()ˇ;",
13547    );
13548
13549    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13550    assert(
13551        indoc! {"
13552            function test() {
13553                console.log('test')ˇ
13554            }"},
13555        indoc! {"
13556            function test() {
13557                console.logˇ('test')
13558            }"},
13559    );
13560}
13561
13562#[gpui::test]
13563async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13564    init_test(cx, |_| {});
13565
13566    let fs = FakeFs::new(cx.executor());
13567    fs.insert_tree(
13568        path!("/a"),
13569        json!({
13570            "main.rs": "fn main() { let a = 5; }",
13571            "other.rs": "// Test file",
13572        }),
13573    )
13574    .await;
13575    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13576
13577    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13578    language_registry.add(Arc::new(Language::new(
13579        LanguageConfig {
13580            name: "Rust".into(),
13581            matcher: LanguageMatcher {
13582                path_suffixes: vec!["rs".to_string()],
13583                ..Default::default()
13584            },
13585            brackets: BracketPairConfig {
13586                pairs: vec![BracketPair {
13587                    start: "{".to_string(),
13588                    end: "}".to_string(),
13589                    close: true,
13590                    surround: true,
13591                    newline: true,
13592                }],
13593                disabled_scopes_by_bracket_ix: Vec::new(),
13594            },
13595            ..Default::default()
13596        },
13597        Some(tree_sitter_rust::LANGUAGE.into()),
13598    )));
13599    let mut fake_servers = language_registry.register_fake_lsp(
13600        "Rust",
13601        FakeLspAdapter {
13602            capabilities: lsp::ServerCapabilities {
13603                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13604                    first_trigger_character: "{".to_string(),
13605                    more_trigger_character: None,
13606                }),
13607                ..Default::default()
13608            },
13609            ..Default::default()
13610        },
13611    );
13612
13613    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13614
13615    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13616
13617    let worktree_id = workspace
13618        .update(cx, |workspace, _, cx| {
13619            workspace.project().update(cx, |project, cx| {
13620                project.worktrees(cx).next().unwrap().read(cx).id()
13621            })
13622        })
13623        .unwrap();
13624
13625    let buffer = project
13626        .update(cx, |project, cx| {
13627            project.open_local_buffer(path!("/a/main.rs"), cx)
13628        })
13629        .await
13630        .unwrap();
13631    let editor_handle = workspace
13632        .update(cx, |workspace, window, cx| {
13633            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13634        })
13635        .unwrap()
13636        .await
13637        .unwrap()
13638        .downcast::<Editor>()
13639        .unwrap();
13640
13641    cx.executor().start_waiting();
13642    let fake_server = fake_servers.next().await.unwrap();
13643
13644    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13645        |params, _| async move {
13646            assert_eq!(
13647                params.text_document_position.text_document.uri,
13648                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13649            );
13650            assert_eq!(
13651                params.text_document_position.position,
13652                lsp::Position::new(0, 21),
13653            );
13654
13655            Ok(Some(vec![lsp::TextEdit {
13656                new_text: "]".to_string(),
13657                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13658            }]))
13659        },
13660    );
13661
13662    editor_handle.update_in(cx, |editor, window, cx| {
13663        window.focus(&editor.focus_handle(cx));
13664        editor.change_selections(None, window, cx, |s| {
13665            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13666        });
13667        editor.handle_input("{", window, cx);
13668    });
13669
13670    cx.executor().run_until_parked();
13671
13672    buffer.update(cx, |buffer, _| {
13673        assert_eq!(
13674            buffer.text(),
13675            "fn main() { let a = {5}; }",
13676            "No extra braces from on type formatting should appear in the buffer"
13677        )
13678    });
13679}
13680
13681#[gpui::test]
13682async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13683    init_test(cx, |_| {});
13684
13685    let fs = FakeFs::new(cx.executor());
13686    fs.insert_tree(
13687        path!("/a"),
13688        json!({
13689            "main.rs": "fn main() { let a = 5; }",
13690            "other.rs": "// Test file",
13691        }),
13692    )
13693    .await;
13694
13695    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13696
13697    let server_restarts = Arc::new(AtomicUsize::new(0));
13698    let closure_restarts = Arc::clone(&server_restarts);
13699    let language_server_name = "test language server";
13700    let language_name: LanguageName = "Rust".into();
13701
13702    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13703    language_registry.add(Arc::new(Language::new(
13704        LanguageConfig {
13705            name: language_name.clone(),
13706            matcher: LanguageMatcher {
13707                path_suffixes: vec!["rs".to_string()],
13708                ..Default::default()
13709            },
13710            ..Default::default()
13711        },
13712        Some(tree_sitter_rust::LANGUAGE.into()),
13713    )));
13714    let mut fake_servers = language_registry.register_fake_lsp(
13715        "Rust",
13716        FakeLspAdapter {
13717            name: language_server_name,
13718            initialization_options: Some(json!({
13719                "testOptionValue": true
13720            })),
13721            initializer: Some(Box::new(move |fake_server| {
13722                let task_restarts = Arc::clone(&closure_restarts);
13723                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13724                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13725                    futures::future::ready(Ok(()))
13726                });
13727            })),
13728            ..Default::default()
13729        },
13730    );
13731
13732    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13733    let _buffer = project
13734        .update(cx, |project, cx| {
13735            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13736        })
13737        .await
13738        .unwrap();
13739    let _fake_server = fake_servers.next().await.unwrap();
13740    update_test_language_settings(cx, |language_settings| {
13741        language_settings.languages.insert(
13742            language_name.clone(),
13743            LanguageSettingsContent {
13744                tab_size: NonZeroU32::new(8),
13745                ..Default::default()
13746            },
13747        );
13748    });
13749    cx.executor().run_until_parked();
13750    assert_eq!(
13751        server_restarts.load(atomic::Ordering::Acquire),
13752        0,
13753        "Should not restart LSP server on an unrelated change"
13754    );
13755
13756    update_test_project_settings(cx, |project_settings| {
13757        project_settings.lsp.insert(
13758            "Some other server name".into(),
13759            LspSettings {
13760                binary: None,
13761                settings: None,
13762                initialization_options: Some(json!({
13763                    "some other init value": false
13764                })),
13765                enable_lsp_tasks: false,
13766            },
13767        );
13768    });
13769    cx.executor().run_until_parked();
13770    assert_eq!(
13771        server_restarts.load(atomic::Ordering::Acquire),
13772        0,
13773        "Should not restart LSP server on an unrelated LSP settings change"
13774    );
13775
13776    update_test_project_settings(cx, |project_settings| {
13777        project_settings.lsp.insert(
13778            language_server_name.into(),
13779            LspSettings {
13780                binary: None,
13781                settings: None,
13782                initialization_options: Some(json!({
13783                    "anotherInitValue": false
13784                })),
13785                enable_lsp_tasks: false,
13786            },
13787        );
13788    });
13789    cx.executor().run_until_parked();
13790    assert_eq!(
13791        server_restarts.load(atomic::Ordering::Acquire),
13792        1,
13793        "Should restart LSP server on a related LSP settings change"
13794    );
13795
13796    update_test_project_settings(cx, |project_settings| {
13797        project_settings.lsp.insert(
13798            language_server_name.into(),
13799            LspSettings {
13800                binary: None,
13801                settings: None,
13802                initialization_options: Some(json!({
13803                    "anotherInitValue": false
13804                })),
13805                enable_lsp_tasks: false,
13806            },
13807        );
13808    });
13809    cx.executor().run_until_parked();
13810    assert_eq!(
13811        server_restarts.load(atomic::Ordering::Acquire),
13812        1,
13813        "Should not restart LSP server on a related LSP settings change that is the same"
13814    );
13815
13816    update_test_project_settings(cx, |project_settings| {
13817        project_settings.lsp.insert(
13818            language_server_name.into(),
13819            LspSettings {
13820                binary: None,
13821                settings: None,
13822                initialization_options: None,
13823                enable_lsp_tasks: false,
13824            },
13825        );
13826    });
13827    cx.executor().run_until_parked();
13828    assert_eq!(
13829        server_restarts.load(atomic::Ordering::Acquire),
13830        2,
13831        "Should restart LSP server on another related LSP settings change"
13832    );
13833}
13834
13835#[gpui::test]
13836async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13837    init_test(cx, |_| {});
13838
13839    let mut cx = EditorLspTestContext::new_rust(
13840        lsp::ServerCapabilities {
13841            completion_provider: Some(lsp::CompletionOptions {
13842                trigger_characters: Some(vec![".".to_string()]),
13843                resolve_provider: Some(true),
13844                ..Default::default()
13845            }),
13846            ..Default::default()
13847        },
13848        cx,
13849    )
13850    .await;
13851
13852    cx.set_state("fn main() { let a = 2ˇ; }");
13853    cx.simulate_keystroke(".");
13854    let completion_item = lsp::CompletionItem {
13855        label: "some".into(),
13856        kind: Some(lsp::CompletionItemKind::SNIPPET),
13857        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13858        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13859            kind: lsp::MarkupKind::Markdown,
13860            value: "```rust\nSome(2)\n```".to_string(),
13861        })),
13862        deprecated: Some(false),
13863        sort_text: Some("fffffff2".to_string()),
13864        filter_text: Some("some".to_string()),
13865        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13866        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13867            range: lsp::Range {
13868                start: lsp::Position {
13869                    line: 0,
13870                    character: 22,
13871                },
13872                end: lsp::Position {
13873                    line: 0,
13874                    character: 22,
13875                },
13876            },
13877            new_text: "Some(2)".to_string(),
13878        })),
13879        additional_text_edits: Some(vec![lsp::TextEdit {
13880            range: lsp::Range {
13881                start: lsp::Position {
13882                    line: 0,
13883                    character: 20,
13884                },
13885                end: lsp::Position {
13886                    line: 0,
13887                    character: 22,
13888                },
13889            },
13890            new_text: "".to_string(),
13891        }]),
13892        ..Default::default()
13893    };
13894
13895    let closure_completion_item = completion_item.clone();
13896    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13897        let task_completion_item = closure_completion_item.clone();
13898        async move {
13899            Ok(Some(lsp::CompletionResponse::Array(vec![
13900                task_completion_item,
13901            ])))
13902        }
13903    });
13904
13905    request.next().await;
13906
13907    cx.condition(|editor, _| editor.context_menu_visible())
13908        .await;
13909    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13910        editor
13911            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13912            .unwrap()
13913    });
13914    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13915
13916    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13917        let task_completion_item = completion_item.clone();
13918        async move { Ok(task_completion_item) }
13919    })
13920    .next()
13921    .await
13922    .unwrap();
13923    apply_additional_edits.await.unwrap();
13924    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13925}
13926
13927#[gpui::test]
13928async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13929    init_test(cx, |_| {});
13930
13931    let mut cx = EditorLspTestContext::new_rust(
13932        lsp::ServerCapabilities {
13933            completion_provider: Some(lsp::CompletionOptions {
13934                trigger_characters: Some(vec![".".to_string()]),
13935                resolve_provider: Some(true),
13936                ..Default::default()
13937            }),
13938            ..Default::default()
13939        },
13940        cx,
13941    )
13942    .await;
13943
13944    cx.set_state("fn main() { let a = 2ˇ; }");
13945    cx.simulate_keystroke(".");
13946
13947    let item1 = lsp::CompletionItem {
13948        label: "method id()".to_string(),
13949        filter_text: Some("id".to_string()),
13950        detail: None,
13951        documentation: None,
13952        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13953            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13954            new_text: ".id".to_string(),
13955        })),
13956        ..lsp::CompletionItem::default()
13957    };
13958
13959    let item2 = lsp::CompletionItem {
13960        label: "other".to_string(),
13961        filter_text: Some("other".to_string()),
13962        detail: None,
13963        documentation: None,
13964        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13965            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13966            new_text: ".other".to_string(),
13967        })),
13968        ..lsp::CompletionItem::default()
13969    };
13970
13971    let item1 = item1.clone();
13972    cx.set_request_handler::<lsp::request::Completion, _, _>({
13973        let item1 = item1.clone();
13974        move |_, _, _| {
13975            let item1 = item1.clone();
13976            let item2 = item2.clone();
13977            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13978        }
13979    })
13980    .next()
13981    .await;
13982
13983    cx.condition(|editor, _| editor.context_menu_visible())
13984        .await;
13985    cx.update_editor(|editor, _, _| {
13986        let context_menu = editor.context_menu.borrow_mut();
13987        let context_menu = context_menu
13988            .as_ref()
13989            .expect("Should have the context menu deployed");
13990        match context_menu {
13991            CodeContextMenu::Completions(completions_menu) => {
13992                let completions = completions_menu.completions.borrow_mut();
13993                assert_eq!(
13994                    completions
13995                        .iter()
13996                        .map(|completion| &completion.label.text)
13997                        .collect::<Vec<_>>(),
13998                    vec!["method id()", "other"]
13999                )
14000            }
14001            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14002        }
14003    });
14004
14005    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
14006        let item1 = item1.clone();
14007        move |_, item_to_resolve, _| {
14008            let item1 = item1.clone();
14009            async move {
14010                if item1 == item_to_resolve {
14011                    Ok(lsp::CompletionItem {
14012                        label: "method id()".to_string(),
14013                        filter_text: Some("id".to_string()),
14014                        detail: Some("Now resolved!".to_string()),
14015                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
14016                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14017                            range: lsp::Range::new(
14018                                lsp::Position::new(0, 22),
14019                                lsp::Position::new(0, 22),
14020                            ),
14021                            new_text: ".id".to_string(),
14022                        })),
14023                        ..lsp::CompletionItem::default()
14024                    })
14025                } else {
14026                    Ok(item_to_resolve)
14027                }
14028            }
14029        }
14030    })
14031    .next()
14032    .await
14033    .unwrap();
14034    cx.run_until_parked();
14035
14036    cx.update_editor(|editor, window, cx| {
14037        editor.context_menu_next(&Default::default(), window, cx);
14038    });
14039
14040    cx.update_editor(|editor, _, _| {
14041        let context_menu = editor.context_menu.borrow_mut();
14042        let context_menu = context_menu
14043            .as_ref()
14044            .expect("Should have the context menu deployed");
14045        match context_menu {
14046            CodeContextMenu::Completions(completions_menu) => {
14047                let completions = completions_menu.completions.borrow_mut();
14048                assert_eq!(
14049                    completions
14050                        .iter()
14051                        .map(|completion| &completion.label.text)
14052                        .collect::<Vec<_>>(),
14053                    vec!["method id() Now resolved!", "other"],
14054                    "Should update first completion label, but not second as the filter text did not match."
14055                );
14056            }
14057            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14058        }
14059    });
14060}
14061
14062#[gpui::test]
14063async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14064    init_test(cx, |_| {});
14065    let mut cx = EditorLspTestContext::new_rust(
14066        lsp::ServerCapabilities {
14067            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14068            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14069            completion_provider: Some(lsp::CompletionOptions {
14070                resolve_provider: Some(true),
14071                ..Default::default()
14072            }),
14073            ..Default::default()
14074        },
14075        cx,
14076    )
14077    .await;
14078    cx.set_state(indoc! {"
14079        struct TestStruct {
14080            field: i32
14081        }
14082
14083        fn mainˇ() {
14084            let unused_var = 42;
14085            let test_struct = TestStruct { field: 42 };
14086        }
14087    "});
14088    let symbol_range = cx.lsp_range(indoc! {"
14089        struct TestStruct {
14090            field: i32
14091        }
14092
14093        «fn main»() {
14094            let unused_var = 42;
14095            let test_struct = TestStruct { field: 42 };
14096        }
14097    "});
14098    let mut hover_requests =
14099        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14100            Ok(Some(lsp::Hover {
14101                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14102                    kind: lsp::MarkupKind::Markdown,
14103                    value: "Function documentation".to_string(),
14104                }),
14105                range: Some(symbol_range),
14106            }))
14107        });
14108
14109    // Case 1: Test that code action menu hide hover popover
14110    cx.dispatch_action(Hover);
14111    hover_requests.next().await;
14112    cx.condition(|editor, _| editor.hover_state.visible()).await;
14113    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14114        move |_, _, _| async move {
14115            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14116                lsp::CodeAction {
14117                    title: "Remove unused variable".to_string(),
14118                    kind: Some(CodeActionKind::QUICKFIX),
14119                    edit: Some(lsp::WorkspaceEdit {
14120                        changes: Some(
14121                            [(
14122                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14123                                vec![lsp::TextEdit {
14124                                    range: lsp::Range::new(
14125                                        lsp::Position::new(5, 4),
14126                                        lsp::Position::new(5, 27),
14127                                    ),
14128                                    new_text: "".to_string(),
14129                                }],
14130                            )]
14131                            .into_iter()
14132                            .collect(),
14133                        ),
14134                        ..Default::default()
14135                    }),
14136                    ..Default::default()
14137                },
14138            )]))
14139        },
14140    );
14141    cx.update_editor(|editor, window, cx| {
14142        editor.toggle_code_actions(
14143            &ToggleCodeActions {
14144                deployed_from: None,
14145                quick_launch: false,
14146            },
14147            window,
14148            cx,
14149        );
14150    });
14151    code_action_requests.next().await;
14152    cx.run_until_parked();
14153    cx.condition(|editor, _| editor.context_menu_visible())
14154        .await;
14155    cx.update_editor(|editor, _, _| {
14156        assert!(
14157            !editor.hover_state.visible(),
14158            "Hover popover should be hidden when code action menu is shown"
14159        );
14160        // Hide code actions
14161        editor.context_menu.take();
14162    });
14163
14164    // Case 2: Test that code completions hide hover popover
14165    cx.dispatch_action(Hover);
14166    hover_requests.next().await;
14167    cx.condition(|editor, _| editor.hover_state.visible()).await;
14168    let counter = Arc::new(AtomicUsize::new(0));
14169    let mut completion_requests =
14170        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14171            let counter = counter.clone();
14172            async move {
14173                counter.fetch_add(1, atomic::Ordering::Release);
14174                Ok(Some(lsp::CompletionResponse::Array(vec![
14175                    lsp::CompletionItem {
14176                        label: "main".into(),
14177                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14178                        detail: Some("() -> ()".to_string()),
14179                        ..Default::default()
14180                    },
14181                    lsp::CompletionItem {
14182                        label: "TestStruct".into(),
14183                        kind: Some(lsp::CompletionItemKind::STRUCT),
14184                        detail: Some("struct TestStruct".to_string()),
14185                        ..Default::default()
14186                    },
14187                ])))
14188            }
14189        });
14190    cx.update_editor(|editor, window, cx| {
14191        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14192    });
14193    completion_requests.next().await;
14194    cx.condition(|editor, _| editor.context_menu_visible())
14195        .await;
14196    cx.update_editor(|editor, _, _| {
14197        assert!(
14198            !editor.hover_state.visible(),
14199            "Hover popover should be hidden when completion menu is shown"
14200        );
14201    });
14202}
14203
14204#[gpui::test]
14205async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14206    init_test(cx, |_| {});
14207
14208    let mut cx = EditorLspTestContext::new_rust(
14209        lsp::ServerCapabilities {
14210            completion_provider: Some(lsp::CompletionOptions {
14211                trigger_characters: Some(vec![".".to_string()]),
14212                resolve_provider: Some(true),
14213                ..Default::default()
14214            }),
14215            ..Default::default()
14216        },
14217        cx,
14218    )
14219    .await;
14220
14221    cx.set_state("fn main() { let a = 2ˇ; }");
14222    cx.simulate_keystroke(".");
14223
14224    let unresolved_item_1 = lsp::CompletionItem {
14225        label: "id".to_string(),
14226        filter_text: Some("id".to_string()),
14227        detail: None,
14228        documentation: None,
14229        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14230            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14231            new_text: ".id".to_string(),
14232        })),
14233        ..lsp::CompletionItem::default()
14234    };
14235    let resolved_item_1 = lsp::CompletionItem {
14236        additional_text_edits: Some(vec![lsp::TextEdit {
14237            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14238            new_text: "!!".to_string(),
14239        }]),
14240        ..unresolved_item_1.clone()
14241    };
14242    let unresolved_item_2 = lsp::CompletionItem {
14243        label: "other".to_string(),
14244        filter_text: Some("other".to_string()),
14245        detail: None,
14246        documentation: None,
14247        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14248            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14249            new_text: ".other".to_string(),
14250        })),
14251        ..lsp::CompletionItem::default()
14252    };
14253    let resolved_item_2 = lsp::CompletionItem {
14254        additional_text_edits: Some(vec![lsp::TextEdit {
14255            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14256            new_text: "??".to_string(),
14257        }]),
14258        ..unresolved_item_2.clone()
14259    };
14260
14261    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14262    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14263    cx.lsp
14264        .server
14265        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14266            let unresolved_item_1 = unresolved_item_1.clone();
14267            let resolved_item_1 = resolved_item_1.clone();
14268            let unresolved_item_2 = unresolved_item_2.clone();
14269            let resolved_item_2 = resolved_item_2.clone();
14270            let resolve_requests_1 = resolve_requests_1.clone();
14271            let resolve_requests_2 = resolve_requests_2.clone();
14272            move |unresolved_request, _| {
14273                let unresolved_item_1 = unresolved_item_1.clone();
14274                let resolved_item_1 = resolved_item_1.clone();
14275                let unresolved_item_2 = unresolved_item_2.clone();
14276                let resolved_item_2 = resolved_item_2.clone();
14277                let resolve_requests_1 = resolve_requests_1.clone();
14278                let resolve_requests_2 = resolve_requests_2.clone();
14279                async move {
14280                    if unresolved_request == unresolved_item_1 {
14281                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14282                        Ok(resolved_item_1.clone())
14283                    } else if unresolved_request == unresolved_item_2 {
14284                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14285                        Ok(resolved_item_2.clone())
14286                    } else {
14287                        panic!("Unexpected completion item {unresolved_request:?}")
14288                    }
14289                }
14290            }
14291        })
14292        .detach();
14293
14294    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14295        let unresolved_item_1 = unresolved_item_1.clone();
14296        let unresolved_item_2 = unresolved_item_2.clone();
14297        async move {
14298            Ok(Some(lsp::CompletionResponse::Array(vec![
14299                unresolved_item_1,
14300                unresolved_item_2,
14301            ])))
14302        }
14303    })
14304    .next()
14305    .await;
14306
14307    cx.condition(|editor, _| editor.context_menu_visible())
14308        .await;
14309    cx.update_editor(|editor, _, _| {
14310        let context_menu = editor.context_menu.borrow_mut();
14311        let context_menu = context_menu
14312            .as_ref()
14313            .expect("Should have the context menu deployed");
14314        match context_menu {
14315            CodeContextMenu::Completions(completions_menu) => {
14316                let completions = completions_menu.completions.borrow_mut();
14317                assert_eq!(
14318                    completions
14319                        .iter()
14320                        .map(|completion| &completion.label.text)
14321                        .collect::<Vec<_>>(),
14322                    vec!["id", "other"]
14323                )
14324            }
14325            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14326        }
14327    });
14328    cx.run_until_parked();
14329
14330    cx.update_editor(|editor, window, cx| {
14331        editor.context_menu_next(&ContextMenuNext, window, cx);
14332    });
14333    cx.run_until_parked();
14334    cx.update_editor(|editor, window, cx| {
14335        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14336    });
14337    cx.run_until_parked();
14338    cx.update_editor(|editor, window, cx| {
14339        editor.context_menu_next(&ContextMenuNext, window, cx);
14340    });
14341    cx.run_until_parked();
14342    cx.update_editor(|editor, window, cx| {
14343        editor
14344            .compose_completion(&ComposeCompletion::default(), window, cx)
14345            .expect("No task returned")
14346    })
14347    .await
14348    .expect("Completion failed");
14349    cx.run_until_parked();
14350
14351    cx.update_editor(|editor, _, cx| {
14352        assert_eq!(
14353            resolve_requests_1.load(atomic::Ordering::Acquire),
14354            1,
14355            "Should always resolve once despite multiple selections"
14356        );
14357        assert_eq!(
14358            resolve_requests_2.load(atomic::Ordering::Acquire),
14359            1,
14360            "Should always resolve once after multiple selections and applying the completion"
14361        );
14362        assert_eq!(
14363            editor.text(cx),
14364            "fn main() { let a = ??.other; }",
14365            "Should use resolved data when applying the completion"
14366        );
14367    });
14368}
14369
14370#[gpui::test]
14371async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14372    init_test(cx, |_| {});
14373
14374    let item_0 = lsp::CompletionItem {
14375        label: "abs".into(),
14376        insert_text: Some("abs".into()),
14377        data: Some(json!({ "very": "special"})),
14378        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14379        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14380            lsp::InsertReplaceEdit {
14381                new_text: "abs".to_string(),
14382                insert: lsp::Range::default(),
14383                replace: lsp::Range::default(),
14384            },
14385        )),
14386        ..lsp::CompletionItem::default()
14387    };
14388    let items = iter::once(item_0.clone())
14389        .chain((11..51).map(|i| lsp::CompletionItem {
14390            label: format!("item_{}", i),
14391            insert_text: Some(format!("item_{}", i)),
14392            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14393            ..lsp::CompletionItem::default()
14394        }))
14395        .collect::<Vec<_>>();
14396
14397    let default_commit_characters = vec!["?".to_string()];
14398    let default_data = json!({ "default": "data"});
14399    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14400    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14401    let default_edit_range = lsp::Range {
14402        start: lsp::Position {
14403            line: 0,
14404            character: 5,
14405        },
14406        end: lsp::Position {
14407            line: 0,
14408            character: 5,
14409        },
14410    };
14411
14412    let mut cx = EditorLspTestContext::new_rust(
14413        lsp::ServerCapabilities {
14414            completion_provider: Some(lsp::CompletionOptions {
14415                trigger_characters: Some(vec![".".to_string()]),
14416                resolve_provider: Some(true),
14417                ..Default::default()
14418            }),
14419            ..Default::default()
14420        },
14421        cx,
14422    )
14423    .await;
14424
14425    cx.set_state("fn main() { let a = 2ˇ; }");
14426    cx.simulate_keystroke(".");
14427
14428    let completion_data = default_data.clone();
14429    let completion_characters = default_commit_characters.clone();
14430    let completion_items = items.clone();
14431    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14432        let default_data = completion_data.clone();
14433        let default_commit_characters = completion_characters.clone();
14434        let items = completion_items.clone();
14435        async move {
14436            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14437                items,
14438                item_defaults: Some(lsp::CompletionListItemDefaults {
14439                    data: Some(default_data.clone()),
14440                    commit_characters: Some(default_commit_characters.clone()),
14441                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14442                        default_edit_range,
14443                    )),
14444                    insert_text_format: Some(default_insert_text_format),
14445                    insert_text_mode: Some(default_insert_text_mode),
14446                }),
14447                ..lsp::CompletionList::default()
14448            })))
14449        }
14450    })
14451    .next()
14452    .await;
14453
14454    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14455    cx.lsp
14456        .server
14457        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14458            let closure_resolved_items = resolved_items.clone();
14459            move |item_to_resolve, _| {
14460                let closure_resolved_items = closure_resolved_items.clone();
14461                async move {
14462                    closure_resolved_items.lock().push(item_to_resolve.clone());
14463                    Ok(item_to_resolve)
14464                }
14465            }
14466        })
14467        .detach();
14468
14469    cx.condition(|editor, _| editor.context_menu_visible())
14470        .await;
14471    cx.run_until_parked();
14472    cx.update_editor(|editor, _, _| {
14473        let menu = editor.context_menu.borrow_mut();
14474        match menu.as_ref().expect("should have the completions menu") {
14475            CodeContextMenu::Completions(completions_menu) => {
14476                assert_eq!(
14477                    completions_menu
14478                        .entries
14479                        .borrow()
14480                        .iter()
14481                        .map(|mat| mat.string.clone())
14482                        .collect::<Vec<String>>(),
14483                    items
14484                        .iter()
14485                        .map(|completion| completion.label.clone())
14486                        .collect::<Vec<String>>()
14487                );
14488            }
14489            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14490        }
14491    });
14492    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14493    // with 4 from the end.
14494    assert_eq!(
14495        *resolved_items.lock(),
14496        [&items[0..16], &items[items.len() - 4..items.len()]]
14497            .concat()
14498            .iter()
14499            .cloned()
14500            .map(|mut item| {
14501                if item.data.is_none() {
14502                    item.data = Some(default_data.clone());
14503                }
14504                item
14505            })
14506            .collect::<Vec<lsp::CompletionItem>>(),
14507        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14508    );
14509    resolved_items.lock().clear();
14510
14511    cx.update_editor(|editor, window, cx| {
14512        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14513    });
14514    cx.run_until_parked();
14515    // Completions that have already been resolved are skipped.
14516    assert_eq!(
14517        *resolved_items.lock(),
14518        items[items.len() - 16..items.len() - 4]
14519            .iter()
14520            .cloned()
14521            .map(|mut item| {
14522                if item.data.is_none() {
14523                    item.data = Some(default_data.clone());
14524                }
14525                item
14526            })
14527            .collect::<Vec<lsp::CompletionItem>>()
14528    );
14529    resolved_items.lock().clear();
14530}
14531
14532#[gpui::test]
14533async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14534    init_test(cx, |_| {});
14535
14536    let mut cx = EditorLspTestContext::new(
14537        Language::new(
14538            LanguageConfig {
14539                matcher: LanguageMatcher {
14540                    path_suffixes: vec!["jsx".into()],
14541                    ..Default::default()
14542                },
14543                overrides: [(
14544                    "element".into(),
14545                    LanguageConfigOverride {
14546                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14547                        ..Default::default()
14548                    },
14549                )]
14550                .into_iter()
14551                .collect(),
14552                ..Default::default()
14553            },
14554            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14555        )
14556        .with_override_query("(jsx_self_closing_element) @element")
14557        .unwrap(),
14558        lsp::ServerCapabilities {
14559            completion_provider: Some(lsp::CompletionOptions {
14560                trigger_characters: Some(vec![":".to_string()]),
14561                ..Default::default()
14562            }),
14563            ..Default::default()
14564        },
14565        cx,
14566    )
14567    .await;
14568
14569    cx.lsp
14570        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14571            Ok(Some(lsp::CompletionResponse::Array(vec![
14572                lsp::CompletionItem {
14573                    label: "bg-blue".into(),
14574                    ..Default::default()
14575                },
14576                lsp::CompletionItem {
14577                    label: "bg-red".into(),
14578                    ..Default::default()
14579                },
14580                lsp::CompletionItem {
14581                    label: "bg-yellow".into(),
14582                    ..Default::default()
14583                },
14584            ])))
14585        });
14586
14587    cx.set_state(r#"<p class="bgˇ" />"#);
14588
14589    // Trigger completion when typing a dash, because the dash is an extra
14590    // word character in the 'element' scope, which contains the cursor.
14591    cx.simulate_keystroke("-");
14592    cx.executor().run_until_parked();
14593    cx.update_editor(|editor, _, _| {
14594        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14595        {
14596            assert_eq!(
14597                completion_menu_entries(&menu),
14598                &["bg-red", "bg-blue", "bg-yellow"]
14599            );
14600        } else {
14601            panic!("expected completion menu to be open");
14602        }
14603    });
14604
14605    cx.simulate_keystroke("l");
14606    cx.executor().run_until_parked();
14607    cx.update_editor(|editor, _, _| {
14608        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14609        {
14610            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14611        } else {
14612            panic!("expected completion menu to be open");
14613        }
14614    });
14615
14616    // When filtering completions, consider the character after the '-' to
14617    // be the start of a subword.
14618    cx.set_state(r#"<p class="yelˇ" />"#);
14619    cx.simulate_keystroke("l");
14620    cx.executor().run_until_parked();
14621    cx.update_editor(|editor, _, _| {
14622        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14623        {
14624            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14625        } else {
14626            panic!("expected completion menu to be open");
14627        }
14628    });
14629}
14630
14631fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14632    let entries = menu.entries.borrow();
14633    entries.iter().map(|mat| mat.string.clone()).collect()
14634}
14635
14636#[gpui::test]
14637async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14638    init_test(cx, |settings| {
14639        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14640            FormatterList(vec![Formatter::Prettier].into()),
14641        ))
14642    });
14643
14644    let fs = FakeFs::new(cx.executor());
14645    fs.insert_file(path!("/file.ts"), Default::default()).await;
14646
14647    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14648    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14649
14650    language_registry.add(Arc::new(Language::new(
14651        LanguageConfig {
14652            name: "TypeScript".into(),
14653            matcher: LanguageMatcher {
14654                path_suffixes: vec!["ts".to_string()],
14655                ..Default::default()
14656            },
14657            ..Default::default()
14658        },
14659        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14660    )));
14661    update_test_language_settings(cx, |settings| {
14662        settings.defaults.prettier = Some(PrettierSettings {
14663            allowed: true,
14664            ..PrettierSettings::default()
14665        });
14666    });
14667
14668    let test_plugin = "test_plugin";
14669    let _ = language_registry.register_fake_lsp(
14670        "TypeScript",
14671        FakeLspAdapter {
14672            prettier_plugins: vec![test_plugin],
14673            ..Default::default()
14674        },
14675    );
14676
14677    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14678    let buffer = project
14679        .update(cx, |project, cx| {
14680            project.open_local_buffer(path!("/file.ts"), cx)
14681        })
14682        .await
14683        .unwrap();
14684
14685    let buffer_text = "one\ntwo\nthree\n";
14686    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14687    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14688    editor.update_in(cx, |editor, window, cx| {
14689        editor.set_text(buffer_text, window, cx)
14690    });
14691
14692    editor
14693        .update_in(cx, |editor, window, cx| {
14694            editor.perform_format(
14695                project.clone(),
14696                FormatTrigger::Manual,
14697                FormatTarget::Buffers,
14698                window,
14699                cx,
14700            )
14701        })
14702        .unwrap()
14703        .await;
14704    assert_eq!(
14705        editor.update(cx, |editor, cx| editor.text(cx)),
14706        buffer_text.to_string() + prettier_format_suffix,
14707        "Test prettier formatting was not applied to the original buffer text",
14708    );
14709
14710    update_test_language_settings(cx, |settings| {
14711        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14712    });
14713    let format = editor.update_in(cx, |editor, window, cx| {
14714        editor.perform_format(
14715            project.clone(),
14716            FormatTrigger::Manual,
14717            FormatTarget::Buffers,
14718            window,
14719            cx,
14720        )
14721    });
14722    format.await.unwrap();
14723    assert_eq!(
14724        editor.update(cx, |editor, cx| editor.text(cx)),
14725        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14726        "Autoformatting (via test prettier) was not applied to the original buffer text",
14727    );
14728}
14729
14730#[gpui::test]
14731async fn test_addition_reverts(cx: &mut TestAppContext) {
14732    init_test(cx, |_| {});
14733    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14734    let base_text = indoc! {r#"
14735        struct Row;
14736        struct Row1;
14737        struct Row2;
14738
14739        struct Row4;
14740        struct Row5;
14741        struct Row6;
14742
14743        struct Row8;
14744        struct Row9;
14745        struct Row10;"#};
14746
14747    // When addition hunks are not adjacent to carets, no hunk revert is performed
14748    assert_hunk_revert(
14749        indoc! {r#"struct Row;
14750                   struct Row1;
14751                   struct Row1.1;
14752                   struct Row1.2;
14753                   struct Row2;ˇ
14754
14755                   struct Row4;
14756                   struct Row5;
14757                   struct Row6;
14758
14759                   struct Row8;
14760                   ˇstruct Row9;
14761                   struct Row9.1;
14762                   struct Row9.2;
14763                   struct Row9.3;
14764                   struct Row10;"#},
14765        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14766        indoc! {r#"struct Row;
14767                   struct Row1;
14768                   struct Row1.1;
14769                   struct Row1.2;
14770                   struct Row2;ˇ
14771
14772                   struct Row4;
14773                   struct Row5;
14774                   struct Row6;
14775
14776                   struct Row8;
14777                   ˇstruct Row9;
14778                   struct Row9.1;
14779                   struct Row9.2;
14780                   struct Row9.3;
14781                   struct Row10;"#},
14782        base_text,
14783        &mut cx,
14784    );
14785    // Same for selections
14786    assert_hunk_revert(
14787        indoc! {r#"struct Row;
14788                   struct Row1;
14789                   struct Row2;
14790                   struct Row2.1;
14791                   struct Row2.2;
14792                   «ˇ
14793                   struct Row4;
14794                   struct» Row5;
14795                   «struct Row6;
14796                   ˇ»
14797                   struct Row9.1;
14798                   struct Row9.2;
14799                   struct Row9.3;
14800                   struct Row8;
14801                   struct Row9;
14802                   struct Row10;"#},
14803        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14804        indoc! {r#"struct Row;
14805                   struct Row1;
14806                   struct Row2;
14807                   struct Row2.1;
14808                   struct Row2.2;
14809                   «ˇ
14810                   struct Row4;
14811                   struct» Row5;
14812                   «struct Row6;
14813                   ˇ»
14814                   struct Row9.1;
14815                   struct Row9.2;
14816                   struct Row9.3;
14817                   struct Row8;
14818                   struct Row9;
14819                   struct Row10;"#},
14820        base_text,
14821        &mut cx,
14822    );
14823
14824    // When carets and selections intersect the addition hunks, those are reverted.
14825    // Adjacent carets got merged.
14826    assert_hunk_revert(
14827        indoc! {r#"struct Row;
14828                   ˇ// something on the top
14829                   struct Row1;
14830                   struct Row2;
14831                   struct Roˇw3.1;
14832                   struct Row2.2;
14833                   struct Row2.3;ˇ
14834
14835                   struct Row4;
14836                   struct ˇRow5.1;
14837                   struct Row5.2;
14838                   struct «Rowˇ»5.3;
14839                   struct Row5;
14840                   struct Row6;
14841                   ˇ
14842                   struct Row9.1;
14843                   struct «Rowˇ»9.2;
14844                   struct «ˇRow»9.3;
14845                   struct Row8;
14846                   struct Row9;
14847                   «ˇ// something on bottom»
14848                   struct Row10;"#},
14849        vec![
14850            DiffHunkStatusKind::Added,
14851            DiffHunkStatusKind::Added,
14852            DiffHunkStatusKind::Added,
14853            DiffHunkStatusKind::Added,
14854            DiffHunkStatusKind::Added,
14855        ],
14856        indoc! {r#"struct Row;
14857                   ˇstruct Row1;
14858                   struct Row2;
14859                   ˇ
14860                   struct Row4;
14861                   ˇstruct Row5;
14862                   struct Row6;
14863                   ˇ
14864                   ˇstruct Row8;
14865                   struct Row9;
14866                   ˇstruct Row10;"#},
14867        base_text,
14868        &mut cx,
14869    );
14870}
14871
14872#[gpui::test]
14873async fn test_modification_reverts(cx: &mut TestAppContext) {
14874    init_test(cx, |_| {});
14875    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14876    let base_text = indoc! {r#"
14877        struct Row;
14878        struct Row1;
14879        struct Row2;
14880
14881        struct Row4;
14882        struct Row5;
14883        struct Row6;
14884
14885        struct Row8;
14886        struct Row9;
14887        struct Row10;"#};
14888
14889    // Modification hunks behave the same as the addition ones.
14890    assert_hunk_revert(
14891        indoc! {r#"struct Row;
14892                   struct Row1;
14893                   struct Row33;
14894                   ˇ
14895                   struct Row4;
14896                   struct Row5;
14897                   struct Row6;
14898                   ˇ
14899                   struct Row99;
14900                   struct Row9;
14901                   struct Row10;"#},
14902        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14903        indoc! {r#"struct Row;
14904                   struct Row1;
14905                   struct Row33;
14906                   ˇ
14907                   struct Row4;
14908                   struct Row5;
14909                   struct Row6;
14910                   ˇ
14911                   struct Row99;
14912                   struct Row9;
14913                   struct Row10;"#},
14914        base_text,
14915        &mut cx,
14916    );
14917    assert_hunk_revert(
14918        indoc! {r#"struct Row;
14919                   struct Row1;
14920                   struct Row33;
14921                   «ˇ
14922                   struct Row4;
14923                   struct» Row5;
14924                   «struct Row6;
14925                   ˇ»
14926                   struct Row99;
14927                   struct Row9;
14928                   struct Row10;"#},
14929        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14930        indoc! {r#"struct Row;
14931                   struct Row1;
14932                   struct Row33;
14933                   «ˇ
14934                   struct Row4;
14935                   struct» Row5;
14936                   «struct Row6;
14937                   ˇ»
14938                   struct Row99;
14939                   struct Row9;
14940                   struct Row10;"#},
14941        base_text,
14942        &mut cx,
14943    );
14944
14945    assert_hunk_revert(
14946        indoc! {r#"ˇstruct Row1.1;
14947                   struct Row1;
14948                   «ˇstr»uct Row22;
14949
14950                   struct ˇRow44;
14951                   struct Row5;
14952                   struct «Rˇ»ow66;ˇ
14953
14954                   «struˇ»ct Row88;
14955                   struct Row9;
14956                   struct Row1011;ˇ"#},
14957        vec![
14958            DiffHunkStatusKind::Modified,
14959            DiffHunkStatusKind::Modified,
14960            DiffHunkStatusKind::Modified,
14961            DiffHunkStatusKind::Modified,
14962            DiffHunkStatusKind::Modified,
14963            DiffHunkStatusKind::Modified,
14964        ],
14965        indoc! {r#"struct Row;
14966                   ˇstruct Row1;
14967                   struct Row2;
14968                   ˇ
14969                   struct Row4;
14970                   ˇstruct Row5;
14971                   struct Row6;
14972                   ˇ
14973                   struct Row8;
14974                   ˇstruct Row9;
14975                   struct Row10;ˇ"#},
14976        base_text,
14977        &mut cx,
14978    );
14979}
14980
14981#[gpui::test]
14982async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14983    init_test(cx, |_| {});
14984    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14985    let base_text = indoc! {r#"
14986        one
14987
14988        two
14989        three
14990        "#};
14991
14992    cx.set_head_text(base_text);
14993    cx.set_state("\nˇ\n");
14994    cx.executor().run_until_parked();
14995    cx.update_editor(|editor, _window, cx| {
14996        editor.expand_selected_diff_hunks(cx);
14997    });
14998    cx.executor().run_until_parked();
14999    cx.update_editor(|editor, window, cx| {
15000        editor.backspace(&Default::default(), window, cx);
15001    });
15002    cx.run_until_parked();
15003    cx.assert_state_with_diff(
15004        indoc! {r#"
15005
15006        - two
15007        - threeˇ
15008        +
15009        "#}
15010        .to_string(),
15011    );
15012}
15013
15014#[gpui::test]
15015async fn test_deletion_reverts(cx: &mut TestAppContext) {
15016    init_test(cx, |_| {});
15017    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
15018    let base_text = indoc! {r#"struct Row;
15019struct Row1;
15020struct Row2;
15021
15022struct Row4;
15023struct Row5;
15024struct Row6;
15025
15026struct Row8;
15027struct Row9;
15028struct Row10;"#};
15029
15030    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
15031    assert_hunk_revert(
15032        indoc! {r#"struct Row;
15033                   struct Row2;
15034
15035                   ˇstruct Row4;
15036                   struct Row5;
15037                   struct Row6;
15038                   ˇ
15039                   struct Row8;
15040                   struct Row10;"#},
15041        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15042        indoc! {r#"struct Row;
15043                   struct Row2;
15044
15045                   ˇstruct Row4;
15046                   struct Row5;
15047                   struct Row6;
15048                   ˇ
15049                   struct Row8;
15050                   struct Row10;"#},
15051        base_text,
15052        &mut cx,
15053    );
15054    assert_hunk_revert(
15055        indoc! {r#"struct Row;
15056                   struct Row2;
15057
15058                   «ˇstruct Row4;
15059                   struct» Row5;
15060                   «struct Row6;
15061                   ˇ»
15062                   struct Row8;
15063                   struct Row10;"#},
15064        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15065        indoc! {r#"struct Row;
15066                   struct Row2;
15067
15068                   «ˇstruct Row4;
15069                   struct» Row5;
15070                   «struct Row6;
15071                   ˇ»
15072                   struct Row8;
15073                   struct Row10;"#},
15074        base_text,
15075        &mut cx,
15076    );
15077
15078    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15079    assert_hunk_revert(
15080        indoc! {r#"struct Row;
15081                   ˇstruct Row2;
15082
15083                   struct Row4;
15084                   struct Row5;
15085                   struct Row6;
15086
15087                   struct Row8;ˇ
15088                   struct Row10;"#},
15089        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15090        indoc! {r#"struct Row;
15091                   struct Row1;
15092                   ˇstruct Row2;
15093
15094                   struct Row4;
15095                   struct Row5;
15096                   struct Row6;
15097
15098                   struct Row8;ˇ
15099                   struct Row9;
15100                   struct Row10;"#},
15101        base_text,
15102        &mut cx,
15103    );
15104    assert_hunk_revert(
15105        indoc! {r#"struct Row;
15106                   struct Row2«ˇ;
15107                   struct Row4;
15108                   struct» Row5;
15109                   «struct Row6;
15110
15111                   struct Row8;ˇ»
15112                   struct Row10;"#},
15113        vec![
15114            DiffHunkStatusKind::Deleted,
15115            DiffHunkStatusKind::Deleted,
15116            DiffHunkStatusKind::Deleted,
15117        ],
15118        indoc! {r#"struct Row;
15119                   struct Row1;
15120                   struct Row2«ˇ;
15121
15122                   struct Row4;
15123                   struct» Row5;
15124                   «struct Row6;
15125
15126                   struct Row8;ˇ»
15127                   struct Row9;
15128                   struct Row10;"#},
15129        base_text,
15130        &mut cx,
15131    );
15132}
15133
15134#[gpui::test]
15135async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15136    init_test(cx, |_| {});
15137
15138    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15139    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15140    let base_text_3 =
15141        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15142
15143    let text_1 = edit_first_char_of_every_line(base_text_1);
15144    let text_2 = edit_first_char_of_every_line(base_text_2);
15145    let text_3 = edit_first_char_of_every_line(base_text_3);
15146
15147    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15148    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15149    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15150
15151    let multibuffer = cx.new(|cx| {
15152        let mut multibuffer = MultiBuffer::new(ReadWrite);
15153        multibuffer.push_excerpts(
15154            buffer_1.clone(),
15155            [
15156                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15157                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15158                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15159            ],
15160            cx,
15161        );
15162        multibuffer.push_excerpts(
15163            buffer_2.clone(),
15164            [
15165                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15166                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15167                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15168            ],
15169            cx,
15170        );
15171        multibuffer.push_excerpts(
15172            buffer_3.clone(),
15173            [
15174                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15175                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15176                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15177            ],
15178            cx,
15179        );
15180        multibuffer
15181    });
15182
15183    let fs = FakeFs::new(cx.executor());
15184    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15185    let (editor, cx) = cx
15186        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15187    editor.update_in(cx, |editor, _window, cx| {
15188        for (buffer, diff_base) in [
15189            (buffer_1.clone(), base_text_1),
15190            (buffer_2.clone(), base_text_2),
15191            (buffer_3.clone(), base_text_3),
15192        ] {
15193            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15194            editor
15195                .buffer
15196                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15197        }
15198    });
15199    cx.executor().run_until_parked();
15200
15201    editor.update_in(cx, |editor, window, cx| {
15202        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}");
15203        editor.select_all(&SelectAll, window, cx);
15204        editor.git_restore(&Default::default(), window, cx);
15205    });
15206    cx.executor().run_until_parked();
15207
15208    // When all ranges are selected, all buffer hunks are reverted.
15209    editor.update(cx, |editor, cx| {
15210        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");
15211    });
15212    buffer_1.update(cx, |buffer, _| {
15213        assert_eq!(buffer.text(), base_text_1);
15214    });
15215    buffer_2.update(cx, |buffer, _| {
15216        assert_eq!(buffer.text(), base_text_2);
15217    });
15218    buffer_3.update(cx, |buffer, _| {
15219        assert_eq!(buffer.text(), base_text_3);
15220    });
15221
15222    editor.update_in(cx, |editor, window, cx| {
15223        editor.undo(&Default::default(), window, cx);
15224    });
15225
15226    editor.update_in(cx, |editor, window, cx| {
15227        editor.change_selections(None, window, cx, |s| {
15228            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15229        });
15230        editor.git_restore(&Default::default(), window, cx);
15231    });
15232
15233    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15234    // but not affect buffer_2 and its related excerpts.
15235    editor.update(cx, |editor, cx| {
15236        assert_eq!(
15237            editor.text(cx),
15238            "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}"
15239        );
15240    });
15241    buffer_1.update(cx, |buffer, _| {
15242        assert_eq!(buffer.text(), base_text_1);
15243    });
15244    buffer_2.update(cx, |buffer, _| {
15245        assert_eq!(
15246            buffer.text(),
15247            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15248        );
15249    });
15250    buffer_3.update(cx, |buffer, _| {
15251        assert_eq!(
15252            buffer.text(),
15253            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15254        );
15255    });
15256
15257    fn edit_first_char_of_every_line(text: &str) -> String {
15258        text.split('\n')
15259            .map(|line| format!("X{}", &line[1..]))
15260            .collect::<Vec<_>>()
15261            .join("\n")
15262    }
15263}
15264
15265#[gpui::test]
15266async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15267    init_test(cx, |_| {});
15268
15269    let cols = 4;
15270    let rows = 10;
15271    let sample_text_1 = sample_text(rows, cols, 'a');
15272    assert_eq!(
15273        sample_text_1,
15274        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15275    );
15276    let sample_text_2 = sample_text(rows, cols, 'l');
15277    assert_eq!(
15278        sample_text_2,
15279        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15280    );
15281    let sample_text_3 = sample_text(rows, cols, 'v');
15282    assert_eq!(
15283        sample_text_3,
15284        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15285    );
15286
15287    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15288    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15289    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15290
15291    let multi_buffer = cx.new(|cx| {
15292        let mut multibuffer = MultiBuffer::new(ReadWrite);
15293        multibuffer.push_excerpts(
15294            buffer_1.clone(),
15295            [
15296                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15297                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15298                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15299            ],
15300            cx,
15301        );
15302        multibuffer.push_excerpts(
15303            buffer_2.clone(),
15304            [
15305                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15306                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15307                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15308            ],
15309            cx,
15310        );
15311        multibuffer.push_excerpts(
15312            buffer_3.clone(),
15313            [
15314                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15315                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15316                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15317            ],
15318            cx,
15319        );
15320        multibuffer
15321    });
15322
15323    let fs = FakeFs::new(cx.executor());
15324    fs.insert_tree(
15325        "/a",
15326        json!({
15327            "main.rs": sample_text_1,
15328            "other.rs": sample_text_2,
15329            "lib.rs": sample_text_3,
15330        }),
15331    )
15332    .await;
15333    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15334    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15335    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15336    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15337        Editor::new(
15338            EditorMode::full(),
15339            multi_buffer,
15340            Some(project.clone()),
15341            window,
15342            cx,
15343        )
15344    });
15345    let multibuffer_item_id = workspace
15346        .update(cx, |workspace, window, cx| {
15347            assert!(
15348                workspace.active_item(cx).is_none(),
15349                "active item should be None before the first item is added"
15350            );
15351            workspace.add_item_to_active_pane(
15352                Box::new(multi_buffer_editor.clone()),
15353                None,
15354                true,
15355                window,
15356                cx,
15357            );
15358            let active_item = workspace
15359                .active_item(cx)
15360                .expect("should have an active item after adding the multi buffer");
15361            assert!(
15362                !active_item.is_singleton(cx),
15363                "A multi buffer was expected to active after adding"
15364            );
15365            active_item.item_id()
15366        })
15367        .unwrap();
15368    cx.executor().run_until_parked();
15369
15370    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15371        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15372            s.select_ranges(Some(1..2))
15373        });
15374        editor.open_excerpts(&OpenExcerpts, window, cx);
15375    });
15376    cx.executor().run_until_parked();
15377    let first_item_id = workspace
15378        .update(cx, |workspace, window, cx| {
15379            let active_item = workspace
15380                .active_item(cx)
15381                .expect("should have an active item after navigating into the 1st buffer");
15382            let first_item_id = active_item.item_id();
15383            assert_ne!(
15384                first_item_id, multibuffer_item_id,
15385                "Should navigate into the 1st buffer and activate it"
15386            );
15387            assert!(
15388                active_item.is_singleton(cx),
15389                "New active item should be a singleton buffer"
15390            );
15391            assert_eq!(
15392                active_item
15393                    .act_as::<Editor>(cx)
15394                    .expect("should have navigated into an editor for the 1st buffer")
15395                    .read(cx)
15396                    .text(cx),
15397                sample_text_1
15398            );
15399
15400            workspace
15401                .go_back(workspace.active_pane().downgrade(), window, cx)
15402                .detach_and_log_err(cx);
15403
15404            first_item_id
15405        })
15406        .unwrap();
15407    cx.executor().run_until_parked();
15408    workspace
15409        .update(cx, |workspace, _, cx| {
15410            let active_item = workspace
15411                .active_item(cx)
15412                .expect("should have an active item after navigating back");
15413            assert_eq!(
15414                active_item.item_id(),
15415                multibuffer_item_id,
15416                "Should navigate back to the multi buffer"
15417            );
15418            assert!(!active_item.is_singleton(cx));
15419        })
15420        .unwrap();
15421
15422    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15423        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15424            s.select_ranges(Some(39..40))
15425        });
15426        editor.open_excerpts(&OpenExcerpts, window, cx);
15427    });
15428    cx.executor().run_until_parked();
15429    let second_item_id = workspace
15430        .update(cx, |workspace, window, cx| {
15431            let active_item = workspace
15432                .active_item(cx)
15433                .expect("should have an active item after navigating into the 2nd buffer");
15434            let second_item_id = active_item.item_id();
15435            assert_ne!(
15436                second_item_id, multibuffer_item_id,
15437                "Should navigate away from the multibuffer"
15438            );
15439            assert_ne!(
15440                second_item_id, first_item_id,
15441                "Should navigate into the 2nd buffer and activate it"
15442            );
15443            assert!(
15444                active_item.is_singleton(cx),
15445                "New active item should be a singleton buffer"
15446            );
15447            assert_eq!(
15448                active_item
15449                    .act_as::<Editor>(cx)
15450                    .expect("should have navigated into an editor")
15451                    .read(cx)
15452                    .text(cx),
15453                sample_text_2
15454            );
15455
15456            workspace
15457                .go_back(workspace.active_pane().downgrade(), window, cx)
15458                .detach_and_log_err(cx);
15459
15460            second_item_id
15461        })
15462        .unwrap();
15463    cx.executor().run_until_parked();
15464    workspace
15465        .update(cx, |workspace, _, cx| {
15466            let active_item = workspace
15467                .active_item(cx)
15468                .expect("should have an active item after navigating back from the 2nd buffer");
15469            assert_eq!(
15470                active_item.item_id(),
15471                multibuffer_item_id,
15472                "Should navigate back from the 2nd buffer to the multi buffer"
15473            );
15474            assert!(!active_item.is_singleton(cx));
15475        })
15476        .unwrap();
15477
15478    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15479        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15480            s.select_ranges(Some(70..70))
15481        });
15482        editor.open_excerpts(&OpenExcerpts, window, cx);
15483    });
15484    cx.executor().run_until_parked();
15485    workspace
15486        .update(cx, |workspace, window, cx| {
15487            let active_item = workspace
15488                .active_item(cx)
15489                .expect("should have an active item after navigating into the 3rd buffer");
15490            let third_item_id = active_item.item_id();
15491            assert_ne!(
15492                third_item_id, multibuffer_item_id,
15493                "Should navigate into the 3rd buffer and activate it"
15494            );
15495            assert_ne!(third_item_id, first_item_id);
15496            assert_ne!(third_item_id, second_item_id);
15497            assert!(
15498                active_item.is_singleton(cx),
15499                "New active item should be a singleton buffer"
15500            );
15501            assert_eq!(
15502                active_item
15503                    .act_as::<Editor>(cx)
15504                    .expect("should have navigated into an editor")
15505                    .read(cx)
15506                    .text(cx),
15507                sample_text_3
15508            );
15509
15510            workspace
15511                .go_back(workspace.active_pane().downgrade(), window, cx)
15512                .detach_and_log_err(cx);
15513        })
15514        .unwrap();
15515    cx.executor().run_until_parked();
15516    workspace
15517        .update(cx, |workspace, _, cx| {
15518            let active_item = workspace
15519                .active_item(cx)
15520                .expect("should have an active item after navigating back from the 3rd buffer");
15521            assert_eq!(
15522                active_item.item_id(),
15523                multibuffer_item_id,
15524                "Should navigate back from the 3rd buffer to the multi buffer"
15525            );
15526            assert!(!active_item.is_singleton(cx));
15527        })
15528        .unwrap();
15529}
15530
15531#[gpui::test]
15532async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15533    init_test(cx, |_| {});
15534
15535    let mut cx = EditorTestContext::new(cx).await;
15536
15537    let diff_base = r#"
15538        use some::mod;
15539
15540        const A: u32 = 42;
15541
15542        fn main() {
15543            println!("hello");
15544
15545            println!("world");
15546        }
15547        "#
15548    .unindent();
15549
15550    cx.set_state(
15551        &r#"
15552        use some::modified;
15553
15554        ˇ
15555        fn main() {
15556            println!("hello there");
15557
15558            println!("around the");
15559            println!("world");
15560        }
15561        "#
15562        .unindent(),
15563    );
15564
15565    cx.set_head_text(&diff_base);
15566    executor.run_until_parked();
15567
15568    cx.update_editor(|editor, window, cx| {
15569        editor.go_to_next_hunk(&GoToHunk, window, cx);
15570        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15571    });
15572    executor.run_until_parked();
15573    cx.assert_state_with_diff(
15574        r#"
15575          use some::modified;
15576
15577
15578          fn main() {
15579        -     println!("hello");
15580        + ˇ    println!("hello there");
15581
15582              println!("around the");
15583              println!("world");
15584          }
15585        "#
15586        .unindent(),
15587    );
15588
15589    cx.update_editor(|editor, window, cx| {
15590        for _ in 0..2 {
15591            editor.go_to_next_hunk(&GoToHunk, window, cx);
15592            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15593        }
15594    });
15595    executor.run_until_parked();
15596    cx.assert_state_with_diff(
15597        r#"
15598        - use some::mod;
15599        + ˇuse some::modified;
15600
15601
15602          fn main() {
15603        -     println!("hello");
15604        +     println!("hello there");
15605
15606        +     println!("around the");
15607              println!("world");
15608          }
15609        "#
15610        .unindent(),
15611    );
15612
15613    cx.update_editor(|editor, window, cx| {
15614        editor.go_to_next_hunk(&GoToHunk, window, cx);
15615        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15616    });
15617    executor.run_until_parked();
15618    cx.assert_state_with_diff(
15619        r#"
15620        - use some::mod;
15621        + use some::modified;
15622
15623        - const A: u32 = 42;
15624          ˇ
15625          fn main() {
15626        -     println!("hello");
15627        +     println!("hello there");
15628
15629        +     println!("around the");
15630              println!("world");
15631          }
15632        "#
15633        .unindent(),
15634    );
15635
15636    cx.update_editor(|editor, window, cx| {
15637        editor.cancel(&Cancel, window, cx);
15638    });
15639
15640    cx.assert_state_with_diff(
15641        r#"
15642          use some::modified;
15643
15644          ˇ
15645          fn main() {
15646              println!("hello there");
15647
15648              println!("around the");
15649              println!("world");
15650          }
15651        "#
15652        .unindent(),
15653    );
15654}
15655
15656#[gpui::test]
15657async fn test_diff_base_change_with_expanded_diff_hunks(
15658    executor: BackgroundExecutor,
15659    cx: &mut TestAppContext,
15660) {
15661    init_test(cx, |_| {});
15662
15663    let mut cx = EditorTestContext::new(cx).await;
15664
15665    let diff_base = r#"
15666        use some::mod1;
15667        use some::mod2;
15668
15669        const A: u32 = 42;
15670        const B: u32 = 42;
15671        const C: u32 = 42;
15672
15673        fn main() {
15674            println!("hello");
15675
15676            println!("world");
15677        }
15678        "#
15679    .unindent();
15680
15681    cx.set_state(
15682        &r#"
15683        use some::mod2;
15684
15685        const A: u32 = 42;
15686        const C: u32 = 42;
15687
15688        fn main(ˇ) {
15689            //println!("hello");
15690
15691            println!("world");
15692            //
15693            //
15694        }
15695        "#
15696        .unindent(),
15697    );
15698
15699    cx.set_head_text(&diff_base);
15700    executor.run_until_parked();
15701
15702    cx.update_editor(|editor, window, cx| {
15703        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15704    });
15705    executor.run_until_parked();
15706    cx.assert_state_with_diff(
15707        r#"
15708        - use some::mod1;
15709          use some::mod2;
15710
15711          const A: u32 = 42;
15712        - const B: u32 = 42;
15713          const C: u32 = 42;
15714
15715          fn main(ˇ) {
15716        -     println!("hello");
15717        +     //println!("hello");
15718
15719              println!("world");
15720        +     //
15721        +     //
15722          }
15723        "#
15724        .unindent(),
15725    );
15726
15727    cx.set_head_text("new diff base!");
15728    executor.run_until_parked();
15729    cx.assert_state_with_diff(
15730        r#"
15731        - new diff base!
15732        + use some::mod2;
15733        +
15734        + const A: u32 = 42;
15735        + const C: u32 = 42;
15736        +
15737        + fn main(ˇ) {
15738        +     //println!("hello");
15739        +
15740        +     println!("world");
15741        +     //
15742        +     //
15743        + }
15744        "#
15745        .unindent(),
15746    );
15747}
15748
15749#[gpui::test]
15750async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15751    init_test(cx, |_| {});
15752
15753    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15754    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15755    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15756    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15757    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15758    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15759
15760    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15761    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15762    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15763
15764    let multi_buffer = cx.new(|cx| {
15765        let mut multibuffer = MultiBuffer::new(ReadWrite);
15766        multibuffer.push_excerpts(
15767            buffer_1.clone(),
15768            [
15769                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15770                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15771                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15772            ],
15773            cx,
15774        );
15775        multibuffer.push_excerpts(
15776            buffer_2.clone(),
15777            [
15778                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15779                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15780                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15781            ],
15782            cx,
15783        );
15784        multibuffer.push_excerpts(
15785            buffer_3.clone(),
15786            [
15787                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15788                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15789                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15790            ],
15791            cx,
15792        );
15793        multibuffer
15794    });
15795
15796    let editor =
15797        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15798    editor
15799        .update(cx, |editor, _window, cx| {
15800            for (buffer, diff_base) in [
15801                (buffer_1.clone(), file_1_old),
15802                (buffer_2.clone(), file_2_old),
15803                (buffer_3.clone(), file_3_old),
15804            ] {
15805                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15806                editor
15807                    .buffer
15808                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15809            }
15810        })
15811        .unwrap();
15812
15813    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15814    cx.run_until_parked();
15815
15816    cx.assert_editor_state(
15817        &"
15818            ˇaaa
15819            ccc
15820            ddd
15821
15822            ggg
15823            hhh
15824
15825
15826            lll
15827            mmm
15828            NNN
15829
15830            qqq
15831            rrr
15832
15833            uuu
15834            111
15835            222
15836            333
15837
15838            666
15839            777
15840
15841            000
15842            !!!"
15843        .unindent(),
15844    );
15845
15846    cx.update_editor(|editor, window, cx| {
15847        editor.select_all(&SelectAll, window, cx);
15848        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15849    });
15850    cx.executor().run_until_parked();
15851
15852    cx.assert_state_with_diff(
15853        "
15854            «aaa
15855          - bbb
15856            ccc
15857            ddd
15858
15859            ggg
15860            hhh
15861
15862
15863            lll
15864            mmm
15865          - nnn
15866          + NNN
15867
15868            qqq
15869            rrr
15870
15871            uuu
15872            111
15873            222
15874            333
15875
15876          + 666
15877            777
15878
15879            000
15880            !!!ˇ»"
15881            .unindent(),
15882    );
15883}
15884
15885#[gpui::test]
15886async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15887    init_test(cx, |_| {});
15888
15889    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15890    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15891
15892    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15893    let multi_buffer = cx.new(|cx| {
15894        let mut multibuffer = MultiBuffer::new(ReadWrite);
15895        multibuffer.push_excerpts(
15896            buffer.clone(),
15897            [
15898                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15899                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15900                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15901            ],
15902            cx,
15903        );
15904        multibuffer
15905    });
15906
15907    let editor =
15908        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15909    editor
15910        .update(cx, |editor, _window, cx| {
15911            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15912            editor
15913                .buffer
15914                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15915        })
15916        .unwrap();
15917
15918    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15919    cx.run_until_parked();
15920
15921    cx.update_editor(|editor, window, cx| {
15922        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15923    });
15924    cx.executor().run_until_parked();
15925
15926    // When the start of a hunk coincides with the start of its excerpt,
15927    // the hunk is expanded. When the start of a a hunk is earlier than
15928    // the start of its excerpt, the hunk is not expanded.
15929    cx.assert_state_with_diff(
15930        "
15931            ˇaaa
15932          - bbb
15933          + BBB
15934
15935          - ddd
15936          - eee
15937          + DDD
15938          + EEE
15939            fff
15940
15941            iii
15942        "
15943        .unindent(),
15944    );
15945}
15946
15947#[gpui::test]
15948async fn test_edits_around_expanded_insertion_hunks(
15949    executor: BackgroundExecutor,
15950    cx: &mut TestAppContext,
15951) {
15952    init_test(cx, |_| {});
15953
15954    let mut cx = EditorTestContext::new(cx).await;
15955
15956    let diff_base = r#"
15957        use some::mod1;
15958        use some::mod2;
15959
15960        const A: u32 = 42;
15961
15962        fn main() {
15963            println!("hello");
15964
15965            println!("world");
15966        }
15967        "#
15968    .unindent();
15969    executor.run_until_parked();
15970    cx.set_state(
15971        &r#"
15972        use some::mod1;
15973        use some::mod2;
15974
15975        const A: u32 = 42;
15976        const B: u32 = 42;
15977        const C: u32 = 42;
15978        ˇ
15979
15980        fn main() {
15981            println!("hello");
15982
15983            println!("world");
15984        }
15985        "#
15986        .unindent(),
15987    );
15988
15989    cx.set_head_text(&diff_base);
15990    executor.run_until_parked();
15991
15992    cx.update_editor(|editor, window, cx| {
15993        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15994    });
15995    executor.run_until_parked();
15996
15997    cx.assert_state_with_diff(
15998        r#"
15999        use some::mod1;
16000        use some::mod2;
16001
16002        const A: u32 = 42;
16003      + const B: u32 = 42;
16004      + const C: u32 = 42;
16005      + ˇ
16006
16007        fn main() {
16008            println!("hello");
16009
16010            println!("world");
16011        }
16012      "#
16013        .unindent(),
16014    );
16015
16016    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
16017    executor.run_until_parked();
16018
16019    cx.assert_state_with_diff(
16020        r#"
16021        use some::mod1;
16022        use some::mod2;
16023
16024        const A: u32 = 42;
16025      + const B: u32 = 42;
16026      + const C: u32 = 42;
16027      + const D: u32 = 42;
16028      + ˇ
16029
16030        fn main() {
16031            println!("hello");
16032
16033            println!("world");
16034        }
16035      "#
16036        .unindent(),
16037    );
16038
16039    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
16040    executor.run_until_parked();
16041
16042    cx.assert_state_with_diff(
16043        r#"
16044        use some::mod1;
16045        use some::mod2;
16046
16047        const A: u32 = 42;
16048      + const B: u32 = 42;
16049      + const C: u32 = 42;
16050      + const D: u32 = 42;
16051      + const E: u32 = 42;
16052      + ˇ
16053
16054        fn main() {
16055            println!("hello");
16056
16057            println!("world");
16058        }
16059      "#
16060        .unindent(),
16061    );
16062
16063    cx.update_editor(|editor, window, cx| {
16064        editor.delete_line(&DeleteLine, window, cx);
16065    });
16066    executor.run_until_parked();
16067
16068    cx.assert_state_with_diff(
16069        r#"
16070        use some::mod1;
16071        use some::mod2;
16072
16073        const A: u32 = 42;
16074      + const B: u32 = 42;
16075      + const C: u32 = 42;
16076      + const D: u32 = 42;
16077      + const E: u32 = 42;
16078        ˇ
16079        fn main() {
16080            println!("hello");
16081
16082            println!("world");
16083        }
16084      "#
16085        .unindent(),
16086    );
16087
16088    cx.update_editor(|editor, window, cx| {
16089        editor.move_up(&MoveUp, window, cx);
16090        editor.delete_line(&DeleteLine, window, cx);
16091        editor.move_up(&MoveUp, window, cx);
16092        editor.delete_line(&DeleteLine, window, cx);
16093        editor.move_up(&MoveUp, window, cx);
16094        editor.delete_line(&DeleteLine, window, cx);
16095    });
16096    executor.run_until_parked();
16097    cx.assert_state_with_diff(
16098        r#"
16099        use some::mod1;
16100        use some::mod2;
16101
16102        const A: u32 = 42;
16103      + const B: u32 = 42;
16104        ˇ
16105        fn main() {
16106            println!("hello");
16107
16108            println!("world");
16109        }
16110      "#
16111        .unindent(),
16112    );
16113
16114    cx.update_editor(|editor, window, cx| {
16115        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16116        editor.delete_line(&DeleteLine, window, cx);
16117    });
16118    executor.run_until_parked();
16119    cx.assert_state_with_diff(
16120        r#"
16121        ˇ
16122        fn main() {
16123            println!("hello");
16124
16125            println!("world");
16126        }
16127      "#
16128        .unindent(),
16129    );
16130}
16131
16132#[gpui::test]
16133async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16134    init_test(cx, |_| {});
16135
16136    let mut cx = EditorTestContext::new(cx).await;
16137    cx.set_head_text(indoc! { "
16138        one
16139        two
16140        three
16141        four
16142        five
16143        "
16144    });
16145    cx.set_state(indoc! { "
16146        one
16147        ˇthree
16148        five
16149    "});
16150    cx.run_until_parked();
16151    cx.update_editor(|editor, window, cx| {
16152        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16153    });
16154    cx.assert_state_with_diff(
16155        indoc! { "
16156        one
16157      - two
16158        ˇthree
16159      - four
16160        five
16161    "}
16162        .to_string(),
16163    );
16164    cx.update_editor(|editor, window, cx| {
16165        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16166    });
16167
16168    cx.assert_state_with_diff(
16169        indoc! { "
16170        one
16171        ˇthree
16172        five
16173    "}
16174        .to_string(),
16175    );
16176
16177    cx.set_state(indoc! { "
16178        one
16179        ˇTWO
16180        three
16181        four
16182        five
16183    "});
16184    cx.run_until_parked();
16185    cx.update_editor(|editor, window, cx| {
16186        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16187    });
16188
16189    cx.assert_state_with_diff(
16190        indoc! { "
16191            one
16192          - two
16193          + ˇTWO
16194            three
16195            four
16196            five
16197        "}
16198        .to_string(),
16199    );
16200    cx.update_editor(|editor, window, cx| {
16201        editor.move_up(&Default::default(), window, cx);
16202        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16203    });
16204    cx.assert_state_with_diff(
16205        indoc! { "
16206            one
16207            ˇTWO
16208            three
16209            four
16210            five
16211        "}
16212        .to_string(),
16213    );
16214}
16215
16216#[gpui::test]
16217async fn test_edits_around_expanded_deletion_hunks(
16218    executor: BackgroundExecutor,
16219    cx: &mut TestAppContext,
16220) {
16221    init_test(cx, |_| {});
16222
16223    let mut cx = EditorTestContext::new(cx).await;
16224
16225    let diff_base = r#"
16226        use some::mod1;
16227        use some::mod2;
16228
16229        const A: u32 = 42;
16230        const B: u32 = 42;
16231        const C: u32 = 42;
16232
16233
16234        fn main() {
16235            println!("hello");
16236
16237            println!("world");
16238        }
16239    "#
16240    .unindent();
16241    executor.run_until_parked();
16242    cx.set_state(
16243        &r#"
16244        use some::mod1;
16245        use some::mod2;
16246
16247        ˇconst B: u32 = 42;
16248        const C: u32 = 42;
16249
16250
16251        fn main() {
16252            println!("hello");
16253
16254            println!("world");
16255        }
16256        "#
16257        .unindent(),
16258    );
16259
16260    cx.set_head_text(&diff_base);
16261    executor.run_until_parked();
16262
16263    cx.update_editor(|editor, window, cx| {
16264        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16265    });
16266    executor.run_until_parked();
16267
16268    cx.assert_state_with_diff(
16269        r#"
16270        use some::mod1;
16271        use some::mod2;
16272
16273      - const A: u32 = 42;
16274        ˇconst B: u32 = 42;
16275        const C: u32 = 42;
16276
16277
16278        fn main() {
16279            println!("hello");
16280
16281            println!("world");
16282        }
16283      "#
16284        .unindent(),
16285    );
16286
16287    cx.update_editor(|editor, window, cx| {
16288        editor.delete_line(&DeleteLine, window, cx);
16289    });
16290    executor.run_until_parked();
16291    cx.assert_state_with_diff(
16292        r#"
16293        use some::mod1;
16294        use some::mod2;
16295
16296      - const A: u32 = 42;
16297      - const B: u32 = 42;
16298        ˇconst C: u32 = 42;
16299
16300
16301        fn main() {
16302            println!("hello");
16303
16304            println!("world");
16305        }
16306      "#
16307        .unindent(),
16308    );
16309
16310    cx.update_editor(|editor, window, cx| {
16311        editor.delete_line(&DeleteLine, window, cx);
16312    });
16313    executor.run_until_parked();
16314    cx.assert_state_with_diff(
16315        r#"
16316        use some::mod1;
16317        use some::mod2;
16318
16319      - const A: u32 = 42;
16320      - const B: u32 = 42;
16321      - const C: u32 = 42;
16322        ˇ
16323
16324        fn main() {
16325            println!("hello");
16326
16327            println!("world");
16328        }
16329      "#
16330        .unindent(),
16331    );
16332
16333    cx.update_editor(|editor, window, cx| {
16334        editor.handle_input("replacement", window, cx);
16335    });
16336    executor.run_until_parked();
16337    cx.assert_state_with_diff(
16338        r#"
16339        use some::mod1;
16340        use some::mod2;
16341
16342      - const A: u32 = 42;
16343      - const B: u32 = 42;
16344      - const C: u32 = 42;
16345      -
16346      + replacementˇ
16347
16348        fn main() {
16349            println!("hello");
16350
16351            println!("world");
16352        }
16353      "#
16354        .unindent(),
16355    );
16356}
16357
16358#[gpui::test]
16359async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16360    init_test(cx, |_| {});
16361
16362    let mut cx = EditorTestContext::new(cx).await;
16363
16364    let base_text = r#"
16365        one
16366        two
16367        three
16368        four
16369        five
16370    "#
16371    .unindent();
16372    executor.run_until_parked();
16373    cx.set_state(
16374        &r#"
16375        one
16376        two
16377        fˇour
16378        five
16379        "#
16380        .unindent(),
16381    );
16382
16383    cx.set_head_text(&base_text);
16384    executor.run_until_parked();
16385
16386    cx.update_editor(|editor, window, cx| {
16387        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16388    });
16389    executor.run_until_parked();
16390
16391    cx.assert_state_with_diff(
16392        r#"
16393          one
16394          two
16395        - three
16396          fˇour
16397          five
16398        "#
16399        .unindent(),
16400    );
16401
16402    cx.update_editor(|editor, window, cx| {
16403        editor.backspace(&Backspace, window, cx);
16404        editor.backspace(&Backspace, window, cx);
16405    });
16406    executor.run_until_parked();
16407    cx.assert_state_with_diff(
16408        r#"
16409          one
16410          two
16411        - threeˇ
16412        - four
16413        + our
16414          five
16415        "#
16416        .unindent(),
16417    );
16418}
16419
16420#[gpui::test]
16421async fn test_edit_after_expanded_modification_hunk(
16422    executor: BackgroundExecutor,
16423    cx: &mut TestAppContext,
16424) {
16425    init_test(cx, |_| {});
16426
16427    let mut cx = EditorTestContext::new(cx).await;
16428
16429    let diff_base = r#"
16430        use some::mod1;
16431        use some::mod2;
16432
16433        const A: u32 = 42;
16434        const B: u32 = 42;
16435        const C: u32 = 42;
16436        const D: u32 = 42;
16437
16438
16439        fn main() {
16440            println!("hello");
16441
16442            println!("world");
16443        }"#
16444    .unindent();
16445
16446    cx.set_state(
16447        &r#"
16448        use some::mod1;
16449        use some::mod2;
16450
16451        const A: u32 = 42;
16452        const B: u32 = 42;
16453        const C: u32 = 43ˇ
16454        const D: u32 = 42;
16455
16456
16457        fn main() {
16458            println!("hello");
16459
16460            println!("world");
16461        }"#
16462        .unindent(),
16463    );
16464
16465    cx.set_head_text(&diff_base);
16466    executor.run_until_parked();
16467    cx.update_editor(|editor, window, cx| {
16468        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16469    });
16470    executor.run_until_parked();
16471
16472    cx.assert_state_with_diff(
16473        r#"
16474        use some::mod1;
16475        use some::mod2;
16476
16477        const A: u32 = 42;
16478        const B: u32 = 42;
16479      - const C: u32 = 42;
16480      + const C: u32 = 43ˇ
16481        const D: u32 = 42;
16482
16483
16484        fn main() {
16485            println!("hello");
16486
16487            println!("world");
16488        }"#
16489        .unindent(),
16490    );
16491
16492    cx.update_editor(|editor, window, cx| {
16493        editor.handle_input("\nnew_line\n", window, cx);
16494    });
16495    executor.run_until_parked();
16496
16497    cx.assert_state_with_diff(
16498        r#"
16499        use some::mod1;
16500        use some::mod2;
16501
16502        const A: u32 = 42;
16503        const B: u32 = 42;
16504      - const C: u32 = 42;
16505      + const C: u32 = 43
16506      + new_line
16507      + ˇ
16508        const D: u32 = 42;
16509
16510
16511        fn main() {
16512            println!("hello");
16513
16514            println!("world");
16515        }"#
16516        .unindent(),
16517    );
16518}
16519
16520#[gpui::test]
16521async fn test_stage_and_unstage_added_file_hunk(
16522    executor: BackgroundExecutor,
16523    cx: &mut TestAppContext,
16524) {
16525    init_test(cx, |_| {});
16526
16527    let mut cx = EditorTestContext::new(cx).await;
16528    cx.update_editor(|editor, _, cx| {
16529        editor.set_expand_all_diff_hunks(cx);
16530    });
16531
16532    let working_copy = r#"
16533            ˇfn main() {
16534                println!("hello, world!");
16535            }
16536        "#
16537    .unindent();
16538
16539    cx.set_state(&working_copy);
16540    executor.run_until_parked();
16541
16542    cx.assert_state_with_diff(
16543        r#"
16544            + ˇfn main() {
16545            +     println!("hello, world!");
16546            + }
16547        "#
16548        .unindent(),
16549    );
16550    cx.assert_index_text(None);
16551
16552    cx.update_editor(|editor, window, cx| {
16553        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16554    });
16555    executor.run_until_parked();
16556    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16557    cx.assert_state_with_diff(
16558        r#"
16559            + ˇfn main() {
16560            +     println!("hello, world!");
16561            + }
16562        "#
16563        .unindent(),
16564    );
16565
16566    cx.update_editor(|editor, window, cx| {
16567        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16568    });
16569    executor.run_until_parked();
16570    cx.assert_index_text(None);
16571}
16572
16573async fn setup_indent_guides_editor(
16574    text: &str,
16575    cx: &mut TestAppContext,
16576) -> (BufferId, EditorTestContext) {
16577    init_test(cx, |_| {});
16578
16579    let mut cx = EditorTestContext::new(cx).await;
16580
16581    let buffer_id = cx.update_editor(|editor, window, cx| {
16582        editor.set_text(text, window, cx);
16583        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16584
16585        buffer_ids[0]
16586    });
16587
16588    (buffer_id, cx)
16589}
16590
16591fn assert_indent_guides(
16592    range: Range<u32>,
16593    expected: Vec<IndentGuide>,
16594    active_indices: Option<Vec<usize>>,
16595    cx: &mut EditorTestContext,
16596) {
16597    let indent_guides = cx.update_editor(|editor, window, cx| {
16598        let snapshot = editor.snapshot(window, cx).display_snapshot;
16599        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16600            editor,
16601            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16602            true,
16603            &snapshot,
16604            cx,
16605        );
16606
16607        indent_guides.sort_by(|a, b| {
16608            a.depth.cmp(&b.depth).then(
16609                a.start_row
16610                    .cmp(&b.start_row)
16611                    .then(a.end_row.cmp(&b.end_row)),
16612            )
16613        });
16614        indent_guides
16615    });
16616
16617    if let Some(expected) = active_indices {
16618        let active_indices = cx.update_editor(|editor, window, cx| {
16619            let snapshot = editor.snapshot(window, cx).display_snapshot;
16620            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16621        });
16622
16623        assert_eq!(
16624            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16625            expected,
16626            "Active indent guide indices do not match"
16627        );
16628    }
16629
16630    assert_eq!(indent_guides, expected, "Indent guides do not match");
16631}
16632
16633fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16634    IndentGuide {
16635        buffer_id,
16636        start_row: MultiBufferRow(start_row),
16637        end_row: MultiBufferRow(end_row),
16638        depth,
16639        tab_size: 4,
16640        settings: IndentGuideSettings {
16641            enabled: true,
16642            line_width: 1,
16643            active_line_width: 1,
16644            ..Default::default()
16645        },
16646    }
16647}
16648
16649#[gpui::test]
16650async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16651    let (buffer_id, mut cx) = setup_indent_guides_editor(
16652        &"
16653    fn main() {
16654        let a = 1;
16655    }"
16656        .unindent(),
16657        cx,
16658    )
16659    .await;
16660
16661    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16662}
16663
16664#[gpui::test]
16665async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16666    let (buffer_id, mut cx) = setup_indent_guides_editor(
16667        &"
16668    fn main() {
16669        let a = 1;
16670        let b = 2;
16671    }"
16672        .unindent(),
16673        cx,
16674    )
16675    .await;
16676
16677    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16678}
16679
16680#[gpui::test]
16681async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16682    let (buffer_id, mut cx) = setup_indent_guides_editor(
16683        &"
16684    fn main() {
16685        let a = 1;
16686        if a == 3 {
16687            let b = 2;
16688        } else {
16689            let c = 3;
16690        }
16691    }"
16692        .unindent(),
16693        cx,
16694    )
16695    .await;
16696
16697    assert_indent_guides(
16698        0..8,
16699        vec![
16700            indent_guide(buffer_id, 1, 6, 0),
16701            indent_guide(buffer_id, 3, 3, 1),
16702            indent_guide(buffer_id, 5, 5, 1),
16703        ],
16704        None,
16705        &mut cx,
16706    );
16707}
16708
16709#[gpui::test]
16710async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16711    let (buffer_id, mut cx) = setup_indent_guides_editor(
16712        &"
16713    fn main() {
16714        let a = 1;
16715            let b = 2;
16716        let c = 3;
16717    }"
16718        .unindent(),
16719        cx,
16720    )
16721    .await;
16722
16723    assert_indent_guides(
16724        0..5,
16725        vec![
16726            indent_guide(buffer_id, 1, 3, 0),
16727            indent_guide(buffer_id, 2, 2, 1),
16728        ],
16729        None,
16730        &mut cx,
16731    );
16732}
16733
16734#[gpui::test]
16735async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16736    let (buffer_id, mut cx) = setup_indent_guides_editor(
16737        &"
16738        fn main() {
16739            let a = 1;
16740
16741            let c = 3;
16742        }"
16743        .unindent(),
16744        cx,
16745    )
16746    .await;
16747
16748    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16749}
16750
16751#[gpui::test]
16752async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16753    let (buffer_id, mut cx) = setup_indent_guides_editor(
16754        &"
16755        fn main() {
16756            let a = 1;
16757
16758            let c = 3;
16759
16760            if a == 3 {
16761                let b = 2;
16762            } else {
16763                let c = 3;
16764            }
16765        }"
16766        .unindent(),
16767        cx,
16768    )
16769    .await;
16770
16771    assert_indent_guides(
16772        0..11,
16773        vec![
16774            indent_guide(buffer_id, 1, 9, 0),
16775            indent_guide(buffer_id, 6, 6, 1),
16776            indent_guide(buffer_id, 8, 8, 1),
16777        ],
16778        None,
16779        &mut cx,
16780    );
16781}
16782
16783#[gpui::test]
16784async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16785    let (buffer_id, mut cx) = setup_indent_guides_editor(
16786        &"
16787        fn main() {
16788            let a = 1;
16789
16790            let c = 3;
16791
16792            if a == 3 {
16793                let b = 2;
16794            } else {
16795                let c = 3;
16796            }
16797        }"
16798        .unindent(),
16799        cx,
16800    )
16801    .await;
16802
16803    assert_indent_guides(
16804        1..11,
16805        vec![
16806            indent_guide(buffer_id, 1, 9, 0),
16807            indent_guide(buffer_id, 6, 6, 1),
16808            indent_guide(buffer_id, 8, 8, 1),
16809        ],
16810        None,
16811        &mut cx,
16812    );
16813}
16814
16815#[gpui::test]
16816async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16817    let (buffer_id, mut cx) = setup_indent_guides_editor(
16818        &"
16819        fn main() {
16820            let a = 1;
16821
16822            let c = 3;
16823
16824            if a == 3 {
16825                let b = 2;
16826            } else {
16827                let c = 3;
16828            }
16829        }"
16830        .unindent(),
16831        cx,
16832    )
16833    .await;
16834
16835    assert_indent_guides(
16836        1..10,
16837        vec![
16838            indent_guide(buffer_id, 1, 9, 0),
16839            indent_guide(buffer_id, 6, 6, 1),
16840            indent_guide(buffer_id, 8, 8, 1),
16841        ],
16842        None,
16843        &mut cx,
16844    );
16845}
16846
16847#[gpui::test]
16848async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16849    let (buffer_id, mut cx) = setup_indent_guides_editor(
16850        &"
16851        block1
16852            block2
16853                block3
16854                    block4
16855            block2
16856        block1
16857        block1"
16858            .unindent(),
16859        cx,
16860    )
16861    .await;
16862
16863    assert_indent_guides(
16864        1..10,
16865        vec![
16866            indent_guide(buffer_id, 1, 4, 0),
16867            indent_guide(buffer_id, 2, 3, 1),
16868            indent_guide(buffer_id, 3, 3, 2),
16869        ],
16870        None,
16871        &mut cx,
16872    );
16873}
16874
16875#[gpui::test]
16876async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16877    let (buffer_id, mut cx) = setup_indent_guides_editor(
16878        &"
16879        block1
16880            block2
16881                block3
16882
16883        block1
16884        block1"
16885            .unindent(),
16886        cx,
16887    )
16888    .await;
16889
16890    assert_indent_guides(
16891        0..6,
16892        vec![
16893            indent_guide(buffer_id, 1, 2, 0),
16894            indent_guide(buffer_id, 2, 2, 1),
16895        ],
16896        None,
16897        &mut cx,
16898    );
16899}
16900
16901#[gpui::test]
16902async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16903    let (buffer_id, mut cx) = setup_indent_guides_editor(
16904        &"
16905        block1
16906
16907
16908
16909            block2
16910        "
16911        .unindent(),
16912        cx,
16913    )
16914    .await;
16915
16916    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16917}
16918
16919#[gpui::test]
16920async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16921    let (buffer_id, mut cx) = setup_indent_guides_editor(
16922        &"
16923        def a:
16924        \tb = 3
16925        \tif True:
16926        \t\tc = 4
16927        \t\td = 5
16928        \tprint(b)
16929        "
16930        .unindent(),
16931        cx,
16932    )
16933    .await;
16934
16935    assert_indent_guides(
16936        0..6,
16937        vec![
16938            indent_guide(buffer_id, 1, 5, 0),
16939            indent_guide(buffer_id, 3, 4, 1),
16940        ],
16941        None,
16942        &mut cx,
16943    );
16944}
16945
16946#[gpui::test]
16947async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16948    let (buffer_id, mut cx) = setup_indent_guides_editor(
16949        &"
16950    fn main() {
16951        let a = 1;
16952    }"
16953        .unindent(),
16954        cx,
16955    )
16956    .await;
16957
16958    cx.update_editor(|editor, window, cx| {
16959        editor.change_selections(None, window, cx, |s| {
16960            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16961        });
16962    });
16963
16964    assert_indent_guides(
16965        0..3,
16966        vec![indent_guide(buffer_id, 1, 1, 0)],
16967        Some(vec![0]),
16968        &mut cx,
16969    );
16970}
16971
16972#[gpui::test]
16973async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16974    let (buffer_id, mut cx) = setup_indent_guides_editor(
16975        &"
16976    fn main() {
16977        if 1 == 2 {
16978            let a = 1;
16979        }
16980    }"
16981        .unindent(),
16982        cx,
16983    )
16984    .await;
16985
16986    cx.update_editor(|editor, window, cx| {
16987        editor.change_selections(None, window, cx, |s| {
16988            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16989        });
16990    });
16991
16992    assert_indent_guides(
16993        0..4,
16994        vec![
16995            indent_guide(buffer_id, 1, 3, 0),
16996            indent_guide(buffer_id, 2, 2, 1),
16997        ],
16998        Some(vec![1]),
16999        &mut cx,
17000    );
17001
17002    cx.update_editor(|editor, window, cx| {
17003        editor.change_selections(None, window, cx, |s| {
17004            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17005        });
17006    });
17007
17008    assert_indent_guides(
17009        0..4,
17010        vec![
17011            indent_guide(buffer_id, 1, 3, 0),
17012            indent_guide(buffer_id, 2, 2, 1),
17013        ],
17014        Some(vec![1]),
17015        &mut cx,
17016    );
17017
17018    cx.update_editor(|editor, window, cx| {
17019        editor.change_selections(None, window, cx, |s| {
17020            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
17021        });
17022    });
17023
17024    assert_indent_guides(
17025        0..4,
17026        vec![
17027            indent_guide(buffer_id, 1, 3, 0),
17028            indent_guide(buffer_id, 2, 2, 1),
17029        ],
17030        Some(vec![0]),
17031        &mut cx,
17032    );
17033}
17034
17035#[gpui::test]
17036async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
17037    let (buffer_id, mut cx) = setup_indent_guides_editor(
17038        &"
17039    fn main() {
17040        let a = 1;
17041
17042        let b = 2;
17043    }"
17044        .unindent(),
17045        cx,
17046    )
17047    .await;
17048
17049    cx.update_editor(|editor, window, cx| {
17050        editor.change_selections(None, window, cx, |s| {
17051            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
17052        });
17053    });
17054
17055    assert_indent_guides(
17056        0..5,
17057        vec![indent_guide(buffer_id, 1, 3, 0)],
17058        Some(vec![0]),
17059        &mut cx,
17060    );
17061}
17062
17063#[gpui::test]
17064async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17065    let (buffer_id, mut cx) = setup_indent_guides_editor(
17066        &"
17067    def m:
17068        a = 1
17069        pass"
17070            .unindent(),
17071        cx,
17072    )
17073    .await;
17074
17075    cx.update_editor(|editor, window, cx| {
17076        editor.change_selections(None, window, cx, |s| {
17077            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17078        });
17079    });
17080
17081    assert_indent_guides(
17082        0..3,
17083        vec![indent_guide(buffer_id, 1, 2, 0)],
17084        Some(vec![0]),
17085        &mut cx,
17086    );
17087}
17088
17089#[gpui::test]
17090async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17091    init_test(cx, |_| {});
17092    let mut cx = EditorTestContext::new(cx).await;
17093    let text = indoc! {
17094        "
17095        impl A {
17096            fn b() {
17097                0;
17098                3;
17099                5;
17100                6;
17101                7;
17102            }
17103        }
17104        "
17105    };
17106    let base_text = indoc! {
17107        "
17108        impl A {
17109            fn b() {
17110                0;
17111                1;
17112                2;
17113                3;
17114                4;
17115            }
17116            fn c() {
17117                5;
17118                6;
17119                7;
17120            }
17121        }
17122        "
17123    };
17124
17125    cx.update_editor(|editor, window, cx| {
17126        editor.set_text(text, window, cx);
17127
17128        editor.buffer().update(cx, |multibuffer, cx| {
17129            let buffer = multibuffer.as_singleton().unwrap();
17130            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17131
17132            multibuffer.set_all_diff_hunks_expanded(cx);
17133            multibuffer.add_diff(diff, cx);
17134
17135            buffer.read(cx).remote_id()
17136        })
17137    });
17138    cx.run_until_parked();
17139
17140    cx.assert_state_with_diff(
17141        indoc! { "
17142          impl A {
17143              fn b() {
17144                  0;
17145        -         1;
17146        -         2;
17147                  3;
17148        -         4;
17149        -     }
17150        -     fn c() {
17151                  5;
17152                  6;
17153                  7;
17154              }
17155          }
17156          ˇ"
17157        }
17158        .to_string(),
17159    );
17160
17161    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17162        editor
17163            .snapshot(window, cx)
17164            .buffer_snapshot
17165            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17166            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17167            .collect::<Vec<_>>()
17168    });
17169    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17170    assert_eq!(
17171        actual_guides,
17172        vec![
17173            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17174            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17175            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17176        ]
17177    );
17178}
17179
17180#[gpui::test]
17181async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17182    init_test(cx, |_| {});
17183    let mut cx = EditorTestContext::new(cx).await;
17184
17185    let diff_base = r#"
17186        a
17187        b
17188        c
17189        "#
17190    .unindent();
17191
17192    cx.set_state(
17193        &r#"
17194        ˇA
17195        b
17196        C
17197        "#
17198        .unindent(),
17199    );
17200    cx.set_head_text(&diff_base);
17201    cx.update_editor(|editor, window, cx| {
17202        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17203    });
17204    executor.run_until_parked();
17205
17206    let both_hunks_expanded = r#"
17207        - a
17208        + ˇA
17209          b
17210        - c
17211        + C
17212        "#
17213    .unindent();
17214
17215    cx.assert_state_with_diff(both_hunks_expanded.clone());
17216
17217    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17218        let snapshot = editor.snapshot(window, cx);
17219        let hunks = editor
17220            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17221            .collect::<Vec<_>>();
17222        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17223        let buffer_id = hunks[0].buffer_id;
17224        hunks
17225            .into_iter()
17226            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17227            .collect::<Vec<_>>()
17228    });
17229    assert_eq!(hunk_ranges.len(), 2);
17230
17231    cx.update_editor(|editor, _, cx| {
17232        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17233    });
17234    executor.run_until_parked();
17235
17236    let second_hunk_expanded = r#"
17237          ˇA
17238          b
17239        - c
17240        + C
17241        "#
17242    .unindent();
17243
17244    cx.assert_state_with_diff(second_hunk_expanded);
17245
17246    cx.update_editor(|editor, _, cx| {
17247        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17248    });
17249    executor.run_until_parked();
17250
17251    cx.assert_state_with_diff(both_hunks_expanded.clone());
17252
17253    cx.update_editor(|editor, _, cx| {
17254        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17255    });
17256    executor.run_until_parked();
17257
17258    let first_hunk_expanded = r#"
17259        - a
17260        + ˇA
17261          b
17262          C
17263        "#
17264    .unindent();
17265
17266    cx.assert_state_with_diff(first_hunk_expanded);
17267
17268    cx.update_editor(|editor, _, cx| {
17269        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17270    });
17271    executor.run_until_parked();
17272
17273    cx.assert_state_with_diff(both_hunks_expanded);
17274
17275    cx.set_state(
17276        &r#"
17277        ˇA
17278        b
17279        "#
17280        .unindent(),
17281    );
17282    cx.run_until_parked();
17283
17284    // TODO this cursor position seems bad
17285    cx.assert_state_with_diff(
17286        r#"
17287        - ˇa
17288        + A
17289          b
17290        "#
17291        .unindent(),
17292    );
17293
17294    cx.update_editor(|editor, window, cx| {
17295        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17296    });
17297
17298    cx.assert_state_with_diff(
17299        r#"
17300            - ˇa
17301            + A
17302              b
17303            - c
17304            "#
17305        .unindent(),
17306    );
17307
17308    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17309        let snapshot = editor.snapshot(window, cx);
17310        let hunks = editor
17311            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17312            .collect::<Vec<_>>();
17313        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17314        let buffer_id = hunks[0].buffer_id;
17315        hunks
17316            .into_iter()
17317            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17318            .collect::<Vec<_>>()
17319    });
17320    assert_eq!(hunk_ranges.len(), 2);
17321
17322    cx.update_editor(|editor, _, cx| {
17323        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17324    });
17325    executor.run_until_parked();
17326
17327    cx.assert_state_with_diff(
17328        r#"
17329        - ˇa
17330        + A
17331          b
17332        "#
17333        .unindent(),
17334    );
17335}
17336
17337#[gpui::test]
17338async fn test_toggle_deletion_hunk_at_start_of_file(
17339    executor: BackgroundExecutor,
17340    cx: &mut TestAppContext,
17341) {
17342    init_test(cx, |_| {});
17343    let mut cx = EditorTestContext::new(cx).await;
17344
17345    let diff_base = r#"
17346        a
17347        b
17348        c
17349        "#
17350    .unindent();
17351
17352    cx.set_state(
17353        &r#"
17354        ˇb
17355        c
17356        "#
17357        .unindent(),
17358    );
17359    cx.set_head_text(&diff_base);
17360    cx.update_editor(|editor, window, cx| {
17361        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17362    });
17363    executor.run_until_parked();
17364
17365    let hunk_expanded = r#"
17366        - a
17367          ˇb
17368          c
17369        "#
17370    .unindent();
17371
17372    cx.assert_state_with_diff(hunk_expanded.clone());
17373
17374    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17375        let snapshot = editor.snapshot(window, cx);
17376        let hunks = editor
17377            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17378            .collect::<Vec<_>>();
17379        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17380        let buffer_id = hunks[0].buffer_id;
17381        hunks
17382            .into_iter()
17383            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17384            .collect::<Vec<_>>()
17385    });
17386    assert_eq!(hunk_ranges.len(), 1);
17387
17388    cx.update_editor(|editor, _, cx| {
17389        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17390    });
17391    executor.run_until_parked();
17392
17393    let hunk_collapsed = r#"
17394          ˇb
17395          c
17396        "#
17397    .unindent();
17398
17399    cx.assert_state_with_diff(hunk_collapsed);
17400
17401    cx.update_editor(|editor, _, cx| {
17402        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17403    });
17404    executor.run_until_parked();
17405
17406    cx.assert_state_with_diff(hunk_expanded.clone());
17407}
17408
17409#[gpui::test]
17410async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17411    init_test(cx, |_| {});
17412
17413    let fs = FakeFs::new(cx.executor());
17414    fs.insert_tree(
17415        path!("/test"),
17416        json!({
17417            ".git": {},
17418            "file-1": "ONE\n",
17419            "file-2": "TWO\n",
17420            "file-3": "THREE\n",
17421        }),
17422    )
17423    .await;
17424
17425    fs.set_head_for_repo(
17426        path!("/test/.git").as_ref(),
17427        &[
17428            ("file-1".into(), "one\n".into()),
17429            ("file-2".into(), "two\n".into()),
17430            ("file-3".into(), "three\n".into()),
17431        ],
17432    );
17433
17434    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17435    let mut buffers = vec![];
17436    for i in 1..=3 {
17437        let buffer = project
17438            .update(cx, |project, cx| {
17439                let path = format!(path!("/test/file-{}"), i);
17440                project.open_local_buffer(path, cx)
17441            })
17442            .await
17443            .unwrap();
17444        buffers.push(buffer);
17445    }
17446
17447    let multibuffer = cx.new(|cx| {
17448        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17449        multibuffer.set_all_diff_hunks_expanded(cx);
17450        for buffer in &buffers {
17451            let snapshot = buffer.read(cx).snapshot();
17452            multibuffer.set_excerpts_for_path(
17453                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17454                buffer.clone(),
17455                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17456                DEFAULT_MULTIBUFFER_CONTEXT,
17457                cx,
17458            );
17459        }
17460        multibuffer
17461    });
17462
17463    let editor = cx.add_window(|window, cx| {
17464        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17465    });
17466    cx.run_until_parked();
17467
17468    let snapshot = editor
17469        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17470        .unwrap();
17471    let hunks = snapshot
17472        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17473        .map(|hunk| match hunk {
17474            DisplayDiffHunk::Unfolded {
17475                display_row_range, ..
17476            } => display_row_range,
17477            DisplayDiffHunk::Folded { .. } => unreachable!(),
17478        })
17479        .collect::<Vec<_>>();
17480    assert_eq!(
17481        hunks,
17482        [
17483            DisplayRow(2)..DisplayRow(4),
17484            DisplayRow(7)..DisplayRow(9),
17485            DisplayRow(12)..DisplayRow(14),
17486        ]
17487    );
17488}
17489
17490#[gpui::test]
17491async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17492    init_test(cx, |_| {});
17493
17494    let mut cx = EditorTestContext::new(cx).await;
17495    cx.set_head_text(indoc! { "
17496        one
17497        two
17498        three
17499        four
17500        five
17501        "
17502    });
17503    cx.set_index_text(indoc! { "
17504        one
17505        two
17506        three
17507        four
17508        five
17509        "
17510    });
17511    cx.set_state(indoc! {"
17512        one
17513        TWO
17514        ˇTHREE
17515        FOUR
17516        five
17517    "});
17518    cx.run_until_parked();
17519    cx.update_editor(|editor, window, cx| {
17520        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17521    });
17522    cx.run_until_parked();
17523    cx.assert_index_text(Some(indoc! {"
17524        one
17525        TWO
17526        THREE
17527        FOUR
17528        five
17529    "}));
17530    cx.set_state(indoc! { "
17531        one
17532        TWO
17533        ˇTHREE-HUNDRED
17534        FOUR
17535        five
17536    "});
17537    cx.run_until_parked();
17538    cx.update_editor(|editor, window, cx| {
17539        let snapshot = editor.snapshot(window, cx);
17540        let hunks = editor
17541            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17542            .collect::<Vec<_>>();
17543        assert_eq!(hunks.len(), 1);
17544        assert_eq!(
17545            hunks[0].status(),
17546            DiffHunkStatus {
17547                kind: DiffHunkStatusKind::Modified,
17548                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17549            }
17550        );
17551
17552        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17553    });
17554    cx.run_until_parked();
17555    cx.assert_index_text(Some(indoc! {"
17556        one
17557        TWO
17558        THREE-HUNDRED
17559        FOUR
17560        five
17561    "}));
17562}
17563
17564#[gpui::test]
17565fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17566    init_test(cx, |_| {});
17567
17568    let editor = cx.add_window(|window, cx| {
17569        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17570        build_editor(buffer, window, cx)
17571    });
17572
17573    let render_args = Arc::new(Mutex::new(None));
17574    let snapshot = editor
17575        .update(cx, |editor, window, cx| {
17576            let snapshot = editor.buffer().read(cx).snapshot(cx);
17577            let range =
17578                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17579
17580            struct RenderArgs {
17581                row: MultiBufferRow,
17582                folded: bool,
17583                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17584            }
17585
17586            let crease = Crease::inline(
17587                range,
17588                FoldPlaceholder::test(),
17589                {
17590                    let toggle_callback = render_args.clone();
17591                    move |row, folded, callback, _window, _cx| {
17592                        *toggle_callback.lock() = Some(RenderArgs {
17593                            row,
17594                            folded,
17595                            callback,
17596                        });
17597                        div()
17598                    }
17599                },
17600                |_row, _folded, _window, _cx| div(),
17601            );
17602
17603            editor.insert_creases(Some(crease), cx);
17604            let snapshot = editor.snapshot(window, cx);
17605            let _div = snapshot.render_crease_toggle(
17606                MultiBufferRow(1),
17607                false,
17608                cx.entity().clone(),
17609                window,
17610                cx,
17611            );
17612            snapshot
17613        })
17614        .unwrap();
17615
17616    let render_args = render_args.lock().take().unwrap();
17617    assert_eq!(render_args.row, MultiBufferRow(1));
17618    assert!(!render_args.folded);
17619    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17620
17621    cx.update_window(*editor, |_, window, cx| {
17622        (render_args.callback)(true, window, cx)
17623    })
17624    .unwrap();
17625    let snapshot = editor
17626        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17627        .unwrap();
17628    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17629
17630    cx.update_window(*editor, |_, window, cx| {
17631        (render_args.callback)(false, window, cx)
17632    })
17633    .unwrap();
17634    let snapshot = editor
17635        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17636        .unwrap();
17637    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17638}
17639
17640#[gpui::test]
17641async fn test_input_text(cx: &mut TestAppContext) {
17642    init_test(cx, |_| {});
17643    let mut cx = EditorTestContext::new(cx).await;
17644
17645    cx.set_state(
17646        &r#"ˇone
17647        two
17648
17649        three
17650        fourˇ
17651        five
17652
17653        siˇx"#
17654            .unindent(),
17655    );
17656
17657    cx.dispatch_action(HandleInput(String::new()));
17658    cx.assert_editor_state(
17659        &r#"ˇone
17660        two
17661
17662        three
17663        fourˇ
17664        five
17665
17666        siˇx"#
17667            .unindent(),
17668    );
17669
17670    cx.dispatch_action(HandleInput("AAAA".to_string()));
17671    cx.assert_editor_state(
17672        &r#"AAAAˇone
17673        two
17674
17675        three
17676        fourAAAAˇ
17677        five
17678
17679        siAAAAˇx"#
17680            .unindent(),
17681    );
17682}
17683
17684#[gpui::test]
17685async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17686    init_test(cx, |_| {});
17687
17688    let mut cx = EditorTestContext::new(cx).await;
17689    cx.set_state(
17690        r#"let foo = 1;
17691let foo = 2;
17692let foo = 3;
17693let fooˇ = 4;
17694let foo = 5;
17695let foo = 6;
17696let foo = 7;
17697let foo = 8;
17698let foo = 9;
17699let foo = 10;
17700let foo = 11;
17701let foo = 12;
17702let foo = 13;
17703let foo = 14;
17704let foo = 15;"#,
17705    );
17706
17707    cx.update_editor(|e, window, cx| {
17708        assert_eq!(
17709            e.next_scroll_position,
17710            NextScrollCursorCenterTopBottom::Center,
17711            "Default next scroll direction is center",
17712        );
17713
17714        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17715        assert_eq!(
17716            e.next_scroll_position,
17717            NextScrollCursorCenterTopBottom::Top,
17718            "After center, next scroll direction should be top",
17719        );
17720
17721        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17722        assert_eq!(
17723            e.next_scroll_position,
17724            NextScrollCursorCenterTopBottom::Bottom,
17725            "After top, next scroll direction should be bottom",
17726        );
17727
17728        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17729        assert_eq!(
17730            e.next_scroll_position,
17731            NextScrollCursorCenterTopBottom::Center,
17732            "After bottom, scrolling should start over",
17733        );
17734
17735        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17736        assert_eq!(
17737            e.next_scroll_position,
17738            NextScrollCursorCenterTopBottom::Top,
17739            "Scrolling continues if retriggered fast enough"
17740        );
17741    });
17742
17743    cx.executor()
17744        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17745    cx.executor().run_until_parked();
17746    cx.update_editor(|e, _, _| {
17747        assert_eq!(
17748            e.next_scroll_position,
17749            NextScrollCursorCenterTopBottom::Center,
17750            "If scrolling is not triggered fast enough, it should reset"
17751        );
17752    });
17753}
17754
17755#[gpui::test]
17756async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17757    init_test(cx, |_| {});
17758    let mut cx = EditorLspTestContext::new_rust(
17759        lsp::ServerCapabilities {
17760            definition_provider: Some(lsp::OneOf::Left(true)),
17761            references_provider: Some(lsp::OneOf::Left(true)),
17762            ..lsp::ServerCapabilities::default()
17763        },
17764        cx,
17765    )
17766    .await;
17767
17768    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17769        let go_to_definition = cx
17770            .lsp
17771            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17772                move |params, _| async move {
17773                    if empty_go_to_definition {
17774                        Ok(None)
17775                    } else {
17776                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17777                            uri: params.text_document_position_params.text_document.uri,
17778                            range: lsp::Range::new(
17779                                lsp::Position::new(4, 3),
17780                                lsp::Position::new(4, 6),
17781                            ),
17782                        })))
17783                    }
17784                },
17785            );
17786        let references = cx
17787            .lsp
17788            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17789                Ok(Some(vec![lsp::Location {
17790                    uri: params.text_document_position.text_document.uri,
17791                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17792                }]))
17793            });
17794        (go_to_definition, references)
17795    };
17796
17797    cx.set_state(
17798        &r#"fn one() {
17799            let mut a = ˇtwo();
17800        }
17801
17802        fn two() {}"#
17803            .unindent(),
17804    );
17805    set_up_lsp_handlers(false, &mut cx);
17806    let navigated = cx
17807        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17808        .await
17809        .expect("Failed to navigate to definition");
17810    assert_eq!(
17811        navigated,
17812        Navigated::Yes,
17813        "Should have navigated to definition from the GetDefinition response"
17814    );
17815    cx.assert_editor_state(
17816        &r#"fn one() {
17817            let mut a = two();
17818        }
17819
17820        fn «twoˇ»() {}"#
17821            .unindent(),
17822    );
17823
17824    let editors = cx.update_workspace(|workspace, _, cx| {
17825        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17826    });
17827    cx.update_editor(|_, _, test_editor_cx| {
17828        assert_eq!(
17829            editors.len(),
17830            1,
17831            "Initially, only one, test, editor should be open in the workspace"
17832        );
17833        assert_eq!(
17834            test_editor_cx.entity(),
17835            editors.last().expect("Asserted len is 1").clone()
17836        );
17837    });
17838
17839    set_up_lsp_handlers(true, &mut cx);
17840    let navigated = cx
17841        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17842        .await
17843        .expect("Failed to navigate to lookup references");
17844    assert_eq!(
17845        navigated,
17846        Navigated::Yes,
17847        "Should have navigated to references as a fallback after empty GoToDefinition response"
17848    );
17849    // We should not change the selections in the existing file,
17850    // if opening another milti buffer with the references
17851    cx.assert_editor_state(
17852        &r#"fn one() {
17853            let mut a = two();
17854        }
17855
17856        fn «twoˇ»() {}"#
17857            .unindent(),
17858    );
17859    let editors = cx.update_workspace(|workspace, _, cx| {
17860        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17861    });
17862    cx.update_editor(|_, _, test_editor_cx| {
17863        assert_eq!(
17864            editors.len(),
17865            2,
17866            "After falling back to references search, we open a new editor with the results"
17867        );
17868        let references_fallback_text = editors
17869            .into_iter()
17870            .find(|new_editor| *new_editor != test_editor_cx.entity())
17871            .expect("Should have one non-test editor now")
17872            .read(test_editor_cx)
17873            .text(test_editor_cx);
17874        assert_eq!(
17875            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17876            "Should use the range from the references response and not the GoToDefinition one"
17877        );
17878    });
17879}
17880
17881#[gpui::test]
17882async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17883    init_test(cx, |_| {});
17884    cx.update(|cx| {
17885        let mut editor_settings = EditorSettings::get_global(cx).clone();
17886        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17887        EditorSettings::override_global(editor_settings, cx);
17888    });
17889    let mut cx = EditorLspTestContext::new_rust(
17890        lsp::ServerCapabilities {
17891            definition_provider: Some(lsp::OneOf::Left(true)),
17892            references_provider: Some(lsp::OneOf::Left(true)),
17893            ..lsp::ServerCapabilities::default()
17894        },
17895        cx,
17896    )
17897    .await;
17898    let original_state = r#"fn one() {
17899        let mut a = ˇtwo();
17900    }
17901
17902    fn two() {}"#
17903        .unindent();
17904    cx.set_state(&original_state);
17905
17906    let mut go_to_definition = cx
17907        .lsp
17908        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17909            move |_, _| async move { Ok(None) },
17910        );
17911    let _references = cx
17912        .lsp
17913        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17914            panic!("Should not call for references with no go to definition fallback")
17915        });
17916
17917    let navigated = cx
17918        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17919        .await
17920        .expect("Failed to navigate to lookup references");
17921    go_to_definition
17922        .next()
17923        .await
17924        .expect("Should have called the go_to_definition handler");
17925
17926    assert_eq!(
17927        navigated,
17928        Navigated::No,
17929        "Should have navigated to references as a fallback after empty GoToDefinition response"
17930    );
17931    cx.assert_editor_state(&original_state);
17932    let editors = cx.update_workspace(|workspace, _, cx| {
17933        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17934    });
17935    cx.update_editor(|_, _, _| {
17936        assert_eq!(
17937            editors.len(),
17938            1,
17939            "After unsuccessful fallback, no other editor should have been opened"
17940        );
17941    });
17942}
17943
17944#[gpui::test]
17945async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17946    init_test(cx, |_| {});
17947
17948    let language = Arc::new(Language::new(
17949        LanguageConfig::default(),
17950        Some(tree_sitter_rust::LANGUAGE.into()),
17951    ));
17952
17953    let text = r#"
17954        #[cfg(test)]
17955        mod tests() {
17956            #[test]
17957            fn runnable_1() {
17958                let a = 1;
17959            }
17960
17961            #[test]
17962            fn runnable_2() {
17963                let a = 1;
17964                let b = 2;
17965            }
17966        }
17967    "#
17968    .unindent();
17969
17970    let fs = FakeFs::new(cx.executor());
17971    fs.insert_file("/file.rs", Default::default()).await;
17972
17973    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17974    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17975    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17976    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17977    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17978
17979    let editor = cx.new_window_entity(|window, cx| {
17980        Editor::new(
17981            EditorMode::full(),
17982            multi_buffer,
17983            Some(project.clone()),
17984            window,
17985            cx,
17986        )
17987    });
17988
17989    editor.update_in(cx, |editor, window, cx| {
17990        let snapshot = editor.buffer().read(cx).snapshot(cx);
17991        editor.tasks.insert(
17992            (buffer.read(cx).remote_id(), 3),
17993            RunnableTasks {
17994                templates: vec![],
17995                offset: snapshot.anchor_before(43),
17996                column: 0,
17997                extra_variables: HashMap::default(),
17998                context_range: BufferOffset(43)..BufferOffset(85),
17999            },
18000        );
18001        editor.tasks.insert(
18002            (buffer.read(cx).remote_id(), 8),
18003            RunnableTasks {
18004                templates: vec![],
18005                offset: snapshot.anchor_before(86),
18006                column: 0,
18007                extra_variables: HashMap::default(),
18008                context_range: BufferOffset(86)..BufferOffset(191),
18009            },
18010        );
18011
18012        // Test finding task when cursor is inside function body
18013        editor.change_selections(None, window, cx, |s| {
18014            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
18015        });
18016        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18017        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
18018
18019        // Test finding task when cursor is on function name
18020        editor.change_selections(None, window, cx, |s| {
18021            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
18022        });
18023        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
18024        assert_eq!(row, 8, "Should find task when cursor is on function name");
18025    });
18026}
18027
18028#[gpui::test]
18029async fn test_folding_buffers(cx: &mut TestAppContext) {
18030    init_test(cx, |_| {});
18031
18032    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18033    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
18034    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
18035
18036    let fs = FakeFs::new(cx.executor());
18037    fs.insert_tree(
18038        path!("/a"),
18039        json!({
18040            "first.rs": sample_text_1,
18041            "second.rs": sample_text_2,
18042            "third.rs": sample_text_3,
18043        }),
18044    )
18045    .await;
18046    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18047    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18048    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18049    let worktree = project.update(cx, |project, cx| {
18050        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18051        assert_eq!(worktrees.len(), 1);
18052        worktrees.pop().unwrap()
18053    });
18054    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18055
18056    let buffer_1 = project
18057        .update(cx, |project, cx| {
18058            project.open_buffer((worktree_id, "first.rs"), cx)
18059        })
18060        .await
18061        .unwrap();
18062    let buffer_2 = project
18063        .update(cx, |project, cx| {
18064            project.open_buffer((worktree_id, "second.rs"), cx)
18065        })
18066        .await
18067        .unwrap();
18068    let buffer_3 = project
18069        .update(cx, |project, cx| {
18070            project.open_buffer((worktree_id, "third.rs"), cx)
18071        })
18072        .await
18073        .unwrap();
18074
18075    let multi_buffer = cx.new(|cx| {
18076        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18077        multi_buffer.push_excerpts(
18078            buffer_1.clone(),
18079            [
18080                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18081                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18082                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18083            ],
18084            cx,
18085        );
18086        multi_buffer.push_excerpts(
18087            buffer_2.clone(),
18088            [
18089                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18090                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18091                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18092            ],
18093            cx,
18094        );
18095        multi_buffer.push_excerpts(
18096            buffer_3.clone(),
18097            [
18098                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18099                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18100                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18101            ],
18102            cx,
18103        );
18104        multi_buffer
18105    });
18106    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18107        Editor::new(
18108            EditorMode::full(),
18109            multi_buffer.clone(),
18110            Some(project.clone()),
18111            window,
18112            cx,
18113        )
18114    });
18115
18116    assert_eq!(
18117        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18118        "\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",
18119    );
18120
18121    multi_buffer_editor.update(cx, |editor, cx| {
18122        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18123    });
18124    assert_eq!(
18125        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18126        "\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",
18127        "After folding the first buffer, its text should not be displayed"
18128    );
18129
18130    multi_buffer_editor.update(cx, |editor, cx| {
18131        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18132    });
18133    assert_eq!(
18134        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18135        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18136        "After folding the second buffer, its text should not be displayed"
18137    );
18138
18139    multi_buffer_editor.update(cx, |editor, cx| {
18140        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18141    });
18142    assert_eq!(
18143        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18144        "\n\n\n\n\n",
18145        "After folding the third buffer, its text should not be displayed"
18146    );
18147
18148    // Emulate selection inside the fold logic, that should work
18149    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18150        editor
18151            .snapshot(window, cx)
18152            .next_line_boundary(Point::new(0, 4));
18153    });
18154
18155    multi_buffer_editor.update(cx, |editor, cx| {
18156        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18157    });
18158    assert_eq!(
18159        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18160        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18161        "After unfolding the second buffer, its text should be displayed"
18162    );
18163
18164    // Typing inside of buffer 1 causes that buffer to be unfolded.
18165    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18166        assert_eq!(
18167            multi_buffer
18168                .read(cx)
18169                .snapshot(cx)
18170                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18171                .collect::<String>(),
18172            "bbbb"
18173        );
18174        editor.change_selections(None, window, cx, |selections| {
18175            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18176        });
18177        editor.handle_input("B", window, cx);
18178    });
18179
18180    assert_eq!(
18181        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18182        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18183        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18184    );
18185
18186    multi_buffer_editor.update(cx, |editor, cx| {
18187        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18188    });
18189    assert_eq!(
18190        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18191        "\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",
18192        "After unfolding the all buffers, all original text should be displayed"
18193    );
18194}
18195
18196#[gpui::test]
18197async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18198    init_test(cx, |_| {});
18199
18200    let sample_text_1 = "1111\n2222\n3333".to_string();
18201    let sample_text_2 = "4444\n5555\n6666".to_string();
18202    let sample_text_3 = "7777\n8888\n9999".to_string();
18203
18204    let fs = FakeFs::new(cx.executor());
18205    fs.insert_tree(
18206        path!("/a"),
18207        json!({
18208            "first.rs": sample_text_1,
18209            "second.rs": sample_text_2,
18210            "third.rs": sample_text_3,
18211        }),
18212    )
18213    .await;
18214    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18215    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18216    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18217    let worktree = project.update(cx, |project, cx| {
18218        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18219        assert_eq!(worktrees.len(), 1);
18220        worktrees.pop().unwrap()
18221    });
18222    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18223
18224    let buffer_1 = project
18225        .update(cx, |project, cx| {
18226            project.open_buffer((worktree_id, "first.rs"), cx)
18227        })
18228        .await
18229        .unwrap();
18230    let buffer_2 = project
18231        .update(cx, |project, cx| {
18232            project.open_buffer((worktree_id, "second.rs"), cx)
18233        })
18234        .await
18235        .unwrap();
18236    let buffer_3 = project
18237        .update(cx, |project, cx| {
18238            project.open_buffer((worktree_id, "third.rs"), cx)
18239        })
18240        .await
18241        .unwrap();
18242
18243    let multi_buffer = cx.new(|cx| {
18244        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18245        multi_buffer.push_excerpts(
18246            buffer_1.clone(),
18247            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18248            cx,
18249        );
18250        multi_buffer.push_excerpts(
18251            buffer_2.clone(),
18252            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18253            cx,
18254        );
18255        multi_buffer.push_excerpts(
18256            buffer_3.clone(),
18257            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18258            cx,
18259        );
18260        multi_buffer
18261    });
18262
18263    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18264        Editor::new(
18265            EditorMode::full(),
18266            multi_buffer,
18267            Some(project.clone()),
18268            window,
18269            cx,
18270        )
18271    });
18272
18273    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18274    assert_eq!(
18275        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18276        full_text,
18277    );
18278
18279    multi_buffer_editor.update(cx, |editor, cx| {
18280        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18281    });
18282    assert_eq!(
18283        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18284        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18285        "After folding the first buffer, its text should not be displayed"
18286    );
18287
18288    multi_buffer_editor.update(cx, |editor, cx| {
18289        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18290    });
18291
18292    assert_eq!(
18293        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18294        "\n\n\n\n\n\n7777\n8888\n9999",
18295        "After folding the second buffer, its text should not be displayed"
18296    );
18297
18298    multi_buffer_editor.update(cx, |editor, cx| {
18299        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18300    });
18301    assert_eq!(
18302        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18303        "\n\n\n\n\n",
18304        "After folding the third buffer, its text should not be displayed"
18305    );
18306
18307    multi_buffer_editor.update(cx, |editor, cx| {
18308        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18309    });
18310    assert_eq!(
18311        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18312        "\n\n\n\n4444\n5555\n6666\n\n",
18313        "After unfolding the second buffer, its text should be displayed"
18314    );
18315
18316    multi_buffer_editor.update(cx, |editor, cx| {
18317        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18318    });
18319    assert_eq!(
18320        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18321        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18322        "After unfolding the first buffer, its text should be displayed"
18323    );
18324
18325    multi_buffer_editor.update(cx, |editor, cx| {
18326        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18327    });
18328    assert_eq!(
18329        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18330        full_text,
18331        "After unfolding all buffers, all original text should be displayed"
18332    );
18333}
18334
18335#[gpui::test]
18336async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18337    init_test(cx, |_| {});
18338
18339    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18340
18341    let fs = FakeFs::new(cx.executor());
18342    fs.insert_tree(
18343        path!("/a"),
18344        json!({
18345            "main.rs": sample_text,
18346        }),
18347    )
18348    .await;
18349    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18350    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18351    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18352    let worktree = project.update(cx, |project, cx| {
18353        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18354        assert_eq!(worktrees.len(), 1);
18355        worktrees.pop().unwrap()
18356    });
18357    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18358
18359    let buffer_1 = project
18360        .update(cx, |project, cx| {
18361            project.open_buffer((worktree_id, "main.rs"), cx)
18362        })
18363        .await
18364        .unwrap();
18365
18366    let multi_buffer = cx.new(|cx| {
18367        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18368        multi_buffer.push_excerpts(
18369            buffer_1.clone(),
18370            [ExcerptRange::new(
18371                Point::new(0, 0)
18372                    ..Point::new(
18373                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18374                        0,
18375                    ),
18376            )],
18377            cx,
18378        );
18379        multi_buffer
18380    });
18381    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18382        Editor::new(
18383            EditorMode::full(),
18384            multi_buffer,
18385            Some(project.clone()),
18386            window,
18387            cx,
18388        )
18389    });
18390
18391    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18392    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18393        enum TestHighlight {}
18394        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18395        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18396        editor.highlight_text::<TestHighlight>(
18397            vec![highlight_range.clone()],
18398            HighlightStyle::color(Hsla::green()),
18399            cx,
18400        );
18401        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18402    });
18403
18404    let full_text = format!("\n\n{sample_text}");
18405    assert_eq!(
18406        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18407        full_text,
18408    );
18409}
18410
18411#[gpui::test]
18412async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18413    init_test(cx, |_| {});
18414    cx.update(|cx| {
18415        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18416            "keymaps/default-linux.json",
18417            cx,
18418        )
18419        .unwrap();
18420        cx.bind_keys(default_key_bindings);
18421    });
18422
18423    let (editor, cx) = cx.add_window_view(|window, cx| {
18424        let multi_buffer = MultiBuffer::build_multi(
18425            [
18426                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18427                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18428                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18429                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18430            ],
18431            cx,
18432        );
18433        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18434
18435        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18436        // fold all but the second buffer, so that we test navigating between two
18437        // adjacent folded buffers, as well as folded buffers at the start and
18438        // end the multibuffer
18439        editor.fold_buffer(buffer_ids[0], cx);
18440        editor.fold_buffer(buffer_ids[2], cx);
18441        editor.fold_buffer(buffer_ids[3], cx);
18442
18443        editor
18444    });
18445    cx.simulate_resize(size(px(1000.), px(1000.)));
18446
18447    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18448    cx.assert_excerpts_with_selections(indoc! {"
18449        [EXCERPT]
18450        ˇ[FOLDED]
18451        [EXCERPT]
18452        a1
18453        b1
18454        [EXCERPT]
18455        [FOLDED]
18456        [EXCERPT]
18457        [FOLDED]
18458        "
18459    });
18460    cx.simulate_keystroke("down");
18461    cx.assert_excerpts_with_selections(indoc! {"
18462        [EXCERPT]
18463        [FOLDED]
18464        [EXCERPT]
18465        ˇa1
18466        b1
18467        [EXCERPT]
18468        [FOLDED]
18469        [EXCERPT]
18470        [FOLDED]
18471        "
18472    });
18473    cx.simulate_keystroke("down");
18474    cx.assert_excerpts_with_selections(indoc! {"
18475        [EXCERPT]
18476        [FOLDED]
18477        [EXCERPT]
18478        a1
18479        ˇb1
18480        [EXCERPT]
18481        [FOLDED]
18482        [EXCERPT]
18483        [FOLDED]
18484        "
18485    });
18486    cx.simulate_keystroke("down");
18487    cx.assert_excerpts_with_selections(indoc! {"
18488        [EXCERPT]
18489        [FOLDED]
18490        [EXCERPT]
18491        a1
18492        b1
18493        ˇ[EXCERPT]
18494        [FOLDED]
18495        [EXCERPT]
18496        [FOLDED]
18497        "
18498    });
18499    cx.simulate_keystroke("down");
18500    cx.assert_excerpts_with_selections(indoc! {"
18501        [EXCERPT]
18502        [FOLDED]
18503        [EXCERPT]
18504        a1
18505        b1
18506        [EXCERPT]
18507        ˇ[FOLDED]
18508        [EXCERPT]
18509        [FOLDED]
18510        "
18511    });
18512    for _ in 0..5 {
18513        cx.simulate_keystroke("down");
18514        cx.assert_excerpts_with_selections(indoc! {"
18515            [EXCERPT]
18516            [FOLDED]
18517            [EXCERPT]
18518            a1
18519            b1
18520            [EXCERPT]
18521            [FOLDED]
18522            [EXCERPT]
18523            ˇ[FOLDED]
18524            "
18525        });
18526    }
18527
18528    cx.simulate_keystroke("up");
18529    cx.assert_excerpts_with_selections(indoc! {"
18530        [EXCERPT]
18531        [FOLDED]
18532        [EXCERPT]
18533        a1
18534        b1
18535        [EXCERPT]
18536        ˇ[FOLDED]
18537        [EXCERPT]
18538        [FOLDED]
18539        "
18540    });
18541    cx.simulate_keystroke("up");
18542    cx.assert_excerpts_with_selections(indoc! {"
18543        [EXCERPT]
18544        [FOLDED]
18545        [EXCERPT]
18546        a1
18547        b1
18548        ˇ[EXCERPT]
18549        [FOLDED]
18550        [EXCERPT]
18551        [FOLDED]
18552        "
18553    });
18554    cx.simulate_keystroke("up");
18555    cx.assert_excerpts_with_selections(indoc! {"
18556        [EXCERPT]
18557        [FOLDED]
18558        [EXCERPT]
18559        a1
18560        ˇb1
18561        [EXCERPT]
18562        [FOLDED]
18563        [EXCERPT]
18564        [FOLDED]
18565        "
18566    });
18567    cx.simulate_keystroke("up");
18568    cx.assert_excerpts_with_selections(indoc! {"
18569        [EXCERPT]
18570        [FOLDED]
18571        [EXCERPT]
18572        ˇa1
18573        b1
18574        [EXCERPT]
18575        [FOLDED]
18576        [EXCERPT]
18577        [FOLDED]
18578        "
18579    });
18580    for _ in 0..5 {
18581        cx.simulate_keystroke("up");
18582        cx.assert_excerpts_with_selections(indoc! {"
18583            [EXCERPT]
18584            ˇ[FOLDED]
18585            [EXCERPT]
18586            a1
18587            b1
18588            [EXCERPT]
18589            [FOLDED]
18590            [EXCERPT]
18591            [FOLDED]
18592            "
18593        });
18594    }
18595}
18596
18597#[gpui::test]
18598async fn test_inline_completion_text(cx: &mut TestAppContext) {
18599    init_test(cx, |_| {});
18600
18601    // Simple insertion
18602    assert_highlighted_edits(
18603        "Hello, world!",
18604        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18605        true,
18606        cx,
18607        |highlighted_edits, cx| {
18608            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18609            assert_eq!(highlighted_edits.highlights.len(), 1);
18610            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18611            assert_eq!(
18612                highlighted_edits.highlights[0].1.background_color,
18613                Some(cx.theme().status().created_background)
18614            );
18615        },
18616    )
18617    .await;
18618
18619    // Replacement
18620    assert_highlighted_edits(
18621        "This is a test.",
18622        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18623        false,
18624        cx,
18625        |highlighted_edits, cx| {
18626            assert_eq!(highlighted_edits.text, "That is a test.");
18627            assert_eq!(highlighted_edits.highlights.len(), 1);
18628            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18629            assert_eq!(
18630                highlighted_edits.highlights[0].1.background_color,
18631                Some(cx.theme().status().created_background)
18632            );
18633        },
18634    )
18635    .await;
18636
18637    // Multiple edits
18638    assert_highlighted_edits(
18639        "Hello, world!",
18640        vec![
18641            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18642            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18643        ],
18644        false,
18645        cx,
18646        |highlighted_edits, cx| {
18647            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18648            assert_eq!(highlighted_edits.highlights.len(), 2);
18649            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18650            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18651            assert_eq!(
18652                highlighted_edits.highlights[0].1.background_color,
18653                Some(cx.theme().status().created_background)
18654            );
18655            assert_eq!(
18656                highlighted_edits.highlights[1].1.background_color,
18657                Some(cx.theme().status().created_background)
18658            );
18659        },
18660    )
18661    .await;
18662
18663    // Multiple lines with edits
18664    assert_highlighted_edits(
18665        "First line\nSecond line\nThird line\nFourth line",
18666        vec![
18667            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18668            (
18669                Point::new(2, 0)..Point::new(2, 10),
18670                "New third line".to_string(),
18671            ),
18672            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18673        ],
18674        false,
18675        cx,
18676        |highlighted_edits, cx| {
18677            assert_eq!(
18678                highlighted_edits.text,
18679                "Second modified\nNew third line\nFourth updated line"
18680            );
18681            assert_eq!(highlighted_edits.highlights.len(), 3);
18682            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18683            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18684            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18685            for highlight in &highlighted_edits.highlights {
18686                assert_eq!(
18687                    highlight.1.background_color,
18688                    Some(cx.theme().status().created_background)
18689                );
18690            }
18691        },
18692    )
18693    .await;
18694}
18695
18696#[gpui::test]
18697async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18698    init_test(cx, |_| {});
18699
18700    // Deletion
18701    assert_highlighted_edits(
18702        "Hello, world!",
18703        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18704        true,
18705        cx,
18706        |highlighted_edits, cx| {
18707            assert_eq!(highlighted_edits.text, "Hello, world!");
18708            assert_eq!(highlighted_edits.highlights.len(), 1);
18709            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18710            assert_eq!(
18711                highlighted_edits.highlights[0].1.background_color,
18712                Some(cx.theme().status().deleted_background)
18713            );
18714        },
18715    )
18716    .await;
18717
18718    // Insertion
18719    assert_highlighted_edits(
18720        "Hello, world!",
18721        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18722        true,
18723        cx,
18724        |highlighted_edits, cx| {
18725            assert_eq!(highlighted_edits.highlights.len(), 1);
18726            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18727            assert_eq!(
18728                highlighted_edits.highlights[0].1.background_color,
18729                Some(cx.theme().status().created_background)
18730            );
18731        },
18732    )
18733    .await;
18734}
18735
18736async fn assert_highlighted_edits(
18737    text: &str,
18738    edits: Vec<(Range<Point>, String)>,
18739    include_deletions: bool,
18740    cx: &mut TestAppContext,
18741    assertion_fn: impl Fn(HighlightedText, &App),
18742) {
18743    let window = cx.add_window(|window, cx| {
18744        let buffer = MultiBuffer::build_simple(text, cx);
18745        Editor::new(EditorMode::full(), buffer, None, window, cx)
18746    });
18747    let cx = &mut VisualTestContext::from_window(*window, cx);
18748
18749    let (buffer, snapshot) = window
18750        .update(cx, |editor, _window, cx| {
18751            (
18752                editor.buffer().clone(),
18753                editor.buffer().read(cx).snapshot(cx),
18754            )
18755        })
18756        .unwrap();
18757
18758    let edits = edits
18759        .into_iter()
18760        .map(|(range, edit)| {
18761            (
18762                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18763                edit,
18764            )
18765        })
18766        .collect::<Vec<_>>();
18767
18768    let text_anchor_edits = edits
18769        .clone()
18770        .into_iter()
18771        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18772        .collect::<Vec<_>>();
18773
18774    let edit_preview = window
18775        .update(cx, |_, _window, cx| {
18776            buffer
18777                .read(cx)
18778                .as_singleton()
18779                .unwrap()
18780                .read(cx)
18781                .preview_edits(text_anchor_edits.into(), cx)
18782        })
18783        .unwrap()
18784        .await;
18785
18786    cx.update(|_window, cx| {
18787        let highlighted_edits = inline_completion_edit_text(
18788            &snapshot.as_singleton().unwrap().2,
18789            &edits,
18790            &edit_preview,
18791            include_deletions,
18792            cx,
18793        );
18794        assertion_fn(highlighted_edits, cx)
18795    });
18796}
18797
18798#[track_caller]
18799fn assert_breakpoint(
18800    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18801    path: &Arc<Path>,
18802    expected: Vec<(u32, Breakpoint)>,
18803) {
18804    if expected.len() == 0usize {
18805        assert!(!breakpoints.contains_key(path), "{}", path.display());
18806    } else {
18807        let mut breakpoint = breakpoints
18808            .get(path)
18809            .unwrap()
18810            .into_iter()
18811            .map(|breakpoint| {
18812                (
18813                    breakpoint.row,
18814                    Breakpoint {
18815                        message: breakpoint.message.clone(),
18816                        state: breakpoint.state,
18817                        condition: breakpoint.condition.clone(),
18818                        hit_condition: breakpoint.hit_condition.clone(),
18819                    },
18820                )
18821            })
18822            .collect::<Vec<_>>();
18823
18824        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18825
18826        assert_eq!(expected, breakpoint);
18827    }
18828}
18829
18830fn add_log_breakpoint_at_cursor(
18831    editor: &mut Editor,
18832    log_message: &str,
18833    window: &mut Window,
18834    cx: &mut Context<Editor>,
18835) {
18836    let (anchor, bp) = editor
18837        .breakpoints_at_cursors(window, cx)
18838        .first()
18839        .and_then(|(anchor, bp)| {
18840            if let Some(bp) = bp {
18841                Some((*anchor, bp.clone()))
18842            } else {
18843                None
18844            }
18845        })
18846        .unwrap_or_else(|| {
18847            let cursor_position: Point = editor.selections.newest(cx).head();
18848
18849            let breakpoint_position = editor
18850                .snapshot(window, cx)
18851                .display_snapshot
18852                .buffer_snapshot
18853                .anchor_before(Point::new(cursor_position.row, 0));
18854
18855            (breakpoint_position, Breakpoint::new_log(&log_message))
18856        });
18857
18858    editor.edit_breakpoint_at_anchor(
18859        anchor,
18860        bp,
18861        BreakpointEditAction::EditLogMessage(log_message.into()),
18862        cx,
18863    );
18864}
18865
18866#[gpui::test]
18867async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18868    init_test(cx, |_| {});
18869
18870    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18871    let fs = FakeFs::new(cx.executor());
18872    fs.insert_tree(
18873        path!("/a"),
18874        json!({
18875            "main.rs": sample_text,
18876        }),
18877    )
18878    .await;
18879    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18880    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18881    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18882
18883    let fs = FakeFs::new(cx.executor());
18884    fs.insert_tree(
18885        path!("/a"),
18886        json!({
18887            "main.rs": sample_text,
18888        }),
18889    )
18890    .await;
18891    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18892    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18893    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18894    let worktree_id = workspace
18895        .update(cx, |workspace, _window, cx| {
18896            workspace.project().update(cx, |project, cx| {
18897                project.worktrees(cx).next().unwrap().read(cx).id()
18898            })
18899        })
18900        .unwrap();
18901
18902    let buffer = project
18903        .update(cx, |project, cx| {
18904            project.open_buffer((worktree_id, "main.rs"), cx)
18905        })
18906        .await
18907        .unwrap();
18908
18909    let (editor, cx) = cx.add_window_view(|window, cx| {
18910        Editor::new(
18911            EditorMode::full(),
18912            MultiBuffer::build_from_buffer(buffer, cx),
18913            Some(project.clone()),
18914            window,
18915            cx,
18916        )
18917    });
18918
18919    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18920    let abs_path = project.read_with(cx, |project, cx| {
18921        project
18922            .absolute_path(&project_path, cx)
18923            .map(|path_buf| Arc::from(path_buf.to_owned()))
18924            .unwrap()
18925    });
18926
18927    // assert we can add breakpoint on the first line
18928    editor.update_in(cx, |editor, window, cx| {
18929        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18930        editor.move_to_end(&MoveToEnd, window, cx);
18931        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18932    });
18933
18934    let breakpoints = editor.update(cx, |editor, cx| {
18935        editor
18936            .breakpoint_store()
18937            .as_ref()
18938            .unwrap()
18939            .read(cx)
18940            .all_source_breakpoints(cx)
18941            .clone()
18942    });
18943
18944    assert_eq!(1, breakpoints.len());
18945    assert_breakpoint(
18946        &breakpoints,
18947        &abs_path,
18948        vec![
18949            (0, Breakpoint::new_standard()),
18950            (3, Breakpoint::new_standard()),
18951        ],
18952    );
18953
18954    editor.update_in(cx, |editor, window, cx| {
18955        editor.move_to_beginning(&MoveToBeginning, window, cx);
18956        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18957    });
18958
18959    let breakpoints = editor.update(cx, |editor, cx| {
18960        editor
18961            .breakpoint_store()
18962            .as_ref()
18963            .unwrap()
18964            .read(cx)
18965            .all_source_breakpoints(cx)
18966            .clone()
18967    });
18968
18969    assert_eq!(1, breakpoints.len());
18970    assert_breakpoint(
18971        &breakpoints,
18972        &abs_path,
18973        vec![(3, Breakpoint::new_standard())],
18974    );
18975
18976    editor.update_in(cx, |editor, window, cx| {
18977        editor.move_to_end(&MoveToEnd, window, cx);
18978        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18979    });
18980
18981    let breakpoints = editor.update(cx, |editor, cx| {
18982        editor
18983            .breakpoint_store()
18984            .as_ref()
18985            .unwrap()
18986            .read(cx)
18987            .all_source_breakpoints(cx)
18988            .clone()
18989    });
18990
18991    assert_eq!(0, breakpoints.len());
18992    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18993}
18994
18995#[gpui::test]
18996async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18997    init_test(cx, |_| {});
18998
18999    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19000
19001    let fs = FakeFs::new(cx.executor());
19002    fs.insert_tree(
19003        path!("/a"),
19004        json!({
19005            "main.rs": sample_text,
19006        }),
19007    )
19008    .await;
19009    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19010    let (workspace, cx) =
19011        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19012
19013    let worktree_id = workspace.update(cx, |workspace, cx| {
19014        workspace.project().update(cx, |project, cx| {
19015            project.worktrees(cx).next().unwrap().read(cx).id()
19016        })
19017    });
19018
19019    let buffer = project
19020        .update(cx, |project, cx| {
19021            project.open_buffer((worktree_id, "main.rs"), cx)
19022        })
19023        .await
19024        .unwrap();
19025
19026    let (editor, cx) = cx.add_window_view(|window, cx| {
19027        Editor::new(
19028            EditorMode::full(),
19029            MultiBuffer::build_from_buffer(buffer, cx),
19030            Some(project.clone()),
19031            window,
19032            cx,
19033        )
19034    });
19035
19036    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19037    let abs_path = project.read_with(cx, |project, cx| {
19038        project
19039            .absolute_path(&project_path, cx)
19040            .map(|path_buf| Arc::from(path_buf.to_owned()))
19041            .unwrap()
19042    });
19043
19044    editor.update_in(cx, |editor, window, cx| {
19045        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19046    });
19047
19048    let breakpoints = editor.update(cx, |editor, cx| {
19049        editor
19050            .breakpoint_store()
19051            .as_ref()
19052            .unwrap()
19053            .read(cx)
19054            .all_source_breakpoints(cx)
19055            .clone()
19056    });
19057
19058    assert_breakpoint(
19059        &breakpoints,
19060        &abs_path,
19061        vec![(0, Breakpoint::new_log("hello world"))],
19062    );
19063
19064    // Removing a log message from a log breakpoint should remove it
19065    editor.update_in(cx, |editor, window, cx| {
19066        add_log_breakpoint_at_cursor(editor, "", window, cx);
19067    });
19068
19069    let breakpoints = editor.update(cx, |editor, cx| {
19070        editor
19071            .breakpoint_store()
19072            .as_ref()
19073            .unwrap()
19074            .read(cx)
19075            .all_source_breakpoints(cx)
19076            .clone()
19077    });
19078
19079    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19080
19081    editor.update_in(cx, |editor, window, cx| {
19082        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19083        editor.move_to_end(&MoveToEnd, window, cx);
19084        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19085        // Not adding a log message to a standard breakpoint shouldn't remove it
19086        add_log_breakpoint_at_cursor(editor, "", window, cx);
19087    });
19088
19089    let breakpoints = editor.update(cx, |editor, cx| {
19090        editor
19091            .breakpoint_store()
19092            .as_ref()
19093            .unwrap()
19094            .read(cx)
19095            .all_source_breakpoints(cx)
19096            .clone()
19097    });
19098
19099    assert_breakpoint(
19100        &breakpoints,
19101        &abs_path,
19102        vec![
19103            (0, Breakpoint::new_standard()),
19104            (3, Breakpoint::new_standard()),
19105        ],
19106    );
19107
19108    editor.update_in(cx, |editor, window, cx| {
19109        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19110    });
19111
19112    let breakpoints = editor.update(cx, |editor, cx| {
19113        editor
19114            .breakpoint_store()
19115            .as_ref()
19116            .unwrap()
19117            .read(cx)
19118            .all_source_breakpoints(cx)
19119            .clone()
19120    });
19121
19122    assert_breakpoint(
19123        &breakpoints,
19124        &abs_path,
19125        vec![
19126            (0, Breakpoint::new_standard()),
19127            (3, Breakpoint::new_log("hello world")),
19128        ],
19129    );
19130
19131    editor.update_in(cx, |editor, window, cx| {
19132        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19133    });
19134
19135    let breakpoints = editor.update(cx, |editor, cx| {
19136        editor
19137            .breakpoint_store()
19138            .as_ref()
19139            .unwrap()
19140            .read(cx)
19141            .all_source_breakpoints(cx)
19142            .clone()
19143    });
19144
19145    assert_breakpoint(
19146        &breakpoints,
19147        &abs_path,
19148        vec![
19149            (0, Breakpoint::new_standard()),
19150            (3, Breakpoint::new_log("hello Earth!!")),
19151        ],
19152    );
19153}
19154
19155/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19156/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19157/// or when breakpoints were placed out of order. This tests for a regression too
19158#[gpui::test]
19159async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19160    init_test(cx, |_| {});
19161
19162    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19163    let fs = FakeFs::new(cx.executor());
19164    fs.insert_tree(
19165        path!("/a"),
19166        json!({
19167            "main.rs": sample_text,
19168        }),
19169    )
19170    .await;
19171    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19172    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19173    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19174
19175    let fs = FakeFs::new(cx.executor());
19176    fs.insert_tree(
19177        path!("/a"),
19178        json!({
19179            "main.rs": sample_text,
19180        }),
19181    )
19182    .await;
19183    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19184    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19185    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19186    let worktree_id = workspace
19187        .update(cx, |workspace, _window, cx| {
19188            workspace.project().update(cx, |project, cx| {
19189                project.worktrees(cx).next().unwrap().read(cx).id()
19190            })
19191        })
19192        .unwrap();
19193
19194    let buffer = project
19195        .update(cx, |project, cx| {
19196            project.open_buffer((worktree_id, "main.rs"), cx)
19197        })
19198        .await
19199        .unwrap();
19200
19201    let (editor, cx) = cx.add_window_view(|window, cx| {
19202        Editor::new(
19203            EditorMode::full(),
19204            MultiBuffer::build_from_buffer(buffer, cx),
19205            Some(project.clone()),
19206            window,
19207            cx,
19208        )
19209    });
19210
19211    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19212    let abs_path = project.read_with(cx, |project, cx| {
19213        project
19214            .absolute_path(&project_path, cx)
19215            .map(|path_buf| Arc::from(path_buf.to_owned()))
19216            .unwrap()
19217    });
19218
19219    // assert we can add breakpoint on the first line
19220    editor.update_in(cx, |editor, window, cx| {
19221        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19222        editor.move_to_end(&MoveToEnd, window, cx);
19223        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19224        editor.move_up(&MoveUp, window, cx);
19225        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19226    });
19227
19228    let breakpoints = editor.update(cx, |editor, cx| {
19229        editor
19230            .breakpoint_store()
19231            .as_ref()
19232            .unwrap()
19233            .read(cx)
19234            .all_source_breakpoints(cx)
19235            .clone()
19236    });
19237
19238    assert_eq!(1, breakpoints.len());
19239    assert_breakpoint(
19240        &breakpoints,
19241        &abs_path,
19242        vec![
19243            (0, Breakpoint::new_standard()),
19244            (2, Breakpoint::new_standard()),
19245            (3, Breakpoint::new_standard()),
19246        ],
19247    );
19248
19249    editor.update_in(cx, |editor, window, cx| {
19250        editor.move_to_beginning(&MoveToBeginning, window, cx);
19251        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19252        editor.move_to_end(&MoveToEnd, window, cx);
19253        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19254        // Disabling a breakpoint that doesn't exist should do nothing
19255        editor.move_up(&MoveUp, window, cx);
19256        editor.move_up(&MoveUp, window, cx);
19257        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19258    });
19259
19260    let breakpoints = editor.update(cx, |editor, cx| {
19261        editor
19262            .breakpoint_store()
19263            .as_ref()
19264            .unwrap()
19265            .read(cx)
19266            .all_source_breakpoints(cx)
19267            .clone()
19268    });
19269
19270    let disable_breakpoint = {
19271        let mut bp = Breakpoint::new_standard();
19272        bp.state = BreakpointState::Disabled;
19273        bp
19274    };
19275
19276    assert_eq!(1, breakpoints.len());
19277    assert_breakpoint(
19278        &breakpoints,
19279        &abs_path,
19280        vec![
19281            (0, disable_breakpoint.clone()),
19282            (2, Breakpoint::new_standard()),
19283            (3, disable_breakpoint.clone()),
19284        ],
19285    );
19286
19287    editor.update_in(cx, |editor, window, cx| {
19288        editor.move_to_beginning(&MoveToBeginning, window, cx);
19289        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19290        editor.move_to_end(&MoveToEnd, window, cx);
19291        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19292        editor.move_up(&MoveUp, window, cx);
19293        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19294    });
19295
19296    let breakpoints = editor.update(cx, |editor, cx| {
19297        editor
19298            .breakpoint_store()
19299            .as_ref()
19300            .unwrap()
19301            .read(cx)
19302            .all_source_breakpoints(cx)
19303            .clone()
19304    });
19305
19306    assert_eq!(1, breakpoints.len());
19307    assert_breakpoint(
19308        &breakpoints,
19309        &abs_path,
19310        vec![
19311            (0, Breakpoint::new_standard()),
19312            (2, disable_breakpoint),
19313            (3, Breakpoint::new_standard()),
19314        ],
19315    );
19316}
19317
19318#[gpui::test]
19319async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19320    init_test(cx, |_| {});
19321    let capabilities = lsp::ServerCapabilities {
19322        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19323            prepare_provider: Some(true),
19324            work_done_progress_options: Default::default(),
19325        })),
19326        ..Default::default()
19327    };
19328    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19329
19330    cx.set_state(indoc! {"
19331        struct Fˇoo {}
19332    "});
19333
19334    cx.update_editor(|editor, _, cx| {
19335        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19336        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19337        editor.highlight_background::<DocumentHighlightRead>(
19338            &[highlight_range],
19339            |c| c.editor_document_highlight_read_background,
19340            cx,
19341        );
19342    });
19343
19344    let mut prepare_rename_handler = cx
19345        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19346            move |_, _, _| async move {
19347                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19348                    start: lsp::Position {
19349                        line: 0,
19350                        character: 7,
19351                    },
19352                    end: lsp::Position {
19353                        line: 0,
19354                        character: 10,
19355                    },
19356                })))
19357            },
19358        );
19359    let prepare_rename_task = cx
19360        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19361        .expect("Prepare rename was not started");
19362    prepare_rename_handler.next().await.unwrap();
19363    prepare_rename_task.await.expect("Prepare rename failed");
19364
19365    let mut rename_handler =
19366        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19367            let edit = lsp::TextEdit {
19368                range: lsp::Range {
19369                    start: lsp::Position {
19370                        line: 0,
19371                        character: 7,
19372                    },
19373                    end: lsp::Position {
19374                        line: 0,
19375                        character: 10,
19376                    },
19377                },
19378                new_text: "FooRenamed".to_string(),
19379            };
19380            Ok(Some(lsp::WorkspaceEdit::new(
19381                // Specify the same edit twice
19382                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19383            )))
19384        });
19385    let rename_task = cx
19386        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19387        .expect("Confirm rename was not started");
19388    rename_handler.next().await.unwrap();
19389    rename_task.await.expect("Confirm rename failed");
19390    cx.run_until_parked();
19391
19392    // Despite two edits, only one is actually applied as those are identical
19393    cx.assert_editor_state(indoc! {"
19394        struct FooRenamedˇ {}
19395    "});
19396}
19397
19398#[gpui::test]
19399async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19400    init_test(cx, |_| {});
19401    // These capabilities indicate that the server does not support prepare rename.
19402    let capabilities = lsp::ServerCapabilities {
19403        rename_provider: Some(lsp::OneOf::Left(true)),
19404        ..Default::default()
19405    };
19406    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19407
19408    cx.set_state(indoc! {"
19409        struct Fˇoo {}
19410    "});
19411
19412    cx.update_editor(|editor, _window, cx| {
19413        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19414        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19415        editor.highlight_background::<DocumentHighlightRead>(
19416            &[highlight_range],
19417            |c| c.editor_document_highlight_read_background,
19418            cx,
19419        );
19420    });
19421
19422    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19423        .expect("Prepare rename was not started")
19424        .await
19425        .expect("Prepare rename failed");
19426
19427    let mut rename_handler =
19428        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19429            let edit = lsp::TextEdit {
19430                range: lsp::Range {
19431                    start: lsp::Position {
19432                        line: 0,
19433                        character: 7,
19434                    },
19435                    end: lsp::Position {
19436                        line: 0,
19437                        character: 10,
19438                    },
19439                },
19440                new_text: "FooRenamed".to_string(),
19441            };
19442            Ok(Some(lsp::WorkspaceEdit::new(
19443                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19444            )))
19445        });
19446    let rename_task = cx
19447        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19448        .expect("Confirm rename was not started");
19449    rename_handler.next().await.unwrap();
19450    rename_task.await.expect("Confirm rename failed");
19451    cx.run_until_parked();
19452
19453    // Correct range is renamed, as `surrounding_word` is used to find it.
19454    cx.assert_editor_state(indoc! {"
19455        struct FooRenamedˇ {}
19456    "});
19457}
19458
19459#[gpui::test]
19460async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19461    init_test(cx, |_| {});
19462    let mut cx = EditorTestContext::new(cx).await;
19463
19464    let language = Arc::new(
19465        Language::new(
19466            LanguageConfig::default(),
19467            Some(tree_sitter_html::LANGUAGE.into()),
19468        )
19469        .with_brackets_query(
19470            r#"
19471            ("<" @open "/>" @close)
19472            ("</" @open ">" @close)
19473            ("<" @open ">" @close)
19474            ("\"" @open "\"" @close)
19475            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19476        "#,
19477        )
19478        .unwrap(),
19479    );
19480    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19481
19482    cx.set_state(indoc! {"
19483        <span>ˇ</span>
19484    "});
19485    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19486    cx.assert_editor_state(indoc! {"
19487        <span>
19488        ˇ
19489        </span>
19490    "});
19491
19492    cx.set_state(indoc! {"
19493        <span><span></span>ˇ</span>
19494    "});
19495    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19496    cx.assert_editor_state(indoc! {"
19497        <span><span></span>
19498        ˇ</span>
19499    "});
19500
19501    cx.set_state(indoc! {"
19502        <span>ˇ
19503        </span>
19504    "});
19505    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19506    cx.assert_editor_state(indoc! {"
19507        <span>
19508        ˇ
19509        </span>
19510    "});
19511}
19512
19513#[gpui::test(iterations = 10)]
19514async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19515    init_test(cx, |_| {});
19516
19517    let fs = FakeFs::new(cx.executor());
19518    fs.insert_tree(
19519        path!("/dir"),
19520        json!({
19521            "a.ts": "a",
19522        }),
19523    )
19524    .await;
19525
19526    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19527    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19528    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19529
19530    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19531    language_registry.add(Arc::new(Language::new(
19532        LanguageConfig {
19533            name: "TypeScript".into(),
19534            matcher: LanguageMatcher {
19535                path_suffixes: vec!["ts".to_string()],
19536                ..Default::default()
19537            },
19538            ..Default::default()
19539        },
19540        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19541    )));
19542    let mut fake_language_servers = language_registry.register_fake_lsp(
19543        "TypeScript",
19544        FakeLspAdapter {
19545            capabilities: lsp::ServerCapabilities {
19546                code_lens_provider: Some(lsp::CodeLensOptions {
19547                    resolve_provider: Some(true),
19548                }),
19549                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19550                    commands: vec!["_the/command".to_string()],
19551                    ..lsp::ExecuteCommandOptions::default()
19552                }),
19553                ..lsp::ServerCapabilities::default()
19554            },
19555            ..FakeLspAdapter::default()
19556        },
19557    );
19558
19559    let (buffer, _handle) = project
19560        .update(cx, |p, cx| {
19561            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19562        })
19563        .await
19564        .unwrap();
19565    cx.executor().run_until_parked();
19566
19567    let fake_server = fake_language_servers.next().await.unwrap();
19568
19569    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19570    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19571    drop(buffer_snapshot);
19572    let actions = cx
19573        .update_window(*workspace, |_, window, cx| {
19574            project.code_actions(&buffer, anchor..anchor, window, cx)
19575        })
19576        .unwrap();
19577
19578    fake_server
19579        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19580            Ok(Some(vec![
19581                lsp::CodeLens {
19582                    range: lsp::Range::default(),
19583                    command: Some(lsp::Command {
19584                        title: "Code lens command".to_owned(),
19585                        command: "_the/command".to_owned(),
19586                        arguments: None,
19587                    }),
19588                    data: None,
19589                },
19590                lsp::CodeLens {
19591                    range: lsp::Range::default(),
19592                    command: Some(lsp::Command {
19593                        title: "Command not in capabilities".to_owned(),
19594                        command: "not in capabilities".to_owned(),
19595                        arguments: None,
19596                    }),
19597                    data: None,
19598                },
19599                lsp::CodeLens {
19600                    range: lsp::Range {
19601                        start: lsp::Position {
19602                            line: 1,
19603                            character: 1,
19604                        },
19605                        end: lsp::Position {
19606                            line: 1,
19607                            character: 1,
19608                        },
19609                    },
19610                    command: Some(lsp::Command {
19611                        title: "Command not in range".to_owned(),
19612                        command: "_the/command".to_owned(),
19613                        arguments: None,
19614                    }),
19615                    data: None,
19616                },
19617            ]))
19618        })
19619        .next()
19620        .await;
19621
19622    let actions = actions.await.unwrap();
19623    assert_eq!(
19624        actions.len(),
19625        1,
19626        "Should have only one valid action for the 0..0 range"
19627    );
19628    let action = actions[0].clone();
19629    let apply = project.update(cx, |project, cx| {
19630        project.apply_code_action(buffer.clone(), action, true, cx)
19631    });
19632
19633    // Resolving the code action does not populate its edits. In absence of
19634    // edits, we must execute the given command.
19635    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19636        |mut lens, _| async move {
19637            let lens_command = lens.command.as_mut().expect("should have a command");
19638            assert_eq!(lens_command.title, "Code lens command");
19639            lens_command.arguments = Some(vec![json!("the-argument")]);
19640            Ok(lens)
19641        },
19642    );
19643
19644    // While executing the command, the language server sends the editor
19645    // a `workspaceEdit` request.
19646    fake_server
19647        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19648            let fake = fake_server.clone();
19649            move |params, _| {
19650                assert_eq!(params.command, "_the/command");
19651                let fake = fake.clone();
19652                async move {
19653                    fake.server
19654                        .request::<lsp::request::ApplyWorkspaceEdit>(
19655                            lsp::ApplyWorkspaceEditParams {
19656                                label: None,
19657                                edit: lsp::WorkspaceEdit {
19658                                    changes: Some(
19659                                        [(
19660                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19661                                            vec![lsp::TextEdit {
19662                                                range: lsp::Range::new(
19663                                                    lsp::Position::new(0, 0),
19664                                                    lsp::Position::new(0, 0),
19665                                                ),
19666                                                new_text: "X".into(),
19667                                            }],
19668                                        )]
19669                                        .into_iter()
19670                                        .collect(),
19671                                    ),
19672                                    ..Default::default()
19673                                },
19674                            },
19675                        )
19676                        .await
19677                        .into_response()
19678                        .unwrap();
19679                    Ok(Some(json!(null)))
19680                }
19681            }
19682        })
19683        .next()
19684        .await;
19685
19686    // Applying the code lens command returns a project transaction containing the edits
19687    // sent by the language server in its `workspaceEdit` request.
19688    let transaction = apply.await.unwrap();
19689    assert!(transaction.0.contains_key(&buffer));
19690    buffer.update(cx, |buffer, cx| {
19691        assert_eq!(buffer.text(), "Xa");
19692        buffer.undo(cx);
19693        assert_eq!(buffer.text(), "a");
19694    });
19695}
19696
19697#[gpui::test]
19698async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19699    init_test(cx, |_| {});
19700
19701    let fs = FakeFs::new(cx.executor());
19702    let main_text = r#"fn main() {
19703println!("1");
19704println!("2");
19705println!("3");
19706println!("4");
19707println!("5");
19708}"#;
19709    let lib_text = "mod foo {}";
19710    fs.insert_tree(
19711        path!("/a"),
19712        json!({
19713            "lib.rs": lib_text,
19714            "main.rs": main_text,
19715        }),
19716    )
19717    .await;
19718
19719    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19720    let (workspace, cx) =
19721        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19722    let worktree_id = workspace.update(cx, |workspace, cx| {
19723        workspace.project().update(cx, |project, cx| {
19724            project.worktrees(cx).next().unwrap().read(cx).id()
19725        })
19726    });
19727
19728    let expected_ranges = vec![
19729        Point::new(0, 0)..Point::new(0, 0),
19730        Point::new(1, 0)..Point::new(1, 1),
19731        Point::new(2, 0)..Point::new(2, 2),
19732        Point::new(3, 0)..Point::new(3, 3),
19733    ];
19734
19735    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19736    let editor_1 = workspace
19737        .update_in(cx, |workspace, window, cx| {
19738            workspace.open_path(
19739                (worktree_id, "main.rs"),
19740                Some(pane_1.downgrade()),
19741                true,
19742                window,
19743                cx,
19744            )
19745        })
19746        .unwrap()
19747        .await
19748        .downcast::<Editor>()
19749        .unwrap();
19750    pane_1.update(cx, |pane, cx| {
19751        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19752        open_editor.update(cx, |editor, cx| {
19753            assert_eq!(
19754                editor.display_text(cx),
19755                main_text,
19756                "Original main.rs text on initial open",
19757            );
19758            assert_eq!(
19759                editor
19760                    .selections
19761                    .all::<Point>(cx)
19762                    .into_iter()
19763                    .map(|s| s.range())
19764                    .collect::<Vec<_>>(),
19765                vec![Point::zero()..Point::zero()],
19766                "Default selections on initial open",
19767            );
19768        })
19769    });
19770    editor_1.update_in(cx, |editor, window, cx| {
19771        editor.change_selections(None, window, cx, |s| {
19772            s.select_ranges(expected_ranges.clone());
19773        });
19774    });
19775
19776    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19777        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19778    });
19779    let editor_2 = workspace
19780        .update_in(cx, |workspace, window, cx| {
19781            workspace.open_path(
19782                (worktree_id, "main.rs"),
19783                Some(pane_2.downgrade()),
19784                true,
19785                window,
19786                cx,
19787            )
19788        })
19789        .unwrap()
19790        .await
19791        .downcast::<Editor>()
19792        .unwrap();
19793    pane_2.update(cx, |pane, cx| {
19794        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19795        open_editor.update(cx, |editor, cx| {
19796            assert_eq!(
19797                editor.display_text(cx),
19798                main_text,
19799                "Original main.rs text on initial open in another panel",
19800            );
19801            assert_eq!(
19802                editor
19803                    .selections
19804                    .all::<Point>(cx)
19805                    .into_iter()
19806                    .map(|s| s.range())
19807                    .collect::<Vec<_>>(),
19808                vec![Point::zero()..Point::zero()],
19809                "Default selections on initial open in another panel",
19810            );
19811        })
19812    });
19813
19814    editor_2.update_in(cx, |editor, window, cx| {
19815        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19816    });
19817
19818    let _other_editor_1 = workspace
19819        .update_in(cx, |workspace, window, cx| {
19820            workspace.open_path(
19821                (worktree_id, "lib.rs"),
19822                Some(pane_1.downgrade()),
19823                true,
19824                window,
19825                cx,
19826            )
19827        })
19828        .unwrap()
19829        .await
19830        .downcast::<Editor>()
19831        .unwrap();
19832    pane_1
19833        .update_in(cx, |pane, window, cx| {
19834            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19835                .unwrap()
19836        })
19837        .await
19838        .unwrap();
19839    drop(editor_1);
19840    pane_1.update(cx, |pane, cx| {
19841        pane.active_item()
19842            .unwrap()
19843            .downcast::<Editor>()
19844            .unwrap()
19845            .update(cx, |editor, cx| {
19846                assert_eq!(
19847                    editor.display_text(cx),
19848                    lib_text,
19849                    "Other file should be open and active",
19850                );
19851            });
19852        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19853    });
19854
19855    let _other_editor_2 = workspace
19856        .update_in(cx, |workspace, window, cx| {
19857            workspace.open_path(
19858                (worktree_id, "lib.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
19870        .update_in(cx, |pane, window, cx| {
19871            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19872                .unwrap()
19873        })
19874        .await
19875        .unwrap();
19876    drop(editor_2);
19877    pane_2.update(cx, |pane, cx| {
19878        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19879        open_editor.update(cx, |editor, cx| {
19880            assert_eq!(
19881                editor.display_text(cx),
19882                lib_text,
19883                "Other file should be open and active in another panel too",
19884            );
19885        });
19886        assert_eq!(
19887            pane.items().count(),
19888            1,
19889            "No other editors should be open in another pane",
19890        );
19891    });
19892
19893    let _editor_1_reopened = workspace
19894        .update_in(cx, |workspace, window, cx| {
19895            workspace.open_path(
19896                (worktree_id, "main.rs"),
19897                Some(pane_1.downgrade()),
19898                true,
19899                window,
19900                cx,
19901            )
19902        })
19903        .unwrap()
19904        .await
19905        .downcast::<Editor>()
19906        .unwrap();
19907    let _editor_2_reopened = workspace
19908        .update_in(cx, |workspace, window, cx| {
19909            workspace.open_path(
19910                (worktree_id, "main.rs"),
19911                Some(pane_2.downgrade()),
19912                true,
19913                window,
19914                cx,
19915            )
19916        })
19917        .unwrap()
19918        .await
19919        .downcast::<Editor>()
19920        .unwrap();
19921    pane_1.update(cx, |pane, cx| {
19922        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19923        open_editor.update(cx, |editor, cx| {
19924            assert_eq!(
19925                editor.display_text(cx),
19926                main_text,
19927                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19928            );
19929            assert_eq!(
19930                editor
19931                    .selections
19932                    .all::<Point>(cx)
19933                    .into_iter()
19934                    .map(|s| s.range())
19935                    .collect::<Vec<_>>(),
19936                expected_ranges,
19937                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19938            );
19939        })
19940    });
19941    pane_2.update(cx, |pane, cx| {
19942        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19943        open_editor.update(cx, |editor, cx| {
19944            assert_eq!(
19945                editor.display_text(cx),
19946                r#"fn main() {
19947⋯rintln!("1");
19948⋯intln!("2");
19949⋯ntln!("3");
19950println!("4");
19951println!("5");
19952}"#,
19953                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19954            );
19955            assert_eq!(
19956                editor
19957                    .selections
19958                    .all::<Point>(cx)
19959                    .into_iter()
19960                    .map(|s| s.range())
19961                    .collect::<Vec<_>>(),
19962                vec![Point::zero()..Point::zero()],
19963                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19964            );
19965        })
19966    });
19967}
19968
19969#[gpui::test]
19970async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19971    init_test(cx, |_| {});
19972
19973    let fs = FakeFs::new(cx.executor());
19974    let main_text = r#"fn main() {
19975println!("1");
19976println!("2");
19977println!("3");
19978println!("4");
19979println!("5");
19980}"#;
19981    let lib_text = "mod foo {}";
19982    fs.insert_tree(
19983        path!("/a"),
19984        json!({
19985            "lib.rs": lib_text,
19986            "main.rs": main_text,
19987        }),
19988    )
19989    .await;
19990
19991    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19992    let (workspace, cx) =
19993        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19994    let worktree_id = workspace.update(cx, |workspace, cx| {
19995        workspace.project().update(cx, |project, cx| {
19996            project.worktrees(cx).next().unwrap().read(cx).id()
19997        })
19998    });
19999
20000    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20001    let editor = workspace
20002        .update_in(cx, |workspace, window, cx| {
20003            workspace.open_path(
20004                (worktree_id, "main.rs"),
20005                Some(pane.downgrade()),
20006                true,
20007                window,
20008                cx,
20009            )
20010        })
20011        .unwrap()
20012        .await
20013        .downcast::<Editor>()
20014        .unwrap();
20015    pane.update(cx, |pane, cx| {
20016        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20017        open_editor.update(cx, |editor, cx| {
20018            assert_eq!(
20019                editor.display_text(cx),
20020                main_text,
20021                "Original main.rs text on initial open",
20022            );
20023        })
20024    });
20025    editor.update_in(cx, |editor, window, cx| {
20026        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
20027    });
20028
20029    cx.update_global(|store: &mut SettingsStore, cx| {
20030        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20031            s.restore_on_file_reopen = Some(false);
20032        });
20033    });
20034    editor.update_in(cx, |editor, window, cx| {
20035        editor.fold_ranges(
20036            vec![
20037                Point::new(1, 0)..Point::new(1, 1),
20038                Point::new(2, 0)..Point::new(2, 2),
20039                Point::new(3, 0)..Point::new(3, 3),
20040            ],
20041            false,
20042            window,
20043            cx,
20044        );
20045    });
20046    pane.update_in(cx, |pane, window, cx| {
20047        pane.close_all_items(&CloseAllItems::default(), window, cx)
20048            .unwrap()
20049    })
20050    .await
20051    .unwrap();
20052    pane.update(cx, |pane, _| {
20053        assert!(pane.active_item().is_none());
20054    });
20055    cx.update_global(|store: &mut SettingsStore, cx| {
20056        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20057            s.restore_on_file_reopen = Some(true);
20058        });
20059    });
20060
20061    let _editor_reopened = workspace
20062        .update_in(cx, |workspace, window, cx| {
20063            workspace.open_path(
20064                (worktree_id, "main.rs"),
20065                Some(pane.downgrade()),
20066                true,
20067                window,
20068                cx,
20069            )
20070        })
20071        .unwrap()
20072        .await
20073        .downcast::<Editor>()
20074        .unwrap();
20075    pane.update(cx, |pane, cx| {
20076        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20077        open_editor.update(cx, |editor, cx| {
20078            assert_eq!(
20079                editor.display_text(cx),
20080                main_text,
20081                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20082            );
20083        })
20084    });
20085}
20086
20087#[gpui::test]
20088async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20089    struct EmptyModalView {
20090        focus_handle: gpui::FocusHandle,
20091    }
20092    impl EventEmitter<DismissEvent> for EmptyModalView {}
20093    impl Render for EmptyModalView {
20094        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20095            div()
20096        }
20097    }
20098    impl Focusable for EmptyModalView {
20099        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20100            self.focus_handle.clone()
20101        }
20102    }
20103    impl workspace::ModalView for EmptyModalView {}
20104    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20105        EmptyModalView {
20106            focus_handle: cx.focus_handle(),
20107        }
20108    }
20109
20110    init_test(cx, |_| {});
20111
20112    let fs = FakeFs::new(cx.executor());
20113    let project = Project::test(fs, [], cx).await;
20114    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20115    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20116    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20117    let editor = cx.new_window_entity(|window, cx| {
20118        Editor::new(
20119            EditorMode::full(),
20120            buffer,
20121            Some(project.clone()),
20122            window,
20123            cx,
20124        )
20125    });
20126    workspace
20127        .update(cx, |workspace, window, cx| {
20128            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20129        })
20130        .unwrap();
20131    editor.update_in(cx, |editor, window, cx| {
20132        editor.open_context_menu(&OpenContextMenu, window, cx);
20133        assert!(editor.mouse_context_menu.is_some());
20134    });
20135    workspace
20136        .update(cx, |workspace, window, cx| {
20137            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20138        })
20139        .unwrap();
20140    cx.read(|cx| {
20141        assert!(editor.read(cx).mouse_context_menu.is_none());
20142    });
20143}
20144
20145#[gpui::test]
20146async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20147    init_test(cx, |_| {});
20148
20149    let fs = FakeFs::new(cx.executor());
20150    fs.insert_file(path!("/file.html"), Default::default())
20151        .await;
20152
20153    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20154
20155    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20156    let html_language = Arc::new(Language::new(
20157        LanguageConfig {
20158            name: "HTML".into(),
20159            matcher: LanguageMatcher {
20160                path_suffixes: vec!["html".to_string()],
20161                ..LanguageMatcher::default()
20162            },
20163            brackets: BracketPairConfig {
20164                pairs: vec![BracketPair {
20165                    start: "<".into(),
20166                    end: ">".into(),
20167                    close: true,
20168                    ..Default::default()
20169                }],
20170                ..Default::default()
20171            },
20172            ..Default::default()
20173        },
20174        Some(tree_sitter_html::LANGUAGE.into()),
20175    ));
20176    language_registry.add(html_language);
20177    let mut fake_servers = language_registry.register_fake_lsp(
20178        "HTML",
20179        FakeLspAdapter {
20180            capabilities: lsp::ServerCapabilities {
20181                completion_provider: Some(lsp::CompletionOptions {
20182                    resolve_provider: Some(true),
20183                    ..Default::default()
20184                }),
20185                ..Default::default()
20186            },
20187            ..Default::default()
20188        },
20189    );
20190
20191    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20192    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20193
20194    let worktree_id = workspace
20195        .update(cx, |workspace, _window, cx| {
20196            workspace.project().update(cx, |project, cx| {
20197                project.worktrees(cx).next().unwrap().read(cx).id()
20198            })
20199        })
20200        .unwrap();
20201    project
20202        .update(cx, |project, cx| {
20203            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20204        })
20205        .await
20206        .unwrap();
20207    let editor = workspace
20208        .update(cx, |workspace, window, cx| {
20209            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20210        })
20211        .unwrap()
20212        .await
20213        .unwrap()
20214        .downcast::<Editor>()
20215        .unwrap();
20216
20217    let fake_server = fake_servers.next().await.unwrap();
20218    editor.update_in(cx, |editor, window, cx| {
20219        editor.set_text("<ad></ad>", window, cx);
20220        editor.change_selections(None, window, cx, |selections| {
20221            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20222        });
20223        let Some((buffer, _)) = editor
20224            .buffer
20225            .read(cx)
20226            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20227        else {
20228            panic!("Failed to get buffer for selection position");
20229        };
20230        let buffer = buffer.read(cx);
20231        let buffer_id = buffer.remote_id();
20232        let opening_range =
20233            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20234        let closing_range =
20235            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20236        let mut linked_ranges = HashMap::default();
20237        linked_ranges.insert(
20238            buffer_id,
20239            vec![(opening_range.clone(), vec![closing_range.clone()])],
20240        );
20241        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20242    });
20243    let mut completion_handle =
20244        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20245            Ok(Some(lsp::CompletionResponse::Array(vec![
20246                lsp::CompletionItem {
20247                    label: "head".to_string(),
20248                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20249                        lsp::InsertReplaceEdit {
20250                            new_text: "head".to_string(),
20251                            insert: lsp::Range::new(
20252                                lsp::Position::new(0, 1),
20253                                lsp::Position::new(0, 3),
20254                            ),
20255                            replace: lsp::Range::new(
20256                                lsp::Position::new(0, 1),
20257                                lsp::Position::new(0, 3),
20258                            ),
20259                        },
20260                    )),
20261                    ..Default::default()
20262                },
20263            ])))
20264        });
20265    editor.update_in(cx, |editor, window, cx| {
20266        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20267    });
20268    cx.run_until_parked();
20269    completion_handle.next().await.unwrap();
20270    editor.update(cx, |editor, _| {
20271        assert!(
20272            editor.context_menu_visible(),
20273            "Completion menu should be visible"
20274        );
20275    });
20276    editor.update_in(cx, |editor, window, cx| {
20277        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20278    });
20279    cx.executor().run_until_parked();
20280    editor.update(cx, |editor, cx| {
20281        assert_eq!(editor.text(cx), "<head></head>");
20282    });
20283}
20284
20285#[gpui::test]
20286async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20287    init_test(cx, |_| {});
20288
20289    let fs = FakeFs::new(cx.executor());
20290    fs.insert_tree(
20291        path!("/root"),
20292        json!({
20293            "a": {
20294                "main.rs": "fn main() {}",
20295            },
20296            "foo": {
20297                "bar": {
20298                    "external_file.rs": "pub mod external {}",
20299                }
20300            }
20301        }),
20302    )
20303    .await;
20304
20305    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20306    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20307    language_registry.add(rust_lang());
20308    let _fake_servers = language_registry.register_fake_lsp(
20309        "Rust",
20310        FakeLspAdapter {
20311            ..FakeLspAdapter::default()
20312        },
20313    );
20314    let (workspace, cx) =
20315        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20316    let worktree_id = workspace.update(cx, |workspace, cx| {
20317        workspace.project().update(cx, |project, cx| {
20318            project.worktrees(cx).next().unwrap().read(cx).id()
20319        })
20320    });
20321
20322    let assert_language_servers_count =
20323        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20324            project.update(cx, |project, cx| {
20325                let current = project
20326                    .lsp_store()
20327                    .read(cx)
20328                    .as_local()
20329                    .unwrap()
20330                    .language_servers
20331                    .len();
20332                assert_eq!(expected, current, "{context}");
20333            });
20334        };
20335
20336    assert_language_servers_count(
20337        0,
20338        "No servers should be running before any file is open",
20339        cx,
20340    );
20341    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20342    let main_editor = workspace
20343        .update_in(cx, |workspace, window, cx| {
20344            workspace.open_path(
20345                (worktree_id, "main.rs"),
20346                Some(pane.downgrade()),
20347                true,
20348                window,
20349                cx,
20350            )
20351        })
20352        .unwrap()
20353        .await
20354        .downcast::<Editor>()
20355        .unwrap();
20356    pane.update(cx, |pane, cx| {
20357        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20358        open_editor.update(cx, |editor, cx| {
20359            assert_eq!(
20360                editor.display_text(cx),
20361                "fn main() {}",
20362                "Original main.rs text on initial open",
20363            );
20364        });
20365        assert_eq!(open_editor, main_editor);
20366    });
20367    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20368
20369    let external_editor = workspace
20370        .update_in(cx, |workspace, window, cx| {
20371            workspace.open_abs_path(
20372                PathBuf::from("/root/foo/bar/external_file.rs"),
20373                OpenOptions::default(),
20374                window,
20375                cx,
20376            )
20377        })
20378        .await
20379        .expect("opening external file")
20380        .downcast::<Editor>()
20381        .expect("downcasted external file's open element to editor");
20382    pane.update(cx, |pane, cx| {
20383        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20384        open_editor.update(cx, |editor, cx| {
20385            assert_eq!(
20386                editor.display_text(cx),
20387                "pub mod external {}",
20388                "External file is open now",
20389            );
20390        });
20391        assert_eq!(open_editor, external_editor);
20392    });
20393    assert_language_servers_count(
20394        1,
20395        "Second, external, *.rs file should join the existing server",
20396        cx,
20397    );
20398
20399    pane.update_in(cx, |pane, window, cx| {
20400        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20401    })
20402    .unwrap()
20403    .await
20404    .unwrap();
20405    pane.update_in(cx, |pane, window, cx| {
20406        pane.navigate_backward(window, cx);
20407    });
20408    cx.run_until_parked();
20409    pane.update(cx, |pane, cx| {
20410        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20411        open_editor.update(cx, |editor, cx| {
20412            assert_eq!(
20413                editor.display_text(cx),
20414                "pub mod external {}",
20415                "External file is open now",
20416            );
20417        });
20418    });
20419    assert_language_servers_count(
20420        1,
20421        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20422        cx,
20423    );
20424
20425    cx.update(|_, cx| {
20426        workspace::reload(&workspace::Reload::default(), cx);
20427    });
20428    assert_language_servers_count(
20429        1,
20430        "After reloading the worktree with local and external files opened, only one project should be started",
20431        cx,
20432    );
20433}
20434
20435#[gpui::test]
20436async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20437    init_test(cx, |_| {});
20438
20439    let mut cx = EditorTestContext::new(cx).await;
20440    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20441    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20442
20443    // test cursor move to start of each line on tab
20444    // for `if`, `elif`, `else`, `while`, `with` and `for`
20445    cx.set_state(indoc! {"
20446        def main():
20447        ˇ    for item in items:
20448        ˇ        while item.active:
20449        ˇ            if item.value > 10:
20450        ˇ                continue
20451        ˇ            elif item.value < 0:
20452        ˇ                break
20453        ˇ            else:
20454        ˇ                with item.context() as ctx:
20455        ˇ                    yield count
20456        ˇ        else:
20457        ˇ            log('while else')
20458        ˇ    else:
20459        ˇ        log('for else')
20460    "});
20461    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20462    cx.assert_editor_state(indoc! {"
20463        def main():
20464            ˇfor item in items:
20465                ˇwhile item.active:
20466                    ˇif item.value > 10:
20467                        ˇcontinue
20468                    ˇelif item.value < 0:
20469                        ˇbreak
20470                    ˇelse:
20471                        ˇwith item.context() as ctx:
20472                            ˇyield count
20473                ˇelse:
20474                    ˇlog('while else')
20475            ˇelse:
20476                ˇlog('for else')
20477    "});
20478    // test relative indent is preserved when tab
20479    // for `if`, `elif`, `else`, `while`, `with` and `for`
20480    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20481    cx.assert_editor_state(indoc! {"
20482        def main():
20483                ˇfor item in items:
20484                    ˇwhile item.active:
20485                        ˇif item.value > 10:
20486                            ˇcontinue
20487                        ˇelif item.value < 0:
20488                            ˇbreak
20489                        ˇelse:
20490                            ˇwith item.context() as ctx:
20491                                ˇyield count
20492                    ˇelse:
20493                        ˇlog('while else')
20494                ˇelse:
20495                    ˇlog('for else')
20496    "});
20497
20498    // test cursor move to start of each line on tab
20499    // for `try`, `except`, `else`, `finally`, `match` and `def`
20500    cx.set_state(indoc! {"
20501        def main():
20502        ˇ    try:
20503        ˇ       fetch()
20504        ˇ    except ValueError:
20505        ˇ       handle_error()
20506        ˇ    else:
20507        ˇ        match value:
20508        ˇ            case _:
20509        ˇ    finally:
20510        ˇ        def status():
20511        ˇ            return 0
20512    "});
20513    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20514    cx.assert_editor_state(indoc! {"
20515        def main():
20516            ˇtry:
20517                ˇfetch()
20518            ˇexcept ValueError:
20519                ˇhandle_error()
20520            ˇelse:
20521                ˇmatch value:
20522                    ˇcase _:
20523            ˇfinally:
20524                ˇdef status():
20525                    ˇreturn 0
20526    "});
20527    // test relative indent is preserved when tab
20528    // for `try`, `except`, `else`, `finally`, `match` and `def`
20529    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20530    cx.assert_editor_state(indoc! {"
20531        def main():
20532                ˇtry:
20533                    ˇfetch()
20534                ˇexcept ValueError:
20535                    ˇhandle_error()
20536                ˇelse:
20537                    ˇmatch value:
20538                        ˇcase _:
20539                ˇfinally:
20540                    ˇdef status():
20541                        ˇreturn 0
20542    "});
20543}
20544
20545#[gpui::test]
20546async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20547    init_test(cx, |_| {});
20548
20549    let mut cx = EditorTestContext::new(cx).await;
20550    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20551    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20552
20553    // test `else` auto outdents when typed inside `if` block
20554    cx.set_state(indoc! {"
20555        def main():
20556            if i == 2:
20557                return
20558                ˇ
20559    "});
20560    cx.update_editor(|editor, window, cx| {
20561        editor.handle_input("else:", window, cx);
20562    });
20563    cx.assert_editor_state(indoc! {"
20564        def main():
20565            if i == 2:
20566                return
20567            else:ˇ
20568    "});
20569
20570    // test `except` auto outdents when typed inside `try` block
20571    cx.set_state(indoc! {"
20572        def main():
20573            try:
20574                i = 2
20575                ˇ
20576    "});
20577    cx.update_editor(|editor, window, cx| {
20578        editor.handle_input("except:", window, cx);
20579    });
20580    cx.assert_editor_state(indoc! {"
20581        def main():
20582            try:
20583                i = 2
20584            except:ˇ
20585    "});
20586
20587    // test `else` auto outdents when typed inside `except` block
20588    cx.set_state(indoc! {"
20589        def main():
20590            try:
20591                i = 2
20592            except:
20593                j = 2
20594                ˇ
20595    "});
20596    cx.update_editor(|editor, window, cx| {
20597        editor.handle_input("else:", window, cx);
20598    });
20599    cx.assert_editor_state(indoc! {"
20600        def main():
20601            try:
20602                i = 2
20603            except:
20604                j = 2
20605            else:ˇ
20606    "});
20607
20608    // test `finally` auto outdents when typed inside `else` block
20609    cx.set_state(indoc! {"
20610        def main():
20611            try:
20612                i = 2
20613            except:
20614                j = 2
20615            else:
20616                k = 2
20617                ˇ
20618    "});
20619    cx.update_editor(|editor, window, cx| {
20620        editor.handle_input("finally:", window, cx);
20621    });
20622    cx.assert_editor_state(indoc! {"
20623        def main():
20624            try:
20625                i = 2
20626            except:
20627                j = 2
20628            else:
20629                k = 2
20630            finally:ˇ
20631    "});
20632
20633    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20634    // cx.set_state(indoc! {"
20635    //     def main():
20636    //         try:
20637    //             for i in range(n):
20638    //                 pass
20639    //             ˇ
20640    // "});
20641    // cx.update_editor(|editor, window, cx| {
20642    //     editor.handle_input("except:", window, cx);
20643    // });
20644    // cx.assert_editor_state(indoc! {"
20645    //     def main():
20646    //         try:
20647    //             for i in range(n):
20648    //                 pass
20649    //         except:ˇ
20650    // "});
20651
20652    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20653    // cx.set_state(indoc! {"
20654    //     def main():
20655    //         try:
20656    //             i = 2
20657    //         except:
20658    //             for i in range(n):
20659    //                 pass
20660    //             ˇ
20661    // "});
20662    // cx.update_editor(|editor, window, cx| {
20663    //     editor.handle_input("else:", window, cx);
20664    // });
20665    // cx.assert_editor_state(indoc! {"
20666    //     def main():
20667    //         try:
20668    //             i = 2
20669    //         except:
20670    //             for i in range(n):
20671    //                 pass
20672    //         else:ˇ
20673    // "});
20674
20675    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20676    // cx.set_state(indoc! {"
20677    //     def main():
20678    //         try:
20679    //             i = 2
20680    //         except:
20681    //             j = 2
20682    //         else:
20683    //             for i in range(n):
20684    //                 pass
20685    //             ˇ
20686    // "});
20687    // cx.update_editor(|editor, window, cx| {
20688    //     editor.handle_input("finally:", window, cx);
20689    // });
20690    // cx.assert_editor_state(indoc! {"
20691    //     def main():
20692    //         try:
20693    //             i = 2
20694    //         except:
20695    //             j = 2
20696    //         else:
20697    //             for i in range(n):
20698    //                 pass
20699    //         finally:ˇ
20700    // "});
20701
20702    // test `else` stays at correct indent when typed after `for` block
20703    cx.set_state(indoc! {"
20704        def main():
20705            for i in range(10):
20706                if i == 3:
20707                    break
20708            ˇ
20709    "});
20710    cx.update_editor(|editor, window, cx| {
20711        editor.handle_input("else:", window, cx);
20712    });
20713    cx.assert_editor_state(indoc! {"
20714        def main():
20715            for i in range(10):
20716                if i == 3:
20717                    break
20718            else:ˇ
20719    "});
20720}
20721
20722#[gpui::test]
20723async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
20724    init_test(cx, |_| {});
20725    update_test_language_settings(cx, |settings| {
20726        settings.defaults.extend_comment_on_newline = Some(false);
20727    });
20728    let mut cx = EditorTestContext::new(cx).await;
20729    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20730    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20731
20732    // test correct indent after newline on comment
20733    cx.set_state(indoc! {"
20734        # COMMENT:ˇ
20735    "});
20736    cx.update_editor(|editor, window, cx| {
20737        editor.newline(&Newline, window, cx);
20738    });
20739    cx.assert_editor_state(indoc! {"
20740        # COMMENT:
20741        ˇ
20742    "});
20743
20744    // test correct indent after newline in brackets
20745    cx.set_state(indoc! {"
20746        {ˇ}
20747    "});
20748    cx.update_editor(|editor, window, cx| {
20749        editor.newline(&Newline, window, cx);
20750    });
20751    cx.run_until_parked();
20752    cx.assert_editor_state(indoc! {"
20753        {
20754            ˇ
20755        }
20756    "});
20757
20758    cx.set_state(indoc! {"
20759        (ˇ)
20760    "});
20761    cx.update_editor(|editor, window, cx| {
20762        editor.newline(&Newline, window, cx);
20763    });
20764    cx.run_until_parked();
20765    cx.assert_editor_state(indoc! {"
20766        (
20767            ˇ
20768        )
20769    "});
20770
20771    // do not indent after empty lists or dictionaries
20772    cx.set_state(indoc! {"
20773        a = []ˇ
20774    "});
20775    cx.update_editor(|editor, window, cx| {
20776        editor.newline(&Newline, window, cx);
20777    });
20778    cx.run_until_parked();
20779    cx.assert_editor_state(indoc! {"
20780        a = []
20781        ˇ
20782    "});
20783}
20784
20785fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20786    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20787    point..point
20788}
20789
20790fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20791    let (text, ranges) = marked_text_ranges(marked_text, true);
20792    assert_eq!(editor.text(cx), text);
20793    assert_eq!(
20794        editor.selections.ranges(cx),
20795        ranges,
20796        "Assert selections are {}",
20797        marked_text
20798    );
20799}
20800
20801pub fn handle_signature_help_request(
20802    cx: &mut EditorLspTestContext,
20803    mocked_response: lsp::SignatureHelp,
20804) -> impl Future<Output = ()> + use<> {
20805    let mut request =
20806        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20807            let mocked_response = mocked_response.clone();
20808            async move { Ok(Some(mocked_response)) }
20809        });
20810
20811    async move {
20812        request.next().await;
20813    }
20814}
20815
20816/// Handle completion request passing a marked string specifying where the completion
20817/// should be triggered from using '|' character, what range should be replaced, and what completions
20818/// should be returned using '<' and '>' to delimit the range.
20819///
20820/// Also see `handle_completion_request_with_insert_and_replace`.
20821#[track_caller]
20822pub fn handle_completion_request(
20823    cx: &mut EditorLspTestContext,
20824    marked_string: &str,
20825    completions: Vec<&'static str>,
20826    counter: Arc<AtomicUsize>,
20827) -> impl Future<Output = ()> {
20828    let complete_from_marker: TextRangeMarker = '|'.into();
20829    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20830    let (_, mut marked_ranges) = marked_text_ranges_by(
20831        marked_string,
20832        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20833    );
20834
20835    let complete_from_position =
20836        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20837    let replace_range =
20838        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20839
20840    let mut request =
20841        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20842            let completions = completions.clone();
20843            counter.fetch_add(1, atomic::Ordering::Release);
20844            async move {
20845                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20846                assert_eq!(
20847                    params.text_document_position.position,
20848                    complete_from_position
20849                );
20850                Ok(Some(lsp::CompletionResponse::Array(
20851                    completions
20852                        .iter()
20853                        .map(|completion_text| lsp::CompletionItem {
20854                            label: completion_text.to_string(),
20855                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20856                                range: replace_range,
20857                                new_text: completion_text.to_string(),
20858                            })),
20859                            ..Default::default()
20860                        })
20861                        .collect(),
20862                )))
20863            }
20864        });
20865
20866    async move {
20867        request.next().await;
20868    }
20869}
20870
20871/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20872/// given instead, which also contains an `insert` range.
20873///
20874/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20875/// that is, `replace_range.start..cursor_pos`.
20876pub fn handle_completion_request_with_insert_and_replace(
20877    cx: &mut EditorLspTestContext,
20878    marked_string: &str,
20879    completions: Vec<&'static str>,
20880    counter: Arc<AtomicUsize>,
20881) -> impl Future<Output = ()> {
20882    let complete_from_marker: TextRangeMarker = '|'.into();
20883    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20884    let (_, mut marked_ranges) = marked_text_ranges_by(
20885        marked_string,
20886        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20887    );
20888
20889    let complete_from_position =
20890        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20891    let replace_range =
20892        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20893
20894    let mut request =
20895        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20896            let completions = completions.clone();
20897            counter.fetch_add(1, atomic::Ordering::Release);
20898            async move {
20899                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20900                assert_eq!(
20901                    params.text_document_position.position, complete_from_position,
20902                    "marker `|` position doesn't match",
20903                );
20904                Ok(Some(lsp::CompletionResponse::Array(
20905                    completions
20906                        .iter()
20907                        .map(|completion_text| lsp::CompletionItem {
20908                            label: completion_text.to_string(),
20909                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20910                                lsp::InsertReplaceEdit {
20911                                    insert: lsp::Range {
20912                                        start: replace_range.start,
20913                                        end: complete_from_position,
20914                                    },
20915                                    replace: replace_range,
20916                                    new_text: completion_text.to_string(),
20917                                },
20918                            )),
20919                            ..Default::default()
20920                        })
20921                        .collect(),
20922                )))
20923            }
20924        });
20925
20926    async move {
20927        request.next().await;
20928    }
20929}
20930
20931fn handle_resolve_completion_request(
20932    cx: &mut EditorLspTestContext,
20933    edits: Option<Vec<(&'static str, &'static str)>>,
20934) -> impl Future<Output = ()> {
20935    let edits = edits.map(|edits| {
20936        edits
20937            .iter()
20938            .map(|(marked_string, new_text)| {
20939                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20940                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20941                lsp::TextEdit::new(replace_range, new_text.to_string())
20942            })
20943            .collect::<Vec<_>>()
20944    });
20945
20946    let mut request =
20947        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20948            let edits = edits.clone();
20949            async move {
20950                Ok(lsp::CompletionItem {
20951                    additional_text_edits: edits,
20952                    ..Default::default()
20953                })
20954            }
20955        });
20956
20957    async move {
20958        request.next().await;
20959    }
20960}
20961
20962pub(crate) fn update_test_language_settings(
20963    cx: &mut TestAppContext,
20964    f: impl Fn(&mut AllLanguageSettingsContent),
20965) {
20966    cx.update(|cx| {
20967        SettingsStore::update_global(cx, |store, cx| {
20968            store.update_user_settings::<AllLanguageSettings>(cx, f);
20969        });
20970    });
20971}
20972
20973pub(crate) fn update_test_project_settings(
20974    cx: &mut TestAppContext,
20975    f: impl Fn(&mut ProjectSettings),
20976) {
20977    cx.update(|cx| {
20978        SettingsStore::update_global(cx, |store, cx| {
20979            store.update_user_settings::<ProjectSettings>(cx, f);
20980        });
20981    });
20982}
20983
20984pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
20985    cx.update(|cx| {
20986        assets::Assets.load_test_fonts(cx);
20987        let store = SettingsStore::test(cx);
20988        cx.set_global(store);
20989        theme::init(theme::LoadThemes::JustBase, cx);
20990        release_channel::init(SemanticVersion::default(), cx);
20991        client::init_settings(cx);
20992        language::init(cx);
20993        Project::init_settings(cx);
20994        workspace::init_settings(cx);
20995        crate::init(cx);
20996    });
20997
20998    update_test_language_settings(cx, f);
20999}
21000
21001#[track_caller]
21002fn assert_hunk_revert(
21003    not_reverted_text_with_selections: &str,
21004    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
21005    expected_reverted_text_with_selections: &str,
21006    base_text: &str,
21007    cx: &mut EditorLspTestContext,
21008) {
21009    cx.set_state(not_reverted_text_with_selections);
21010    cx.set_head_text(base_text);
21011    cx.executor().run_until_parked();
21012
21013    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
21014        let snapshot = editor.snapshot(window, cx);
21015        let reverted_hunk_statuses = snapshot
21016            .buffer_snapshot
21017            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
21018            .map(|hunk| hunk.status().kind)
21019            .collect::<Vec<_>>();
21020
21021        editor.git_restore(&Default::default(), window, cx);
21022        reverted_hunk_statuses
21023    });
21024    cx.executor().run_until_parked();
21025    cx.assert_editor_state(expected_reverted_text_with_selections);
21026    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
21027}