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_documentation_comments(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            documentation: Some(language::DocumentationConfig {
 2832                start: "/**".into(),
 2833                end: "*/".into(),
 2834                prefix: "* ".into(),
 2835                tab_size: NonZeroU32::new(1).unwrap(),
 2836            }),
 2837            ..LanguageConfig::default()
 2838        },
 2839        None,
 2840    ));
 2841    {
 2842        let mut cx = EditorTestContext::new(cx).await;
 2843        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2844        cx.set_state(indoc! {"
 2845        /**ˇ
 2846    "});
 2847
 2848        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2849        cx.assert_editor_state(indoc! {"
 2850        /**
 2851         * ˇ
 2852    "});
 2853        // Ensure that if cursor is before the comment start,
 2854        // we do not actually insert a comment prefix.
 2855        cx.set_state(indoc! {"
 2856        ˇ/**
 2857    "});
 2858        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2859        cx.assert_editor_state(indoc! {"
 2860
 2861        ˇ/**
 2862    "});
 2863        // Ensure that if cursor is between it doesn't add comment prefix.
 2864        cx.set_state(indoc! {"
 2865        /*ˇ*
 2866    "});
 2867        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2868        cx.assert_editor_state(indoc! {"
 2869        /*
 2870        ˇ*
 2871    "});
 2872        // Ensure that if suffix exists on same line after cursor it adds new line.
 2873        cx.set_state(indoc! {"
 2874        /**ˇ*/
 2875    "});
 2876        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2877        cx.assert_editor_state(indoc! {"
 2878        /**
 2879         * ˇ
 2880         */
 2881    "});
 2882        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2883        cx.set_state(indoc! {"
 2884        /**ˇ */
 2885    "});
 2886        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2887        cx.assert_editor_state(indoc! {"
 2888        /**
 2889         * ˇ
 2890         */
 2891    "});
 2892        // Ensure that if suffix exists on same line after cursor with space it adds new line.
 2893        cx.set_state(indoc! {"
 2894        /** ˇ*/
 2895    "});
 2896        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2897        cx.assert_editor_state(
 2898            indoc! {"
 2899        /**s
 2900         * ˇ
 2901         */
 2902    "}
 2903            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2904            .as_str(),
 2905        );
 2906        // Ensure that delimiter space is preserved when newline on already
 2907        // spaced delimiter.
 2908        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2909        cx.assert_editor_state(
 2910            indoc! {"
 2911        /**s
 2912         *s
 2913         * ˇ
 2914         */
 2915    "}
 2916            .replace("s", " ") // s is used as space placeholder to prevent format on save
 2917            .as_str(),
 2918        );
 2919        // Ensure that delimiter space is preserved when space is not
 2920        // on existing delimiter.
 2921        cx.set_state(indoc! {"
 2922        /**
 2923 2924         */
 2925    "});
 2926        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2927        cx.assert_editor_state(indoc! {"
 2928        /**
 2929         *
 2930         * ˇ
 2931         */
 2932    "});
 2933        // Ensure that if suffix exists on same line after cursor it
 2934        // doesn't add extra new line if prefix is not on same line.
 2935        cx.set_state(indoc! {"
 2936        /**
 2937        ˇ*/
 2938    "});
 2939        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2940        cx.assert_editor_state(indoc! {"
 2941        /**
 2942
 2943        ˇ*/
 2944    "});
 2945        // Ensure that it detects suffix after existing prefix.
 2946        cx.set_state(indoc! {"
 2947        /**ˇ/
 2948    "});
 2949        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2950        cx.assert_editor_state(indoc! {"
 2951        /**
 2952        ˇ/
 2953    "});
 2954        // Ensure that if suffix exists on same line before
 2955        // cursor it does not add comment prefix.
 2956        cx.set_state(indoc! {"
 2957        /** */ˇ
 2958    "});
 2959        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2960        cx.assert_editor_state(indoc! {"
 2961        /** */
 2962        ˇ
 2963    "});
 2964        // Ensure that if suffix exists on same line before
 2965        // cursor it does not add comment prefix.
 2966        cx.set_state(indoc! {"
 2967        /**
 2968         *
 2969         */ˇ
 2970    "});
 2971        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2972        cx.assert_editor_state(indoc! {"
 2973        /**
 2974         *
 2975         */
 2976         ˇ
 2977    "});
 2978    }
 2979    // Ensure that comment continuations can be disabled.
 2980    update_test_language_settings(cx, |settings| {
 2981        settings.defaults.extend_comment_on_newline = Some(false);
 2982    });
 2983    let mut cx = EditorTestContext::new(cx).await;
 2984    cx.set_state(indoc! {"
 2985        /**ˇ
 2986    "});
 2987    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2988    cx.assert_editor_state(indoc! {"
 2989        /**
 2990        ˇ
 2991    "});
 2992}
 2993
 2994#[gpui::test]
 2995fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2996    init_test(cx, |_| {});
 2997
 2998    let editor = cx.add_window(|window, cx| {
 2999        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 3000        let mut editor = build_editor(buffer.clone(), window, cx);
 3001        editor.change_selections(None, window, cx, |s| {
 3002            s.select_ranges([3..4, 11..12, 19..20])
 3003        });
 3004        editor
 3005    });
 3006
 3007    _ = editor.update(cx, |editor, window, cx| {
 3008        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 3009        editor.buffer.update(cx, |buffer, cx| {
 3010            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 3011            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 3012        });
 3013        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 3014
 3015        editor.insert("Z", window, cx);
 3016        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 3017
 3018        // The selections are moved after the inserted characters
 3019        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 3020    });
 3021}
 3022
 3023#[gpui::test]
 3024async fn test_tab(cx: &mut TestAppContext) {
 3025    init_test(cx, |settings| {
 3026        settings.defaults.tab_size = NonZeroU32::new(3)
 3027    });
 3028
 3029    let mut cx = EditorTestContext::new(cx).await;
 3030    cx.set_state(indoc! {"
 3031        ˇabˇc
 3032        ˇ🏀ˇ🏀ˇefg
 3033 3034    "});
 3035    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3036    cx.assert_editor_state(indoc! {"
 3037           ˇab ˇc
 3038           ˇ🏀  ˇ🏀  ˇefg
 3039        d  ˇ
 3040    "});
 3041
 3042    cx.set_state(indoc! {"
 3043        a
 3044        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3045    "});
 3046    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3047    cx.assert_editor_state(indoc! {"
 3048        a
 3049           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 3050    "});
 3051}
 3052
 3053#[gpui::test]
 3054async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 3055    init_test(cx, |_| {});
 3056
 3057    let mut cx = EditorTestContext::new(cx).await;
 3058    let language = Arc::new(
 3059        Language::new(
 3060            LanguageConfig::default(),
 3061            Some(tree_sitter_rust::LANGUAGE.into()),
 3062        )
 3063        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 3064        .unwrap(),
 3065    );
 3066    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3067
 3068    // test when all cursors are not at suggested indent
 3069    // then simply move to their suggested indent location
 3070    cx.set_state(indoc! {"
 3071        const a: B = (
 3072            c(
 3073        ˇ
 3074        ˇ    )
 3075        );
 3076    "});
 3077    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        const a: B = (
 3080            c(
 3081                ˇ
 3082            ˇ)
 3083        );
 3084    "});
 3085
 3086    // test cursor already at suggested indent not moving when
 3087    // other cursors are yet to reach their suggested indents
 3088    cx.set_state(indoc! {"
 3089        ˇ
 3090        const a: B = (
 3091            c(
 3092                d(
 3093        ˇ
 3094                )
 3095        ˇ
 3096        ˇ    )
 3097        );
 3098    "});
 3099    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3100    cx.assert_editor_state(indoc! {"
 3101        ˇ
 3102        const a: B = (
 3103            c(
 3104                d(
 3105                    ˇ
 3106                )
 3107                ˇ
 3108            ˇ)
 3109        );
 3110    "});
 3111    // test when all cursors are at suggested indent then tab is inserted
 3112    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3113    cx.assert_editor_state(indoc! {"
 3114            ˇ
 3115        const a: B = (
 3116            c(
 3117                d(
 3118                        ˇ
 3119                )
 3120                    ˇ
 3121                ˇ)
 3122        );
 3123    "});
 3124
 3125    // test when current indent is less than suggested indent,
 3126    // we adjust line to match suggested indent and move cursor to it
 3127    //
 3128    // when no other cursor is at word boundary, all of them should move
 3129    cx.set_state(indoc! {"
 3130        const a: B = (
 3131            c(
 3132                d(
 3133        ˇ
 3134        ˇ   )
 3135        ˇ   )
 3136        );
 3137    "});
 3138    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3139    cx.assert_editor_state(indoc! {"
 3140        const a: B = (
 3141            c(
 3142                d(
 3143                    ˇ
 3144                ˇ)
 3145            ˇ)
 3146        );
 3147    "});
 3148
 3149    // test when current indent is less than suggested indent,
 3150    // we adjust line to match suggested indent and move cursor to it
 3151    //
 3152    // when some other cursor is at word boundary, it should not move
 3153    cx.set_state(indoc! {"
 3154        const a: B = (
 3155            c(
 3156                d(
 3157        ˇ
 3158        ˇ   )
 3159           ˇ)
 3160        );
 3161    "});
 3162    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3163    cx.assert_editor_state(indoc! {"
 3164        const a: B = (
 3165            c(
 3166                d(
 3167                    ˇ
 3168                ˇ)
 3169            ˇ)
 3170        );
 3171    "});
 3172
 3173    // test when current indent is more than suggested indent,
 3174    // we just move cursor to current indent instead of suggested indent
 3175    //
 3176    // when no other cursor is at word boundary, all of them should move
 3177    cx.set_state(indoc! {"
 3178        const a: B = (
 3179            c(
 3180                d(
 3181        ˇ
 3182        ˇ                )
 3183        ˇ   )
 3184        );
 3185    "});
 3186    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3187    cx.assert_editor_state(indoc! {"
 3188        const a: B = (
 3189            c(
 3190                d(
 3191                    ˇ
 3192                        ˇ)
 3193            ˇ)
 3194        );
 3195    "});
 3196    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3197    cx.assert_editor_state(indoc! {"
 3198        const a: B = (
 3199            c(
 3200                d(
 3201                        ˇ
 3202                            ˇ)
 3203                ˇ)
 3204        );
 3205    "});
 3206
 3207    // test when current indent is more than suggested indent,
 3208    // we just move cursor to current indent instead of suggested indent
 3209    //
 3210    // when some other cursor is at word boundary, it doesn't move
 3211    cx.set_state(indoc! {"
 3212        const a: B = (
 3213            c(
 3214                d(
 3215        ˇ
 3216        ˇ                )
 3217            ˇ)
 3218        );
 3219    "});
 3220    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3221    cx.assert_editor_state(indoc! {"
 3222        const a: B = (
 3223            c(
 3224                d(
 3225                    ˇ
 3226                        ˇ)
 3227            ˇ)
 3228        );
 3229    "});
 3230
 3231    // handle auto-indent when there are multiple cursors on the same line
 3232    cx.set_state(indoc! {"
 3233        const a: B = (
 3234            c(
 3235        ˇ    ˇ
 3236        ˇ    )
 3237        );
 3238    "});
 3239    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3240    cx.assert_editor_state(indoc! {"
 3241        const a: B = (
 3242            c(
 3243                ˇ
 3244            ˇ)
 3245        );
 3246    "});
 3247}
 3248
 3249#[gpui::test]
 3250async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3251    init_test(cx, |settings| {
 3252        settings.defaults.tab_size = NonZeroU32::new(3)
 3253    });
 3254
 3255    let mut cx = EditorTestContext::new(cx).await;
 3256    cx.set_state(indoc! {"
 3257         ˇ
 3258        \t ˇ
 3259        \t  ˇ
 3260        \t   ˇ
 3261         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3262    "});
 3263
 3264    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3265    cx.assert_editor_state(indoc! {"
 3266           ˇ
 3267        \t   ˇ
 3268        \t   ˇ
 3269        \t      ˇ
 3270         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3271    "});
 3272}
 3273
 3274#[gpui::test]
 3275async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3276    init_test(cx, |settings| {
 3277        settings.defaults.tab_size = NonZeroU32::new(4)
 3278    });
 3279
 3280    let language = Arc::new(
 3281        Language::new(
 3282            LanguageConfig::default(),
 3283            Some(tree_sitter_rust::LANGUAGE.into()),
 3284        )
 3285        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3286        .unwrap(),
 3287    );
 3288
 3289    let mut cx = EditorTestContext::new(cx).await;
 3290    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3291    cx.set_state(indoc! {"
 3292        fn a() {
 3293            if b {
 3294        \t ˇc
 3295            }
 3296        }
 3297    "});
 3298
 3299    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3300    cx.assert_editor_state(indoc! {"
 3301        fn a() {
 3302            if b {
 3303                ˇc
 3304            }
 3305        }
 3306    "});
 3307}
 3308
 3309#[gpui::test]
 3310async fn test_indent_outdent(cx: &mut TestAppContext) {
 3311    init_test(cx, |settings| {
 3312        settings.defaults.tab_size = NonZeroU32::new(4);
 3313    });
 3314
 3315    let mut cx = EditorTestContext::new(cx).await;
 3316
 3317    cx.set_state(indoc! {"
 3318          «oneˇ» «twoˇ»
 3319        three
 3320         four
 3321    "});
 3322    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3323    cx.assert_editor_state(indoc! {"
 3324            «oneˇ» «twoˇ»
 3325        three
 3326         four
 3327    "});
 3328
 3329    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3330    cx.assert_editor_state(indoc! {"
 3331        «oneˇ» «twoˇ»
 3332        three
 3333         four
 3334    "});
 3335
 3336    // select across line ending
 3337    cx.set_state(indoc! {"
 3338        one two
 3339        t«hree
 3340        ˇ» four
 3341    "});
 3342    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3343    cx.assert_editor_state(indoc! {"
 3344        one two
 3345            t«hree
 3346        ˇ» four
 3347    "});
 3348
 3349    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3350    cx.assert_editor_state(indoc! {"
 3351        one two
 3352        t«hree
 3353        ˇ» four
 3354    "});
 3355
 3356    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3357    cx.set_state(indoc! {"
 3358        one two
 3359        ˇthree
 3360            four
 3361    "});
 3362    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3363    cx.assert_editor_state(indoc! {"
 3364        one two
 3365            ˇthree
 3366            four
 3367    "});
 3368
 3369    cx.set_state(indoc! {"
 3370        one two
 3371        ˇ    three
 3372            four
 3373    "});
 3374    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3375    cx.assert_editor_state(indoc! {"
 3376        one two
 3377        ˇthree
 3378            four
 3379    "});
 3380}
 3381
 3382#[gpui::test]
 3383async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3384    init_test(cx, |settings| {
 3385        settings.defaults.hard_tabs = Some(true);
 3386    });
 3387
 3388    let mut cx = EditorTestContext::new(cx).await;
 3389
 3390    // select two ranges on one line
 3391    cx.set_state(indoc! {"
 3392        «oneˇ» «twoˇ»
 3393        three
 3394        four
 3395    "});
 3396    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3397    cx.assert_editor_state(indoc! {"
 3398        \t«oneˇ» «twoˇ»
 3399        three
 3400        four
 3401    "});
 3402    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3403    cx.assert_editor_state(indoc! {"
 3404        \t\t«oneˇ» «twoˇ»
 3405        three
 3406        four
 3407    "});
 3408    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3409    cx.assert_editor_state(indoc! {"
 3410        \t«oneˇ» «twoˇ»
 3411        three
 3412        four
 3413    "});
 3414    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3415    cx.assert_editor_state(indoc! {"
 3416        «oneˇ» «twoˇ»
 3417        three
 3418        four
 3419    "});
 3420
 3421    // select across a line ending
 3422    cx.set_state(indoc! {"
 3423        one two
 3424        t«hree
 3425        ˇ»four
 3426    "});
 3427    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3428    cx.assert_editor_state(indoc! {"
 3429        one two
 3430        \tt«hree
 3431        ˇ»four
 3432    "});
 3433    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3434    cx.assert_editor_state(indoc! {"
 3435        one two
 3436        \t\tt«hree
 3437        ˇ»four
 3438    "});
 3439    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        one two
 3442        \tt«hree
 3443        ˇ»four
 3444    "});
 3445    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3446    cx.assert_editor_state(indoc! {"
 3447        one two
 3448        t«hree
 3449        ˇ»four
 3450    "});
 3451
 3452    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3453    cx.set_state(indoc! {"
 3454        one two
 3455        ˇthree
 3456        four
 3457    "});
 3458    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3459    cx.assert_editor_state(indoc! {"
 3460        one two
 3461        ˇthree
 3462        four
 3463    "});
 3464    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3465    cx.assert_editor_state(indoc! {"
 3466        one two
 3467        \tˇthree
 3468        four
 3469    "});
 3470    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3471    cx.assert_editor_state(indoc! {"
 3472        one two
 3473        ˇthree
 3474        four
 3475    "});
 3476}
 3477
 3478#[gpui::test]
 3479fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3480    init_test(cx, |settings| {
 3481        settings.languages.extend([
 3482            (
 3483                "TOML".into(),
 3484                LanguageSettingsContent {
 3485                    tab_size: NonZeroU32::new(2),
 3486                    ..Default::default()
 3487                },
 3488            ),
 3489            (
 3490                "Rust".into(),
 3491                LanguageSettingsContent {
 3492                    tab_size: NonZeroU32::new(4),
 3493                    ..Default::default()
 3494                },
 3495            ),
 3496        ]);
 3497    });
 3498
 3499    let toml_language = Arc::new(Language::new(
 3500        LanguageConfig {
 3501            name: "TOML".into(),
 3502            ..Default::default()
 3503        },
 3504        None,
 3505    ));
 3506    let rust_language = Arc::new(Language::new(
 3507        LanguageConfig {
 3508            name: "Rust".into(),
 3509            ..Default::default()
 3510        },
 3511        None,
 3512    ));
 3513
 3514    let toml_buffer =
 3515        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3516    let rust_buffer =
 3517        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3518    let multibuffer = cx.new(|cx| {
 3519        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3520        multibuffer.push_excerpts(
 3521            toml_buffer.clone(),
 3522            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3523            cx,
 3524        );
 3525        multibuffer.push_excerpts(
 3526            rust_buffer.clone(),
 3527            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3528            cx,
 3529        );
 3530        multibuffer
 3531    });
 3532
 3533    cx.add_window(|window, cx| {
 3534        let mut editor = build_editor(multibuffer, window, cx);
 3535
 3536        assert_eq!(
 3537            editor.text(cx),
 3538            indoc! {"
 3539                a = 1
 3540                b = 2
 3541
 3542                const c: usize = 3;
 3543            "}
 3544        );
 3545
 3546        select_ranges(
 3547            &mut editor,
 3548            indoc! {"
 3549                «aˇ» = 1
 3550                b = 2
 3551
 3552                «const c:ˇ» usize = 3;
 3553            "},
 3554            window,
 3555            cx,
 3556        );
 3557
 3558        editor.tab(&Tab, window, cx);
 3559        assert_text_with_selections(
 3560            &mut editor,
 3561            indoc! {"
 3562                  «aˇ» = 1
 3563                b = 2
 3564
 3565                    «const c:ˇ» usize = 3;
 3566            "},
 3567            cx,
 3568        );
 3569        editor.backtab(&Backtab, window, cx);
 3570        assert_text_with_selections(
 3571            &mut editor,
 3572            indoc! {"
 3573                «aˇ» = 1
 3574                b = 2
 3575
 3576                «const c:ˇ» usize = 3;
 3577            "},
 3578            cx,
 3579        );
 3580
 3581        editor
 3582    });
 3583}
 3584
 3585#[gpui::test]
 3586async fn test_backspace(cx: &mut TestAppContext) {
 3587    init_test(cx, |_| {});
 3588
 3589    let mut cx = EditorTestContext::new(cx).await;
 3590
 3591    // Basic backspace
 3592    cx.set_state(indoc! {"
 3593        onˇe two three
 3594        fou«rˇ» five six
 3595        seven «ˇeight nine
 3596        »ten
 3597    "});
 3598    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3599    cx.assert_editor_state(indoc! {"
 3600        oˇe two three
 3601        fouˇ five six
 3602        seven ˇten
 3603    "});
 3604
 3605    // Test backspace inside and around indents
 3606    cx.set_state(indoc! {"
 3607        zero
 3608            ˇone
 3609                ˇtwo
 3610            ˇ ˇ ˇ  three
 3611        ˇ  ˇ  four
 3612    "});
 3613    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3614    cx.assert_editor_state(indoc! {"
 3615        zero
 3616        ˇone
 3617            ˇtwo
 3618        ˇ  threeˇ  four
 3619    "});
 3620}
 3621
 3622#[gpui::test]
 3623async fn test_delete(cx: &mut TestAppContext) {
 3624    init_test(cx, |_| {});
 3625
 3626    let mut cx = EditorTestContext::new(cx).await;
 3627    cx.set_state(indoc! {"
 3628        onˇe two three
 3629        fou«rˇ» five six
 3630        seven «ˇeight nine
 3631        »ten
 3632    "});
 3633    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3634    cx.assert_editor_state(indoc! {"
 3635        onˇ two three
 3636        fouˇ five six
 3637        seven ˇten
 3638    "});
 3639}
 3640
 3641#[gpui::test]
 3642fn test_delete_line(cx: &mut TestAppContext) {
 3643    init_test(cx, |_| {});
 3644
 3645    let editor = cx.add_window(|window, cx| {
 3646        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3647        build_editor(buffer, window, cx)
 3648    });
 3649    _ = editor.update(cx, |editor, window, cx| {
 3650        editor.change_selections(None, window, cx, |s| {
 3651            s.select_display_ranges([
 3652                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3653                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3654                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3655            ])
 3656        });
 3657        editor.delete_line(&DeleteLine, window, cx);
 3658        assert_eq!(editor.display_text(cx), "ghi");
 3659        assert_eq!(
 3660            editor.selections.display_ranges(cx),
 3661            vec![
 3662                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3663                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3664            ]
 3665        );
 3666    });
 3667
 3668    let editor = cx.add_window(|window, cx| {
 3669        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3670        build_editor(buffer, window, cx)
 3671    });
 3672    _ = editor.update(cx, |editor, window, cx| {
 3673        editor.change_selections(None, window, cx, |s| {
 3674            s.select_display_ranges([
 3675                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3676            ])
 3677        });
 3678        editor.delete_line(&DeleteLine, window, cx);
 3679        assert_eq!(editor.display_text(cx), "ghi\n");
 3680        assert_eq!(
 3681            editor.selections.display_ranges(cx),
 3682            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3683        );
 3684    });
 3685}
 3686
 3687#[gpui::test]
 3688fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3689    init_test(cx, |_| {});
 3690
 3691    cx.add_window(|window, cx| {
 3692        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3693        let mut editor = build_editor(buffer.clone(), window, cx);
 3694        let buffer = buffer.read(cx).as_singleton().unwrap();
 3695
 3696        assert_eq!(
 3697            editor.selections.ranges::<Point>(cx),
 3698            &[Point::new(0, 0)..Point::new(0, 0)]
 3699        );
 3700
 3701        // When on single line, replace newline at end by space
 3702        editor.join_lines(&JoinLines, window, cx);
 3703        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3704        assert_eq!(
 3705            editor.selections.ranges::<Point>(cx),
 3706            &[Point::new(0, 3)..Point::new(0, 3)]
 3707        );
 3708
 3709        // When multiple lines are selected, remove newlines that are spanned by the selection
 3710        editor.change_selections(None, window, cx, |s| {
 3711            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3712        });
 3713        editor.join_lines(&JoinLines, window, cx);
 3714        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3715        assert_eq!(
 3716            editor.selections.ranges::<Point>(cx),
 3717            &[Point::new(0, 11)..Point::new(0, 11)]
 3718        );
 3719
 3720        // Undo should be transactional
 3721        editor.undo(&Undo, window, cx);
 3722        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3723        assert_eq!(
 3724            editor.selections.ranges::<Point>(cx),
 3725            &[Point::new(0, 5)..Point::new(2, 2)]
 3726        );
 3727
 3728        // When joining an empty line don't insert a space
 3729        editor.change_selections(None, window, cx, |s| {
 3730            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3731        });
 3732        editor.join_lines(&JoinLines, window, cx);
 3733        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3734        assert_eq!(
 3735            editor.selections.ranges::<Point>(cx),
 3736            [Point::new(2, 3)..Point::new(2, 3)]
 3737        );
 3738
 3739        // We can remove trailing newlines
 3740        editor.join_lines(&JoinLines, window, cx);
 3741        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3742        assert_eq!(
 3743            editor.selections.ranges::<Point>(cx),
 3744            [Point::new(2, 3)..Point::new(2, 3)]
 3745        );
 3746
 3747        // We don't blow up on the last line
 3748        editor.join_lines(&JoinLines, window, cx);
 3749        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3750        assert_eq!(
 3751            editor.selections.ranges::<Point>(cx),
 3752            [Point::new(2, 3)..Point::new(2, 3)]
 3753        );
 3754
 3755        // reset to test indentation
 3756        editor.buffer.update(cx, |buffer, cx| {
 3757            buffer.edit(
 3758                [
 3759                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3760                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3761                ],
 3762                None,
 3763                cx,
 3764            )
 3765        });
 3766
 3767        // We remove any leading spaces
 3768        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3769        editor.change_selections(None, window, cx, |s| {
 3770            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3771        });
 3772        editor.join_lines(&JoinLines, window, cx);
 3773        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3774
 3775        // We don't insert a space for a line containing only spaces
 3776        editor.join_lines(&JoinLines, window, cx);
 3777        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3778
 3779        // We ignore any leading tabs
 3780        editor.join_lines(&JoinLines, window, cx);
 3781        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3782
 3783        editor
 3784    });
 3785}
 3786
 3787#[gpui::test]
 3788fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3789    init_test(cx, |_| {});
 3790
 3791    cx.add_window(|window, cx| {
 3792        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3793        let mut editor = build_editor(buffer.clone(), window, cx);
 3794        let buffer = buffer.read(cx).as_singleton().unwrap();
 3795
 3796        editor.change_selections(None, window, cx, |s| {
 3797            s.select_ranges([
 3798                Point::new(0, 2)..Point::new(1, 1),
 3799                Point::new(1, 2)..Point::new(1, 2),
 3800                Point::new(3, 1)..Point::new(3, 2),
 3801            ])
 3802        });
 3803
 3804        editor.join_lines(&JoinLines, window, cx);
 3805        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3806
 3807        assert_eq!(
 3808            editor.selections.ranges::<Point>(cx),
 3809            [
 3810                Point::new(0, 7)..Point::new(0, 7),
 3811                Point::new(1, 3)..Point::new(1, 3)
 3812            ]
 3813        );
 3814        editor
 3815    });
 3816}
 3817
 3818#[gpui::test]
 3819async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3820    init_test(cx, |_| {});
 3821
 3822    let mut cx = EditorTestContext::new(cx).await;
 3823
 3824    let diff_base = r#"
 3825        Line 0
 3826        Line 1
 3827        Line 2
 3828        Line 3
 3829        "#
 3830    .unindent();
 3831
 3832    cx.set_state(
 3833        &r#"
 3834        ˇLine 0
 3835        Line 1
 3836        Line 2
 3837        Line 3
 3838        "#
 3839        .unindent(),
 3840    );
 3841
 3842    cx.set_head_text(&diff_base);
 3843    executor.run_until_parked();
 3844
 3845    // Join lines
 3846    cx.update_editor(|editor, window, cx| {
 3847        editor.join_lines(&JoinLines, window, cx);
 3848    });
 3849    executor.run_until_parked();
 3850
 3851    cx.assert_editor_state(
 3852        &r#"
 3853        Line 0ˇ Line 1
 3854        Line 2
 3855        Line 3
 3856        "#
 3857        .unindent(),
 3858    );
 3859    // Join again
 3860    cx.update_editor(|editor, window, cx| {
 3861        editor.join_lines(&JoinLines, window, cx);
 3862    });
 3863    executor.run_until_parked();
 3864
 3865    cx.assert_editor_state(
 3866        &r#"
 3867        Line 0 Line 1ˇ Line 2
 3868        Line 3
 3869        "#
 3870        .unindent(),
 3871    );
 3872}
 3873
 3874#[gpui::test]
 3875async fn test_custom_newlines_cause_no_false_positive_diffs(
 3876    executor: BackgroundExecutor,
 3877    cx: &mut TestAppContext,
 3878) {
 3879    init_test(cx, |_| {});
 3880    let mut cx = EditorTestContext::new(cx).await;
 3881    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3882    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3883    executor.run_until_parked();
 3884
 3885    cx.update_editor(|editor, window, cx| {
 3886        let snapshot = editor.snapshot(window, cx);
 3887        assert_eq!(
 3888            snapshot
 3889                .buffer_snapshot
 3890                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3891                .collect::<Vec<_>>(),
 3892            Vec::new(),
 3893            "Should not have any diffs for files with custom newlines"
 3894        );
 3895    });
 3896}
 3897
 3898#[gpui::test]
 3899async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3900    init_test(cx, |_| {});
 3901
 3902    let mut cx = EditorTestContext::new(cx).await;
 3903
 3904    // Test sort_lines_case_insensitive()
 3905    cx.set_state(indoc! {"
 3906        «z
 3907        y
 3908        x
 3909        Z
 3910        Y
 3911        Xˇ»
 3912    "});
 3913    cx.update_editor(|e, window, cx| {
 3914        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3915    });
 3916    cx.assert_editor_state(indoc! {"
 3917        «x
 3918        X
 3919        y
 3920        Y
 3921        z
 3922        Zˇ»
 3923    "});
 3924
 3925    // Test reverse_lines()
 3926    cx.set_state(indoc! {"
 3927        «5
 3928        4
 3929        3
 3930        2
 3931        1ˇ»
 3932    "});
 3933    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3934    cx.assert_editor_state(indoc! {"
 3935        «1
 3936        2
 3937        3
 3938        4
 3939        5ˇ»
 3940    "});
 3941
 3942    // Skip testing shuffle_line()
 3943
 3944    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3945    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3946
 3947    // Don't manipulate when cursor is on single line, but expand the selection
 3948    cx.set_state(indoc! {"
 3949        ddˇdd
 3950        ccc
 3951        bb
 3952        a
 3953    "});
 3954    cx.update_editor(|e, window, cx| {
 3955        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3956    });
 3957    cx.assert_editor_state(indoc! {"
 3958        «ddddˇ»
 3959        ccc
 3960        bb
 3961        a
 3962    "});
 3963
 3964    // Basic manipulate case
 3965    // Start selection moves to column 0
 3966    // End of selection shrinks to fit shorter line
 3967    cx.set_state(indoc! {"
 3968        dd«d
 3969        ccc
 3970        bb
 3971        aaaaaˇ»
 3972    "});
 3973    cx.update_editor(|e, window, cx| {
 3974        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3975    });
 3976    cx.assert_editor_state(indoc! {"
 3977        «aaaaa
 3978        bb
 3979        ccc
 3980        dddˇ»
 3981    "});
 3982
 3983    // Manipulate case with newlines
 3984    cx.set_state(indoc! {"
 3985        dd«d
 3986        ccc
 3987
 3988        bb
 3989        aaaaa
 3990
 3991        ˇ»
 3992    "});
 3993    cx.update_editor(|e, window, cx| {
 3994        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3995    });
 3996    cx.assert_editor_state(indoc! {"
 3997        «
 3998
 3999        aaaaa
 4000        bb
 4001        ccc
 4002        dddˇ»
 4003
 4004    "});
 4005
 4006    // Adding new line
 4007    cx.set_state(indoc! {"
 4008        aa«a
 4009        bbˇ»b
 4010    "});
 4011    cx.update_editor(|e, window, cx| {
 4012        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 4013    });
 4014    cx.assert_editor_state(indoc! {"
 4015        «aaa
 4016        bbb
 4017        added_lineˇ»
 4018    "});
 4019
 4020    // Removing line
 4021    cx.set_state(indoc! {"
 4022        aa«a
 4023        bbbˇ»
 4024    "});
 4025    cx.update_editor(|e, window, cx| {
 4026        e.manipulate_lines(window, cx, |lines| {
 4027            lines.pop();
 4028        })
 4029    });
 4030    cx.assert_editor_state(indoc! {"
 4031        «aaaˇ»
 4032    "});
 4033
 4034    // Removing all lines
 4035    cx.set_state(indoc! {"
 4036        aa«a
 4037        bbbˇ»
 4038    "});
 4039    cx.update_editor(|e, window, cx| {
 4040        e.manipulate_lines(window, cx, |lines| {
 4041            lines.drain(..);
 4042        })
 4043    });
 4044    cx.assert_editor_state(indoc! {"
 4045        ˇ
 4046    "});
 4047}
 4048
 4049#[gpui::test]
 4050async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 4051    init_test(cx, |_| {});
 4052
 4053    let mut cx = EditorTestContext::new(cx).await;
 4054
 4055    // Consider continuous selection as single selection
 4056    cx.set_state(indoc! {"
 4057        Aaa«aa
 4058        cˇ»c«c
 4059        bb
 4060        aaaˇ»aa
 4061    "});
 4062    cx.update_editor(|e, window, cx| {
 4063        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4064    });
 4065    cx.assert_editor_state(indoc! {"
 4066        «Aaaaa
 4067        ccc
 4068        bb
 4069        aaaaaˇ»
 4070    "});
 4071
 4072    cx.set_state(indoc! {"
 4073        Aaa«aa
 4074        cˇ»c«c
 4075        bb
 4076        aaaˇ»aa
 4077    "});
 4078    cx.update_editor(|e, window, cx| {
 4079        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4080    });
 4081    cx.assert_editor_state(indoc! {"
 4082        «Aaaaa
 4083        ccc
 4084        bbˇ»
 4085    "});
 4086
 4087    // Consider non continuous selection as distinct dedup operations
 4088    cx.set_state(indoc! {"
 4089        «aaaaa
 4090        bb
 4091        aaaaa
 4092        aaaaaˇ»
 4093
 4094        aaa«aaˇ»
 4095    "});
 4096    cx.update_editor(|e, window, cx| {
 4097        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4098    });
 4099    cx.assert_editor_state(indoc! {"
 4100        «aaaaa
 4101        bbˇ»
 4102
 4103        «aaaaaˇ»
 4104    "});
 4105}
 4106
 4107#[gpui::test]
 4108async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4109    init_test(cx, |_| {});
 4110
 4111    let mut cx = EditorTestContext::new(cx).await;
 4112
 4113    cx.set_state(indoc! {"
 4114        «Aaa
 4115        aAa
 4116        Aaaˇ»
 4117    "});
 4118    cx.update_editor(|e, window, cx| {
 4119        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4120    });
 4121    cx.assert_editor_state(indoc! {"
 4122        «Aaa
 4123        aAaˇ»
 4124    "});
 4125
 4126    cx.set_state(indoc! {"
 4127        «Aaa
 4128        aAa
 4129        aaAˇ»
 4130    "});
 4131    cx.update_editor(|e, window, cx| {
 4132        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4133    });
 4134    cx.assert_editor_state(indoc! {"
 4135        «Aaaˇ»
 4136    "});
 4137}
 4138
 4139#[gpui::test]
 4140async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4141    init_test(cx, |_| {});
 4142
 4143    let mut cx = EditorTestContext::new(cx).await;
 4144
 4145    // Manipulate with multiple selections on a single line
 4146    cx.set_state(indoc! {"
 4147        dd«dd
 4148        cˇ»c«c
 4149        bb
 4150        aaaˇ»aa
 4151    "});
 4152    cx.update_editor(|e, window, cx| {
 4153        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4154    });
 4155    cx.assert_editor_state(indoc! {"
 4156        «aaaaa
 4157        bb
 4158        ccc
 4159        ddddˇ»
 4160    "});
 4161
 4162    // Manipulate with multiple disjoin selections
 4163    cx.set_state(indoc! {"
 4164 4165        4
 4166        3
 4167        2
 4168        1ˇ»
 4169
 4170        dd«dd
 4171        ccc
 4172        bb
 4173        aaaˇ»aa
 4174    "});
 4175    cx.update_editor(|e, window, cx| {
 4176        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4177    });
 4178    cx.assert_editor_state(indoc! {"
 4179        «1
 4180        2
 4181        3
 4182        4
 4183        5ˇ»
 4184
 4185        «aaaaa
 4186        bb
 4187        ccc
 4188        ddddˇ»
 4189    "});
 4190
 4191    // Adding lines on each selection
 4192    cx.set_state(indoc! {"
 4193 4194        1ˇ»
 4195
 4196        bb«bb
 4197        aaaˇ»aa
 4198    "});
 4199    cx.update_editor(|e, window, cx| {
 4200        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4201    });
 4202    cx.assert_editor_state(indoc! {"
 4203        «2
 4204        1
 4205        added lineˇ»
 4206
 4207        «bbbb
 4208        aaaaa
 4209        added lineˇ»
 4210    "});
 4211
 4212    // Removing lines on each selection
 4213    cx.set_state(indoc! {"
 4214 4215        1ˇ»
 4216
 4217        bb«bb
 4218        aaaˇ»aa
 4219    "});
 4220    cx.update_editor(|e, window, cx| {
 4221        e.manipulate_lines(window, cx, |lines| {
 4222            lines.pop();
 4223        })
 4224    });
 4225    cx.assert_editor_state(indoc! {"
 4226        «2ˇ»
 4227
 4228        «bbbbˇ»
 4229    "});
 4230}
 4231
 4232#[gpui::test]
 4233async fn test_toggle_case(cx: &mut TestAppContext) {
 4234    init_test(cx, |_| {});
 4235
 4236    let mut cx = EditorTestContext::new(cx).await;
 4237
 4238    // If all lower case -> upper case
 4239    cx.set_state(indoc! {"
 4240        «hello worldˇ»
 4241    "});
 4242    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4243    cx.assert_editor_state(indoc! {"
 4244        «HELLO WORLDˇ»
 4245    "});
 4246
 4247    // If all upper case -> lower case
 4248    cx.set_state(indoc! {"
 4249        «HELLO WORLDˇ»
 4250    "});
 4251    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4252    cx.assert_editor_state(indoc! {"
 4253        «hello worldˇ»
 4254    "});
 4255
 4256    // If any upper case characters are identified -> lower case
 4257    // This matches JetBrains IDEs
 4258    cx.set_state(indoc! {"
 4259        «hEllo worldˇ»
 4260    "});
 4261    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4262    cx.assert_editor_state(indoc! {"
 4263        «hello worldˇ»
 4264    "});
 4265}
 4266
 4267#[gpui::test]
 4268async fn test_manipulate_text(cx: &mut TestAppContext) {
 4269    init_test(cx, |_| {});
 4270
 4271    let mut cx = EditorTestContext::new(cx).await;
 4272
 4273    // Test convert_to_upper_case()
 4274    cx.set_state(indoc! {"
 4275        «hello worldˇ»
 4276    "});
 4277    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4278    cx.assert_editor_state(indoc! {"
 4279        «HELLO WORLDˇ»
 4280    "});
 4281
 4282    // Test convert_to_lower_case()
 4283    cx.set_state(indoc! {"
 4284        «HELLO WORLDˇ»
 4285    "});
 4286    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4287    cx.assert_editor_state(indoc! {"
 4288        «hello worldˇ»
 4289    "});
 4290
 4291    // Test multiple line, single selection case
 4292    cx.set_state(indoc! {"
 4293        «The quick brown
 4294        fox jumps over
 4295        the lazy dogˇ»
 4296    "});
 4297    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4298    cx.assert_editor_state(indoc! {"
 4299        «The Quick Brown
 4300        Fox Jumps Over
 4301        The Lazy Dogˇ»
 4302    "});
 4303
 4304    // Test multiple line, single selection case
 4305    cx.set_state(indoc! {"
 4306        «The quick brown
 4307        fox jumps over
 4308        the lazy dogˇ»
 4309    "});
 4310    cx.update_editor(|e, window, cx| {
 4311        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4312    });
 4313    cx.assert_editor_state(indoc! {"
 4314        «TheQuickBrown
 4315        FoxJumpsOver
 4316        TheLazyDogˇ»
 4317    "});
 4318
 4319    // From here on out, test more complex cases of manipulate_text()
 4320
 4321    // Test no selection case - should affect words cursors are in
 4322    // Cursor at beginning, middle, and end of word
 4323    cx.set_state(indoc! {"
 4324        ˇhello big beauˇtiful worldˇ
 4325    "});
 4326    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4327    cx.assert_editor_state(indoc! {"
 4328        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4329    "});
 4330
 4331    // Test multiple selections on a single line and across multiple lines
 4332    cx.set_state(indoc! {"
 4333        «Theˇ» quick «brown
 4334        foxˇ» jumps «overˇ»
 4335        the «lazyˇ» dog
 4336    "});
 4337    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4338    cx.assert_editor_state(indoc! {"
 4339        «THEˇ» quick «BROWN
 4340        FOXˇ» jumps «OVERˇ»
 4341        the «LAZYˇ» dog
 4342    "});
 4343
 4344    // Test case where text length grows
 4345    cx.set_state(indoc! {"
 4346        «tschüߡ»
 4347    "});
 4348    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4349    cx.assert_editor_state(indoc! {"
 4350        «TSCHÜSSˇ»
 4351    "});
 4352
 4353    // Test to make sure we don't crash when text shrinks
 4354    cx.set_state(indoc! {"
 4355        aaa_bbbˇ
 4356    "});
 4357    cx.update_editor(|e, window, cx| {
 4358        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4359    });
 4360    cx.assert_editor_state(indoc! {"
 4361        «aaaBbbˇ»
 4362    "});
 4363
 4364    // Test to make sure we all aware of the fact that each word can grow and shrink
 4365    // Final selections should be aware of this fact
 4366    cx.set_state(indoc! {"
 4367        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4368    "});
 4369    cx.update_editor(|e, window, cx| {
 4370        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4371    });
 4372    cx.assert_editor_state(indoc! {"
 4373        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4374    "});
 4375
 4376    cx.set_state(indoc! {"
 4377        «hElLo, WoRld!ˇ»
 4378    "});
 4379    cx.update_editor(|e, window, cx| {
 4380        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4381    });
 4382    cx.assert_editor_state(indoc! {"
 4383        «HeLlO, wOrLD!ˇ»
 4384    "});
 4385}
 4386
 4387#[gpui::test]
 4388fn test_duplicate_line(cx: &mut TestAppContext) {
 4389    init_test(cx, |_| {});
 4390
 4391    let editor = cx.add_window(|window, cx| {
 4392        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4393        build_editor(buffer, window, cx)
 4394    });
 4395    _ = editor.update(cx, |editor, window, cx| {
 4396        editor.change_selections(None, window, cx, |s| {
 4397            s.select_display_ranges([
 4398                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4399                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4400                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4401                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4402            ])
 4403        });
 4404        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4405        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4406        assert_eq!(
 4407            editor.selections.display_ranges(cx),
 4408            vec![
 4409                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4410                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4411                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4412                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4413            ]
 4414        );
 4415    });
 4416
 4417    let editor = cx.add_window(|window, cx| {
 4418        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4419        build_editor(buffer, window, cx)
 4420    });
 4421    _ = editor.update(cx, |editor, window, cx| {
 4422        editor.change_selections(None, window, cx, |s| {
 4423            s.select_display_ranges([
 4424                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4425                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4426            ])
 4427        });
 4428        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4429        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4430        assert_eq!(
 4431            editor.selections.display_ranges(cx),
 4432            vec![
 4433                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4434                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4435            ]
 4436        );
 4437    });
 4438
 4439    // With `move_upwards` the selections stay in place, except for
 4440    // the lines inserted above them
 4441    let editor = cx.add_window(|window, cx| {
 4442        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4443        build_editor(buffer, window, cx)
 4444    });
 4445    _ = editor.update(cx, |editor, window, cx| {
 4446        editor.change_selections(None, window, cx, |s| {
 4447            s.select_display_ranges([
 4448                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4449                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4450                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4451                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4452            ])
 4453        });
 4454        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4455        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4456        assert_eq!(
 4457            editor.selections.display_ranges(cx),
 4458            vec![
 4459                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4460                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4461                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4462                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4463            ]
 4464        );
 4465    });
 4466
 4467    let editor = cx.add_window(|window, cx| {
 4468        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4469        build_editor(buffer, window, cx)
 4470    });
 4471    _ = editor.update(cx, |editor, window, cx| {
 4472        editor.change_selections(None, window, cx, |s| {
 4473            s.select_display_ranges([
 4474                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4475                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4476            ])
 4477        });
 4478        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4479        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4480        assert_eq!(
 4481            editor.selections.display_ranges(cx),
 4482            vec![
 4483                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4484                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4485            ]
 4486        );
 4487    });
 4488
 4489    let editor = cx.add_window(|window, cx| {
 4490        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4491        build_editor(buffer, window, cx)
 4492    });
 4493    _ = editor.update(cx, |editor, window, cx| {
 4494        editor.change_selections(None, window, cx, |s| {
 4495            s.select_display_ranges([
 4496                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4497                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4498            ])
 4499        });
 4500        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4501        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4502        assert_eq!(
 4503            editor.selections.display_ranges(cx),
 4504            vec![
 4505                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4506                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4507            ]
 4508        );
 4509    });
 4510}
 4511
 4512#[gpui::test]
 4513fn test_move_line_up_down(cx: &mut TestAppContext) {
 4514    init_test(cx, |_| {});
 4515
 4516    let editor = cx.add_window(|window, cx| {
 4517        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4518        build_editor(buffer, window, cx)
 4519    });
 4520    _ = editor.update(cx, |editor, window, cx| {
 4521        editor.fold_creases(
 4522            vec![
 4523                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4524                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4525                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4526            ],
 4527            true,
 4528            window,
 4529            cx,
 4530        );
 4531        editor.change_selections(None, window, cx, |s| {
 4532            s.select_display_ranges([
 4533                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4534                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4535                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4536                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4537            ])
 4538        });
 4539        assert_eq!(
 4540            editor.display_text(cx),
 4541            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4542        );
 4543
 4544        editor.move_line_up(&MoveLineUp, window, cx);
 4545        assert_eq!(
 4546            editor.display_text(cx),
 4547            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4548        );
 4549        assert_eq!(
 4550            editor.selections.display_ranges(cx),
 4551            vec![
 4552                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4553                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4554                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4555                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4556            ]
 4557        );
 4558    });
 4559
 4560    _ = editor.update(cx, |editor, window, cx| {
 4561        editor.move_line_down(&MoveLineDown, window, cx);
 4562        assert_eq!(
 4563            editor.display_text(cx),
 4564            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4565        );
 4566        assert_eq!(
 4567            editor.selections.display_ranges(cx),
 4568            vec![
 4569                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4570                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4571                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4572                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4573            ]
 4574        );
 4575    });
 4576
 4577    _ = editor.update(cx, |editor, window, cx| {
 4578        editor.move_line_down(&MoveLineDown, window, cx);
 4579        assert_eq!(
 4580            editor.display_text(cx),
 4581            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4582        );
 4583        assert_eq!(
 4584            editor.selections.display_ranges(cx),
 4585            vec![
 4586                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4587                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4588                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4589                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4590            ]
 4591        );
 4592    });
 4593
 4594    _ = editor.update(cx, |editor, window, cx| {
 4595        editor.move_line_up(&MoveLineUp, window, cx);
 4596        assert_eq!(
 4597            editor.display_text(cx),
 4598            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4599        );
 4600        assert_eq!(
 4601            editor.selections.display_ranges(cx),
 4602            vec![
 4603                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4604                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4605                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4606                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4607            ]
 4608        );
 4609    });
 4610}
 4611
 4612#[gpui::test]
 4613fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4614    init_test(cx, |_| {});
 4615
 4616    let editor = cx.add_window(|window, cx| {
 4617        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4618        build_editor(buffer, window, cx)
 4619    });
 4620    _ = editor.update(cx, |editor, window, cx| {
 4621        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4622        editor.insert_blocks(
 4623            [BlockProperties {
 4624                style: BlockStyle::Fixed,
 4625                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4626                height: Some(1),
 4627                render: Arc::new(|_| div().into_any()),
 4628                priority: 0,
 4629                render_in_minimap: true,
 4630            }],
 4631            Some(Autoscroll::fit()),
 4632            cx,
 4633        );
 4634        editor.change_selections(None, window, cx, |s| {
 4635            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4636        });
 4637        editor.move_line_down(&MoveLineDown, window, cx);
 4638    });
 4639}
 4640
 4641#[gpui::test]
 4642async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4643    init_test(cx, |_| {});
 4644
 4645    let mut cx = EditorTestContext::new(cx).await;
 4646    cx.set_state(
 4647        &"
 4648            ˇzero
 4649            one
 4650            two
 4651            three
 4652            four
 4653            five
 4654        "
 4655        .unindent(),
 4656    );
 4657
 4658    // Create a four-line block that replaces three lines of text.
 4659    cx.update_editor(|editor, window, cx| {
 4660        let snapshot = editor.snapshot(window, cx);
 4661        let snapshot = &snapshot.buffer_snapshot;
 4662        let placement = BlockPlacement::Replace(
 4663            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4664        );
 4665        editor.insert_blocks(
 4666            [BlockProperties {
 4667                placement,
 4668                height: Some(4),
 4669                style: BlockStyle::Sticky,
 4670                render: Arc::new(|_| gpui::div().into_any_element()),
 4671                priority: 0,
 4672                render_in_minimap: true,
 4673            }],
 4674            None,
 4675            cx,
 4676        );
 4677    });
 4678
 4679    // Move down so that the cursor touches the block.
 4680    cx.update_editor(|editor, window, cx| {
 4681        editor.move_down(&Default::default(), window, cx);
 4682    });
 4683    cx.assert_editor_state(
 4684        &"
 4685            zero
 4686            «one
 4687            two
 4688            threeˇ»
 4689            four
 4690            five
 4691        "
 4692        .unindent(),
 4693    );
 4694
 4695    // Move down past the block.
 4696    cx.update_editor(|editor, window, cx| {
 4697        editor.move_down(&Default::default(), window, cx);
 4698    });
 4699    cx.assert_editor_state(
 4700        &"
 4701            zero
 4702            one
 4703            two
 4704            three
 4705            ˇfour
 4706            five
 4707        "
 4708        .unindent(),
 4709    );
 4710}
 4711
 4712#[gpui::test]
 4713fn test_transpose(cx: &mut TestAppContext) {
 4714    init_test(cx, |_| {});
 4715
 4716    _ = cx.add_window(|window, cx| {
 4717        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4718        editor.set_style(EditorStyle::default(), window, cx);
 4719        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4720        editor.transpose(&Default::default(), window, cx);
 4721        assert_eq!(editor.text(cx), "bac");
 4722        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4723
 4724        editor.transpose(&Default::default(), window, cx);
 4725        assert_eq!(editor.text(cx), "bca");
 4726        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4727
 4728        editor.transpose(&Default::default(), window, cx);
 4729        assert_eq!(editor.text(cx), "bac");
 4730        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4731
 4732        editor
 4733    });
 4734
 4735    _ = cx.add_window(|window, cx| {
 4736        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4737        editor.set_style(EditorStyle::default(), window, cx);
 4738        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4739        editor.transpose(&Default::default(), window, cx);
 4740        assert_eq!(editor.text(cx), "acb\nde");
 4741        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4742
 4743        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4744        editor.transpose(&Default::default(), window, cx);
 4745        assert_eq!(editor.text(cx), "acbd\ne");
 4746        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4747
 4748        editor.transpose(&Default::default(), window, cx);
 4749        assert_eq!(editor.text(cx), "acbde\n");
 4750        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4751
 4752        editor.transpose(&Default::default(), window, cx);
 4753        assert_eq!(editor.text(cx), "acbd\ne");
 4754        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4755
 4756        editor
 4757    });
 4758
 4759    _ = cx.add_window(|window, cx| {
 4760        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4761        editor.set_style(EditorStyle::default(), window, cx);
 4762        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4763        editor.transpose(&Default::default(), window, cx);
 4764        assert_eq!(editor.text(cx), "bacd\ne");
 4765        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4766
 4767        editor.transpose(&Default::default(), window, cx);
 4768        assert_eq!(editor.text(cx), "bcade\n");
 4769        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4770
 4771        editor.transpose(&Default::default(), window, cx);
 4772        assert_eq!(editor.text(cx), "bcda\ne");
 4773        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4774
 4775        editor.transpose(&Default::default(), window, cx);
 4776        assert_eq!(editor.text(cx), "bcade\n");
 4777        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4778
 4779        editor.transpose(&Default::default(), window, cx);
 4780        assert_eq!(editor.text(cx), "bcaed\n");
 4781        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4782
 4783        editor
 4784    });
 4785
 4786    _ = cx.add_window(|window, cx| {
 4787        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4788        editor.set_style(EditorStyle::default(), window, cx);
 4789        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4790        editor.transpose(&Default::default(), window, cx);
 4791        assert_eq!(editor.text(cx), "🏀🍐✋");
 4792        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4793
 4794        editor.transpose(&Default::default(), window, cx);
 4795        assert_eq!(editor.text(cx), "🏀✋🍐");
 4796        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4797
 4798        editor.transpose(&Default::default(), window, cx);
 4799        assert_eq!(editor.text(cx), "🏀🍐✋");
 4800        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4801
 4802        editor
 4803    });
 4804}
 4805
 4806#[gpui::test]
 4807async fn test_rewrap(cx: &mut TestAppContext) {
 4808    init_test(cx, |settings| {
 4809        settings.languages.extend([
 4810            (
 4811                "Markdown".into(),
 4812                LanguageSettingsContent {
 4813                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4814                    ..Default::default()
 4815                },
 4816            ),
 4817            (
 4818                "Plain Text".into(),
 4819                LanguageSettingsContent {
 4820                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4821                    ..Default::default()
 4822                },
 4823            ),
 4824        ])
 4825    });
 4826
 4827    let mut cx = EditorTestContext::new(cx).await;
 4828
 4829    let language_with_c_comments = Arc::new(Language::new(
 4830        LanguageConfig {
 4831            line_comments: vec!["// ".into()],
 4832            ..LanguageConfig::default()
 4833        },
 4834        None,
 4835    ));
 4836    let language_with_pound_comments = Arc::new(Language::new(
 4837        LanguageConfig {
 4838            line_comments: vec!["# ".into()],
 4839            ..LanguageConfig::default()
 4840        },
 4841        None,
 4842    ));
 4843    let markdown_language = Arc::new(Language::new(
 4844        LanguageConfig {
 4845            name: "Markdown".into(),
 4846            ..LanguageConfig::default()
 4847        },
 4848        None,
 4849    ));
 4850    let language_with_doc_comments = Arc::new(Language::new(
 4851        LanguageConfig {
 4852            line_comments: vec!["// ".into(), "/// ".into()],
 4853            ..LanguageConfig::default()
 4854        },
 4855        Some(tree_sitter_rust::LANGUAGE.into()),
 4856    ));
 4857
 4858    let plaintext_language = Arc::new(Language::new(
 4859        LanguageConfig {
 4860            name: "Plain Text".into(),
 4861            ..LanguageConfig::default()
 4862        },
 4863        None,
 4864    ));
 4865
 4866    assert_rewrap(
 4867        indoc! {"
 4868            // ˇ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.
 4869        "},
 4870        indoc! {"
 4871            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4872            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4873            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4874            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4875            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4876            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4877            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4878            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4879            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4880            // porttitor id. Aliquam id accumsan eros.
 4881        "},
 4882        language_with_c_comments.clone(),
 4883        &mut cx,
 4884    );
 4885
 4886    // Test that rewrapping works inside of a selection
 4887    assert_rewrap(
 4888        indoc! {"
 4889            «// 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.ˇ»
 4890        "},
 4891        indoc! {"
 4892            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4893            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4894            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4895            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4896            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4897            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4898            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4899            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4900            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4901            // porttitor id. Aliquam id accumsan eros.ˇ»
 4902        "},
 4903        language_with_c_comments.clone(),
 4904        &mut cx,
 4905    );
 4906
 4907    // Test that cursors that expand to the same region are collapsed.
 4908    assert_rewrap(
 4909        indoc! {"
 4910            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4911            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4912            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4913            // ˇ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.
 4914        "},
 4915        indoc! {"
 4916            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4917            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4918            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4919            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4920            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4921            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4922            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4923            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4924            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4925            // porttitor id. Aliquam id accumsan eros.
 4926        "},
 4927        language_with_c_comments.clone(),
 4928        &mut cx,
 4929    );
 4930
 4931    // Test that non-contiguous selections are treated separately.
 4932    assert_rewrap(
 4933        indoc! {"
 4934            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4935            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4936            //
 4937            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4938            // ˇ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.
 4939        "},
 4940        indoc! {"
 4941            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4942            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4943            // auctor, eu lacinia sapien scelerisque.
 4944            //
 4945            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4946            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4947            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4948            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4949            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4950            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4951            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4952        "},
 4953        language_with_c_comments.clone(),
 4954        &mut cx,
 4955    );
 4956
 4957    // Test that different comment prefixes are supported.
 4958    assert_rewrap(
 4959        indoc! {"
 4960            # ˇ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.
 4961        "},
 4962        indoc! {"
 4963            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4964            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4965            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4966            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4967            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4968            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4969            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4970            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4971            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4972            # accumsan eros.
 4973        "},
 4974        language_with_pound_comments.clone(),
 4975        &mut cx,
 4976    );
 4977
 4978    // Test that rewrapping is ignored outside of comments in most languages.
 4979    assert_rewrap(
 4980        indoc! {"
 4981            /// Adds two numbers.
 4982            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4983            fn add(a: u32, b: u32) -> u32 {
 4984                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ˇ
 4985            }
 4986        "},
 4987        indoc! {"
 4988            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4989            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4990            fn add(a: u32, b: u32) -> u32 {
 4991                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ˇ
 4992            }
 4993        "},
 4994        language_with_doc_comments.clone(),
 4995        &mut cx,
 4996    );
 4997
 4998    // Test that rewrapping works in Markdown and Plain Text languages.
 4999    assert_rewrap(
 5000        indoc! {"
 5001            # Hello
 5002
 5003            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.
 5004        "},
 5005        indoc! {"
 5006            # Hello
 5007
 5008            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5009            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5010            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5011            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5012            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5013            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5014            Integer sit amet scelerisque nisi.
 5015        "},
 5016        markdown_language,
 5017        &mut cx,
 5018    );
 5019
 5020    assert_rewrap(
 5021        indoc! {"
 5022            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.
 5023        "},
 5024        indoc! {"
 5025            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 5026            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 5027            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 5028            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 5029            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 5030            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 5031            Integer sit amet scelerisque nisi.
 5032        "},
 5033        plaintext_language,
 5034        &mut cx,
 5035    );
 5036
 5037    // Test rewrapping unaligned comments in a selection.
 5038    assert_rewrap(
 5039        indoc! {"
 5040            fn foo() {
 5041                if true {
 5042            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5043            // Praesent semper egestas tellus id dignissim.ˇ»
 5044                    do_something();
 5045                } else {
 5046                    //
 5047                }
 5048            }
 5049        "},
 5050        indoc! {"
 5051            fn foo() {
 5052                if true {
 5053            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5054                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5055                    // egestas tellus id dignissim.ˇ»
 5056                    do_something();
 5057                } else {
 5058                    //
 5059                }
 5060            }
 5061        "},
 5062        language_with_doc_comments.clone(),
 5063        &mut cx,
 5064    );
 5065
 5066    assert_rewrap(
 5067        indoc! {"
 5068            fn foo() {
 5069                if true {
 5070            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 5071            // Praesent semper egestas tellus id dignissim.»
 5072                    do_something();
 5073                } else {
 5074                    //
 5075                }
 5076
 5077            }
 5078        "},
 5079        indoc! {"
 5080            fn foo() {
 5081                if true {
 5082            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 5083                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 5084                    // egestas tellus id dignissim.»
 5085                    do_something();
 5086                } else {
 5087                    //
 5088                }
 5089
 5090            }
 5091        "},
 5092        language_with_doc_comments.clone(),
 5093        &mut cx,
 5094    );
 5095
 5096    #[track_caller]
 5097    fn assert_rewrap(
 5098        unwrapped_text: &str,
 5099        wrapped_text: &str,
 5100        language: Arc<Language>,
 5101        cx: &mut EditorTestContext,
 5102    ) {
 5103        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5104        cx.set_state(unwrapped_text);
 5105        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5106        cx.assert_editor_state(wrapped_text);
 5107    }
 5108}
 5109
 5110#[gpui::test]
 5111async fn test_hard_wrap(cx: &mut TestAppContext) {
 5112    init_test(cx, |_| {});
 5113    let mut cx = EditorTestContext::new(cx).await;
 5114
 5115    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5116    cx.update_editor(|editor, _, cx| {
 5117        editor.set_hard_wrap(Some(14), cx);
 5118    });
 5119
 5120    cx.set_state(indoc!(
 5121        "
 5122        one two three ˇ
 5123        "
 5124    ));
 5125    cx.simulate_input("four");
 5126    cx.run_until_parked();
 5127
 5128    cx.assert_editor_state(indoc!(
 5129        "
 5130        one two three
 5131        fourˇ
 5132        "
 5133    ));
 5134
 5135    cx.update_editor(|editor, window, cx| {
 5136        editor.newline(&Default::default(), window, cx);
 5137    });
 5138    cx.run_until_parked();
 5139    cx.assert_editor_state(indoc!(
 5140        "
 5141        one two three
 5142        four
 5143        ˇ
 5144        "
 5145    ));
 5146
 5147    cx.simulate_input("five");
 5148    cx.run_until_parked();
 5149    cx.assert_editor_state(indoc!(
 5150        "
 5151        one two three
 5152        four
 5153        fiveˇ
 5154        "
 5155    ));
 5156
 5157    cx.update_editor(|editor, window, cx| {
 5158        editor.newline(&Default::default(), window, cx);
 5159    });
 5160    cx.run_until_parked();
 5161    cx.simulate_input("# ");
 5162    cx.run_until_parked();
 5163    cx.assert_editor_state(indoc!(
 5164        "
 5165        one two three
 5166        four
 5167        five
 5168        # ˇ
 5169        "
 5170    ));
 5171
 5172    cx.update_editor(|editor, window, cx| {
 5173        editor.newline(&Default::default(), window, cx);
 5174    });
 5175    cx.run_until_parked();
 5176    cx.assert_editor_state(indoc!(
 5177        "
 5178        one two three
 5179        four
 5180        five
 5181        #\x20
 5182 5183        "
 5184    ));
 5185
 5186    cx.simulate_input(" 6");
 5187    cx.run_until_parked();
 5188    cx.assert_editor_state(indoc!(
 5189        "
 5190        one two three
 5191        four
 5192        five
 5193        #
 5194        # 6ˇ
 5195        "
 5196    ));
 5197}
 5198
 5199#[gpui::test]
 5200async fn test_clipboard(cx: &mut TestAppContext) {
 5201    init_test(cx, |_| {});
 5202
 5203    let mut cx = EditorTestContext::new(cx).await;
 5204
 5205    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5206    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5207    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5208
 5209    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5210    cx.set_state("two ˇfour ˇsix ˇ");
 5211    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5212    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5213
 5214    // Paste again but with only two cursors. Since the number of cursors doesn't
 5215    // match the number of slices in the clipboard, the entire clipboard text
 5216    // is pasted at each cursor.
 5217    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5218    cx.update_editor(|e, window, cx| {
 5219        e.handle_input("( ", window, cx);
 5220        e.paste(&Paste, window, cx);
 5221        e.handle_input(") ", window, cx);
 5222    });
 5223    cx.assert_editor_state(
 5224        &([
 5225            "( one✅ ",
 5226            "three ",
 5227            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5228            "three ",
 5229            "five ) ˇ",
 5230        ]
 5231        .join("\n")),
 5232    );
 5233
 5234    // Cut with three selections, one of which is full-line.
 5235    cx.set_state(indoc! {"
 5236        1«2ˇ»3
 5237        4ˇ567
 5238        «8ˇ»9"});
 5239    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5240    cx.assert_editor_state(indoc! {"
 5241        1ˇ3
 5242        ˇ9"});
 5243
 5244    // Paste with three selections, noticing how the copied selection that was full-line
 5245    // gets inserted before the second cursor.
 5246    cx.set_state(indoc! {"
 5247        1ˇ3
 5248 5249        «oˇ»ne"});
 5250    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5251    cx.assert_editor_state(indoc! {"
 5252        12ˇ3
 5253        4567
 5254 5255        8ˇne"});
 5256
 5257    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5258    cx.set_state(indoc! {"
 5259        The quick brown
 5260        fox juˇmps over
 5261        the lazy dog"});
 5262    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5263    assert_eq!(
 5264        cx.read_from_clipboard()
 5265            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5266        Some("fox jumps over\n".to_string())
 5267    );
 5268
 5269    // Paste with three selections, noticing how the copied full-line selection is inserted
 5270    // before the empty selections but replaces the selection that is non-empty.
 5271    cx.set_state(indoc! {"
 5272        Tˇhe quick brown
 5273        «foˇ»x jumps over
 5274        tˇhe lazy dog"});
 5275    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5276    cx.assert_editor_state(indoc! {"
 5277        fox jumps over
 5278        Tˇhe quick brown
 5279        fox jumps over
 5280        ˇx jumps over
 5281        fox jumps over
 5282        tˇhe lazy dog"});
 5283}
 5284
 5285#[gpui::test]
 5286async fn test_copy_trim(cx: &mut TestAppContext) {
 5287    init_test(cx, |_| {});
 5288
 5289    let mut cx = EditorTestContext::new(cx).await;
 5290    cx.set_state(
 5291        r#"            «for selection in selections.iter() {
 5292            let mut start = selection.start;
 5293            let mut end = selection.end;
 5294            let is_entire_line = selection.is_empty();
 5295            if is_entire_line {
 5296                start = Point::new(start.row, 0);ˇ»
 5297                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5298            }
 5299        "#,
 5300    );
 5301    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5302    assert_eq!(
 5303        cx.read_from_clipboard()
 5304            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5305        Some(
 5306            "for selection in selections.iter() {
 5307            let mut start = selection.start;
 5308            let mut end = selection.end;
 5309            let is_entire_line = selection.is_empty();
 5310            if is_entire_line {
 5311                start = Point::new(start.row, 0);"
 5312                .to_string()
 5313        ),
 5314        "Regular copying preserves all indentation selected",
 5315    );
 5316    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5317    assert_eq!(
 5318        cx.read_from_clipboard()
 5319            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5320        Some(
 5321            "for selection in selections.iter() {
 5322let mut start = selection.start;
 5323let mut end = selection.end;
 5324let is_entire_line = selection.is_empty();
 5325if is_entire_line {
 5326    start = Point::new(start.row, 0);"
 5327                .to_string()
 5328        ),
 5329        "Copying with stripping should strip all leading whitespaces"
 5330    );
 5331
 5332    cx.set_state(
 5333        r#"       «     for selection in selections.iter() {
 5334            let mut start = selection.start;
 5335            let mut end = selection.end;
 5336            let is_entire_line = selection.is_empty();
 5337            if is_entire_line {
 5338                start = Point::new(start.row, 0);ˇ»
 5339                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5340            }
 5341        "#,
 5342    );
 5343    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5344    assert_eq!(
 5345        cx.read_from_clipboard()
 5346            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5347        Some(
 5348            "     for selection in selections.iter() {
 5349            let mut start = selection.start;
 5350            let mut end = selection.end;
 5351            let is_entire_line = selection.is_empty();
 5352            if is_entire_line {
 5353                start = Point::new(start.row, 0);"
 5354                .to_string()
 5355        ),
 5356        "Regular copying preserves all indentation selected",
 5357    );
 5358    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5359    assert_eq!(
 5360        cx.read_from_clipboard()
 5361            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5362        Some(
 5363            "for selection in selections.iter() {
 5364let mut start = selection.start;
 5365let mut end = selection.end;
 5366let is_entire_line = selection.is_empty();
 5367if is_entire_line {
 5368    start = Point::new(start.row, 0);"
 5369                .to_string()
 5370        ),
 5371        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5372    );
 5373
 5374    cx.set_state(
 5375        r#"       «ˇ     for selection in selections.iter() {
 5376            let mut start = selection.start;
 5377            let mut end = selection.end;
 5378            let is_entire_line = selection.is_empty();
 5379            if is_entire_line {
 5380                start = Point::new(start.row, 0);»
 5381                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5382            }
 5383        "#,
 5384    );
 5385    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5386    assert_eq!(
 5387        cx.read_from_clipboard()
 5388            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5389        Some(
 5390            "     for selection in selections.iter() {
 5391            let mut start = selection.start;
 5392            let mut end = selection.end;
 5393            let is_entire_line = selection.is_empty();
 5394            if is_entire_line {
 5395                start = Point::new(start.row, 0);"
 5396                .to_string()
 5397        ),
 5398        "Regular copying for reverse selection works the same",
 5399    );
 5400    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5401    assert_eq!(
 5402        cx.read_from_clipboard()
 5403            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5404        Some(
 5405            "for selection in selections.iter() {
 5406let mut start = selection.start;
 5407let mut end = selection.end;
 5408let is_entire_line = selection.is_empty();
 5409if is_entire_line {
 5410    start = Point::new(start.row, 0);"
 5411                .to_string()
 5412        ),
 5413        "Copying with stripping for reverse selection works the same"
 5414    );
 5415
 5416    cx.set_state(
 5417        r#"            for selection «in selections.iter() {
 5418            let mut start = selection.start;
 5419            let mut end = selection.end;
 5420            let is_entire_line = selection.is_empty();
 5421            if is_entire_line {
 5422                start = Point::new(start.row, 0);ˇ»
 5423                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5424            }
 5425        "#,
 5426    );
 5427    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5428    assert_eq!(
 5429        cx.read_from_clipboard()
 5430            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5431        Some(
 5432            "in selections.iter() {
 5433            let mut start = selection.start;
 5434            let mut end = selection.end;
 5435            let is_entire_line = selection.is_empty();
 5436            if is_entire_line {
 5437                start = Point::new(start.row, 0);"
 5438                .to_string()
 5439        ),
 5440        "When selecting past the indent, the copying works as usual",
 5441    );
 5442    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5443    assert_eq!(
 5444        cx.read_from_clipboard()
 5445            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5446        Some(
 5447            "in selections.iter() {
 5448            let mut start = selection.start;
 5449            let mut end = selection.end;
 5450            let is_entire_line = selection.is_empty();
 5451            if is_entire_line {
 5452                start = Point::new(start.row, 0);"
 5453                .to_string()
 5454        ),
 5455        "When selecting past the indent, nothing is trimmed"
 5456    );
 5457
 5458    cx.set_state(
 5459        r#"            «for selection in selections.iter() {
 5460            let mut start = selection.start;
 5461
 5462            let mut end = selection.end;
 5463            let is_entire_line = selection.is_empty();
 5464            if is_entire_line {
 5465                start = Point::new(start.row, 0);
 5466ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5467            }
 5468        "#,
 5469    );
 5470    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5471    assert_eq!(
 5472        cx.read_from_clipboard()
 5473            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5474        Some(
 5475            "for selection in selections.iter() {
 5476let mut start = selection.start;
 5477
 5478let mut end = selection.end;
 5479let is_entire_line = selection.is_empty();
 5480if is_entire_line {
 5481    start = Point::new(start.row, 0);
 5482"
 5483            .to_string()
 5484        ),
 5485        "Copying with stripping should ignore empty lines"
 5486    );
 5487}
 5488
 5489#[gpui::test]
 5490async fn test_paste_multiline(cx: &mut TestAppContext) {
 5491    init_test(cx, |_| {});
 5492
 5493    let mut cx = EditorTestContext::new(cx).await;
 5494    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5495
 5496    // Cut an indented block, without the leading whitespace.
 5497    cx.set_state(indoc! {"
 5498        const a: B = (
 5499            c(),
 5500            «d(
 5501                e,
 5502                f
 5503            )ˇ»
 5504        );
 5505    "});
 5506    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5507    cx.assert_editor_state(indoc! {"
 5508        const a: B = (
 5509            c(),
 5510            ˇ
 5511        );
 5512    "});
 5513
 5514    // Paste it at the same position.
 5515    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5516    cx.assert_editor_state(indoc! {"
 5517        const a: B = (
 5518            c(),
 5519            d(
 5520                e,
 5521                f
 5522 5523        );
 5524    "});
 5525
 5526    // Paste it at a line with a lower indent level.
 5527    cx.set_state(indoc! {"
 5528        ˇ
 5529        const a: B = (
 5530            c(),
 5531        );
 5532    "});
 5533    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5534    cx.assert_editor_state(indoc! {"
 5535        d(
 5536            e,
 5537            f
 5538 5539        const a: B = (
 5540            c(),
 5541        );
 5542    "});
 5543
 5544    // Cut an indented block, with the leading whitespace.
 5545    cx.set_state(indoc! {"
 5546        const a: B = (
 5547            c(),
 5548        «    d(
 5549                e,
 5550                f
 5551            )
 5552        ˇ»);
 5553    "});
 5554    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5555    cx.assert_editor_state(indoc! {"
 5556        const a: B = (
 5557            c(),
 5558        ˇ);
 5559    "});
 5560
 5561    // Paste it at the same position.
 5562    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5563    cx.assert_editor_state(indoc! {"
 5564        const a: B = (
 5565            c(),
 5566            d(
 5567                e,
 5568                f
 5569            )
 5570        ˇ);
 5571    "});
 5572
 5573    // Paste it at a line with a higher indent level.
 5574    cx.set_state(indoc! {"
 5575        const a: B = (
 5576            c(),
 5577            d(
 5578                e,
 5579 5580            )
 5581        );
 5582    "});
 5583    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5584    cx.assert_editor_state(indoc! {"
 5585        const a: B = (
 5586            c(),
 5587            d(
 5588                e,
 5589                f    d(
 5590                    e,
 5591                    f
 5592                )
 5593        ˇ
 5594            )
 5595        );
 5596    "});
 5597
 5598    // Copy an indented block, starting mid-line
 5599    cx.set_state(indoc! {"
 5600        const a: B = (
 5601            c(),
 5602            somethin«g(
 5603                e,
 5604                f
 5605            )ˇ»
 5606        );
 5607    "});
 5608    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5609
 5610    // Paste it on a line with a lower indent level
 5611    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5612    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5613    cx.assert_editor_state(indoc! {"
 5614        const a: B = (
 5615            c(),
 5616            something(
 5617                e,
 5618                f
 5619            )
 5620        );
 5621        g(
 5622            e,
 5623            f
 5624"});
 5625}
 5626
 5627#[gpui::test]
 5628async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5629    init_test(cx, |_| {});
 5630
 5631    cx.write_to_clipboard(ClipboardItem::new_string(
 5632        "    d(\n        e\n    );\n".into(),
 5633    ));
 5634
 5635    let mut cx = EditorTestContext::new(cx).await;
 5636    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5637
 5638    cx.set_state(indoc! {"
 5639        fn a() {
 5640            b();
 5641            if c() {
 5642                ˇ
 5643            }
 5644        }
 5645    "});
 5646
 5647    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5648    cx.assert_editor_state(indoc! {"
 5649        fn a() {
 5650            b();
 5651            if c() {
 5652                d(
 5653                    e
 5654                );
 5655        ˇ
 5656            }
 5657        }
 5658    "});
 5659
 5660    cx.set_state(indoc! {"
 5661        fn a() {
 5662            b();
 5663            ˇ
 5664        }
 5665    "});
 5666
 5667    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5668    cx.assert_editor_state(indoc! {"
 5669        fn a() {
 5670            b();
 5671            d(
 5672                e
 5673            );
 5674        ˇ
 5675        }
 5676    "});
 5677}
 5678
 5679#[gpui::test]
 5680fn test_select_all(cx: &mut TestAppContext) {
 5681    init_test(cx, |_| {});
 5682
 5683    let editor = cx.add_window(|window, cx| {
 5684        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5685        build_editor(buffer, window, cx)
 5686    });
 5687    _ = editor.update(cx, |editor, window, cx| {
 5688        editor.select_all(&SelectAll, window, cx);
 5689        assert_eq!(
 5690            editor.selections.display_ranges(cx),
 5691            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5692        );
 5693    });
 5694}
 5695
 5696#[gpui::test]
 5697fn test_select_line(cx: &mut TestAppContext) {
 5698    init_test(cx, |_| {});
 5699
 5700    let editor = cx.add_window(|window, cx| {
 5701        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5702        build_editor(buffer, window, cx)
 5703    });
 5704    _ = editor.update(cx, |editor, window, cx| {
 5705        editor.change_selections(None, window, cx, |s| {
 5706            s.select_display_ranges([
 5707                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5708                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5709                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5710                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5711            ])
 5712        });
 5713        editor.select_line(&SelectLine, window, cx);
 5714        assert_eq!(
 5715            editor.selections.display_ranges(cx),
 5716            vec![
 5717                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5718                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5719            ]
 5720        );
 5721    });
 5722
 5723    _ = editor.update(cx, |editor, window, cx| {
 5724        editor.select_line(&SelectLine, window, cx);
 5725        assert_eq!(
 5726            editor.selections.display_ranges(cx),
 5727            vec![
 5728                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5729                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5730            ]
 5731        );
 5732    });
 5733
 5734    _ = editor.update(cx, |editor, window, cx| {
 5735        editor.select_line(&SelectLine, window, cx);
 5736        assert_eq!(
 5737            editor.selections.display_ranges(cx),
 5738            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5739        );
 5740    });
 5741}
 5742
 5743#[gpui::test]
 5744async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5745    init_test(cx, |_| {});
 5746    let mut cx = EditorTestContext::new(cx).await;
 5747
 5748    #[track_caller]
 5749    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5750        cx.set_state(initial_state);
 5751        cx.update_editor(|e, window, cx| {
 5752            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5753        });
 5754        cx.assert_editor_state(expected_state);
 5755    }
 5756
 5757    // Selection starts and ends at the middle of lines, left-to-right
 5758    test(
 5759        &mut cx,
 5760        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5761        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5762    );
 5763    // Same thing, right-to-left
 5764    test(
 5765        &mut cx,
 5766        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5767        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5768    );
 5769
 5770    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5771    test(
 5772        &mut cx,
 5773        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5774        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5775    );
 5776    // Same thing, right-to-left
 5777    test(
 5778        &mut cx,
 5779        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5780        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5781    );
 5782
 5783    // Whole buffer, left-to-right, last line ends with newline
 5784    test(
 5785        &mut cx,
 5786        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5787        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5788    );
 5789    // Same thing, right-to-left
 5790    test(
 5791        &mut cx,
 5792        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5793        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5794    );
 5795
 5796    // Starts at the end of a line, ends at the start of another
 5797    test(
 5798        &mut cx,
 5799        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5800        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5801    );
 5802}
 5803
 5804#[gpui::test]
 5805async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5806    init_test(cx, |_| {});
 5807
 5808    let editor = cx.add_window(|window, cx| {
 5809        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5810        build_editor(buffer, window, cx)
 5811    });
 5812
 5813    // setup
 5814    _ = editor.update(cx, |editor, window, cx| {
 5815        editor.fold_creases(
 5816            vec![
 5817                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5818                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5819                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5820            ],
 5821            true,
 5822            window,
 5823            cx,
 5824        );
 5825        assert_eq!(
 5826            editor.display_text(cx),
 5827            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5828        );
 5829    });
 5830
 5831    _ = editor.update(cx, |editor, window, cx| {
 5832        editor.change_selections(None, window, cx, |s| {
 5833            s.select_display_ranges([
 5834                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5835                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5836                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5837                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5838            ])
 5839        });
 5840        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5841        assert_eq!(
 5842            editor.display_text(cx),
 5843            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5844        );
 5845    });
 5846    EditorTestContext::for_editor(editor, cx)
 5847        .await
 5848        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5849
 5850    _ = editor.update(cx, |editor, window, cx| {
 5851        editor.change_selections(None, window, cx, |s| {
 5852            s.select_display_ranges([
 5853                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5854            ])
 5855        });
 5856        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5857        assert_eq!(
 5858            editor.display_text(cx),
 5859            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5860        );
 5861        assert_eq!(
 5862            editor.selections.display_ranges(cx),
 5863            [
 5864                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5865                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5866                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5867                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5868                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5869                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5870                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5871            ]
 5872        );
 5873    });
 5874    EditorTestContext::for_editor(editor, cx)
 5875        .await
 5876        .assert_editor_state(
 5877            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5878        );
 5879}
 5880
 5881#[gpui::test]
 5882async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5883    init_test(cx, |_| {});
 5884
 5885    let mut cx = EditorTestContext::new(cx).await;
 5886
 5887    cx.set_state(indoc!(
 5888        r#"abc
 5889           defˇghi
 5890
 5891           jk
 5892           nlmo
 5893           "#
 5894    ));
 5895
 5896    cx.update_editor(|editor, window, cx| {
 5897        editor.add_selection_above(&Default::default(), window, cx);
 5898    });
 5899
 5900    cx.assert_editor_state(indoc!(
 5901        r#"abcˇ
 5902           defˇghi
 5903
 5904           jk
 5905           nlmo
 5906           "#
 5907    ));
 5908
 5909    cx.update_editor(|editor, window, cx| {
 5910        editor.add_selection_above(&Default::default(), window, cx);
 5911    });
 5912
 5913    cx.assert_editor_state(indoc!(
 5914        r#"abcˇ
 5915            defˇghi
 5916
 5917            jk
 5918            nlmo
 5919            "#
 5920    ));
 5921
 5922    cx.update_editor(|editor, window, cx| {
 5923        editor.add_selection_below(&Default::default(), window, cx);
 5924    });
 5925
 5926    cx.assert_editor_state(indoc!(
 5927        r#"abc
 5928           defˇghi
 5929
 5930           jk
 5931           nlmo
 5932           "#
 5933    ));
 5934
 5935    cx.update_editor(|editor, window, cx| {
 5936        editor.undo_selection(&Default::default(), window, cx);
 5937    });
 5938
 5939    cx.assert_editor_state(indoc!(
 5940        r#"abcˇ
 5941           defˇghi
 5942
 5943           jk
 5944           nlmo
 5945           "#
 5946    ));
 5947
 5948    cx.update_editor(|editor, window, cx| {
 5949        editor.redo_selection(&Default::default(), window, cx);
 5950    });
 5951
 5952    cx.assert_editor_state(indoc!(
 5953        r#"abc
 5954           defˇghi
 5955
 5956           jk
 5957           nlmo
 5958           "#
 5959    ));
 5960
 5961    cx.update_editor(|editor, window, cx| {
 5962        editor.add_selection_below(&Default::default(), window, cx);
 5963    });
 5964
 5965    cx.assert_editor_state(indoc!(
 5966        r#"abc
 5967           defˇghi
 5968           ˇ
 5969           jk
 5970           nlmo
 5971           "#
 5972    ));
 5973
 5974    cx.update_editor(|editor, window, cx| {
 5975        editor.add_selection_below(&Default::default(), window, cx);
 5976    });
 5977
 5978    cx.assert_editor_state(indoc!(
 5979        r#"abc
 5980           defˇghi
 5981           ˇ
 5982           jkˇ
 5983           nlmo
 5984           "#
 5985    ));
 5986
 5987    cx.update_editor(|editor, window, cx| {
 5988        editor.add_selection_below(&Default::default(), window, cx);
 5989    });
 5990
 5991    cx.assert_editor_state(indoc!(
 5992        r#"abc
 5993           defˇghi
 5994           ˇ
 5995           jkˇ
 5996           nlmˇo
 5997           "#
 5998    ));
 5999
 6000    cx.update_editor(|editor, window, cx| {
 6001        editor.add_selection_below(&Default::default(), window, cx);
 6002    });
 6003
 6004    cx.assert_editor_state(indoc!(
 6005        r#"abc
 6006           defˇghi
 6007           ˇ
 6008           jkˇ
 6009           nlmˇo
 6010           ˇ"#
 6011    ));
 6012
 6013    // change selections
 6014    cx.set_state(indoc!(
 6015        r#"abc
 6016           def«ˇg»hi
 6017
 6018           jk
 6019           nlmo
 6020           "#
 6021    ));
 6022
 6023    cx.update_editor(|editor, window, cx| {
 6024        editor.add_selection_below(&Default::default(), window, cx);
 6025    });
 6026
 6027    cx.assert_editor_state(indoc!(
 6028        r#"abc
 6029           def«ˇg»hi
 6030
 6031           jk
 6032           nlm«ˇo»
 6033           "#
 6034    ));
 6035
 6036    cx.update_editor(|editor, window, cx| {
 6037        editor.add_selection_below(&Default::default(), window, cx);
 6038    });
 6039
 6040    cx.assert_editor_state(indoc!(
 6041        r#"abc
 6042           def«ˇg»hi
 6043
 6044           jk
 6045           nlm«ˇo»
 6046           "#
 6047    ));
 6048
 6049    cx.update_editor(|editor, window, cx| {
 6050        editor.add_selection_above(&Default::default(), window, cx);
 6051    });
 6052
 6053    cx.assert_editor_state(indoc!(
 6054        r#"abc
 6055           def«ˇg»hi
 6056
 6057           jk
 6058           nlmo
 6059           "#
 6060    ));
 6061
 6062    cx.update_editor(|editor, window, cx| {
 6063        editor.add_selection_above(&Default::default(), window, cx);
 6064    });
 6065
 6066    cx.assert_editor_state(indoc!(
 6067        r#"abc
 6068           def«ˇg»hi
 6069
 6070           jk
 6071           nlmo
 6072           "#
 6073    ));
 6074
 6075    // Change selections again
 6076    cx.set_state(indoc!(
 6077        r#"a«bc
 6078           defgˇ»hi
 6079
 6080           jk
 6081           nlmo
 6082           "#
 6083    ));
 6084
 6085    cx.update_editor(|editor, window, cx| {
 6086        editor.add_selection_below(&Default::default(), window, cx);
 6087    });
 6088
 6089    cx.assert_editor_state(indoc!(
 6090        r#"a«bcˇ»
 6091           d«efgˇ»hi
 6092
 6093           j«kˇ»
 6094           nlmo
 6095           "#
 6096    ));
 6097
 6098    cx.update_editor(|editor, window, cx| {
 6099        editor.add_selection_below(&Default::default(), window, cx);
 6100    });
 6101    cx.assert_editor_state(indoc!(
 6102        r#"a«bcˇ»
 6103           d«efgˇ»hi
 6104
 6105           j«kˇ»
 6106           n«lmoˇ»
 6107           "#
 6108    ));
 6109    cx.update_editor(|editor, window, cx| {
 6110        editor.add_selection_above(&Default::default(), window, cx);
 6111    });
 6112
 6113    cx.assert_editor_state(indoc!(
 6114        r#"a«bcˇ»
 6115           d«efgˇ»hi
 6116
 6117           j«kˇ»
 6118           nlmo
 6119           "#
 6120    ));
 6121
 6122    // Change selections again
 6123    cx.set_state(indoc!(
 6124        r#"abc
 6125           d«ˇefghi
 6126
 6127           jk
 6128           nlm»o
 6129           "#
 6130    ));
 6131
 6132    cx.update_editor(|editor, window, cx| {
 6133        editor.add_selection_above(&Default::default(), window, cx);
 6134    });
 6135
 6136    cx.assert_editor_state(indoc!(
 6137        r#"a«ˇbc»
 6138           d«ˇef»ghi
 6139
 6140           j«ˇk»
 6141           n«ˇlm»o
 6142           "#
 6143    ));
 6144
 6145    cx.update_editor(|editor, window, cx| {
 6146        editor.add_selection_below(&Default::default(), window, cx);
 6147    });
 6148
 6149    cx.assert_editor_state(indoc!(
 6150        r#"abc
 6151           d«ˇef»ghi
 6152
 6153           j«ˇk»
 6154           n«ˇlm»o
 6155           "#
 6156    ));
 6157}
 6158
 6159#[gpui::test]
 6160async fn test_select_next(cx: &mut TestAppContext) {
 6161    init_test(cx, |_| {});
 6162
 6163    let mut cx = EditorTestContext::new(cx).await;
 6164    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6165
 6166    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6167        .unwrap();
 6168    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6169
 6170    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6171        .unwrap();
 6172    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6173
 6174    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6175    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6176
 6177    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6178    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6179
 6180    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6181        .unwrap();
 6182    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6183
 6184    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6185        .unwrap();
 6186    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6187
 6188    // Test selection direction should be preserved
 6189    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6190
 6191    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6192        .unwrap();
 6193    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6194}
 6195
 6196#[gpui::test]
 6197async fn test_select_all_matches(cx: &mut TestAppContext) {
 6198    init_test(cx, |_| {});
 6199
 6200    let mut cx = EditorTestContext::new(cx).await;
 6201
 6202    // Test caret-only selections
 6203    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6204    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6205        .unwrap();
 6206    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6207
 6208    // Test left-to-right selections
 6209    cx.set_state("abc\n«abcˇ»\nabc");
 6210    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6211        .unwrap();
 6212    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6213
 6214    // Test right-to-left selections
 6215    cx.set_state("abc\n«ˇabc»\nabc");
 6216    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6217        .unwrap();
 6218    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6219
 6220    // Test selecting whitespace with caret selection
 6221    cx.set_state("abc\nˇ   abc\nabc");
 6222    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6223        .unwrap();
 6224    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6225
 6226    // Test selecting whitespace with left-to-right selection
 6227    cx.set_state("abc\n«ˇ  »abc\nabc");
 6228    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6229        .unwrap();
 6230    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6231
 6232    // Test no matches with right-to-left selection
 6233    cx.set_state("abc\n«  ˇ»abc\nabc");
 6234    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6235        .unwrap();
 6236    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6237}
 6238
 6239#[gpui::test]
 6240async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6241    init_test(cx, |_| {});
 6242
 6243    let mut cx = EditorTestContext::new(cx).await;
 6244
 6245    let large_body_1 = "\nd".repeat(200);
 6246    let large_body_2 = "\ne".repeat(200);
 6247
 6248    cx.set_state(&format!(
 6249        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6250    ));
 6251    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6252        let scroll_position = editor.scroll_position(cx);
 6253        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6254        scroll_position
 6255    });
 6256
 6257    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6258        .unwrap();
 6259    cx.assert_editor_state(&format!(
 6260        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6261    ));
 6262    let scroll_position_after_selection =
 6263        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6264    assert_eq!(
 6265        initial_scroll_position, scroll_position_after_selection,
 6266        "Scroll position should not change after selecting all matches"
 6267    );
 6268}
 6269
 6270#[gpui::test]
 6271async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6272    init_test(cx, |_| {});
 6273
 6274    let mut cx = EditorLspTestContext::new_rust(
 6275        lsp::ServerCapabilities {
 6276            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6277            ..Default::default()
 6278        },
 6279        cx,
 6280    )
 6281    .await;
 6282
 6283    cx.set_state(indoc! {"
 6284        line 1
 6285        line 2
 6286        linˇe 3
 6287        line 4
 6288        line 5
 6289    "});
 6290
 6291    // Make an edit
 6292    cx.update_editor(|editor, window, cx| {
 6293        editor.handle_input("X", window, cx);
 6294    });
 6295
 6296    // Move cursor to a different position
 6297    cx.update_editor(|editor, window, cx| {
 6298        editor.change_selections(None, window, cx, |s| {
 6299            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6300        });
 6301    });
 6302
 6303    cx.assert_editor_state(indoc! {"
 6304        line 1
 6305        line 2
 6306        linXe 3
 6307        line 4
 6308        liˇne 5
 6309    "});
 6310
 6311    cx.lsp
 6312        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6313            Ok(Some(vec![lsp::TextEdit::new(
 6314                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6315                "PREFIX ".to_string(),
 6316            )]))
 6317        });
 6318
 6319    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6320        .unwrap()
 6321        .await
 6322        .unwrap();
 6323
 6324    cx.assert_editor_state(indoc! {"
 6325        PREFIX line 1
 6326        line 2
 6327        linXe 3
 6328        line 4
 6329        liˇne 5
 6330    "});
 6331
 6332    // Undo formatting
 6333    cx.update_editor(|editor, window, cx| {
 6334        editor.undo(&Default::default(), window, cx);
 6335    });
 6336
 6337    // Verify cursor moved back to position after edit
 6338    cx.assert_editor_state(indoc! {"
 6339        line 1
 6340        line 2
 6341        linXˇe 3
 6342        line 4
 6343        line 5
 6344    "});
 6345}
 6346
 6347#[gpui::test]
 6348async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6349    init_test(cx, |_| {});
 6350
 6351    let mut cx = EditorTestContext::new(cx).await;
 6352    cx.set_state(
 6353        r#"let foo = 2;
 6354lˇet foo = 2;
 6355let fooˇ = 2;
 6356let foo = 2;
 6357let foo = ˇ2;"#,
 6358    );
 6359
 6360    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6361        .unwrap();
 6362    cx.assert_editor_state(
 6363        r#"let foo = 2;
 6364«letˇ» foo = 2;
 6365let «fooˇ» = 2;
 6366let foo = 2;
 6367let foo = «2ˇ»;"#,
 6368    );
 6369
 6370    // noop for multiple selections with different contents
 6371    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6372        .unwrap();
 6373    cx.assert_editor_state(
 6374        r#"let foo = 2;
 6375«letˇ» foo = 2;
 6376let «fooˇ» = 2;
 6377let foo = 2;
 6378let foo = «2ˇ»;"#,
 6379    );
 6380
 6381    // Test last selection direction should be preserved
 6382    cx.set_state(
 6383        r#"let foo = 2;
 6384let foo = 2;
 6385let «fooˇ» = 2;
 6386let «ˇfoo» = 2;
 6387let foo = 2;"#,
 6388    );
 6389
 6390    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6391        .unwrap();
 6392    cx.assert_editor_state(
 6393        r#"let foo = 2;
 6394let foo = 2;
 6395let «fooˇ» = 2;
 6396let «ˇfoo» = 2;
 6397let «ˇfoo» = 2;"#,
 6398    );
 6399}
 6400
 6401#[gpui::test]
 6402async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6403    init_test(cx, |_| {});
 6404
 6405    let mut cx =
 6406        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6407
 6408    cx.assert_editor_state(indoc! {"
 6409        ˇbbb
 6410        ccc
 6411
 6412        bbb
 6413        ccc
 6414        "});
 6415    cx.dispatch_action(SelectPrevious::default());
 6416    cx.assert_editor_state(indoc! {"
 6417                «bbbˇ»
 6418                ccc
 6419
 6420                bbb
 6421                ccc
 6422                "});
 6423    cx.dispatch_action(SelectPrevious::default());
 6424    cx.assert_editor_state(indoc! {"
 6425                «bbbˇ»
 6426                ccc
 6427
 6428                «bbbˇ»
 6429                ccc
 6430                "});
 6431}
 6432
 6433#[gpui::test]
 6434async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6435    init_test(cx, |_| {});
 6436
 6437    let mut cx = EditorTestContext::new(cx).await;
 6438    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6439
 6440    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6441        .unwrap();
 6442    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6443
 6444    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6445        .unwrap();
 6446    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6447
 6448    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6449    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6450
 6451    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6452    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6453
 6454    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6455        .unwrap();
 6456    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6457
 6458    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6459        .unwrap();
 6460    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6461}
 6462
 6463#[gpui::test]
 6464async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6465    init_test(cx, |_| {});
 6466
 6467    let mut cx = EditorTestContext::new(cx).await;
 6468    cx.set_state("");
 6469
 6470    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6471        .unwrap();
 6472    cx.assert_editor_state("«aˇ»");
 6473    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6474        .unwrap();
 6475    cx.assert_editor_state("«aˇ»");
 6476}
 6477
 6478#[gpui::test]
 6479async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6480    init_test(cx, |_| {});
 6481
 6482    let mut cx = EditorTestContext::new(cx).await;
 6483    cx.set_state(
 6484        r#"let foo = 2;
 6485lˇet foo = 2;
 6486let fooˇ = 2;
 6487let foo = 2;
 6488let foo = ˇ2;"#,
 6489    );
 6490
 6491    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6492        .unwrap();
 6493    cx.assert_editor_state(
 6494        r#"let foo = 2;
 6495«letˇ» foo = 2;
 6496let «fooˇ» = 2;
 6497let foo = 2;
 6498let foo = «2ˇ»;"#,
 6499    );
 6500
 6501    // noop for multiple selections with different contents
 6502    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6503        .unwrap();
 6504    cx.assert_editor_state(
 6505        r#"let foo = 2;
 6506«letˇ» foo = 2;
 6507let «fooˇ» = 2;
 6508let foo = 2;
 6509let foo = «2ˇ»;"#,
 6510    );
 6511}
 6512
 6513#[gpui::test]
 6514async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6515    init_test(cx, |_| {});
 6516
 6517    let mut cx = EditorTestContext::new(cx).await;
 6518    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6519
 6520    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6521        .unwrap();
 6522    // selection direction is preserved
 6523    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6524
 6525    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6526        .unwrap();
 6527    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6528
 6529    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6530    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6531
 6532    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6533    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6534
 6535    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6536        .unwrap();
 6537    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6538
 6539    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6540        .unwrap();
 6541    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6542}
 6543
 6544#[gpui::test]
 6545async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6546    init_test(cx, |_| {});
 6547
 6548    let language = Arc::new(Language::new(
 6549        LanguageConfig::default(),
 6550        Some(tree_sitter_rust::LANGUAGE.into()),
 6551    ));
 6552
 6553    let text = r#"
 6554        use mod1::mod2::{mod3, mod4};
 6555
 6556        fn fn_1(param1: bool, param2: &str) {
 6557            let var1 = "text";
 6558        }
 6559    "#
 6560    .unindent();
 6561
 6562    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6563    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6564    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6565
 6566    editor
 6567        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6568        .await;
 6569
 6570    editor.update_in(cx, |editor, window, cx| {
 6571        editor.change_selections(None, window, cx, |s| {
 6572            s.select_display_ranges([
 6573                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6574                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6575                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6576            ]);
 6577        });
 6578        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6579    });
 6580    editor.update(cx, |editor, cx| {
 6581        assert_text_with_selections(
 6582            editor,
 6583            indoc! {r#"
 6584                use mod1::mod2::{mod3, «mod4ˇ»};
 6585
 6586                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6587                    let var1 = "«ˇtext»";
 6588                }
 6589            "#},
 6590            cx,
 6591        );
 6592    });
 6593
 6594    editor.update_in(cx, |editor, window, cx| {
 6595        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6596    });
 6597    editor.update(cx, |editor, cx| {
 6598        assert_text_with_selections(
 6599            editor,
 6600            indoc! {r#"
 6601                use mod1::mod2::«{mod3, mod4}ˇ»;
 6602
 6603                «ˇfn fn_1(param1: bool, param2: &str) {
 6604                    let var1 = "text";
 6605 6606            "#},
 6607            cx,
 6608        );
 6609    });
 6610
 6611    editor.update_in(cx, |editor, window, cx| {
 6612        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6613    });
 6614    assert_eq!(
 6615        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6616        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6617    );
 6618
 6619    // Trying to expand the selected syntax node one more time has no effect.
 6620    editor.update_in(cx, |editor, window, cx| {
 6621        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6622    });
 6623    assert_eq!(
 6624        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6625        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6626    );
 6627
 6628    editor.update_in(cx, |editor, window, cx| {
 6629        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6630    });
 6631    editor.update(cx, |editor, cx| {
 6632        assert_text_with_selections(
 6633            editor,
 6634            indoc! {r#"
 6635                use mod1::mod2::«{mod3, mod4}ˇ»;
 6636
 6637                «ˇfn fn_1(param1: bool, param2: &str) {
 6638                    let var1 = "text";
 6639 6640            "#},
 6641            cx,
 6642        );
 6643    });
 6644
 6645    editor.update_in(cx, |editor, window, cx| {
 6646        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6647    });
 6648    editor.update(cx, |editor, cx| {
 6649        assert_text_with_selections(
 6650            editor,
 6651            indoc! {r#"
 6652                use mod1::mod2::{mod3, «mod4ˇ»};
 6653
 6654                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6655                    let var1 = "«ˇtext»";
 6656                }
 6657            "#},
 6658            cx,
 6659        );
 6660    });
 6661
 6662    editor.update_in(cx, |editor, window, cx| {
 6663        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6664    });
 6665    editor.update(cx, |editor, cx| {
 6666        assert_text_with_selections(
 6667            editor,
 6668            indoc! {r#"
 6669                use mod1::mod2::{mod3, mo«ˇ»d4};
 6670
 6671                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6672                    let var1 = "te«ˇ»xt";
 6673                }
 6674            "#},
 6675            cx,
 6676        );
 6677    });
 6678
 6679    // Trying to shrink the selected syntax node one more time has no effect.
 6680    editor.update_in(cx, |editor, window, cx| {
 6681        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6682    });
 6683    editor.update_in(cx, |editor, _, cx| {
 6684        assert_text_with_selections(
 6685            editor,
 6686            indoc! {r#"
 6687                use mod1::mod2::{mod3, mo«ˇ»d4};
 6688
 6689                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6690                    let var1 = "te«ˇ»xt";
 6691                }
 6692            "#},
 6693            cx,
 6694        );
 6695    });
 6696
 6697    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6698    // a fold.
 6699    editor.update_in(cx, |editor, window, cx| {
 6700        editor.fold_creases(
 6701            vec![
 6702                Crease::simple(
 6703                    Point::new(0, 21)..Point::new(0, 24),
 6704                    FoldPlaceholder::test(),
 6705                ),
 6706                Crease::simple(
 6707                    Point::new(3, 20)..Point::new(3, 22),
 6708                    FoldPlaceholder::test(),
 6709                ),
 6710            ],
 6711            true,
 6712            window,
 6713            cx,
 6714        );
 6715        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6716    });
 6717    editor.update(cx, |editor, cx| {
 6718        assert_text_with_selections(
 6719            editor,
 6720            indoc! {r#"
 6721                use mod1::mod2::«{mod3, mod4}ˇ»;
 6722
 6723                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6724                    let var1 = "«ˇtext»";
 6725                }
 6726            "#},
 6727            cx,
 6728        );
 6729    });
 6730}
 6731
 6732#[gpui::test]
 6733async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6734    init_test(cx, |_| {});
 6735
 6736    let language = Arc::new(Language::new(
 6737        LanguageConfig::default(),
 6738        Some(tree_sitter_rust::LANGUAGE.into()),
 6739    ));
 6740
 6741    let text = "let a = 2;";
 6742
 6743    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6744    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6745    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6746
 6747    editor
 6748        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6749        .await;
 6750
 6751    // Test case 1: Cursor at end of word
 6752    editor.update_in(cx, |editor, window, cx| {
 6753        editor.change_selections(None, window, cx, |s| {
 6754            s.select_display_ranges([
 6755                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6756            ]);
 6757        });
 6758    });
 6759    editor.update(cx, |editor, cx| {
 6760        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6761    });
 6762    editor.update_in(cx, |editor, window, cx| {
 6763        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6764    });
 6765    editor.update(cx, |editor, cx| {
 6766        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6767    });
 6768    editor.update_in(cx, |editor, window, cx| {
 6769        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6770    });
 6771    editor.update(cx, |editor, cx| {
 6772        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6773    });
 6774
 6775    // Test case 2: Cursor at end of statement
 6776    editor.update_in(cx, |editor, window, cx| {
 6777        editor.change_selections(None, window, cx, |s| {
 6778            s.select_display_ranges([
 6779                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6780            ]);
 6781        });
 6782    });
 6783    editor.update(cx, |editor, cx| {
 6784        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6785    });
 6786    editor.update_in(cx, |editor, window, cx| {
 6787        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6788    });
 6789    editor.update(cx, |editor, cx| {
 6790        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6791    });
 6792}
 6793
 6794#[gpui::test]
 6795async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6796    init_test(cx, |_| {});
 6797
 6798    let language = Arc::new(Language::new(
 6799        LanguageConfig::default(),
 6800        Some(tree_sitter_rust::LANGUAGE.into()),
 6801    ));
 6802
 6803    let text = r#"
 6804        use mod1::mod2::{mod3, mod4};
 6805
 6806        fn fn_1(param1: bool, param2: &str) {
 6807            let var1 = "hello world";
 6808        }
 6809    "#
 6810    .unindent();
 6811
 6812    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6813    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6814    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6815
 6816    editor
 6817        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6818        .await;
 6819
 6820    // Test 1: Cursor on a letter of a string word
 6821    editor.update_in(cx, |editor, window, cx| {
 6822        editor.change_selections(None, window, cx, |s| {
 6823            s.select_display_ranges([
 6824                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6825            ]);
 6826        });
 6827    });
 6828    editor.update_in(cx, |editor, window, cx| {
 6829        assert_text_with_selections(
 6830            editor,
 6831            indoc! {r#"
 6832                use mod1::mod2::{mod3, mod4};
 6833
 6834                fn fn_1(param1: bool, param2: &str) {
 6835                    let var1 = "hˇello world";
 6836                }
 6837            "#},
 6838            cx,
 6839        );
 6840        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6841        assert_text_with_selections(
 6842            editor,
 6843            indoc! {r#"
 6844                use mod1::mod2::{mod3, mod4};
 6845
 6846                fn fn_1(param1: bool, param2: &str) {
 6847                    let var1 = "«ˇhello» world";
 6848                }
 6849            "#},
 6850            cx,
 6851        );
 6852    });
 6853
 6854    // Test 2: Partial selection within a word
 6855    editor.update_in(cx, |editor, window, cx| {
 6856        editor.change_selections(None, window, cx, |s| {
 6857            s.select_display_ranges([
 6858                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6859            ]);
 6860        });
 6861    });
 6862    editor.update_in(cx, |editor, window, cx| {
 6863        assert_text_with_selections(
 6864            editor,
 6865            indoc! {r#"
 6866                use mod1::mod2::{mod3, mod4};
 6867
 6868                fn fn_1(param1: bool, param2: &str) {
 6869                    let var1 = "h«elˇ»lo world";
 6870                }
 6871            "#},
 6872            cx,
 6873        );
 6874        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6875        assert_text_with_selections(
 6876            editor,
 6877            indoc! {r#"
 6878                use mod1::mod2::{mod3, mod4};
 6879
 6880                fn fn_1(param1: bool, param2: &str) {
 6881                    let var1 = "«ˇhello» world";
 6882                }
 6883            "#},
 6884            cx,
 6885        );
 6886    });
 6887
 6888    // Test 3: Complete word already selected
 6889    editor.update_in(cx, |editor, window, cx| {
 6890        editor.change_selections(None, window, cx, |s| {
 6891            s.select_display_ranges([
 6892                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6893            ]);
 6894        });
 6895    });
 6896    editor.update_in(cx, |editor, window, cx| {
 6897        assert_text_with_selections(
 6898            editor,
 6899            indoc! {r#"
 6900                use mod1::mod2::{mod3, mod4};
 6901
 6902                fn fn_1(param1: bool, param2: &str) {
 6903                    let var1 = "«helloˇ» world";
 6904                }
 6905            "#},
 6906            cx,
 6907        );
 6908        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6909        assert_text_with_selections(
 6910            editor,
 6911            indoc! {r#"
 6912                use mod1::mod2::{mod3, mod4};
 6913
 6914                fn fn_1(param1: bool, param2: &str) {
 6915                    let var1 = "«hello worldˇ»";
 6916                }
 6917            "#},
 6918            cx,
 6919        );
 6920    });
 6921
 6922    // Test 4: Selection spanning across words
 6923    editor.update_in(cx, |editor, window, cx| {
 6924        editor.change_selections(None, window, cx, |s| {
 6925            s.select_display_ranges([
 6926                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6927            ]);
 6928        });
 6929    });
 6930    editor.update_in(cx, |editor, window, cx| {
 6931        assert_text_with_selections(
 6932            editor,
 6933            indoc! {r#"
 6934                use mod1::mod2::{mod3, mod4};
 6935
 6936                fn fn_1(param1: bool, param2: &str) {
 6937                    let var1 = "hel«lo woˇ»rld";
 6938                }
 6939            "#},
 6940            cx,
 6941        );
 6942        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6943        assert_text_with_selections(
 6944            editor,
 6945            indoc! {r#"
 6946                use mod1::mod2::{mod3, mod4};
 6947
 6948                fn fn_1(param1: bool, param2: &str) {
 6949                    let var1 = "«ˇhello world»";
 6950                }
 6951            "#},
 6952            cx,
 6953        );
 6954    });
 6955
 6956    // Test 5: Expansion beyond string
 6957    editor.update_in(cx, |editor, window, cx| {
 6958        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6959        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6960        assert_text_with_selections(
 6961            editor,
 6962            indoc! {r#"
 6963                use mod1::mod2::{mod3, mod4};
 6964
 6965                fn fn_1(param1: bool, param2: &str) {
 6966                    «ˇlet var1 = "hello world";»
 6967                }
 6968            "#},
 6969            cx,
 6970        );
 6971    });
 6972}
 6973
 6974#[gpui::test]
 6975async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6976    init_test(cx, |_| {});
 6977
 6978    let base_text = r#"
 6979        impl A {
 6980            // this is an uncommitted comment
 6981
 6982            fn b() {
 6983                c();
 6984            }
 6985
 6986            // this is another uncommitted comment
 6987
 6988            fn d() {
 6989                // e
 6990                // f
 6991            }
 6992        }
 6993
 6994        fn g() {
 6995            // h
 6996        }
 6997    "#
 6998    .unindent();
 6999
 7000    let text = r#"
 7001        ˇimpl A {
 7002
 7003            fn b() {
 7004                c();
 7005            }
 7006
 7007            fn d() {
 7008                // e
 7009                // f
 7010            }
 7011        }
 7012
 7013        fn g() {
 7014            // h
 7015        }
 7016    "#
 7017    .unindent();
 7018
 7019    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7020    cx.set_state(&text);
 7021    cx.set_head_text(&base_text);
 7022    cx.update_editor(|editor, window, cx| {
 7023        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 7024    });
 7025
 7026    cx.assert_state_with_diff(
 7027        "
 7028        ˇimpl A {
 7029      -     // this is an uncommitted comment
 7030
 7031            fn b() {
 7032                c();
 7033            }
 7034
 7035      -     // this is another uncommitted comment
 7036      -
 7037            fn d() {
 7038                // e
 7039                // f
 7040            }
 7041        }
 7042
 7043        fn g() {
 7044            // h
 7045        }
 7046    "
 7047        .unindent(),
 7048    );
 7049
 7050    let expected_display_text = "
 7051        impl A {
 7052            // this is an uncommitted comment
 7053
 7054            fn b() {
 7055 7056            }
 7057
 7058            // this is another uncommitted comment
 7059
 7060            fn d() {
 7061 7062            }
 7063        }
 7064
 7065        fn g() {
 7066 7067        }
 7068        "
 7069    .unindent();
 7070
 7071    cx.update_editor(|editor, window, cx| {
 7072        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7073        assert_eq!(editor.display_text(cx), expected_display_text);
 7074    });
 7075}
 7076
 7077#[gpui::test]
 7078async fn test_autoindent(cx: &mut TestAppContext) {
 7079    init_test(cx, |_| {});
 7080
 7081    let language = Arc::new(
 7082        Language::new(
 7083            LanguageConfig {
 7084                brackets: BracketPairConfig {
 7085                    pairs: vec![
 7086                        BracketPair {
 7087                            start: "{".to_string(),
 7088                            end: "}".to_string(),
 7089                            close: false,
 7090                            surround: false,
 7091                            newline: true,
 7092                        },
 7093                        BracketPair {
 7094                            start: "(".to_string(),
 7095                            end: ")".to_string(),
 7096                            close: false,
 7097                            surround: false,
 7098                            newline: true,
 7099                        },
 7100                    ],
 7101                    ..Default::default()
 7102                },
 7103                ..Default::default()
 7104            },
 7105            Some(tree_sitter_rust::LANGUAGE.into()),
 7106        )
 7107        .with_indents_query(
 7108            r#"
 7109                (_ "(" ")" @end) @indent
 7110                (_ "{" "}" @end) @indent
 7111            "#,
 7112        )
 7113        .unwrap(),
 7114    );
 7115
 7116    let text = "fn a() {}";
 7117
 7118    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7119    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7120    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7121    editor
 7122        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7123        .await;
 7124
 7125    editor.update_in(cx, |editor, window, cx| {
 7126        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7127        editor.newline(&Newline, window, cx);
 7128        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7129        assert_eq!(
 7130            editor.selections.ranges(cx),
 7131            &[
 7132                Point::new(1, 4)..Point::new(1, 4),
 7133                Point::new(3, 4)..Point::new(3, 4),
 7134                Point::new(5, 0)..Point::new(5, 0)
 7135            ]
 7136        );
 7137    });
 7138}
 7139
 7140#[gpui::test]
 7141async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7142    init_test(cx, |_| {});
 7143
 7144    {
 7145        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7146        cx.set_state(indoc! {"
 7147            impl A {
 7148
 7149                fn b() {}
 7150
 7151            «fn c() {
 7152
 7153            }ˇ»
 7154            }
 7155        "});
 7156
 7157        cx.update_editor(|editor, window, cx| {
 7158            editor.autoindent(&Default::default(), window, cx);
 7159        });
 7160
 7161        cx.assert_editor_state(indoc! {"
 7162            impl A {
 7163
 7164                fn b() {}
 7165
 7166                «fn c() {
 7167
 7168                }ˇ»
 7169            }
 7170        "});
 7171    }
 7172
 7173    {
 7174        let mut cx = EditorTestContext::new_multibuffer(
 7175            cx,
 7176            [indoc! { "
 7177                impl A {
 7178                «
 7179                // a
 7180                fn b(){}
 7181                »
 7182                «
 7183                    }
 7184                    fn c(){}
 7185                »
 7186            "}],
 7187        );
 7188
 7189        let buffer = cx.update_editor(|editor, _, cx| {
 7190            let buffer = editor.buffer().update(cx, |buffer, _| {
 7191                buffer.all_buffers().iter().next().unwrap().clone()
 7192            });
 7193            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7194            buffer
 7195        });
 7196
 7197        cx.run_until_parked();
 7198        cx.update_editor(|editor, window, cx| {
 7199            editor.select_all(&Default::default(), window, cx);
 7200            editor.autoindent(&Default::default(), window, cx)
 7201        });
 7202        cx.run_until_parked();
 7203
 7204        cx.update(|_, cx| {
 7205            assert_eq!(
 7206                buffer.read(cx).text(),
 7207                indoc! { "
 7208                    impl A {
 7209
 7210                        // a
 7211                        fn b(){}
 7212
 7213
 7214                    }
 7215                    fn c(){}
 7216
 7217                " }
 7218            )
 7219        });
 7220    }
 7221}
 7222
 7223#[gpui::test]
 7224async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7225    init_test(cx, |_| {});
 7226
 7227    let mut cx = EditorTestContext::new(cx).await;
 7228
 7229    let language = Arc::new(Language::new(
 7230        LanguageConfig {
 7231            brackets: BracketPairConfig {
 7232                pairs: vec![
 7233                    BracketPair {
 7234                        start: "{".to_string(),
 7235                        end: "}".to_string(),
 7236                        close: true,
 7237                        surround: true,
 7238                        newline: true,
 7239                    },
 7240                    BracketPair {
 7241                        start: "(".to_string(),
 7242                        end: ")".to_string(),
 7243                        close: true,
 7244                        surround: true,
 7245                        newline: true,
 7246                    },
 7247                    BracketPair {
 7248                        start: "/*".to_string(),
 7249                        end: " */".to_string(),
 7250                        close: true,
 7251                        surround: true,
 7252                        newline: true,
 7253                    },
 7254                    BracketPair {
 7255                        start: "[".to_string(),
 7256                        end: "]".to_string(),
 7257                        close: false,
 7258                        surround: false,
 7259                        newline: true,
 7260                    },
 7261                    BracketPair {
 7262                        start: "\"".to_string(),
 7263                        end: "\"".to_string(),
 7264                        close: true,
 7265                        surround: true,
 7266                        newline: false,
 7267                    },
 7268                    BracketPair {
 7269                        start: "<".to_string(),
 7270                        end: ">".to_string(),
 7271                        close: false,
 7272                        surround: true,
 7273                        newline: true,
 7274                    },
 7275                ],
 7276                ..Default::default()
 7277            },
 7278            autoclose_before: "})]".to_string(),
 7279            ..Default::default()
 7280        },
 7281        Some(tree_sitter_rust::LANGUAGE.into()),
 7282    ));
 7283
 7284    cx.language_registry().add(language.clone());
 7285    cx.update_buffer(|buffer, cx| {
 7286        buffer.set_language(Some(language), cx);
 7287    });
 7288
 7289    cx.set_state(
 7290        &r#"
 7291            🏀ˇ
 7292            εˇ
 7293            ❤️ˇ
 7294        "#
 7295        .unindent(),
 7296    );
 7297
 7298    // autoclose multiple nested brackets at multiple cursors
 7299    cx.update_editor(|editor, window, cx| {
 7300        editor.handle_input("{", window, cx);
 7301        editor.handle_input("{", window, cx);
 7302        editor.handle_input("{", window, cx);
 7303    });
 7304    cx.assert_editor_state(
 7305        &"
 7306            🏀{{{ˇ}}}
 7307            ε{{{ˇ}}}
 7308            ❤️{{{ˇ}}}
 7309        "
 7310        .unindent(),
 7311    );
 7312
 7313    // insert a different closing bracket
 7314    cx.update_editor(|editor, window, cx| {
 7315        editor.handle_input(")", window, cx);
 7316    });
 7317    cx.assert_editor_state(
 7318        &"
 7319            🏀{{{)ˇ}}}
 7320            ε{{{)ˇ}}}
 7321            ❤️{{{)ˇ}}}
 7322        "
 7323        .unindent(),
 7324    );
 7325
 7326    // skip over the auto-closed brackets when typing a closing bracket
 7327    cx.update_editor(|editor, window, cx| {
 7328        editor.move_right(&MoveRight, window, cx);
 7329        editor.handle_input("}", window, cx);
 7330        editor.handle_input("}", window, cx);
 7331        editor.handle_input("}", window, cx);
 7332    });
 7333    cx.assert_editor_state(
 7334        &"
 7335            🏀{{{)}}}}ˇ
 7336            ε{{{)}}}}ˇ
 7337            ❤️{{{)}}}}ˇ
 7338        "
 7339        .unindent(),
 7340    );
 7341
 7342    // autoclose multi-character pairs
 7343    cx.set_state(
 7344        &"
 7345            ˇ
 7346            ˇ
 7347        "
 7348        .unindent(),
 7349    );
 7350    cx.update_editor(|editor, window, cx| {
 7351        editor.handle_input("/", window, cx);
 7352        editor.handle_input("*", window, cx);
 7353    });
 7354    cx.assert_editor_state(
 7355        &"
 7356            /*ˇ */
 7357            /*ˇ */
 7358        "
 7359        .unindent(),
 7360    );
 7361
 7362    // one cursor autocloses a multi-character pair, one cursor
 7363    // does not autoclose.
 7364    cx.set_state(
 7365        &"
 7366 7367            ˇ
 7368        "
 7369        .unindent(),
 7370    );
 7371    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7372    cx.assert_editor_state(
 7373        &"
 7374            /*ˇ */
 7375 7376        "
 7377        .unindent(),
 7378    );
 7379
 7380    // Don't autoclose if the next character isn't whitespace and isn't
 7381    // listed in the language's "autoclose_before" section.
 7382    cx.set_state("ˇa b");
 7383    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7384    cx.assert_editor_state("{ˇa b");
 7385
 7386    // Don't autoclose if `close` is false for the bracket pair
 7387    cx.set_state("ˇ");
 7388    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7389    cx.assert_editor_state("");
 7390
 7391    // Surround with brackets if text is selected
 7392    cx.set_state("«aˇ» b");
 7393    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7394    cx.assert_editor_state("{«aˇ»} b");
 7395
 7396    // Autoclose when not immediately after a word character
 7397    cx.set_state("a ˇ");
 7398    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7399    cx.assert_editor_state("a \"ˇ\"");
 7400
 7401    // Autoclose pair where the start and end characters are the same
 7402    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7403    cx.assert_editor_state("a \"\"ˇ");
 7404
 7405    // Don't autoclose when immediately after a word character
 7406    cx.set_state("");
 7407    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7408    cx.assert_editor_state("a\"ˇ");
 7409
 7410    // Do autoclose when after a non-word character
 7411    cx.set_state("");
 7412    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7413    cx.assert_editor_state("{\"ˇ\"");
 7414
 7415    // Non identical pairs autoclose regardless of preceding character
 7416    cx.set_state("");
 7417    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7418    cx.assert_editor_state("a{ˇ}");
 7419
 7420    // Don't autoclose pair if autoclose is disabled
 7421    cx.set_state("ˇ");
 7422    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7423    cx.assert_editor_state("");
 7424
 7425    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7426    cx.set_state("«aˇ» b");
 7427    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7428    cx.assert_editor_state("<«aˇ»> b");
 7429}
 7430
 7431#[gpui::test]
 7432async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7433    init_test(cx, |settings| {
 7434        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7435    });
 7436
 7437    let mut cx = EditorTestContext::new(cx).await;
 7438
 7439    let language = Arc::new(Language::new(
 7440        LanguageConfig {
 7441            brackets: BracketPairConfig {
 7442                pairs: vec![
 7443                    BracketPair {
 7444                        start: "{".to_string(),
 7445                        end: "}".to_string(),
 7446                        close: true,
 7447                        surround: true,
 7448                        newline: true,
 7449                    },
 7450                    BracketPair {
 7451                        start: "(".to_string(),
 7452                        end: ")".to_string(),
 7453                        close: true,
 7454                        surround: true,
 7455                        newline: true,
 7456                    },
 7457                    BracketPair {
 7458                        start: "[".to_string(),
 7459                        end: "]".to_string(),
 7460                        close: false,
 7461                        surround: false,
 7462                        newline: true,
 7463                    },
 7464                ],
 7465                ..Default::default()
 7466            },
 7467            autoclose_before: "})]".to_string(),
 7468            ..Default::default()
 7469        },
 7470        Some(tree_sitter_rust::LANGUAGE.into()),
 7471    ));
 7472
 7473    cx.language_registry().add(language.clone());
 7474    cx.update_buffer(|buffer, cx| {
 7475        buffer.set_language(Some(language), cx);
 7476    });
 7477
 7478    cx.set_state(
 7479        &"
 7480            ˇ
 7481            ˇ
 7482            ˇ
 7483        "
 7484        .unindent(),
 7485    );
 7486
 7487    // ensure only matching closing brackets are skipped over
 7488    cx.update_editor(|editor, window, cx| {
 7489        editor.handle_input("}", window, cx);
 7490        editor.move_left(&MoveLeft, window, cx);
 7491        editor.handle_input(")", window, cx);
 7492        editor.move_left(&MoveLeft, window, cx);
 7493    });
 7494    cx.assert_editor_state(
 7495        &"
 7496            ˇ)}
 7497            ˇ)}
 7498            ˇ)}
 7499        "
 7500        .unindent(),
 7501    );
 7502
 7503    // skip-over closing brackets at multiple cursors
 7504    cx.update_editor(|editor, window, cx| {
 7505        editor.handle_input(")", window, cx);
 7506        editor.handle_input("}", window, cx);
 7507    });
 7508    cx.assert_editor_state(
 7509        &"
 7510            )}ˇ
 7511            )}ˇ
 7512            )}ˇ
 7513        "
 7514        .unindent(),
 7515    );
 7516
 7517    // ignore non-close brackets
 7518    cx.update_editor(|editor, window, cx| {
 7519        editor.handle_input("]", window, cx);
 7520        editor.move_left(&MoveLeft, window, cx);
 7521        editor.handle_input("]", window, cx);
 7522    });
 7523    cx.assert_editor_state(
 7524        &"
 7525            )}]ˇ]
 7526            )}]ˇ]
 7527            )}]ˇ]
 7528        "
 7529        .unindent(),
 7530    );
 7531}
 7532
 7533#[gpui::test]
 7534async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7535    init_test(cx, |_| {});
 7536
 7537    let mut cx = EditorTestContext::new(cx).await;
 7538
 7539    let html_language = Arc::new(
 7540        Language::new(
 7541            LanguageConfig {
 7542                name: "HTML".into(),
 7543                brackets: BracketPairConfig {
 7544                    pairs: vec![
 7545                        BracketPair {
 7546                            start: "<".into(),
 7547                            end: ">".into(),
 7548                            close: true,
 7549                            ..Default::default()
 7550                        },
 7551                        BracketPair {
 7552                            start: "{".into(),
 7553                            end: "}".into(),
 7554                            close: true,
 7555                            ..Default::default()
 7556                        },
 7557                        BracketPair {
 7558                            start: "(".into(),
 7559                            end: ")".into(),
 7560                            close: true,
 7561                            ..Default::default()
 7562                        },
 7563                    ],
 7564                    ..Default::default()
 7565                },
 7566                autoclose_before: "})]>".into(),
 7567                ..Default::default()
 7568            },
 7569            Some(tree_sitter_html::LANGUAGE.into()),
 7570        )
 7571        .with_injection_query(
 7572            r#"
 7573            (script_element
 7574                (raw_text) @injection.content
 7575                (#set! injection.language "javascript"))
 7576            "#,
 7577        )
 7578        .unwrap(),
 7579    );
 7580
 7581    let javascript_language = Arc::new(Language::new(
 7582        LanguageConfig {
 7583            name: "JavaScript".into(),
 7584            brackets: BracketPairConfig {
 7585                pairs: vec![
 7586                    BracketPair {
 7587                        start: "/*".into(),
 7588                        end: " */".into(),
 7589                        close: true,
 7590                        ..Default::default()
 7591                    },
 7592                    BracketPair {
 7593                        start: "{".into(),
 7594                        end: "}".into(),
 7595                        close: true,
 7596                        ..Default::default()
 7597                    },
 7598                    BracketPair {
 7599                        start: "(".into(),
 7600                        end: ")".into(),
 7601                        close: true,
 7602                        ..Default::default()
 7603                    },
 7604                ],
 7605                ..Default::default()
 7606            },
 7607            autoclose_before: "})]>".into(),
 7608            ..Default::default()
 7609        },
 7610        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7611    ));
 7612
 7613    cx.language_registry().add(html_language.clone());
 7614    cx.language_registry().add(javascript_language.clone());
 7615
 7616    cx.update_buffer(|buffer, cx| {
 7617        buffer.set_language(Some(html_language), cx);
 7618    });
 7619
 7620    cx.set_state(
 7621        &r#"
 7622            <body>ˇ
 7623                <script>
 7624                    var x = 1;ˇ
 7625                </script>
 7626            </body>ˇ
 7627        "#
 7628        .unindent(),
 7629    );
 7630
 7631    // Precondition: different languages are active at different locations.
 7632    cx.update_editor(|editor, window, cx| {
 7633        let snapshot = editor.snapshot(window, cx);
 7634        let cursors = editor.selections.ranges::<usize>(cx);
 7635        let languages = cursors
 7636            .iter()
 7637            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7638            .collect::<Vec<_>>();
 7639        assert_eq!(
 7640            languages,
 7641            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7642        );
 7643    });
 7644
 7645    // Angle brackets autoclose in HTML, but not JavaScript.
 7646    cx.update_editor(|editor, window, cx| {
 7647        editor.handle_input("<", window, cx);
 7648        editor.handle_input("a", window, cx);
 7649    });
 7650    cx.assert_editor_state(
 7651        &r#"
 7652            <body><aˇ>
 7653                <script>
 7654                    var x = 1;<aˇ
 7655                </script>
 7656            </body><aˇ>
 7657        "#
 7658        .unindent(),
 7659    );
 7660
 7661    // Curly braces and parens autoclose in both HTML and JavaScript.
 7662    cx.update_editor(|editor, window, cx| {
 7663        editor.handle_input(" b=", window, cx);
 7664        editor.handle_input("{", window, cx);
 7665        editor.handle_input("c", window, cx);
 7666        editor.handle_input("(", window, cx);
 7667    });
 7668    cx.assert_editor_state(
 7669        &r#"
 7670            <body><a b={c(ˇ)}>
 7671                <script>
 7672                    var x = 1;<a b={c(ˇ)}
 7673                </script>
 7674            </body><a b={c(ˇ)}>
 7675        "#
 7676        .unindent(),
 7677    );
 7678
 7679    // Brackets that were already autoclosed are skipped.
 7680    cx.update_editor(|editor, window, cx| {
 7681        editor.handle_input(")", window, cx);
 7682        editor.handle_input("d", window, cx);
 7683        editor.handle_input("}", window, cx);
 7684    });
 7685    cx.assert_editor_state(
 7686        &r#"
 7687            <body><a b={c()d}ˇ>
 7688                <script>
 7689                    var x = 1;<a b={c()d}ˇ
 7690                </script>
 7691            </body><a b={c()d}ˇ>
 7692        "#
 7693        .unindent(),
 7694    );
 7695    cx.update_editor(|editor, window, cx| {
 7696        editor.handle_input(">", window, cx);
 7697    });
 7698    cx.assert_editor_state(
 7699        &r#"
 7700            <body><a b={c()d}>ˇ
 7701                <script>
 7702                    var x = 1;<a b={c()d}>ˇ
 7703                </script>
 7704            </body><a b={c()d}>ˇ
 7705        "#
 7706        .unindent(),
 7707    );
 7708
 7709    // Reset
 7710    cx.set_state(
 7711        &r#"
 7712            <body>ˇ
 7713                <script>
 7714                    var x = 1;ˇ
 7715                </script>
 7716            </body>ˇ
 7717        "#
 7718        .unindent(),
 7719    );
 7720
 7721    cx.update_editor(|editor, window, cx| {
 7722        editor.handle_input("<", window, cx);
 7723    });
 7724    cx.assert_editor_state(
 7725        &r#"
 7726            <body><ˇ>
 7727                <script>
 7728                    var x = 1;<ˇ
 7729                </script>
 7730            </body><ˇ>
 7731        "#
 7732        .unindent(),
 7733    );
 7734
 7735    // When backspacing, the closing angle brackets are removed.
 7736    cx.update_editor(|editor, window, cx| {
 7737        editor.backspace(&Backspace, window, cx);
 7738    });
 7739    cx.assert_editor_state(
 7740        &r#"
 7741            <body>ˇ
 7742                <script>
 7743                    var x = 1;ˇ
 7744                </script>
 7745            </body>ˇ
 7746        "#
 7747        .unindent(),
 7748    );
 7749
 7750    // Block comments autoclose in JavaScript, but not HTML.
 7751    cx.update_editor(|editor, window, cx| {
 7752        editor.handle_input("/", window, cx);
 7753        editor.handle_input("*", window, cx);
 7754    });
 7755    cx.assert_editor_state(
 7756        &r#"
 7757            <body>/*ˇ
 7758                <script>
 7759                    var x = 1;/*ˇ */
 7760                </script>
 7761            </body>/*ˇ
 7762        "#
 7763        .unindent(),
 7764    );
 7765}
 7766
 7767#[gpui::test]
 7768async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7769    init_test(cx, |_| {});
 7770
 7771    let mut cx = EditorTestContext::new(cx).await;
 7772
 7773    let rust_language = Arc::new(
 7774        Language::new(
 7775            LanguageConfig {
 7776                name: "Rust".into(),
 7777                brackets: serde_json::from_value(json!([
 7778                    { "start": "{", "end": "}", "close": true, "newline": true },
 7779                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7780                ]))
 7781                .unwrap(),
 7782                autoclose_before: "})]>".into(),
 7783                ..Default::default()
 7784            },
 7785            Some(tree_sitter_rust::LANGUAGE.into()),
 7786        )
 7787        .with_override_query("(string_literal) @string")
 7788        .unwrap(),
 7789    );
 7790
 7791    cx.language_registry().add(rust_language.clone());
 7792    cx.update_buffer(|buffer, cx| {
 7793        buffer.set_language(Some(rust_language), cx);
 7794    });
 7795
 7796    cx.set_state(
 7797        &r#"
 7798            let x = ˇ
 7799        "#
 7800        .unindent(),
 7801    );
 7802
 7803    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7804    cx.update_editor(|editor, window, cx| {
 7805        editor.handle_input("\"", window, cx);
 7806    });
 7807    cx.assert_editor_state(
 7808        &r#"
 7809            let x = "ˇ"
 7810        "#
 7811        .unindent(),
 7812    );
 7813
 7814    // Inserting another quotation mark. The cursor moves across the existing
 7815    // automatically-inserted quotation mark.
 7816    cx.update_editor(|editor, window, cx| {
 7817        editor.handle_input("\"", window, cx);
 7818    });
 7819    cx.assert_editor_state(
 7820        &r#"
 7821            let x = ""ˇ
 7822        "#
 7823        .unindent(),
 7824    );
 7825
 7826    // Reset
 7827    cx.set_state(
 7828        &r#"
 7829            let x = ˇ
 7830        "#
 7831        .unindent(),
 7832    );
 7833
 7834    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7835    cx.update_editor(|editor, window, cx| {
 7836        editor.handle_input("\"", window, cx);
 7837        editor.handle_input(" ", window, cx);
 7838        editor.move_left(&Default::default(), window, cx);
 7839        editor.handle_input("\\", window, cx);
 7840        editor.handle_input("\"", window, cx);
 7841    });
 7842    cx.assert_editor_state(
 7843        &r#"
 7844            let x = "\"ˇ "
 7845        "#
 7846        .unindent(),
 7847    );
 7848
 7849    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7850    // mark. Nothing is inserted.
 7851    cx.update_editor(|editor, window, cx| {
 7852        editor.move_right(&Default::default(), window, cx);
 7853        editor.handle_input("\"", window, cx);
 7854    });
 7855    cx.assert_editor_state(
 7856        &r#"
 7857            let x = "\" "ˇ
 7858        "#
 7859        .unindent(),
 7860    );
 7861}
 7862
 7863#[gpui::test]
 7864async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7865    init_test(cx, |_| {});
 7866
 7867    let language = Arc::new(Language::new(
 7868        LanguageConfig {
 7869            brackets: BracketPairConfig {
 7870                pairs: vec![
 7871                    BracketPair {
 7872                        start: "{".to_string(),
 7873                        end: "}".to_string(),
 7874                        close: true,
 7875                        surround: true,
 7876                        newline: true,
 7877                    },
 7878                    BracketPair {
 7879                        start: "/* ".to_string(),
 7880                        end: "*/".to_string(),
 7881                        close: true,
 7882                        surround: true,
 7883                        ..Default::default()
 7884                    },
 7885                ],
 7886                ..Default::default()
 7887            },
 7888            ..Default::default()
 7889        },
 7890        Some(tree_sitter_rust::LANGUAGE.into()),
 7891    ));
 7892
 7893    let text = r#"
 7894        a
 7895        b
 7896        c
 7897    "#
 7898    .unindent();
 7899
 7900    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7901    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7902    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7903    editor
 7904        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7905        .await;
 7906
 7907    editor.update_in(cx, |editor, window, cx| {
 7908        editor.change_selections(None, window, cx, |s| {
 7909            s.select_display_ranges([
 7910                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7911                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7912                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7913            ])
 7914        });
 7915
 7916        editor.handle_input("{", window, cx);
 7917        editor.handle_input("{", window, cx);
 7918        editor.handle_input("{", window, cx);
 7919        assert_eq!(
 7920            editor.text(cx),
 7921            "
 7922                {{{a}}}
 7923                {{{b}}}
 7924                {{{c}}}
 7925            "
 7926            .unindent()
 7927        );
 7928        assert_eq!(
 7929            editor.selections.display_ranges(cx),
 7930            [
 7931                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7932                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7933                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7934            ]
 7935        );
 7936
 7937        editor.undo(&Undo, window, cx);
 7938        editor.undo(&Undo, window, cx);
 7939        editor.undo(&Undo, window, cx);
 7940        assert_eq!(
 7941            editor.text(cx),
 7942            "
 7943                a
 7944                b
 7945                c
 7946            "
 7947            .unindent()
 7948        );
 7949        assert_eq!(
 7950            editor.selections.display_ranges(cx),
 7951            [
 7952                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7953                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7954                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7955            ]
 7956        );
 7957
 7958        // Ensure inserting the first character of a multi-byte bracket pair
 7959        // doesn't surround the selections with the bracket.
 7960        editor.handle_input("/", window, cx);
 7961        assert_eq!(
 7962            editor.text(cx),
 7963            "
 7964                /
 7965                /
 7966                /
 7967            "
 7968            .unindent()
 7969        );
 7970        assert_eq!(
 7971            editor.selections.display_ranges(cx),
 7972            [
 7973                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7974                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7975                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7976            ]
 7977        );
 7978
 7979        editor.undo(&Undo, window, cx);
 7980        assert_eq!(
 7981            editor.text(cx),
 7982            "
 7983                a
 7984                b
 7985                c
 7986            "
 7987            .unindent()
 7988        );
 7989        assert_eq!(
 7990            editor.selections.display_ranges(cx),
 7991            [
 7992                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7993                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7994                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7995            ]
 7996        );
 7997
 7998        // Ensure inserting the last character of a multi-byte bracket pair
 7999        // doesn't surround the selections with the bracket.
 8000        editor.handle_input("*", window, cx);
 8001        assert_eq!(
 8002            editor.text(cx),
 8003            "
 8004                *
 8005                *
 8006                *
 8007            "
 8008            .unindent()
 8009        );
 8010        assert_eq!(
 8011            editor.selections.display_ranges(cx),
 8012            [
 8013                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 8014                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 8015                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 8016            ]
 8017        );
 8018    });
 8019}
 8020
 8021#[gpui::test]
 8022async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 8023    init_test(cx, |_| {});
 8024
 8025    let language = Arc::new(Language::new(
 8026        LanguageConfig {
 8027            brackets: BracketPairConfig {
 8028                pairs: vec![BracketPair {
 8029                    start: "{".to_string(),
 8030                    end: "}".to_string(),
 8031                    close: true,
 8032                    surround: true,
 8033                    newline: true,
 8034                }],
 8035                ..Default::default()
 8036            },
 8037            autoclose_before: "}".to_string(),
 8038            ..Default::default()
 8039        },
 8040        Some(tree_sitter_rust::LANGUAGE.into()),
 8041    ));
 8042
 8043    let text = r#"
 8044        a
 8045        b
 8046        c
 8047    "#
 8048    .unindent();
 8049
 8050    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8051    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8052    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8053    editor
 8054        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8055        .await;
 8056
 8057    editor.update_in(cx, |editor, window, cx| {
 8058        editor.change_selections(None, window, cx, |s| {
 8059            s.select_ranges([
 8060                Point::new(0, 1)..Point::new(0, 1),
 8061                Point::new(1, 1)..Point::new(1, 1),
 8062                Point::new(2, 1)..Point::new(2, 1),
 8063            ])
 8064        });
 8065
 8066        editor.handle_input("{", window, cx);
 8067        editor.handle_input("{", window, cx);
 8068        editor.handle_input("_", window, cx);
 8069        assert_eq!(
 8070            editor.text(cx),
 8071            "
 8072                a{{_}}
 8073                b{{_}}
 8074                c{{_}}
 8075            "
 8076            .unindent()
 8077        );
 8078        assert_eq!(
 8079            editor.selections.ranges::<Point>(cx),
 8080            [
 8081                Point::new(0, 4)..Point::new(0, 4),
 8082                Point::new(1, 4)..Point::new(1, 4),
 8083                Point::new(2, 4)..Point::new(2, 4)
 8084            ]
 8085        );
 8086
 8087        editor.backspace(&Default::default(), window, cx);
 8088        editor.backspace(&Default::default(), window, cx);
 8089        assert_eq!(
 8090            editor.text(cx),
 8091            "
 8092                a{}
 8093                b{}
 8094                c{}
 8095            "
 8096            .unindent()
 8097        );
 8098        assert_eq!(
 8099            editor.selections.ranges::<Point>(cx),
 8100            [
 8101                Point::new(0, 2)..Point::new(0, 2),
 8102                Point::new(1, 2)..Point::new(1, 2),
 8103                Point::new(2, 2)..Point::new(2, 2)
 8104            ]
 8105        );
 8106
 8107        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8108        assert_eq!(
 8109            editor.text(cx),
 8110            "
 8111                a
 8112                b
 8113                c
 8114            "
 8115            .unindent()
 8116        );
 8117        assert_eq!(
 8118            editor.selections.ranges::<Point>(cx),
 8119            [
 8120                Point::new(0, 1)..Point::new(0, 1),
 8121                Point::new(1, 1)..Point::new(1, 1),
 8122                Point::new(2, 1)..Point::new(2, 1)
 8123            ]
 8124        );
 8125    });
 8126}
 8127
 8128#[gpui::test]
 8129async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8130    init_test(cx, |settings| {
 8131        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8132    });
 8133
 8134    let mut cx = EditorTestContext::new(cx).await;
 8135
 8136    let language = Arc::new(Language::new(
 8137        LanguageConfig {
 8138            brackets: BracketPairConfig {
 8139                pairs: vec![
 8140                    BracketPair {
 8141                        start: "{".to_string(),
 8142                        end: "}".to_string(),
 8143                        close: true,
 8144                        surround: true,
 8145                        newline: true,
 8146                    },
 8147                    BracketPair {
 8148                        start: "(".to_string(),
 8149                        end: ")".to_string(),
 8150                        close: true,
 8151                        surround: true,
 8152                        newline: true,
 8153                    },
 8154                    BracketPair {
 8155                        start: "[".to_string(),
 8156                        end: "]".to_string(),
 8157                        close: false,
 8158                        surround: true,
 8159                        newline: true,
 8160                    },
 8161                ],
 8162                ..Default::default()
 8163            },
 8164            autoclose_before: "})]".to_string(),
 8165            ..Default::default()
 8166        },
 8167        Some(tree_sitter_rust::LANGUAGE.into()),
 8168    ));
 8169
 8170    cx.language_registry().add(language.clone());
 8171    cx.update_buffer(|buffer, cx| {
 8172        buffer.set_language(Some(language), cx);
 8173    });
 8174
 8175    cx.set_state(
 8176        &"
 8177            {(ˇ)}
 8178            [[ˇ]]
 8179            {(ˇ)}
 8180        "
 8181        .unindent(),
 8182    );
 8183
 8184    cx.update_editor(|editor, window, cx| {
 8185        editor.backspace(&Default::default(), window, cx);
 8186        editor.backspace(&Default::default(), window, cx);
 8187    });
 8188
 8189    cx.assert_editor_state(
 8190        &"
 8191            ˇ
 8192            ˇ]]
 8193            ˇ
 8194        "
 8195        .unindent(),
 8196    );
 8197
 8198    cx.update_editor(|editor, window, cx| {
 8199        editor.handle_input("{", window, cx);
 8200        editor.handle_input("{", window, cx);
 8201        editor.move_right(&MoveRight, window, cx);
 8202        editor.move_right(&MoveRight, window, cx);
 8203        editor.move_left(&MoveLeft, window, cx);
 8204        editor.move_left(&MoveLeft, window, cx);
 8205        editor.backspace(&Default::default(), window, cx);
 8206    });
 8207
 8208    cx.assert_editor_state(
 8209        &"
 8210            {ˇ}
 8211            {ˇ}]]
 8212            {ˇ}
 8213        "
 8214        .unindent(),
 8215    );
 8216
 8217    cx.update_editor(|editor, window, cx| {
 8218        editor.backspace(&Default::default(), window, cx);
 8219    });
 8220
 8221    cx.assert_editor_state(
 8222        &"
 8223            ˇ
 8224            ˇ]]
 8225            ˇ
 8226        "
 8227        .unindent(),
 8228    );
 8229}
 8230
 8231#[gpui::test]
 8232async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8233    init_test(cx, |_| {});
 8234
 8235    let language = Arc::new(Language::new(
 8236        LanguageConfig::default(),
 8237        Some(tree_sitter_rust::LANGUAGE.into()),
 8238    ));
 8239
 8240    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8241    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8242    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8243    editor
 8244        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8245        .await;
 8246
 8247    editor.update_in(cx, |editor, window, cx| {
 8248        editor.set_auto_replace_emoji_shortcode(true);
 8249
 8250        editor.handle_input("Hello ", window, cx);
 8251        editor.handle_input(":wave", window, cx);
 8252        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8253
 8254        editor.handle_input(":", window, cx);
 8255        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8256
 8257        editor.handle_input(" :smile", window, cx);
 8258        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8259
 8260        editor.handle_input(":", window, cx);
 8261        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8262
 8263        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8264        editor.handle_input(":wave", window, cx);
 8265        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8266
 8267        editor.handle_input(":", window, cx);
 8268        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8269
 8270        editor.handle_input(":1", window, cx);
 8271        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8272
 8273        editor.handle_input(":", window, cx);
 8274        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8275
 8276        // Ensure shortcode does not get replaced when it is part of a word
 8277        editor.handle_input(" Test:wave", window, cx);
 8278        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8279
 8280        editor.handle_input(":", window, cx);
 8281        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8282
 8283        editor.set_auto_replace_emoji_shortcode(false);
 8284
 8285        // Ensure shortcode does not get replaced when auto replace is off
 8286        editor.handle_input(" :wave", window, cx);
 8287        assert_eq!(
 8288            editor.text(cx),
 8289            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8290        );
 8291
 8292        editor.handle_input(":", window, cx);
 8293        assert_eq!(
 8294            editor.text(cx),
 8295            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8296        );
 8297    });
 8298}
 8299
 8300#[gpui::test]
 8301async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8302    init_test(cx, |_| {});
 8303
 8304    let (text, insertion_ranges) = marked_text_ranges(
 8305        indoc! {"
 8306            ˇ
 8307        "},
 8308        false,
 8309    );
 8310
 8311    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8312    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8313
 8314    _ = editor.update_in(cx, |editor, window, cx| {
 8315        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8316
 8317        editor
 8318            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8319            .unwrap();
 8320
 8321        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8322            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8323            assert_eq!(editor.text(cx), expected_text);
 8324            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8325        }
 8326
 8327        assert(
 8328            editor,
 8329            cx,
 8330            indoc! {"
 8331            type «» =•
 8332            "},
 8333        );
 8334
 8335        assert!(editor.context_menu_visible(), "There should be a matches");
 8336    });
 8337}
 8338
 8339#[gpui::test]
 8340async fn test_snippets(cx: &mut TestAppContext) {
 8341    init_test(cx, |_| {});
 8342
 8343    let (text, insertion_ranges) = marked_text_ranges(
 8344        indoc! {"
 8345            a.ˇ b
 8346            a.ˇ b
 8347            a.ˇ b
 8348        "},
 8349        false,
 8350    );
 8351
 8352    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8353    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8354
 8355    editor.update_in(cx, |editor, window, cx| {
 8356        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8357
 8358        editor
 8359            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8360            .unwrap();
 8361
 8362        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8363            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8364            assert_eq!(editor.text(cx), expected_text);
 8365            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8366        }
 8367
 8368        assert(
 8369            editor,
 8370            cx,
 8371            indoc! {"
 8372                a.f(«one», two, «three») b
 8373                a.f(«one», two, «three») b
 8374                a.f(«one», two, «three») b
 8375            "},
 8376        );
 8377
 8378        // Can't move earlier than the first tab stop
 8379        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8380        assert(
 8381            editor,
 8382            cx,
 8383            indoc! {"
 8384                a.f(«one», two, «three») b
 8385                a.f(«one», two, «three») b
 8386                a.f(«one», two, «three») b
 8387            "},
 8388        );
 8389
 8390        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8391        assert(
 8392            editor,
 8393            cx,
 8394            indoc! {"
 8395                a.f(one, «two», three) b
 8396                a.f(one, «two», three) b
 8397                a.f(one, «two», three) b
 8398            "},
 8399        );
 8400
 8401        editor.move_to_prev_snippet_tabstop(window, cx);
 8402        assert(
 8403            editor,
 8404            cx,
 8405            indoc! {"
 8406                a.f(«one», two, «three») b
 8407                a.f(«one», two, «three») b
 8408                a.f(«one», two, «three») b
 8409            "},
 8410        );
 8411
 8412        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8413        assert(
 8414            editor,
 8415            cx,
 8416            indoc! {"
 8417                a.f(one, «two», three) b
 8418                a.f(one, «two», three) b
 8419                a.f(one, «two», three) b
 8420            "},
 8421        );
 8422        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8423        assert(
 8424            editor,
 8425            cx,
 8426            indoc! {"
 8427                a.f(one, two, three)ˇ b
 8428                a.f(one, two, three)ˇ b
 8429                a.f(one, two, three)ˇ b
 8430            "},
 8431        );
 8432
 8433        // As soon as the last tab stop is reached, snippet state is gone
 8434        editor.move_to_prev_snippet_tabstop(window, cx);
 8435        assert(
 8436            editor,
 8437            cx,
 8438            indoc! {"
 8439                a.f(one, two, three)ˇ b
 8440                a.f(one, two, three)ˇ b
 8441                a.f(one, two, three)ˇ b
 8442            "},
 8443        );
 8444    });
 8445}
 8446
 8447#[gpui::test]
 8448async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8449    init_test(cx, |_| {});
 8450
 8451    let fs = FakeFs::new(cx.executor());
 8452    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8453
 8454    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8455
 8456    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8457    language_registry.add(rust_lang());
 8458    let mut fake_servers = language_registry.register_fake_lsp(
 8459        "Rust",
 8460        FakeLspAdapter {
 8461            capabilities: lsp::ServerCapabilities {
 8462                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8463                ..Default::default()
 8464            },
 8465            ..Default::default()
 8466        },
 8467    );
 8468
 8469    let buffer = project
 8470        .update(cx, |project, cx| {
 8471            project.open_local_buffer(path!("/file.rs"), cx)
 8472        })
 8473        .await
 8474        .unwrap();
 8475
 8476    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8477    let (editor, cx) = cx.add_window_view(|window, cx| {
 8478        build_editor_with_project(project.clone(), buffer, window, cx)
 8479    });
 8480    editor.update_in(cx, |editor, window, cx| {
 8481        editor.set_text("one\ntwo\nthree\n", window, cx)
 8482    });
 8483    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8484
 8485    cx.executor().start_waiting();
 8486    let fake_server = fake_servers.next().await.unwrap();
 8487
 8488    {
 8489        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8490            move |params, _| async move {
 8491                assert_eq!(
 8492                    params.text_document.uri,
 8493                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8494                );
 8495                assert_eq!(params.options.tab_size, 4);
 8496                Ok(Some(vec![lsp::TextEdit::new(
 8497                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8498                    ", ".to_string(),
 8499                )]))
 8500            },
 8501        );
 8502        let save = editor
 8503            .update_in(cx, |editor, window, cx| {
 8504                editor.save(true, project.clone(), window, cx)
 8505            })
 8506            .unwrap();
 8507        cx.executor().start_waiting();
 8508        save.await;
 8509
 8510        assert_eq!(
 8511            editor.update(cx, |editor, cx| editor.text(cx)),
 8512            "one, two\nthree\n"
 8513        );
 8514        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8515    }
 8516
 8517    {
 8518        editor.update_in(cx, |editor, window, cx| {
 8519            editor.set_text("one\ntwo\nthree\n", window, cx)
 8520        });
 8521        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8522
 8523        // Ensure we can still save even if formatting hangs.
 8524        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8525            move |params, _| async move {
 8526                assert_eq!(
 8527                    params.text_document.uri,
 8528                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8529                );
 8530                futures::future::pending::<()>().await;
 8531                unreachable!()
 8532            },
 8533        );
 8534        let save = editor
 8535            .update_in(cx, |editor, window, cx| {
 8536                editor.save(true, project.clone(), window, cx)
 8537            })
 8538            .unwrap();
 8539        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8540        cx.executor().start_waiting();
 8541        save.await;
 8542        assert_eq!(
 8543            editor.update(cx, |editor, cx| editor.text(cx)),
 8544            "one\ntwo\nthree\n"
 8545        );
 8546    }
 8547
 8548    // For non-dirty buffer, no formatting request should be sent
 8549    {
 8550        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8551
 8552        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8553            panic!("Should not be invoked on non-dirty buffer");
 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
 8564    // Set rust language override and assert overridden tabsize is sent to language server
 8565    update_test_language_settings(cx, |settings| {
 8566        settings.languages.insert(
 8567            "Rust".into(),
 8568            LanguageSettingsContent {
 8569                tab_size: NonZeroU32::new(8),
 8570                ..Default::default()
 8571            },
 8572        );
 8573    });
 8574
 8575    {
 8576        editor.update_in(cx, |editor, window, cx| {
 8577            editor.set_text("somehting_new\n", window, cx)
 8578        });
 8579        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8580        let _formatting_request_signal = fake_server
 8581            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8582                assert_eq!(
 8583                    params.text_document.uri,
 8584                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8585                );
 8586                assert_eq!(params.options.tab_size, 8);
 8587                Ok(Some(vec![]))
 8588            });
 8589        let save = editor
 8590            .update_in(cx, |editor, window, cx| {
 8591                editor.save(true, project.clone(), window, cx)
 8592            })
 8593            .unwrap();
 8594        cx.executor().start_waiting();
 8595        save.await;
 8596    }
 8597}
 8598
 8599#[gpui::test]
 8600async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8601    init_test(cx, |_| {});
 8602
 8603    let cols = 4;
 8604    let rows = 10;
 8605    let sample_text_1 = sample_text(rows, cols, 'a');
 8606    assert_eq!(
 8607        sample_text_1,
 8608        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8609    );
 8610    let sample_text_2 = sample_text(rows, cols, 'l');
 8611    assert_eq!(
 8612        sample_text_2,
 8613        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8614    );
 8615    let sample_text_3 = sample_text(rows, cols, 'v');
 8616    assert_eq!(
 8617        sample_text_3,
 8618        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8619    );
 8620
 8621    let fs = FakeFs::new(cx.executor());
 8622    fs.insert_tree(
 8623        path!("/a"),
 8624        json!({
 8625            "main.rs": sample_text_1,
 8626            "other.rs": sample_text_2,
 8627            "lib.rs": sample_text_3,
 8628        }),
 8629    )
 8630    .await;
 8631
 8632    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8633    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8634    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8635
 8636    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8637    language_registry.add(rust_lang());
 8638    let mut fake_servers = language_registry.register_fake_lsp(
 8639        "Rust",
 8640        FakeLspAdapter {
 8641            capabilities: lsp::ServerCapabilities {
 8642                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8643                ..Default::default()
 8644            },
 8645            ..Default::default()
 8646        },
 8647    );
 8648
 8649    let worktree = project.update(cx, |project, cx| {
 8650        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8651        assert_eq!(worktrees.len(), 1);
 8652        worktrees.pop().unwrap()
 8653    });
 8654    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8655
 8656    let buffer_1 = project
 8657        .update(cx, |project, cx| {
 8658            project.open_buffer((worktree_id, "main.rs"), cx)
 8659        })
 8660        .await
 8661        .unwrap();
 8662    let buffer_2 = project
 8663        .update(cx, |project, cx| {
 8664            project.open_buffer((worktree_id, "other.rs"), cx)
 8665        })
 8666        .await
 8667        .unwrap();
 8668    let buffer_3 = project
 8669        .update(cx, |project, cx| {
 8670            project.open_buffer((worktree_id, "lib.rs"), cx)
 8671        })
 8672        .await
 8673        .unwrap();
 8674
 8675    let multi_buffer = cx.new(|cx| {
 8676        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8677        multi_buffer.push_excerpts(
 8678            buffer_1.clone(),
 8679            [
 8680                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8681                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8682                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8683            ],
 8684            cx,
 8685        );
 8686        multi_buffer.push_excerpts(
 8687            buffer_2.clone(),
 8688            [
 8689                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8690                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8691                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8692            ],
 8693            cx,
 8694        );
 8695        multi_buffer.push_excerpts(
 8696            buffer_3.clone(),
 8697            [
 8698                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8699                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8700                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8701            ],
 8702            cx,
 8703        );
 8704        multi_buffer
 8705    });
 8706    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8707        Editor::new(
 8708            EditorMode::full(),
 8709            multi_buffer,
 8710            Some(project.clone()),
 8711            window,
 8712            cx,
 8713        )
 8714    });
 8715
 8716    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8717        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8718            s.select_ranges(Some(1..2))
 8719        });
 8720        editor.insert("|one|two|three|", window, cx);
 8721    });
 8722    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8723    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8724        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8725            s.select_ranges(Some(60..70))
 8726        });
 8727        editor.insert("|four|five|six|", window, cx);
 8728    });
 8729    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8730
 8731    // First two buffers should be edited, but not the third one.
 8732    assert_eq!(
 8733        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8734        "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}",
 8735    );
 8736    buffer_1.update(cx, |buffer, _| {
 8737        assert!(buffer.is_dirty());
 8738        assert_eq!(
 8739            buffer.text(),
 8740            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8741        )
 8742    });
 8743    buffer_2.update(cx, |buffer, _| {
 8744        assert!(buffer.is_dirty());
 8745        assert_eq!(
 8746            buffer.text(),
 8747            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8748        )
 8749    });
 8750    buffer_3.update(cx, |buffer, _| {
 8751        assert!(!buffer.is_dirty());
 8752        assert_eq!(buffer.text(), sample_text_3,)
 8753    });
 8754    cx.executor().run_until_parked();
 8755
 8756    cx.executor().start_waiting();
 8757    let save = multi_buffer_editor
 8758        .update_in(cx, |editor, window, cx| {
 8759            editor.save(true, project.clone(), window, cx)
 8760        })
 8761        .unwrap();
 8762
 8763    let fake_server = fake_servers.next().await.unwrap();
 8764    fake_server
 8765        .server
 8766        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8767            Ok(Some(vec![lsp::TextEdit::new(
 8768                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8769                format!("[{} formatted]", params.text_document.uri),
 8770            )]))
 8771        })
 8772        .detach();
 8773    save.await;
 8774
 8775    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8776    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8777    assert_eq!(
 8778        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8779        uri!(
 8780            "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}"
 8781        ),
 8782    );
 8783    buffer_1.update(cx, |buffer, _| {
 8784        assert!(!buffer.is_dirty());
 8785        assert_eq!(
 8786            buffer.text(),
 8787            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8788        )
 8789    });
 8790    buffer_2.update(cx, |buffer, _| {
 8791        assert!(!buffer.is_dirty());
 8792        assert_eq!(
 8793            buffer.text(),
 8794            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8795        )
 8796    });
 8797    buffer_3.update(cx, |buffer, _| {
 8798        assert!(!buffer.is_dirty());
 8799        assert_eq!(buffer.text(), sample_text_3,)
 8800    });
 8801}
 8802
 8803#[gpui::test]
 8804async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8805    init_test(cx, |_| {});
 8806
 8807    let fs = FakeFs::new(cx.executor());
 8808    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8809
 8810    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8811
 8812    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8813    language_registry.add(rust_lang());
 8814    let mut fake_servers = language_registry.register_fake_lsp(
 8815        "Rust",
 8816        FakeLspAdapter {
 8817            capabilities: lsp::ServerCapabilities {
 8818                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8819                ..Default::default()
 8820            },
 8821            ..Default::default()
 8822        },
 8823    );
 8824
 8825    let buffer = project
 8826        .update(cx, |project, cx| {
 8827            project.open_local_buffer(path!("/file.rs"), cx)
 8828        })
 8829        .await
 8830        .unwrap();
 8831
 8832    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8833    let (editor, cx) = cx.add_window_view(|window, cx| {
 8834        build_editor_with_project(project.clone(), buffer, window, cx)
 8835    });
 8836    editor.update_in(cx, |editor, window, cx| {
 8837        editor.set_text("one\ntwo\nthree\n", window, cx)
 8838    });
 8839    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8840
 8841    cx.executor().start_waiting();
 8842    let fake_server = fake_servers.next().await.unwrap();
 8843
 8844    let save = editor
 8845        .update_in(cx, |editor, window, cx| {
 8846            editor.save(true, project.clone(), window, cx)
 8847        })
 8848        .unwrap();
 8849    fake_server
 8850        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8851            assert_eq!(
 8852                params.text_document.uri,
 8853                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8854            );
 8855            assert_eq!(params.options.tab_size, 4);
 8856            Ok(Some(vec![lsp::TextEdit::new(
 8857                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8858                ", ".to_string(),
 8859            )]))
 8860        })
 8861        .next()
 8862        .await;
 8863    cx.executor().start_waiting();
 8864    save.await;
 8865    assert_eq!(
 8866        editor.update(cx, |editor, cx| editor.text(cx)),
 8867        "one, two\nthree\n"
 8868    );
 8869    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8870
 8871    editor.update_in(cx, |editor, window, cx| {
 8872        editor.set_text("one\ntwo\nthree\n", window, cx)
 8873    });
 8874    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8875
 8876    // Ensure we can still save even if formatting hangs.
 8877    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8878        move |params, _| async move {
 8879            assert_eq!(
 8880                params.text_document.uri,
 8881                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8882            );
 8883            futures::future::pending::<()>().await;
 8884            unreachable!()
 8885        },
 8886    );
 8887    let save = editor
 8888        .update_in(cx, |editor, window, cx| {
 8889            editor.save(true, project.clone(), window, cx)
 8890        })
 8891        .unwrap();
 8892    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8893    cx.executor().start_waiting();
 8894    save.await;
 8895    assert_eq!(
 8896        editor.update(cx, |editor, cx| editor.text(cx)),
 8897        "one\ntwo\nthree\n"
 8898    );
 8899    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8900
 8901    // For non-dirty buffer, no formatting request should be sent
 8902    let save = editor
 8903        .update_in(cx, |editor, window, cx| {
 8904            editor.save(true, project.clone(), window, cx)
 8905        })
 8906        .unwrap();
 8907    let _pending_format_request = fake_server
 8908        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8909            panic!("Should not be invoked on non-dirty buffer");
 8910        })
 8911        .next();
 8912    cx.executor().start_waiting();
 8913    save.await;
 8914
 8915    // Set Rust language override and assert overridden tabsize is sent to language server
 8916    update_test_language_settings(cx, |settings| {
 8917        settings.languages.insert(
 8918            "Rust".into(),
 8919            LanguageSettingsContent {
 8920                tab_size: NonZeroU32::new(8),
 8921                ..Default::default()
 8922            },
 8923        );
 8924    });
 8925
 8926    editor.update_in(cx, |editor, window, cx| {
 8927        editor.set_text("somehting_new\n", window, cx)
 8928    });
 8929    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8930    let save = editor
 8931        .update_in(cx, |editor, window, cx| {
 8932            editor.save(true, project.clone(), window, cx)
 8933        })
 8934        .unwrap();
 8935    fake_server
 8936        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8937            assert_eq!(
 8938                params.text_document.uri,
 8939                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8940            );
 8941            assert_eq!(params.options.tab_size, 8);
 8942            Ok(Some(vec![]))
 8943        })
 8944        .next()
 8945        .await;
 8946    cx.executor().start_waiting();
 8947    save.await;
 8948}
 8949
 8950#[gpui::test]
 8951async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8952    init_test(cx, |settings| {
 8953        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8954            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8955        ))
 8956    });
 8957
 8958    let fs = FakeFs::new(cx.executor());
 8959    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8960
 8961    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8962
 8963    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8964    language_registry.add(Arc::new(Language::new(
 8965        LanguageConfig {
 8966            name: "Rust".into(),
 8967            matcher: LanguageMatcher {
 8968                path_suffixes: vec!["rs".to_string()],
 8969                ..Default::default()
 8970            },
 8971            ..LanguageConfig::default()
 8972        },
 8973        Some(tree_sitter_rust::LANGUAGE.into()),
 8974    )));
 8975    update_test_language_settings(cx, |settings| {
 8976        // Enable Prettier formatting for the same buffer, and ensure
 8977        // LSP is called instead of Prettier.
 8978        settings.defaults.prettier = Some(PrettierSettings {
 8979            allowed: true,
 8980            ..PrettierSettings::default()
 8981        });
 8982    });
 8983    let mut fake_servers = language_registry.register_fake_lsp(
 8984        "Rust",
 8985        FakeLspAdapter {
 8986            capabilities: lsp::ServerCapabilities {
 8987                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8988                ..Default::default()
 8989            },
 8990            ..Default::default()
 8991        },
 8992    );
 8993
 8994    let buffer = project
 8995        .update(cx, |project, cx| {
 8996            project.open_local_buffer(path!("/file.rs"), cx)
 8997        })
 8998        .await
 8999        .unwrap();
 9000
 9001    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9002    let (editor, cx) = cx.add_window_view(|window, cx| {
 9003        build_editor_with_project(project.clone(), buffer, window, cx)
 9004    });
 9005    editor.update_in(cx, |editor, window, cx| {
 9006        editor.set_text("one\ntwo\nthree\n", window, cx)
 9007    });
 9008
 9009    cx.executor().start_waiting();
 9010    let fake_server = fake_servers.next().await.unwrap();
 9011
 9012    let format = editor
 9013        .update_in(cx, |editor, window, cx| {
 9014            editor.perform_format(
 9015                project.clone(),
 9016                FormatTrigger::Manual,
 9017                FormatTarget::Buffers,
 9018                window,
 9019                cx,
 9020            )
 9021        })
 9022        .unwrap();
 9023    fake_server
 9024        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 9025            assert_eq!(
 9026                params.text_document.uri,
 9027                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9028            );
 9029            assert_eq!(params.options.tab_size, 4);
 9030            Ok(Some(vec![lsp::TextEdit::new(
 9031                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9032                ", ".to_string(),
 9033            )]))
 9034        })
 9035        .next()
 9036        .await;
 9037    cx.executor().start_waiting();
 9038    format.await;
 9039    assert_eq!(
 9040        editor.update(cx, |editor, cx| editor.text(cx)),
 9041        "one, two\nthree\n"
 9042    );
 9043
 9044    editor.update_in(cx, |editor, window, cx| {
 9045        editor.set_text("one\ntwo\nthree\n", window, cx)
 9046    });
 9047    // Ensure we don't lock if formatting hangs.
 9048    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9049        move |params, _| async move {
 9050            assert_eq!(
 9051                params.text_document.uri,
 9052                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9053            );
 9054            futures::future::pending::<()>().await;
 9055            unreachable!()
 9056        },
 9057    );
 9058    let format = editor
 9059        .update_in(cx, |editor, window, cx| {
 9060            editor.perform_format(
 9061                project,
 9062                FormatTrigger::Manual,
 9063                FormatTarget::Buffers,
 9064                window,
 9065                cx,
 9066            )
 9067        })
 9068        .unwrap();
 9069    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9070    cx.executor().start_waiting();
 9071    format.await;
 9072    assert_eq!(
 9073        editor.update(cx, |editor, cx| editor.text(cx)),
 9074        "one\ntwo\nthree\n"
 9075    );
 9076}
 9077
 9078#[gpui::test]
 9079async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9080    init_test(cx, |settings| {
 9081        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9082        settings.defaults.formatter =
 9083            Some(language_settings::SelectedFormatter::List(FormatterList(
 9084                vec![
 9085                    Formatter::LanguageServer { name: None },
 9086                    Formatter::CodeActions(
 9087                        [
 9088                            ("code-action-1".into(), true),
 9089                            ("code-action-2".into(), true),
 9090                        ]
 9091                        .into_iter()
 9092                        .collect(),
 9093                    ),
 9094                ]
 9095                .into(),
 9096            )))
 9097    });
 9098
 9099    let fs = FakeFs::new(cx.executor());
 9100    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9101        .await;
 9102
 9103    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9104    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9105    language_registry.add(rust_lang());
 9106
 9107    let mut fake_servers = language_registry.register_fake_lsp(
 9108        "Rust",
 9109        FakeLspAdapter {
 9110            capabilities: lsp::ServerCapabilities {
 9111                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9112                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9113                    commands: vec!["the-command-for-code-action-1".into()],
 9114                    ..Default::default()
 9115                }),
 9116                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9117                ..Default::default()
 9118            },
 9119            ..Default::default()
 9120        },
 9121    );
 9122
 9123    let buffer = project
 9124        .update(cx, |project, cx| {
 9125            project.open_local_buffer(path!("/file.rs"), cx)
 9126        })
 9127        .await
 9128        .unwrap();
 9129
 9130    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9131    let (editor, cx) = cx.add_window_view(|window, cx| {
 9132        build_editor_with_project(project.clone(), buffer, window, cx)
 9133    });
 9134
 9135    cx.executor().start_waiting();
 9136
 9137    let fake_server = fake_servers.next().await.unwrap();
 9138    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9139        move |_params, _| async move {
 9140            Ok(Some(vec![lsp::TextEdit::new(
 9141                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9142                "applied-formatting\n".to_string(),
 9143            )]))
 9144        },
 9145    );
 9146    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9147        move |params, _| async move {
 9148            assert_eq!(
 9149                params.context.only,
 9150                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9151            );
 9152            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9153            Ok(Some(vec![
 9154                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9155                    kind: Some("code-action-1".into()),
 9156                    edit: Some(lsp::WorkspaceEdit::new(
 9157                        [(
 9158                            uri.clone(),
 9159                            vec![lsp::TextEdit::new(
 9160                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9161                                "applied-code-action-1-edit\n".to_string(),
 9162                            )],
 9163                        )]
 9164                        .into_iter()
 9165                        .collect(),
 9166                    )),
 9167                    command: Some(lsp::Command {
 9168                        command: "the-command-for-code-action-1".into(),
 9169                        ..Default::default()
 9170                    }),
 9171                    ..Default::default()
 9172                }),
 9173                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9174                    kind: Some("code-action-2".into()),
 9175                    edit: Some(lsp::WorkspaceEdit::new(
 9176                        [(
 9177                            uri.clone(),
 9178                            vec![lsp::TextEdit::new(
 9179                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9180                                "applied-code-action-2-edit\n".to_string(),
 9181                            )],
 9182                        )]
 9183                        .into_iter()
 9184                        .collect(),
 9185                    )),
 9186                    ..Default::default()
 9187                }),
 9188            ]))
 9189        },
 9190    );
 9191
 9192    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9193        move |params, _| async move { Ok(params) }
 9194    });
 9195
 9196    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9197    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9198        let fake = fake_server.clone();
 9199        let lock = command_lock.clone();
 9200        move |params, _| {
 9201            assert_eq!(params.command, "the-command-for-code-action-1");
 9202            let fake = fake.clone();
 9203            let lock = lock.clone();
 9204            async move {
 9205                lock.lock().await;
 9206                fake.server
 9207                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9208                        label: None,
 9209                        edit: lsp::WorkspaceEdit {
 9210                            changes: Some(
 9211                                [(
 9212                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9213                                    vec![lsp::TextEdit {
 9214                                        range: lsp::Range::new(
 9215                                            lsp::Position::new(0, 0),
 9216                                            lsp::Position::new(0, 0),
 9217                                        ),
 9218                                        new_text: "applied-code-action-1-command\n".into(),
 9219                                    }],
 9220                                )]
 9221                                .into_iter()
 9222                                .collect(),
 9223                            ),
 9224                            ..Default::default()
 9225                        },
 9226                    })
 9227                    .await
 9228                    .into_response()
 9229                    .unwrap();
 9230                Ok(Some(json!(null)))
 9231            }
 9232        }
 9233    });
 9234
 9235    cx.executor().start_waiting();
 9236    editor
 9237        .update_in(cx, |editor, window, cx| {
 9238            editor.perform_format(
 9239                project.clone(),
 9240                FormatTrigger::Manual,
 9241                FormatTarget::Buffers,
 9242                window,
 9243                cx,
 9244            )
 9245        })
 9246        .unwrap()
 9247        .await;
 9248    editor.update(cx, |editor, cx| {
 9249        assert_eq!(
 9250            editor.text(cx),
 9251            r#"
 9252                applied-code-action-2-edit
 9253                applied-code-action-1-command
 9254                applied-code-action-1-edit
 9255                applied-formatting
 9256                one
 9257                two
 9258                three
 9259            "#
 9260            .unindent()
 9261        );
 9262    });
 9263
 9264    editor.update_in(cx, |editor, window, cx| {
 9265        editor.undo(&Default::default(), window, cx);
 9266        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9267    });
 9268
 9269    // Perform a manual edit while waiting for an LSP command
 9270    // that's being run as part of a formatting code action.
 9271    let lock_guard = command_lock.lock().await;
 9272    let format = editor
 9273        .update_in(cx, |editor, window, cx| {
 9274            editor.perform_format(
 9275                project.clone(),
 9276                FormatTrigger::Manual,
 9277                FormatTarget::Buffers,
 9278                window,
 9279                cx,
 9280            )
 9281        })
 9282        .unwrap();
 9283    cx.run_until_parked();
 9284    editor.update(cx, |editor, cx| {
 9285        assert_eq!(
 9286            editor.text(cx),
 9287            r#"
 9288                applied-code-action-1-edit
 9289                applied-formatting
 9290                one
 9291                two
 9292                three
 9293            "#
 9294            .unindent()
 9295        );
 9296
 9297        editor.buffer.update(cx, |buffer, cx| {
 9298            let ix = buffer.len(cx);
 9299            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9300        });
 9301    });
 9302
 9303    // Allow the LSP command to proceed. Because the buffer was edited,
 9304    // the second code action will not be run.
 9305    drop(lock_guard);
 9306    format.await;
 9307    editor.update_in(cx, |editor, window, cx| {
 9308        assert_eq!(
 9309            editor.text(cx),
 9310            r#"
 9311                applied-code-action-1-command
 9312                applied-code-action-1-edit
 9313                applied-formatting
 9314                one
 9315                two
 9316                three
 9317                edited
 9318            "#
 9319            .unindent()
 9320        );
 9321
 9322        // The manual edit is undone first, because it is the last thing the user did
 9323        // (even though the command completed afterwards).
 9324        editor.undo(&Default::default(), window, cx);
 9325        assert_eq!(
 9326            editor.text(cx),
 9327            r#"
 9328                applied-code-action-1-command
 9329                applied-code-action-1-edit
 9330                applied-formatting
 9331                one
 9332                two
 9333                three
 9334            "#
 9335            .unindent()
 9336        );
 9337
 9338        // All the formatting (including the command, which completed after the manual edit)
 9339        // is undone together.
 9340        editor.undo(&Default::default(), window, cx);
 9341        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9342    });
 9343}
 9344
 9345#[gpui::test]
 9346async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9347    init_test(cx, |settings| {
 9348        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9349            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9350        ))
 9351    });
 9352
 9353    let fs = FakeFs::new(cx.executor());
 9354    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9355
 9356    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9357
 9358    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9359    language_registry.add(Arc::new(Language::new(
 9360        LanguageConfig {
 9361            name: "TypeScript".into(),
 9362            matcher: LanguageMatcher {
 9363                path_suffixes: vec!["ts".to_string()],
 9364                ..Default::default()
 9365            },
 9366            ..LanguageConfig::default()
 9367        },
 9368        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9369    )));
 9370    update_test_language_settings(cx, |settings| {
 9371        settings.defaults.prettier = Some(PrettierSettings {
 9372            allowed: true,
 9373            ..PrettierSettings::default()
 9374        });
 9375    });
 9376    let mut fake_servers = language_registry.register_fake_lsp(
 9377        "TypeScript",
 9378        FakeLspAdapter {
 9379            capabilities: lsp::ServerCapabilities {
 9380                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9381                ..Default::default()
 9382            },
 9383            ..Default::default()
 9384        },
 9385    );
 9386
 9387    let buffer = project
 9388        .update(cx, |project, cx| {
 9389            project.open_local_buffer(path!("/file.ts"), cx)
 9390        })
 9391        .await
 9392        .unwrap();
 9393
 9394    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9395    let (editor, cx) = cx.add_window_view(|window, cx| {
 9396        build_editor_with_project(project.clone(), buffer, window, cx)
 9397    });
 9398    editor.update_in(cx, |editor, window, cx| {
 9399        editor.set_text(
 9400            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9401            window,
 9402            cx,
 9403        )
 9404    });
 9405
 9406    cx.executor().start_waiting();
 9407    let fake_server = fake_servers.next().await.unwrap();
 9408
 9409    let format = editor
 9410        .update_in(cx, |editor, window, cx| {
 9411            editor.perform_code_action_kind(
 9412                project.clone(),
 9413                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9414                window,
 9415                cx,
 9416            )
 9417        })
 9418        .unwrap();
 9419    fake_server
 9420        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9421            assert_eq!(
 9422                params.text_document.uri,
 9423                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9424            );
 9425            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9426                lsp::CodeAction {
 9427                    title: "Organize Imports".to_string(),
 9428                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9429                    edit: Some(lsp::WorkspaceEdit {
 9430                        changes: Some(
 9431                            [(
 9432                                params.text_document.uri.clone(),
 9433                                vec![lsp::TextEdit::new(
 9434                                    lsp::Range::new(
 9435                                        lsp::Position::new(1, 0),
 9436                                        lsp::Position::new(2, 0),
 9437                                    ),
 9438                                    "".to_string(),
 9439                                )],
 9440                            )]
 9441                            .into_iter()
 9442                            .collect(),
 9443                        ),
 9444                        ..Default::default()
 9445                    }),
 9446                    ..Default::default()
 9447                },
 9448            )]))
 9449        })
 9450        .next()
 9451        .await;
 9452    cx.executor().start_waiting();
 9453    format.await;
 9454    assert_eq!(
 9455        editor.update(cx, |editor, cx| editor.text(cx)),
 9456        "import { a } from 'module';\n\nconst x = a;\n"
 9457    );
 9458
 9459    editor.update_in(cx, |editor, window, cx| {
 9460        editor.set_text(
 9461            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9462            window,
 9463            cx,
 9464        )
 9465    });
 9466    // Ensure we don't lock if code action hangs.
 9467    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9468        move |params, _| async move {
 9469            assert_eq!(
 9470                params.text_document.uri,
 9471                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9472            );
 9473            futures::future::pending::<()>().await;
 9474            unreachable!()
 9475        },
 9476    );
 9477    let format = editor
 9478        .update_in(cx, |editor, window, cx| {
 9479            editor.perform_code_action_kind(
 9480                project,
 9481                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9482                window,
 9483                cx,
 9484            )
 9485        })
 9486        .unwrap();
 9487    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9488    cx.executor().start_waiting();
 9489    format.await;
 9490    assert_eq!(
 9491        editor.update(cx, |editor, cx| editor.text(cx)),
 9492        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9493    );
 9494}
 9495
 9496#[gpui::test]
 9497async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9498    init_test(cx, |_| {});
 9499
 9500    let mut cx = EditorLspTestContext::new_rust(
 9501        lsp::ServerCapabilities {
 9502            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9503            ..Default::default()
 9504        },
 9505        cx,
 9506    )
 9507    .await;
 9508
 9509    cx.set_state(indoc! {"
 9510        one.twoˇ
 9511    "});
 9512
 9513    // The format request takes a long time. When it completes, it inserts
 9514    // a newline and an indent before the `.`
 9515    cx.lsp
 9516        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9517            let executor = cx.background_executor().clone();
 9518            async move {
 9519                executor.timer(Duration::from_millis(100)).await;
 9520                Ok(Some(vec![lsp::TextEdit {
 9521                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9522                    new_text: "\n    ".into(),
 9523                }]))
 9524            }
 9525        });
 9526
 9527    // Submit a format request.
 9528    let format_1 = cx
 9529        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9530        .unwrap();
 9531    cx.executor().run_until_parked();
 9532
 9533    // Submit a second format request.
 9534    let format_2 = cx
 9535        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9536        .unwrap();
 9537    cx.executor().run_until_parked();
 9538
 9539    // Wait for both format requests to complete
 9540    cx.executor().advance_clock(Duration::from_millis(200));
 9541    cx.executor().start_waiting();
 9542    format_1.await.unwrap();
 9543    cx.executor().start_waiting();
 9544    format_2.await.unwrap();
 9545
 9546    // The formatting edits only happens once.
 9547    cx.assert_editor_state(indoc! {"
 9548        one
 9549            .twoˇ
 9550    "});
 9551}
 9552
 9553#[gpui::test]
 9554async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9555    init_test(cx, |settings| {
 9556        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9557    });
 9558
 9559    let mut cx = EditorLspTestContext::new_rust(
 9560        lsp::ServerCapabilities {
 9561            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9562            ..Default::default()
 9563        },
 9564        cx,
 9565    )
 9566    .await;
 9567
 9568    // Set up a buffer white some trailing whitespace and no trailing newline.
 9569    cx.set_state(
 9570        &[
 9571            "one ",   //
 9572            "twoˇ",   //
 9573            "three ", //
 9574            "four",   //
 9575        ]
 9576        .join("\n"),
 9577    );
 9578
 9579    // Submit a format request.
 9580    let format = cx
 9581        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9582        .unwrap();
 9583
 9584    // Record which buffer changes have been sent to the language server
 9585    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9586    cx.lsp
 9587        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9588            let buffer_changes = buffer_changes.clone();
 9589            move |params, _| {
 9590                buffer_changes.lock().extend(
 9591                    params
 9592                        .content_changes
 9593                        .into_iter()
 9594                        .map(|e| (e.range.unwrap(), e.text)),
 9595                );
 9596            }
 9597        });
 9598
 9599    // Handle formatting requests to the language server.
 9600    cx.lsp
 9601        .set_request_handler::<lsp::request::Formatting, _, _>({
 9602            let buffer_changes = buffer_changes.clone();
 9603            move |_, _| {
 9604                // When formatting is requested, trailing whitespace has already been stripped,
 9605                // and the trailing newline has already been added.
 9606                assert_eq!(
 9607                    &buffer_changes.lock()[1..],
 9608                    &[
 9609                        (
 9610                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9611                            "".into()
 9612                        ),
 9613                        (
 9614                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9615                            "".into()
 9616                        ),
 9617                        (
 9618                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9619                            "\n".into()
 9620                        ),
 9621                    ]
 9622                );
 9623
 9624                // Insert blank lines between each line of the buffer.
 9625                async move {
 9626                    Ok(Some(vec![
 9627                        lsp::TextEdit {
 9628                            range: lsp::Range::new(
 9629                                lsp::Position::new(1, 0),
 9630                                lsp::Position::new(1, 0),
 9631                            ),
 9632                            new_text: "\n".into(),
 9633                        },
 9634                        lsp::TextEdit {
 9635                            range: lsp::Range::new(
 9636                                lsp::Position::new(2, 0),
 9637                                lsp::Position::new(2, 0),
 9638                            ),
 9639                            new_text: "\n".into(),
 9640                        },
 9641                    ]))
 9642                }
 9643            }
 9644        });
 9645
 9646    // After formatting the buffer, the trailing whitespace is stripped,
 9647    // a newline is appended, and the edits provided by the language server
 9648    // have been applied.
 9649    format.await.unwrap();
 9650    cx.assert_editor_state(
 9651        &[
 9652            "one",   //
 9653            "",      //
 9654            "twoˇ",  //
 9655            "",      //
 9656            "three", //
 9657            "four",  //
 9658            "",      //
 9659        ]
 9660        .join("\n"),
 9661    );
 9662
 9663    // Undoing the formatting undoes the trailing whitespace removal, the
 9664    // trailing newline, and the LSP edits.
 9665    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9666    cx.assert_editor_state(
 9667        &[
 9668            "one ",   //
 9669            "twoˇ",   //
 9670            "three ", //
 9671            "four",   //
 9672        ]
 9673        .join("\n"),
 9674    );
 9675}
 9676
 9677#[gpui::test]
 9678async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9679    cx: &mut TestAppContext,
 9680) {
 9681    init_test(cx, |_| {});
 9682
 9683    cx.update(|cx| {
 9684        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9685            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9686                settings.auto_signature_help = Some(true);
 9687            });
 9688        });
 9689    });
 9690
 9691    let mut cx = EditorLspTestContext::new_rust(
 9692        lsp::ServerCapabilities {
 9693            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9694                ..Default::default()
 9695            }),
 9696            ..Default::default()
 9697        },
 9698        cx,
 9699    )
 9700    .await;
 9701
 9702    let language = Language::new(
 9703        LanguageConfig {
 9704            name: "Rust".into(),
 9705            brackets: BracketPairConfig {
 9706                pairs: vec![
 9707                    BracketPair {
 9708                        start: "{".to_string(),
 9709                        end: "}".to_string(),
 9710                        close: true,
 9711                        surround: true,
 9712                        newline: true,
 9713                    },
 9714                    BracketPair {
 9715                        start: "(".to_string(),
 9716                        end: ")".to_string(),
 9717                        close: true,
 9718                        surround: true,
 9719                        newline: true,
 9720                    },
 9721                    BracketPair {
 9722                        start: "/*".to_string(),
 9723                        end: " */".to_string(),
 9724                        close: true,
 9725                        surround: true,
 9726                        newline: true,
 9727                    },
 9728                    BracketPair {
 9729                        start: "[".to_string(),
 9730                        end: "]".to_string(),
 9731                        close: false,
 9732                        surround: false,
 9733                        newline: true,
 9734                    },
 9735                    BracketPair {
 9736                        start: "\"".to_string(),
 9737                        end: "\"".to_string(),
 9738                        close: true,
 9739                        surround: true,
 9740                        newline: false,
 9741                    },
 9742                    BracketPair {
 9743                        start: "<".to_string(),
 9744                        end: ">".to_string(),
 9745                        close: false,
 9746                        surround: true,
 9747                        newline: true,
 9748                    },
 9749                ],
 9750                ..Default::default()
 9751            },
 9752            autoclose_before: "})]".to_string(),
 9753            ..Default::default()
 9754        },
 9755        Some(tree_sitter_rust::LANGUAGE.into()),
 9756    );
 9757    let language = Arc::new(language);
 9758
 9759    cx.language_registry().add(language.clone());
 9760    cx.update_buffer(|buffer, cx| {
 9761        buffer.set_language(Some(language), cx);
 9762    });
 9763
 9764    cx.set_state(
 9765        &r#"
 9766            fn main() {
 9767                sampleˇ
 9768            }
 9769        "#
 9770        .unindent(),
 9771    );
 9772
 9773    cx.update_editor(|editor, window, cx| {
 9774        editor.handle_input("(", window, cx);
 9775    });
 9776    cx.assert_editor_state(
 9777        &"
 9778            fn main() {
 9779                sample(ˇ)
 9780            }
 9781        "
 9782        .unindent(),
 9783    );
 9784
 9785    let mocked_response = lsp::SignatureHelp {
 9786        signatures: vec![lsp::SignatureInformation {
 9787            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9788            documentation: None,
 9789            parameters: Some(vec![
 9790                lsp::ParameterInformation {
 9791                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9792                    documentation: None,
 9793                },
 9794                lsp::ParameterInformation {
 9795                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9796                    documentation: None,
 9797                },
 9798            ]),
 9799            active_parameter: None,
 9800        }],
 9801        active_signature: Some(0),
 9802        active_parameter: Some(0),
 9803    };
 9804    handle_signature_help_request(&mut cx, mocked_response).await;
 9805
 9806    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9807        .await;
 9808
 9809    cx.editor(|editor, _, _| {
 9810        let signature_help_state = editor.signature_help_state.popover().cloned();
 9811        assert_eq!(
 9812            signature_help_state.unwrap().label,
 9813            "param1: u8, param2: u8"
 9814        );
 9815    });
 9816}
 9817
 9818#[gpui::test]
 9819async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9820    init_test(cx, |_| {});
 9821
 9822    cx.update(|cx| {
 9823        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9824            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9825                settings.auto_signature_help = Some(false);
 9826                settings.show_signature_help_after_edits = Some(false);
 9827            });
 9828        });
 9829    });
 9830
 9831    let mut cx = EditorLspTestContext::new_rust(
 9832        lsp::ServerCapabilities {
 9833            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9834                ..Default::default()
 9835            }),
 9836            ..Default::default()
 9837        },
 9838        cx,
 9839    )
 9840    .await;
 9841
 9842    let language = Language::new(
 9843        LanguageConfig {
 9844            name: "Rust".into(),
 9845            brackets: BracketPairConfig {
 9846                pairs: vec![
 9847                    BracketPair {
 9848                        start: "{".to_string(),
 9849                        end: "}".to_string(),
 9850                        close: true,
 9851                        surround: true,
 9852                        newline: true,
 9853                    },
 9854                    BracketPair {
 9855                        start: "(".to_string(),
 9856                        end: ")".to_string(),
 9857                        close: true,
 9858                        surround: true,
 9859                        newline: true,
 9860                    },
 9861                    BracketPair {
 9862                        start: "/*".to_string(),
 9863                        end: " */".to_string(),
 9864                        close: true,
 9865                        surround: true,
 9866                        newline: true,
 9867                    },
 9868                    BracketPair {
 9869                        start: "[".to_string(),
 9870                        end: "]".to_string(),
 9871                        close: false,
 9872                        surround: false,
 9873                        newline: true,
 9874                    },
 9875                    BracketPair {
 9876                        start: "\"".to_string(),
 9877                        end: "\"".to_string(),
 9878                        close: true,
 9879                        surround: true,
 9880                        newline: false,
 9881                    },
 9882                    BracketPair {
 9883                        start: "<".to_string(),
 9884                        end: ">".to_string(),
 9885                        close: false,
 9886                        surround: true,
 9887                        newline: true,
 9888                    },
 9889                ],
 9890                ..Default::default()
 9891            },
 9892            autoclose_before: "})]".to_string(),
 9893            ..Default::default()
 9894        },
 9895        Some(tree_sitter_rust::LANGUAGE.into()),
 9896    );
 9897    let language = Arc::new(language);
 9898
 9899    cx.language_registry().add(language.clone());
 9900    cx.update_buffer(|buffer, cx| {
 9901        buffer.set_language(Some(language), cx);
 9902    });
 9903
 9904    // Ensure that signature_help is not called when no signature help is enabled.
 9905    cx.set_state(
 9906        &r#"
 9907            fn main() {
 9908                sampleˇ
 9909            }
 9910        "#
 9911        .unindent(),
 9912    );
 9913    cx.update_editor(|editor, window, cx| {
 9914        editor.handle_input("(", window, cx);
 9915    });
 9916    cx.assert_editor_state(
 9917        &"
 9918            fn main() {
 9919                sample(ˇ)
 9920            }
 9921        "
 9922        .unindent(),
 9923    );
 9924    cx.editor(|editor, _, _| {
 9925        assert!(editor.signature_help_state.task().is_none());
 9926    });
 9927
 9928    let mocked_response = lsp::SignatureHelp {
 9929        signatures: vec![lsp::SignatureInformation {
 9930            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9931            documentation: None,
 9932            parameters: Some(vec![
 9933                lsp::ParameterInformation {
 9934                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9935                    documentation: None,
 9936                },
 9937                lsp::ParameterInformation {
 9938                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9939                    documentation: None,
 9940                },
 9941            ]),
 9942            active_parameter: None,
 9943        }],
 9944        active_signature: Some(0),
 9945        active_parameter: Some(0),
 9946    };
 9947
 9948    // Ensure that signature_help is called when enabled afte edits
 9949    cx.update(|_, cx| {
 9950        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9951            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9952                settings.auto_signature_help = Some(false);
 9953                settings.show_signature_help_after_edits = Some(true);
 9954            });
 9955        });
 9956    });
 9957    cx.set_state(
 9958        &r#"
 9959            fn main() {
 9960                sampleˇ
 9961            }
 9962        "#
 9963        .unindent(),
 9964    );
 9965    cx.update_editor(|editor, window, cx| {
 9966        editor.handle_input("(", window, cx);
 9967    });
 9968    cx.assert_editor_state(
 9969        &"
 9970            fn main() {
 9971                sample(ˇ)
 9972            }
 9973        "
 9974        .unindent(),
 9975    );
 9976    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9977    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9978        .await;
 9979    cx.update_editor(|editor, _, _| {
 9980        let signature_help_state = editor.signature_help_state.popover().cloned();
 9981        assert!(signature_help_state.is_some());
 9982        assert_eq!(
 9983            signature_help_state.unwrap().label,
 9984            "param1: u8, param2: u8"
 9985        );
 9986        editor.signature_help_state = SignatureHelpState::default();
 9987    });
 9988
 9989    // Ensure that signature_help is called when auto signature help override is enabled
 9990    cx.update(|_, cx| {
 9991        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9992            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9993                settings.auto_signature_help = Some(true);
 9994                settings.show_signature_help_after_edits = Some(false);
 9995            });
 9996        });
 9997    });
 9998    cx.set_state(
 9999        &r#"
10000            fn main() {
10001                sampleˇ
10002            }
10003        "#
10004        .unindent(),
10005    );
10006    cx.update_editor(|editor, window, cx| {
10007        editor.handle_input("(", window, cx);
10008    });
10009    cx.assert_editor_state(
10010        &"
10011            fn main() {
10012                sample(ˇ)
10013            }
10014        "
10015        .unindent(),
10016    );
10017    handle_signature_help_request(&mut cx, mocked_response).await;
10018    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10019        .await;
10020    cx.editor(|editor, _, _| {
10021        let signature_help_state = editor.signature_help_state.popover().cloned();
10022        assert!(signature_help_state.is_some());
10023        assert_eq!(
10024            signature_help_state.unwrap().label,
10025            "param1: u8, param2: u8"
10026        );
10027    });
10028}
10029
10030#[gpui::test]
10031async fn test_signature_help(cx: &mut TestAppContext) {
10032    init_test(cx, |_| {});
10033    cx.update(|cx| {
10034        cx.update_global::<SettingsStore, _>(|settings, cx| {
10035            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10036                settings.auto_signature_help = Some(true);
10037            });
10038        });
10039    });
10040
10041    let mut cx = EditorLspTestContext::new_rust(
10042        lsp::ServerCapabilities {
10043            signature_help_provider: Some(lsp::SignatureHelpOptions {
10044                ..Default::default()
10045            }),
10046            ..Default::default()
10047        },
10048        cx,
10049    )
10050    .await;
10051
10052    // A test that directly calls `show_signature_help`
10053    cx.update_editor(|editor, window, cx| {
10054        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10055    });
10056
10057    let mocked_response = lsp::SignatureHelp {
10058        signatures: vec![lsp::SignatureInformation {
10059            label: "fn sample(param1: u8, param2: u8)".to_string(),
10060            documentation: None,
10061            parameters: Some(vec![
10062                lsp::ParameterInformation {
10063                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10064                    documentation: None,
10065                },
10066                lsp::ParameterInformation {
10067                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10068                    documentation: None,
10069                },
10070            ]),
10071            active_parameter: None,
10072        }],
10073        active_signature: Some(0),
10074        active_parameter: Some(0),
10075    };
10076    handle_signature_help_request(&mut cx, mocked_response).await;
10077
10078    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10079        .await;
10080
10081    cx.editor(|editor, _, _| {
10082        let signature_help_state = editor.signature_help_state.popover().cloned();
10083        assert!(signature_help_state.is_some());
10084        assert_eq!(
10085            signature_help_state.unwrap().label,
10086            "param1: u8, param2: u8"
10087        );
10088    });
10089
10090    // When exiting outside from inside the brackets, `signature_help` is closed.
10091    cx.set_state(indoc! {"
10092        fn main() {
10093            sample(ˇ);
10094        }
10095
10096        fn sample(param1: u8, param2: u8) {}
10097    "});
10098
10099    cx.update_editor(|editor, window, cx| {
10100        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10101    });
10102
10103    let mocked_response = lsp::SignatureHelp {
10104        signatures: Vec::new(),
10105        active_signature: None,
10106        active_parameter: None,
10107    };
10108    handle_signature_help_request(&mut cx, mocked_response).await;
10109
10110    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10111        .await;
10112
10113    cx.editor(|editor, _, _| {
10114        assert!(!editor.signature_help_state.is_shown());
10115    });
10116
10117    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10118    cx.set_state(indoc! {"
10119        fn main() {
10120            sample(ˇ);
10121        }
10122
10123        fn sample(param1: u8, param2: u8) {}
10124    "});
10125
10126    let mocked_response = lsp::SignatureHelp {
10127        signatures: vec![lsp::SignatureInformation {
10128            label: "fn sample(param1: u8, param2: u8)".to_string(),
10129            documentation: None,
10130            parameters: Some(vec![
10131                lsp::ParameterInformation {
10132                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10133                    documentation: None,
10134                },
10135                lsp::ParameterInformation {
10136                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10137                    documentation: None,
10138                },
10139            ]),
10140            active_parameter: None,
10141        }],
10142        active_signature: Some(0),
10143        active_parameter: Some(0),
10144    };
10145    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10146    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10147        .await;
10148    cx.editor(|editor, _, _| {
10149        assert!(editor.signature_help_state.is_shown());
10150    });
10151
10152    // Restore the popover with more parameter input
10153    cx.set_state(indoc! {"
10154        fn main() {
10155            sample(param1, param2ˇ);
10156        }
10157
10158        fn sample(param1: u8, param2: u8) {}
10159    "});
10160
10161    let mocked_response = lsp::SignatureHelp {
10162        signatures: vec![lsp::SignatureInformation {
10163            label: "fn sample(param1: u8, param2: u8)".to_string(),
10164            documentation: None,
10165            parameters: Some(vec![
10166                lsp::ParameterInformation {
10167                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10168                    documentation: None,
10169                },
10170                lsp::ParameterInformation {
10171                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10172                    documentation: None,
10173                },
10174            ]),
10175            active_parameter: None,
10176        }],
10177        active_signature: Some(0),
10178        active_parameter: Some(1),
10179    };
10180    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10182        .await;
10183
10184    // When selecting a range, the popover is gone.
10185    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10186    cx.update_editor(|editor, window, cx| {
10187        editor.change_selections(None, window, cx, |s| {
10188            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10189        })
10190    });
10191    cx.assert_editor_state(indoc! {"
10192        fn main() {
10193            sample(param1, «ˇparam2»);
10194        }
10195
10196        fn sample(param1: u8, param2: u8) {}
10197    "});
10198    cx.editor(|editor, _, _| {
10199        assert!(!editor.signature_help_state.is_shown());
10200    });
10201
10202    // When unselecting again, the popover is back if within the brackets.
10203    cx.update_editor(|editor, window, cx| {
10204        editor.change_selections(None, window, cx, |s| {
10205            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10206        })
10207    });
10208    cx.assert_editor_state(indoc! {"
10209        fn main() {
10210            sample(param1, ˇparam2);
10211        }
10212
10213        fn sample(param1: u8, param2: u8) {}
10214    "});
10215    handle_signature_help_request(&mut cx, mocked_response).await;
10216    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10217        .await;
10218    cx.editor(|editor, _, _| {
10219        assert!(editor.signature_help_state.is_shown());
10220    });
10221
10222    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10223    cx.update_editor(|editor, window, cx| {
10224        editor.change_selections(None, window, cx, |s| {
10225            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10226            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10227        })
10228    });
10229    cx.assert_editor_state(indoc! {"
10230        fn main() {
10231            sample(param1, ˇparam2);
10232        }
10233
10234        fn sample(param1: u8, param2: u8) {}
10235    "});
10236
10237    let mocked_response = lsp::SignatureHelp {
10238        signatures: vec![lsp::SignatureInformation {
10239            label: "fn sample(param1: u8, param2: u8)".to_string(),
10240            documentation: None,
10241            parameters: Some(vec![
10242                lsp::ParameterInformation {
10243                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10244                    documentation: None,
10245                },
10246                lsp::ParameterInformation {
10247                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10248                    documentation: None,
10249                },
10250            ]),
10251            active_parameter: None,
10252        }],
10253        active_signature: Some(0),
10254        active_parameter: Some(1),
10255    };
10256    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10257    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10258        .await;
10259    cx.update_editor(|editor, _, cx| {
10260        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10261    });
10262    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10263        .await;
10264    cx.update_editor(|editor, window, cx| {
10265        editor.change_selections(None, window, cx, |s| {
10266            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10267        })
10268    });
10269    cx.assert_editor_state(indoc! {"
10270        fn main() {
10271            sample(param1, «ˇparam2»);
10272        }
10273
10274        fn sample(param1: u8, param2: u8) {}
10275    "});
10276    cx.update_editor(|editor, window, cx| {
10277        editor.change_selections(None, window, cx, |s| {
10278            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10279        })
10280    });
10281    cx.assert_editor_state(indoc! {"
10282        fn main() {
10283            sample(param1, ˇparam2);
10284        }
10285
10286        fn sample(param1: u8, param2: u8) {}
10287    "});
10288    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10289        .await;
10290}
10291
10292#[gpui::test]
10293async fn test_completion_mode(cx: &mut TestAppContext) {
10294    init_test(cx, |_| {});
10295    let mut cx = EditorLspTestContext::new_rust(
10296        lsp::ServerCapabilities {
10297            completion_provider: Some(lsp::CompletionOptions {
10298                resolve_provider: Some(true),
10299                ..Default::default()
10300            }),
10301            ..Default::default()
10302        },
10303        cx,
10304    )
10305    .await;
10306
10307    struct Run {
10308        run_description: &'static str,
10309        initial_state: String,
10310        buffer_marked_text: String,
10311        completion_text: &'static str,
10312        expected_with_insert_mode: String,
10313        expected_with_replace_mode: String,
10314        expected_with_replace_subsequence_mode: String,
10315        expected_with_replace_suffix_mode: String,
10316    }
10317
10318    let runs = [
10319        Run {
10320            run_description: "Start of word matches completion text",
10321            initial_state: "before ediˇ after".into(),
10322            buffer_marked_text: "before <edi|> after".into(),
10323            completion_text: "editor",
10324            expected_with_insert_mode: "before editorˇ after".into(),
10325            expected_with_replace_mode: "before editorˇ after".into(),
10326            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10327            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10328        },
10329        Run {
10330            run_description: "Accept same text at the middle of the word",
10331            initial_state: "before ediˇtor after".into(),
10332            buffer_marked_text: "before <edi|tor> after".into(),
10333            completion_text: "editor",
10334            expected_with_insert_mode: "before editorˇtor after".into(),
10335            expected_with_replace_mode: "before editorˇ after".into(),
10336            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10337            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10338        },
10339        Run {
10340            run_description: "End of word matches completion text -- cursor at end",
10341            initial_state: "before torˇ after".into(),
10342            buffer_marked_text: "before <tor|> after".into(),
10343            completion_text: "editor",
10344            expected_with_insert_mode: "before editorˇ after".into(),
10345            expected_with_replace_mode: "before editorˇ after".into(),
10346            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10347            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10348        },
10349        Run {
10350            run_description: "End of word matches completion text -- cursor at start",
10351            initial_state: "before ˇtor after".into(),
10352            buffer_marked_text: "before <|tor> after".into(),
10353            completion_text: "editor",
10354            expected_with_insert_mode: "before editorˇtor after".into(),
10355            expected_with_replace_mode: "before editorˇ after".into(),
10356            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10357            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10358        },
10359        Run {
10360            run_description: "Prepend text containing whitespace",
10361            initial_state: "pˇfield: bool".into(),
10362            buffer_marked_text: "<p|field>: bool".into(),
10363            completion_text: "pub ",
10364            expected_with_insert_mode: "pub ˇfield: bool".into(),
10365            expected_with_replace_mode: "pub ˇ: bool".into(),
10366            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10367            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10368        },
10369        Run {
10370            run_description: "Add element to start of list",
10371            initial_state: "[element_ˇelement_2]".into(),
10372            buffer_marked_text: "[<element_|element_2>]".into(),
10373            completion_text: "element_1",
10374            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10375            expected_with_replace_mode: "[element_1ˇ]".into(),
10376            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10377            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10378        },
10379        Run {
10380            run_description: "Add element to start of list -- first and second elements are equal",
10381            initial_state: "[elˇelement]".into(),
10382            buffer_marked_text: "[<el|element>]".into(),
10383            completion_text: "element",
10384            expected_with_insert_mode: "[elementˇelement]".into(),
10385            expected_with_replace_mode: "[elementˇ]".into(),
10386            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10387            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10388        },
10389        Run {
10390            run_description: "Ends with matching suffix",
10391            initial_state: "SubˇError".into(),
10392            buffer_marked_text: "<Sub|Error>".into(),
10393            completion_text: "SubscriptionError",
10394            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10395            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10396            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10397            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10398        },
10399        Run {
10400            run_description: "Suffix is a subsequence -- contiguous",
10401            initial_state: "SubˇErr".into(),
10402            buffer_marked_text: "<Sub|Err>".into(),
10403            completion_text: "SubscriptionError",
10404            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10405            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10406            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10407            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10408        },
10409        Run {
10410            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10411            initial_state: "Suˇscrirr".into(),
10412            buffer_marked_text: "<Su|scrirr>".into(),
10413            completion_text: "SubscriptionError",
10414            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10415            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10416            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10417            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10418        },
10419        Run {
10420            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10421            initial_state: "foo(indˇix)".into(),
10422            buffer_marked_text: "foo(<ind|ix>)".into(),
10423            completion_text: "node_index",
10424            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10425            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10426            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10427            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10428        },
10429    ];
10430
10431    for run in runs {
10432        let run_variations = [
10433            (LspInsertMode::Insert, run.expected_with_insert_mode),
10434            (LspInsertMode::Replace, run.expected_with_replace_mode),
10435            (
10436                LspInsertMode::ReplaceSubsequence,
10437                run.expected_with_replace_subsequence_mode,
10438            ),
10439            (
10440                LspInsertMode::ReplaceSuffix,
10441                run.expected_with_replace_suffix_mode,
10442            ),
10443        ];
10444
10445        for (lsp_insert_mode, expected_text) in run_variations {
10446            eprintln!(
10447                "run = {:?}, mode = {lsp_insert_mode:.?}",
10448                run.run_description,
10449            );
10450
10451            update_test_language_settings(&mut cx, |settings| {
10452                settings.defaults.completions = Some(CompletionSettings {
10453                    lsp_insert_mode,
10454                    words: WordsCompletionMode::Disabled,
10455                    lsp: true,
10456                    lsp_fetch_timeout_ms: 0,
10457                });
10458            });
10459
10460            cx.set_state(&run.initial_state);
10461            cx.update_editor(|editor, window, cx| {
10462                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10463            });
10464
10465            let counter = Arc::new(AtomicUsize::new(0));
10466            handle_completion_request_with_insert_and_replace(
10467                &mut cx,
10468                &run.buffer_marked_text,
10469                vec![run.completion_text],
10470                counter.clone(),
10471            )
10472            .await;
10473            cx.condition(|editor, _| editor.context_menu_visible())
10474                .await;
10475            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10476
10477            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10478                editor
10479                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10480                    .unwrap()
10481            });
10482            cx.assert_editor_state(&expected_text);
10483            handle_resolve_completion_request(&mut cx, None).await;
10484            apply_additional_edits.await.unwrap();
10485        }
10486    }
10487}
10488
10489#[gpui::test]
10490async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10491    init_test(cx, |_| {});
10492    let mut cx = EditorLspTestContext::new_rust(
10493        lsp::ServerCapabilities {
10494            completion_provider: Some(lsp::CompletionOptions {
10495                resolve_provider: Some(true),
10496                ..Default::default()
10497            }),
10498            ..Default::default()
10499        },
10500        cx,
10501    )
10502    .await;
10503
10504    let initial_state = "SubˇError";
10505    let buffer_marked_text = "<Sub|Error>";
10506    let completion_text = "SubscriptionError";
10507    let expected_with_insert_mode = "SubscriptionErrorˇError";
10508    let expected_with_replace_mode = "SubscriptionErrorˇ";
10509
10510    update_test_language_settings(&mut cx, |settings| {
10511        settings.defaults.completions = Some(CompletionSettings {
10512            words: WordsCompletionMode::Disabled,
10513            // set the opposite here to ensure that the action is overriding the default behavior
10514            lsp_insert_mode: LspInsertMode::Insert,
10515            lsp: true,
10516            lsp_fetch_timeout_ms: 0,
10517        });
10518    });
10519
10520    cx.set_state(initial_state);
10521    cx.update_editor(|editor, window, cx| {
10522        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10523    });
10524
10525    let counter = Arc::new(AtomicUsize::new(0));
10526    handle_completion_request_with_insert_and_replace(
10527        &mut cx,
10528        &buffer_marked_text,
10529        vec![completion_text],
10530        counter.clone(),
10531    )
10532    .await;
10533    cx.condition(|editor, _| editor.context_menu_visible())
10534        .await;
10535    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10536
10537    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10538        editor
10539            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10540            .unwrap()
10541    });
10542    cx.assert_editor_state(&expected_with_replace_mode);
10543    handle_resolve_completion_request(&mut cx, None).await;
10544    apply_additional_edits.await.unwrap();
10545
10546    update_test_language_settings(&mut cx, |settings| {
10547        settings.defaults.completions = Some(CompletionSettings {
10548            words: WordsCompletionMode::Disabled,
10549            // set the opposite here to ensure that the action is overriding the default behavior
10550            lsp_insert_mode: LspInsertMode::Replace,
10551            lsp: true,
10552            lsp_fetch_timeout_ms: 0,
10553        });
10554    });
10555
10556    cx.set_state(initial_state);
10557    cx.update_editor(|editor, window, cx| {
10558        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10559    });
10560    handle_completion_request_with_insert_and_replace(
10561        &mut cx,
10562        &buffer_marked_text,
10563        vec![completion_text],
10564        counter.clone(),
10565    )
10566    .await;
10567    cx.condition(|editor, _| editor.context_menu_visible())
10568        .await;
10569    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10570
10571    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10572        editor
10573            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10574            .unwrap()
10575    });
10576    cx.assert_editor_state(&expected_with_insert_mode);
10577    handle_resolve_completion_request(&mut cx, None).await;
10578    apply_additional_edits.await.unwrap();
10579}
10580
10581#[gpui::test]
10582async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10583    init_test(cx, |_| {});
10584    let mut cx = EditorLspTestContext::new_rust(
10585        lsp::ServerCapabilities {
10586            completion_provider: Some(lsp::CompletionOptions {
10587                resolve_provider: Some(true),
10588                ..Default::default()
10589            }),
10590            ..Default::default()
10591        },
10592        cx,
10593    )
10594    .await;
10595
10596    // scenario: surrounding text matches completion text
10597    let completion_text = "to_offset";
10598    let initial_state = indoc! {"
10599        1. buf.to_offˇsuffix
10600        2. buf.to_offˇsuf
10601        3. buf.to_offˇfix
10602        4. buf.to_offˇ
10603        5. into_offˇensive
10604        6. ˇsuffix
10605        7. let ˇ //
10606        8. aaˇzz
10607        9. buf.to_off«zzzzzˇ»suffix
10608        10. buf.«ˇzzzzz»suffix
10609        11. to_off«ˇzzzzz»
10610
10611        buf.to_offˇsuffix  // newest cursor
10612    "};
10613    let completion_marked_buffer = indoc! {"
10614        1. buf.to_offsuffix
10615        2. buf.to_offsuf
10616        3. buf.to_offfix
10617        4. buf.to_off
10618        5. into_offensive
10619        6. suffix
10620        7. let  //
10621        8. aazz
10622        9. buf.to_offzzzzzsuffix
10623        10. buf.zzzzzsuffix
10624        11. to_offzzzzz
10625
10626        buf.<to_off|suffix>  // newest cursor
10627    "};
10628    let expected = indoc! {"
10629        1. buf.to_offsetˇ
10630        2. buf.to_offsetˇsuf
10631        3. buf.to_offsetˇfix
10632        4. buf.to_offsetˇ
10633        5. into_offsetˇensive
10634        6. to_offsetˇsuffix
10635        7. let to_offsetˇ //
10636        8. aato_offsetˇzz
10637        9. buf.to_offsetˇ
10638        10. buf.to_offsetˇsuffix
10639        11. to_offsetˇ
10640
10641        buf.to_offsetˇ  // newest cursor
10642    "};
10643    cx.set_state(initial_state);
10644    cx.update_editor(|editor, window, cx| {
10645        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10646    });
10647    handle_completion_request_with_insert_and_replace(
10648        &mut cx,
10649        completion_marked_buffer,
10650        vec![completion_text],
10651        Arc::new(AtomicUsize::new(0)),
10652    )
10653    .await;
10654    cx.condition(|editor, _| editor.context_menu_visible())
10655        .await;
10656    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10657        editor
10658            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10659            .unwrap()
10660    });
10661    cx.assert_editor_state(expected);
10662    handle_resolve_completion_request(&mut cx, None).await;
10663    apply_additional_edits.await.unwrap();
10664
10665    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10666    let completion_text = "foo_and_bar";
10667    let initial_state = indoc! {"
10668        1. ooanbˇ
10669        2. zooanbˇ
10670        3. ooanbˇz
10671        4. zooanbˇz
10672        5. ooanˇ
10673        6. oanbˇ
10674
10675        ooanbˇ
10676    "};
10677    let completion_marked_buffer = indoc! {"
10678        1. ooanb
10679        2. zooanb
10680        3. ooanbz
10681        4. zooanbz
10682        5. ooan
10683        6. oanb
10684
10685        <ooanb|>
10686    "};
10687    let expected = indoc! {"
10688        1. foo_and_barˇ
10689        2. zfoo_and_barˇ
10690        3. foo_and_barˇz
10691        4. zfoo_and_barˇz
10692        5. ooanfoo_and_barˇ
10693        6. oanbfoo_and_barˇ
10694
10695        foo_and_barˇ
10696    "};
10697    cx.set_state(initial_state);
10698    cx.update_editor(|editor, window, cx| {
10699        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10700    });
10701    handle_completion_request_with_insert_and_replace(
10702        &mut cx,
10703        completion_marked_buffer,
10704        vec![completion_text],
10705        Arc::new(AtomicUsize::new(0)),
10706    )
10707    .await;
10708    cx.condition(|editor, _| editor.context_menu_visible())
10709        .await;
10710    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10711        editor
10712            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10713            .unwrap()
10714    });
10715    cx.assert_editor_state(expected);
10716    handle_resolve_completion_request(&mut cx, None).await;
10717    apply_additional_edits.await.unwrap();
10718
10719    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10720    // (expects the same as if it was inserted at the end)
10721    let completion_text = "foo_and_bar";
10722    let initial_state = indoc! {"
10723        1. ooˇanb
10724        2. zooˇanb
10725        3. ooˇanbz
10726        4. zooˇanbz
10727
10728        ooˇanb
10729    "};
10730    let completion_marked_buffer = indoc! {"
10731        1. ooanb
10732        2. zooanb
10733        3. ooanbz
10734        4. zooanbz
10735
10736        <oo|anb>
10737    "};
10738    let expected = indoc! {"
10739        1. foo_and_barˇ
10740        2. zfoo_and_barˇ
10741        3. foo_and_barˇz
10742        4. zfoo_and_barˇz
10743
10744        foo_and_barˇ
10745    "};
10746    cx.set_state(initial_state);
10747    cx.update_editor(|editor, window, cx| {
10748        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10749    });
10750    handle_completion_request_with_insert_and_replace(
10751        &mut cx,
10752        completion_marked_buffer,
10753        vec![completion_text],
10754        Arc::new(AtomicUsize::new(0)),
10755    )
10756    .await;
10757    cx.condition(|editor, _| editor.context_menu_visible())
10758        .await;
10759    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10760        editor
10761            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10762            .unwrap()
10763    });
10764    cx.assert_editor_state(expected);
10765    handle_resolve_completion_request(&mut cx, None).await;
10766    apply_additional_edits.await.unwrap();
10767}
10768
10769// This used to crash
10770#[gpui::test]
10771async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10772    init_test(cx, |_| {});
10773
10774    let buffer_text = indoc! {"
10775        fn main() {
10776            10.satu;
10777
10778            //
10779            // separate cursors so they open in different excerpts (manually reproducible)
10780            //
10781
10782            10.satu20;
10783        }
10784    "};
10785    let multibuffer_text_with_selections = indoc! {"
10786        fn main() {
10787            10.satuˇ;
10788
10789            //
10790
10791            //
10792
10793            10.satuˇ20;
10794        }
10795    "};
10796    let expected_multibuffer = indoc! {"
10797        fn main() {
10798            10.saturating_sub()ˇ;
10799
10800            //
10801
10802            //
10803
10804            10.saturating_sub()ˇ;
10805        }
10806    "};
10807
10808    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10809    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10810
10811    let fs = FakeFs::new(cx.executor());
10812    fs.insert_tree(
10813        path!("/a"),
10814        json!({
10815            "main.rs": buffer_text,
10816        }),
10817    )
10818    .await;
10819
10820    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10821    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10822    language_registry.add(rust_lang());
10823    let mut fake_servers = language_registry.register_fake_lsp(
10824        "Rust",
10825        FakeLspAdapter {
10826            capabilities: lsp::ServerCapabilities {
10827                completion_provider: Some(lsp::CompletionOptions {
10828                    resolve_provider: None,
10829                    ..lsp::CompletionOptions::default()
10830                }),
10831                ..lsp::ServerCapabilities::default()
10832            },
10833            ..FakeLspAdapter::default()
10834        },
10835    );
10836    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10837    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10838    let buffer = project
10839        .update(cx, |project, cx| {
10840            project.open_local_buffer(path!("/a/main.rs"), cx)
10841        })
10842        .await
10843        .unwrap();
10844
10845    let multi_buffer = cx.new(|cx| {
10846        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10847        multi_buffer.push_excerpts(
10848            buffer.clone(),
10849            [ExcerptRange::new(0..first_excerpt_end)],
10850            cx,
10851        );
10852        multi_buffer.push_excerpts(
10853            buffer.clone(),
10854            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10855            cx,
10856        );
10857        multi_buffer
10858    });
10859
10860    let editor = workspace
10861        .update(cx, |_, window, cx| {
10862            cx.new(|cx| {
10863                Editor::new(
10864                    EditorMode::Full {
10865                        scale_ui_elements_with_buffer_font_size: false,
10866                        show_active_line_background: false,
10867                        sized_by_content: false,
10868                    },
10869                    multi_buffer.clone(),
10870                    Some(project.clone()),
10871                    window,
10872                    cx,
10873                )
10874            })
10875        })
10876        .unwrap();
10877
10878    let pane = workspace
10879        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10880        .unwrap();
10881    pane.update_in(cx, |pane, window, cx| {
10882        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10883    });
10884
10885    let fake_server = fake_servers.next().await.unwrap();
10886
10887    editor.update_in(cx, |editor, window, cx| {
10888        editor.change_selections(None, window, cx, |s| {
10889            s.select_ranges([
10890                Point::new(1, 11)..Point::new(1, 11),
10891                Point::new(7, 11)..Point::new(7, 11),
10892            ])
10893        });
10894
10895        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10896    });
10897
10898    editor.update_in(cx, |editor, window, cx| {
10899        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10900    });
10901
10902    fake_server
10903        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10904            let completion_item = lsp::CompletionItem {
10905                label: "saturating_sub()".into(),
10906                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10907                    lsp::InsertReplaceEdit {
10908                        new_text: "saturating_sub()".to_owned(),
10909                        insert: lsp::Range::new(
10910                            lsp::Position::new(7, 7),
10911                            lsp::Position::new(7, 11),
10912                        ),
10913                        replace: lsp::Range::new(
10914                            lsp::Position::new(7, 7),
10915                            lsp::Position::new(7, 13),
10916                        ),
10917                    },
10918                )),
10919                ..lsp::CompletionItem::default()
10920            };
10921
10922            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10923        })
10924        .next()
10925        .await
10926        .unwrap();
10927
10928    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10929        .await;
10930
10931    editor
10932        .update_in(cx, |editor, window, cx| {
10933            editor
10934                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10935                .unwrap()
10936        })
10937        .await
10938        .unwrap();
10939
10940    editor.update(cx, |editor, cx| {
10941        assert_text_with_selections(editor, expected_multibuffer, cx);
10942    })
10943}
10944
10945#[gpui::test]
10946async fn test_completion(cx: &mut TestAppContext) {
10947    init_test(cx, |_| {});
10948
10949    let mut cx = EditorLspTestContext::new_rust(
10950        lsp::ServerCapabilities {
10951            completion_provider: Some(lsp::CompletionOptions {
10952                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10953                resolve_provider: Some(true),
10954                ..Default::default()
10955            }),
10956            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10957            ..Default::default()
10958        },
10959        cx,
10960    )
10961    .await;
10962    let counter = Arc::new(AtomicUsize::new(0));
10963
10964    cx.set_state(indoc! {"
10965        oneˇ
10966        two
10967        three
10968    "});
10969    cx.simulate_keystroke(".");
10970    handle_completion_request(
10971        &mut cx,
10972        indoc! {"
10973            one.|<>
10974            two
10975            three
10976        "},
10977        vec!["first_completion", "second_completion"],
10978        counter.clone(),
10979    )
10980    .await;
10981    cx.condition(|editor, _| editor.context_menu_visible())
10982        .await;
10983    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10984
10985    let _handler = handle_signature_help_request(
10986        &mut cx,
10987        lsp::SignatureHelp {
10988            signatures: vec![lsp::SignatureInformation {
10989                label: "test signature".to_string(),
10990                documentation: None,
10991                parameters: Some(vec![lsp::ParameterInformation {
10992                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10993                    documentation: None,
10994                }]),
10995                active_parameter: None,
10996            }],
10997            active_signature: None,
10998            active_parameter: None,
10999        },
11000    );
11001    cx.update_editor(|editor, window, cx| {
11002        assert!(
11003            !editor.signature_help_state.is_shown(),
11004            "No signature help was called for"
11005        );
11006        editor.show_signature_help(&ShowSignatureHelp, window, cx);
11007    });
11008    cx.run_until_parked();
11009    cx.update_editor(|editor, _, _| {
11010        assert!(
11011            !editor.signature_help_state.is_shown(),
11012            "No signature help should be shown when completions menu is open"
11013        );
11014    });
11015
11016    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11017        editor.context_menu_next(&Default::default(), window, cx);
11018        editor
11019            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11020            .unwrap()
11021    });
11022    cx.assert_editor_state(indoc! {"
11023        one.second_completionˇ
11024        two
11025        three
11026    "});
11027
11028    handle_resolve_completion_request(
11029        &mut cx,
11030        Some(vec![
11031            (
11032                //This overlaps with the primary completion edit which is
11033                //misbehavior from the LSP spec, test that we filter it out
11034                indoc! {"
11035                    one.second_ˇcompletion
11036                    two
11037                    threeˇ
11038                "},
11039                "overlapping additional edit",
11040            ),
11041            (
11042                indoc! {"
11043                    one.second_completion
11044                    two
11045                    threeˇ
11046                "},
11047                "\nadditional edit",
11048            ),
11049        ]),
11050    )
11051    .await;
11052    apply_additional_edits.await.unwrap();
11053    cx.assert_editor_state(indoc! {"
11054        one.second_completionˇ
11055        two
11056        three
11057        additional edit
11058    "});
11059
11060    cx.set_state(indoc! {"
11061        one.second_completion
11062        twoˇ
11063        threeˇ
11064        additional edit
11065    "});
11066    cx.simulate_keystroke(" ");
11067    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11068    cx.simulate_keystroke("s");
11069    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11070
11071    cx.assert_editor_state(indoc! {"
11072        one.second_completion
11073        two sˇ
11074        three sˇ
11075        additional edit
11076    "});
11077    handle_completion_request(
11078        &mut cx,
11079        indoc! {"
11080            one.second_completion
11081            two s
11082            three <s|>
11083            additional edit
11084        "},
11085        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11086        counter.clone(),
11087    )
11088    .await;
11089    cx.condition(|editor, _| editor.context_menu_visible())
11090        .await;
11091    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11092
11093    cx.simulate_keystroke("i");
11094
11095    handle_completion_request(
11096        &mut cx,
11097        indoc! {"
11098            one.second_completion
11099            two si
11100            three <si|>
11101            additional edit
11102        "},
11103        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11104        counter.clone(),
11105    )
11106    .await;
11107    cx.condition(|editor, _| editor.context_menu_visible())
11108        .await;
11109    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11110
11111    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11112        editor
11113            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11114            .unwrap()
11115    });
11116    cx.assert_editor_state(indoc! {"
11117        one.second_completion
11118        two sixth_completionˇ
11119        three sixth_completionˇ
11120        additional edit
11121    "});
11122
11123    apply_additional_edits.await.unwrap();
11124
11125    update_test_language_settings(&mut cx, |settings| {
11126        settings.defaults.show_completions_on_input = Some(false);
11127    });
11128    cx.set_state("editorˇ");
11129    cx.simulate_keystroke(".");
11130    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11131    cx.simulate_keystrokes("c l o");
11132    cx.assert_editor_state("editor.cloˇ");
11133    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11134    cx.update_editor(|editor, window, cx| {
11135        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11136    });
11137    handle_completion_request(
11138        &mut cx,
11139        "editor.<clo|>",
11140        vec!["close", "clobber"],
11141        counter.clone(),
11142    )
11143    .await;
11144    cx.condition(|editor, _| editor.context_menu_visible())
11145        .await;
11146    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11147
11148    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11149        editor
11150            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11151            .unwrap()
11152    });
11153    cx.assert_editor_state("editor.closeˇ");
11154    handle_resolve_completion_request(&mut cx, None).await;
11155    apply_additional_edits.await.unwrap();
11156}
11157
11158#[gpui::test]
11159async fn test_word_completion(cx: &mut TestAppContext) {
11160    let lsp_fetch_timeout_ms = 10;
11161    init_test(cx, |language_settings| {
11162        language_settings.defaults.completions = Some(CompletionSettings {
11163            words: WordsCompletionMode::Fallback,
11164            lsp: true,
11165            lsp_fetch_timeout_ms: 10,
11166            lsp_insert_mode: LspInsertMode::Insert,
11167        });
11168    });
11169
11170    let mut cx = EditorLspTestContext::new_rust(
11171        lsp::ServerCapabilities {
11172            completion_provider: Some(lsp::CompletionOptions {
11173                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11174                ..lsp::CompletionOptions::default()
11175            }),
11176            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11177            ..lsp::ServerCapabilities::default()
11178        },
11179        cx,
11180    )
11181    .await;
11182
11183    let throttle_completions = Arc::new(AtomicBool::new(false));
11184
11185    let lsp_throttle_completions = throttle_completions.clone();
11186    let _completion_requests_handler =
11187        cx.lsp
11188            .server
11189            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11190                let lsp_throttle_completions = lsp_throttle_completions.clone();
11191                let cx = cx.clone();
11192                async move {
11193                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11194                        cx.background_executor()
11195                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11196                            .await;
11197                    }
11198                    Ok(Some(lsp::CompletionResponse::Array(vec![
11199                        lsp::CompletionItem {
11200                            label: "first".into(),
11201                            ..lsp::CompletionItem::default()
11202                        },
11203                        lsp::CompletionItem {
11204                            label: "last".into(),
11205                            ..lsp::CompletionItem::default()
11206                        },
11207                    ])))
11208                }
11209            });
11210
11211    cx.set_state(indoc! {"
11212        oneˇ
11213        two
11214        three
11215    "});
11216    cx.simulate_keystroke(".");
11217    cx.executor().run_until_parked();
11218    cx.condition(|editor, _| editor.context_menu_visible())
11219        .await;
11220    cx.update_editor(|editor, window, cx| {
11221        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11222        {
11223            assert_eq!(
11224                completion_menu_entries(&menu),
11225                &["first", "last"],
11226                "When LSP server is fast to reply, no fallback word completions are used"
11227            );
11228        } else {
11229            panic!("expected completion menu to be open");
11230        }
11231        editor.cancel(&Cancel, window, cx);
11232    });
11233    cx.executor().run_until_parked();
11234    cx.condition(|editor, _| !editor.context_menu_visible())
11235        .await;
11236
11237    throttle_completions.store(true, atomic::Ordering::Release);
11238    cx.simulate_keystroke(".");
11239    cx.executor()
11240        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11241    cx.executor().run_until_parked();
11242    cx.condition(|editor, _| editor.context_menu_visible())
11243        .await;
11244    cx.update_editor(|editor, _, _| {
11245        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11246        {
11247            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11248                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11249        } else {
11250            panic!("expected completion menu to be open");
11251        }
11252    });
11253}
11254
11255#[gpui::test]
11256async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11257    init_test(cx, |language_settings| {
11258        language_settings.defaults.completions = Some(CompletionSettings {
11259            words: WordsCompletionMode::Enabled,
11260            lsp: true,
11261            lsp_fetch_timeout_ms: 0,
11262            lsp_insert_mode: LspInsertMode::Insert,
11263        });
11264    });
11265
11266    let mut cx = EditorLspTestContext::new_rust(
11267        lsp::ServerCapabilities {
11268            completion_provider: Some(lsp::CompletionOptions {
11269                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11270                ..lsp::CompletionOptions::default()
11271            }),
11272            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11273            ..lsp::ServerCapabilities::default()
11274        },
11275        cx,
11276    )
11277    .await;
11278
11279    let _completion_requests_handler =
11280        cx.lsp
11281            .server
11282            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11283                Ok(Some(lsp::CompletionResponse::Array(vec![
11284                    lsp::CompletionItem {
11285                        label: "first".into(),
11286                        ..lsp::CompletionItem::default()
11287                    },
11288                    lsp::CompletionItem {
11289                        label: "last".into(),
11290                        ..lsp::CompletionItem::default()
11291                    },
11292                ])))
11293            });
11294
11295    cx.set_state(indoc! {"ˇ
11296        first
11297        last
11298        second
11299    "});
11300    cx.simulate_keystroke(".");
11301    cx.executor().run_until_parked();
11302    cx.condition(|editor, _| editor.context_menu_visible())
11303        .await;
11304    cx.update_editor(|editor, _, _| {
11305        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11306        {
11307            assert_eq!(
11308                completion_menu_entries(&menu),
11309                &["first", "last", "second"],
11310                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11311            );
11312        } else {
11313            panic!("expected completion menu to be open");
11314        }
11315    });
11316}
11317
11318#[gpui::test]
11319async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11320    init_test(cx, |language_settings| {
11321        language_settings.defaults.completions = Some(CompletionSettings {
11322            words: WordsCompletionMode::Disabled,
11323            lsp: true,
11324            lsp_fetch_timeout_ms: 0,
11325            lsp_insert_mode: LspInsertMode::Insert,
11326        });
11327    });
11328
11329    let mut cx = EditorLspTestContext::new_rust(
11330        lsp::ServerCapabilities {
11331            completion_provider: Some(lsp::CompletionOptions {
11332                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11333                ..lsp::CompletionOptions::default()
11334            }),
11335            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11336            ..lsp::ServerCapabilities::default()
11337        },
11338        cx,
11339    )
11340    .await;
11341
11342    let _completion_requests_handler =
11343        cx.lsp
11344            .server
11345            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11346                panic!("LSP completions should not be queried when dealing with word completions")
11347            });
11348
11349    cx.set_state(indoc! {"ˇ
11350        first
11351        last
11352        second
11353    "});
11354    cx.update_editor(|editor, window, cx| {
11355        editor.show_word_completions(&ShowWordCompletions, window, cx);
11356    });
11357    cx.executor().run_until_parked();
11358    cx.condition(|editor, _| editor.context_menu_visible())
11359        .await;
11360    cx.update_editor(|editor, _, _| {
11361        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11362        {
11363            assert_eq!(
11364                completion_menu_entries(&menu),
11365                &["first", "last", "second"],
11366                "`ShowWordCompletions` action should show word completions"
11367            );
11368        } else {
11369            panic!("expected completion menu to be open");
11370        }
11371    });
11372
11373    cx.simulate_keystroke("l");
11374    cx.executor().run_until_parked();
11375    cx.condition(|editor, _| editor.context_menu_visible())
11376        .await;
11377    cx.update_editor(|editor, _, _| {
11378        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11379        {
11380            assert_eq!(
11381                completion_menu_entries(&menu),
11382                &["last"],
11383                "After showing word completions, further editing should filter them and not query the LSP"
11384            );
11385        } else {
11386            panic!("expected completion menu to be open");
11387        }
11388    });
11389}
11390
11391#[gpui::test]
11392async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11393    init_test(cx, |language_settings| {
11394        language_settings.defaults.completions = Some(CompletionSettings {
11395            words: WordsCompletionMode::Fallback,
11396            lsp: false,
11397            lsp_fetch_timeout_ms: 0,
11398            lsp_insert_mode: LspInsertMode::Insert,
11399        });
11400    });
11401
11402    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11403
11404    cx.set_state(indoc! {"ˇ
11405        0_usize
11406        let
11407        33
11408        4.5f32
11409    "});
11410    cx.update_editor(|editor, window, cx| {
11411        editor.show_completions(&ShowCompletions::default(), window, cx);
11412    });
11413    cx.executor().run_until_parked();
11414    cx.condition(|editor, _| editor.context_menu_visible())
11415        .await;
11416    cx.update_editor(|editor, window, cx| {
11417        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11418        {
11419            assert_eq!(
11420                completion_menu_entries(&menu),
11421                &["let"],
11422                "With no digits in the completion query, no digits should be in the word completions"
11423            );
11424        } else {
11425            panic!("expected completion menu to be open");
11426        }
11427        editor.cancel(&Cancel, window, cx);
11428    });
11429
11430    cx.set_state(indoc! {"11431        0_usize
11432        let
11433        3
11434        33.35f32
11435    "});
11436    cx.update_editor(|editor, window, cx| {
11437        editor.show_completions(&ShowCompletions::default(), window, cx);
11438    });
11439    cx.executor().run_until_parked();
11440    cx.condition(|editor, _| editor.context_menu_visible())
11441        .await;
11442    cx.update_editor(|editor, _, _| {
11443        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11444        {
11445            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11446                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11447        } else {
11448            panic!("expected completion menu to be open");
11449        }
11450    });
11451}
11452
11453fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11454    let position = || lsp::Position {
11455        line: params.text_document_position.position.line,
11456        character: params.text_document_position.position.character,
11457    };
11458    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11459        range: lsp::Range {
11460            start: position(),
11461            end: position(),
11462        },
11463        new_text: text.to_string(),
11464    }))
11465}
11466
11467#[gpui::test]
11468async fn test_multiline_completion(cx: &mut TestAppContext) {
11469    init_test(cx, |_| {});
11470
11471    let fs = FakeFs::new(cx.executor());
11472    fs.insert_tree(
11473        path!("/a"),
11474        json!({
11475            "main.ts": "a",
11476        }),
11477    )
11478    .await;
11479
11480    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11481    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11482    let typescript_language = Arc::new(Language::new(
11483        LanguageConfig {
11484            name: "TypeScript".into(),
11485            matcher: LanguageMatcher {
11486                path_suffixes: vec!["ts".to_string()],
11487                ..LanguageMatcher::default()
11488            },
11489            line_comments: vec!["// ".into()],
11490            ..LanguageConfig::default()
11491        },
11492        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11493    ));
11494    language_registry.add(typescript_language.clone());
11495    let mut fake_servers = language_registry.register_fake_lsp(
11496        "TypeScript",
11497        FakeLspAdapter {
11498            capabilities: lsp::ServerCapabilities {
11499                completion_provider: Some(lsp::CompletionOptions {
11500                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11501                    ..lsp::CompletionOptions::default()
11502                }),
11503                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11504                ..lsp::ServerCapabilities::default()
11505            },
11506            // Emulate vtsls label generation
11507            label_for_completion: Some(Box::new(|item, _| {
11508                let text = if let Some(description) = item
11509                    .label_details
11510                    .as_ref()
11511                    .and_then(|label_details| label_details.description.as_ref())
11512                {
11513                    format!("{} {}", item.label, description)
11514                } else if let Some(detail) = &item.detail {
11515                    format!("{} {}", item.label, detail)
11516                } else {
11517                    item.label.clone()
11518                };
11519                let len = text.len();
11520                Some(language::CodeLabel {
11521                    text,
11522                    runs: Vec::new(),
11523                    filter_range: 0..len,
11524                })
11525            })),
11526            ..FakeLspAdapter::default()
11527        },
11528    );
11529    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11530    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11531    let worktree_id = workspace
11532        .update(cx, |workspace, _window, cx| {
11533            workspace.project().update(cx, |project, cx| {
11534                project.worktrees(cx).next().unwrap().read(cx).id()
11535            })
11536        })
11537        .unwrap();
11538    let _buffer = project
11539        .update(cx, |project, cx| {
11540            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11541        })
11542        .await
11543        .unwrap();
11544    let editor = workspace
11545        .update(cx, |workspace, window, cx| {
11546            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11547        })
11548        .unwrap()
11549        .await
11550        .unwrap()
11551        .downcast::<Editor>()
11552        .unwrap();
11553    let fake_server = fake_servers.next().await.unwrap();
11554
11555    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11556    let multiline_label_2 = "a\nb\nc\n";
11557    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11558    let multiline_description = "d\ne\nf\n";
11559    let multiline_detail_2 = "g\nh\ni\n";
11560
11561    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11562        move |params, _| async move {
11563            Ok(Some(lsp::CompletionResponse::Array(vec![
11564                lsp::CompletionItem {
11565                    label: multiline_label.to_string(),
11566                    text_edit: gen_text_edit(&params, "new_text_1"),
11567                    ..lsp::CompletionItem::default()
11568                },
11569                lsp::CompletionItem {
11570                    label: "single line label 1".to_string(),
11571                    detail: Some(multiline_detail.to_string()),
11572                    text_edit: gen_text_edit(&params, "new_text_2"),
11573                    ..lsp::CompletionItem::default()
11574                },
11575                lsp::CompletionItem {
11576                    label: "single line label 2".to_string(),
11577                    label_details: Some(lsp::CompletionItemLabelDetails {
11578                        description: Some(multiline_description.to_string()),
11579                        detail: None,
11580                    }),
11581                    text_edit: gen_text_edit(&params, "new_text_2"),
11582                    ..lsp::CompletionItem::default()
11583                },
11584                lsp::CompletionItem {
11585                    label: multiline_label_2.to_string(),
11586                    detail: Some(multiline_detail_2.to_string()),
11587                    text_edit: gen_text_edit(&params, "new_text_3"),
11588                    ..lsp::CompletionItem::default()
11589                },
11590                lsp::CompletionItem {
11591                    label: "Label with many     spaces and \t but without newlines".to_string(),
11592                    detail: Some(
11593                        "Details with many     spaces and \t but without newlines".to_string(),
11594                    ),
11595                    text_edit: gen_text_edit(&params, "new_text_4"),
11596                    ..lsp::CompletionItem::default()
11597                },
11598            ])))
11599        },
11600    );
11601
11602    editor.update_in(cx, |editor, window, cx| {
11603        cx.focus_self(window);
11604        editor.move_to_end(&MoveToEnd, window, cx);
11605        editor.handle_input(".", window, cx);
11606    });
11607    cx.run_until_parked();
11608    completion_handle.next().await.unwrap();
11609
11610    editor.update(cx, |editor, _| {
11611        assert!(editor.context_menu_visible());
11612        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11613        {
11614            let completion_labels = menu
11615                .completions
11616                .borrow()
11617                .iter()
11618                .map(|c| c.label.text.clone())
11619                .collect::<Vec<_>>();
11620            assert_eq!(
11621                completion_labels,
11622                &[
11623                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11624                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11625                    "single line label 2 d e f ",
11626                    "a b c g h i ",
11627                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11628                ],
11629                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11630            );
11631
11632            for completion in menu
11633                .completions
11634                .borrow()
11635                .iter() {
11636                    assert_eq!(
11637                        completion.label.filter_range,
11638                        0..completion.label.text.len(),
11639                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11640                    );
11641                }
11642        } else {
11643            panic!("expected completion menu to be open");
11644        }
11645    });
11646}
11647
11648#[gpui::test]
11649async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11650    init_test(cx, |_| {});
11651    let mut cx = EditorLspTestContext::new_rust(
11652        lsp::ServerCapabilities {
11653            completion_provider: Some(lsp::CompletionOptions {
11654                trigger_characters: Some(vec![".".to_string()]),
11655                ..Default::default()
11656            }),
11657            ..Default::default()
11658        },
11659        cx,
11660    )
11661    .await;
11662    cx.lsp
11663        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11664            Ok(Some(lsp::CompletionResponse::Array(vec![
11665                lsp::CompletionItem {
11666                    label: "first".into(),
11667                    ..Default::default()
11668                },
11669                lsp::CompletionItem {
11670                    label: "last".into(),
11671                    ..Default::default()
11672                },
11673            ])))
11674        });
11675    cx.set_state("variableˇ");
11676    cx.simulate_keystroke(".");
11677    cx.executor().run_until_parked();
11678
11679    cx.update_editor(|editor, _, _| {
11680        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11681        {
11682            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11683        } else {
11684            panic!("expected completion menu to be open");
11685        }
11686    });
11687
11688    cx.update_editor(|editor, window, cx| {
11689        editor.move_page_down(&MovePageDown::default(), window, cx);
11690        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11691        {
11692            assert!(
11693                menu.selected_item == 1,
11694                "expected PageDown to select the last item from the context menu"
11695            );
11696        } else {
11697            panic!("expected completion menu to stay open after PageDown");
11698        }
11699    });
11700
11701    cx.update_editor(|editor, window, cx| {
11702        editor.move_page_up(&MovePageUp::default(), window, cx);
11703        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11704        {
11705            assert!(
11706                menu.selected_item == 0,
11707                "expected PageUp to select the first item from the context menu"
11708            );
11709        } else {
11710            panic!("expected completion menu to stay open after PageUp");
11711        }
11712    });
11713}
11714
11715#[gpui::test]
11716async fn test_as_is_completions(cx: &mut TestAppContext) {
11717    init_test(cx, |_| {});
11718    let mut cx = EditorLspTestContext::new_rust(
11719        lsp::ServerCapabilities {
11720            completion_provider: Some(lsp::CompletionOptions {
11721                ..Default::default()
11722            }),
11723            ..Default::default()
11724        },
11725        cx,
11726    )
11727    .await;
11728    cx.lsp
11729        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11730            Ok(Some(lsp::CompletionResponse::Array(vec![
11731                lsp::CompletionItem {
11732                    label: "unsafe".into(),
11733                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11734                        range: lsp::Range {
11735                            start: lsp::Position {
11736                                line: 1,
11737                                character: 2,
11738                            },
11739                            end: lsp::Position {
11740                                line: 1,
11741                                character: 3,
11742                            },
11743                        },
11744                        new_text: "unsafe".to_string(),
11745                    })),
11746                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11747                    ..Default::default()
11748                },
11749            ])))
11750        });
11751    cx.set_state("fn a() {}\n");
11752    cx.executor().run_until_parked();
11753    cx.update_editor(|editor, window, cx| {
11754        editor.show_completions(
11755            &ShowCompletions {
11756                trigger: Some("\n".into()),
11757            },
11758            window,
11759            cx,
11760        );
11761    });
11762    cx.executor().run_until_parked();
11763
11764    cx.update_editor(|editor, window, cx| {
11765        editor.confirm_completion(&Default::default(), window, cx)
11766    });
11767    cx.executor().run_until_parked();
11768    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11769}
11770
11771#[gpui::test]
11772async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11773    init_test(cx, |_| {});
11774
11775    let mut cx = EditorLspTestContext::new_rust(
11776        lsp::ServerCapabilities {
11777            completion_provider: Some(lsp::CompletionOptions {
11778                trigger_characters: Some(vec![".".to_string()]),
11779                resolve_provider: Some(true),
11780                ..Default::default()
11781            }),
11782            ..Default::default()
11783        },
11784        cx,
11785    )
11786    .await;
11787
11788    cx.set_state("fn main() { let a = 2ˇ; }");
11789    cx.simulate_keystroke(".");
11790    let completion_item = lsp::CompletionItem {
11791        label: "Some".into(),
11792        kind: Some(lsp::CompletionItemKind::SNIPPET),
11793        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11794        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11795            kind: lsp::MarkupKind::Markdown,
11796            value: "```rust\nSome(2)\n```".to_string(),
11797        })),
11798        deprecated: Some(false),
11799        sort_text: Some("Some".to_string()),
11800        filter_text: Some("Some".to_string()),
11801        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11802        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11803            range: lsp::Range {
11804                start: lsp::Position {
11805                    line: 0,
11806                    character: 22,
11807                },
11808                end: lsp::Position {
11809                    line: 0,
11810                    character: 22,
11811                },
11812            },
11813            new_text: "Some(2)".to_string(),
11814        })),
11815        additional_text_edits: Some(vec![lsp::TextEdit {
11816            range: lsp::Range {
11817                start: lsp::Position {
11818                    line: 0,
11819                    character: 20,
11820                },
11821                end: lsp::Position {
11822                    line: 0,
11823                    character: 22,
11824                },
11825            },
11826            new_text: "".to_string(),
11827        }]),
11828        ..Default::default()
11829    };
11830
11831    let closure_completion_item = completion_item.clone();
11832    let counter = Arc::new(AtomicUsize::new(0));
11833    let counter_clone = counter.clone();
11834    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11835        let task_completion_item = closure_completion_item.clone();
11836        counter_clone.fetch_add(1, atomic::Ordering::Release);
11837        async move {
11838            Ok(Some(lsp::CompletionResponse::Array(vec![
11839                task_completion_item,
11840            ])))
11841        }
11842    });
11843
11844    cx.condition(|editor, _| editor.context_menu_visible())
11845        .await;
11846    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11847    assert!(request.next().await.is_some());
11848    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11849
11850    cx.simulate_keystrokes("S o m");
11851    cx.condition(|editor, _| editor.context_menu_visible())
11852        .await;
11853    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11854    assert!(request.next().await.is_some());
11855    assert!(request.next().await.is_some());
11856    assert!(request.next().await.is_some());
11857    request.close();
11858    assert!(request.next().await.is_none());
11859    assert_eq!(
11860        counter.load(atomic::Ordering::Acquire),
11861        4,
11862        "With the completions menu open, only one LSP request should happen per input"
11863    );
11864}
11865
11866#[gpui::test]
11867async fn test_toggle_comment(cx: &mut TestAppContext) {
11868    init_test(cx, |_| {});
11869    let mut cx = EditorTestContext::new(cx).await;
11870    let language = Arc::new(Language::new(
11871        LanguageConfig {
11872            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11873            ..Default::default()
11874        },
11875        Some(tree_sitter_rust::LANGUAGE.into()),
11876    ));
11877    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11878
11879    // If multiple selections intersect a line, the line is only toggled once.
11880    cx.set_state(indoc! {"
11881        fn a() {
11882            «//b();
11883            ˇ»// «c();
11884            //ˇ»  d();
11885        }
11886    "});
11887
11888    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11889
11890    cx.assert_editor_state(indoc! {"
11891        fn a() {
11892            «b();
11893            c();
11894            ˇ» d();
11895        }
11896    "});
11897
11898    // The comment prefix is inserted at the same column for every line in a
11899    // selection.
11900    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11901
11902    cx.assert_editor_state(indoc! {"
11903        fn a() {
11904            // «b();
11905            // c();
11906            ˇ»//  d();
11907        }
11908    "});
11909
11910    // If a selection ends at the beginning of a line, that line is not toggled.
11911    cx.set_selections_state(indoc! {"
11912        fn a() {
11913            // b();
11914            «// c();
11915        ˇ»    //  d();
11916        }
11917    "});
11918
11919    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11920
11921    cx.assert_editor_state(indoc! {"
11922        fn a() {
11923            // b();
11924            «c();
11925        ˇ»    //  d();
11926        }
11927    "});
11928
11929    // If a selection span a single line and is empty, the line is toggled.
11930    cx.set_state(indoc! {"
11931        fn a() {
11932            a();
11933            b();
11934        ˇ
11935        }
11936    "});
11937
11938    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11939
11940    cx.assert_editor_state(indoc! {"
11941        fn a() {
11942            a();
11943            b();
11944        //•ˇ
11945        }
11946    "});
11947
11948    // If a selection span multiple lines, empty lines are not toggled.
11949    cx.set_state(indoc! {"
11950        fn a() {
11951            «a();
11952
11953            c();ˇ»
11954        }
11955    "});
11956
11957    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11958
11959    cx.assert_editor_state(indoc! {"
11960        fn a() {
11961            // «a();
11962
11963            // c();ˇ»
11964        }
11965    "});
11966
11967    // If a selection includes multiple comment prefixes, all lines are uncommented.
11968    cx.set_state(indoc! {"
11969        fn a() {
11970            «// a();
11971            /// b();
11972            //! c();ˇ»
11973        }
11974    "});
11975
11976    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11977
11978    cx.assert_editor_state(indoc! {"
11979        fn a() {
11980            «a();
11981            b();
11982            c();ˇ»
11983        }
11984    "});
11985}
11986
11987#[gpui::test]
11988async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11989    init_test(cx, |_| {});
11990    let mut cx = EditorTestContext::new(cx).await;
11991    let language = Arc::new(Language::new(
11992        LanguageConfig {
11993            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11994            ..Default::default()
11995        },
11996        Some(tree_sitter_rust::LANGUAGE.into()),
11997    ));
11998    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11999
12000    let toggle_comments = &ToggleComments {
12001        advance_downwards: false,
12002        ignore_indent: true,
12003    };
12004
12005    // If multiple selections intersect a line, the line is only toggled once.
12006    cx.set_state(indoc! {"
12007        fn a() {
12008        //    «b();
12009        //    c();
12010        //    ˇ» d();
12011        }
12012    "});
12013
12014    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12015
12016    cx.assert_editor_state(indoc! {"
12017        fn a() {
12018            «b();
12019            c();
12020            ˇ» d();
12021        }
12022    "});
12023
12024    // The comment prefix is inserted at the beginning of each line
12025    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12026
12027    cx.assert_editor_state(indoc! {"
12028        fn a() {
12029        //    «b();
12030        //    c();
12031        //    ˇ» d();
12032        }
12033    "});
12034
12035    // If a selection ends at the beginning of a line, that line is not toggled.
12036    cx.set_selections_state(indoc! {"
12037        fn a() {
12038        //    b();
12039        //    «c();
12040        ˇ»//     d();
12041        }
12042    "});
12043
12044    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12045
12046    cx.assert_editor_state(indoc! {"
12047        fn a() {
12048        //    b();
12049            «c();
12050        ˇ»//     d();
12051        }
12052    "});
12053
12054    // If a selection span a single line and is empty, the line is toggled.
12055    cx.set_state(indoc! {"
12056        fn a() {
12057            a();
12058            b();
12059        ˇ
12060        }
12061    "});
12062
12063    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12064
12065    cx.assert_editor_state(indoc! {"
12066        fn a() {
12067            a();
12068            b();
12069        //ˇ
12070        }
12071    "});
12072
12073    // If a selection span multiple lines, empty lines are not toggled.
12074    cx.set_state(indoc! {"
12075        fn a() {
12076            «a();
12077
12078            c();ˇ»
12079        }
12080    "});
12081
12082    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12083
12084    cx.assert_editor_state(indoc! {"
12085        fn a() {
12086        //    «a();
12087
12088        //    c();ˇ»
12089        }
12090    "});
12091
12092    // If a selection includes multiple comment prefixes, all lines are uncommented.
12093    cx.set_state(indoc! {"
12094        fn a() {
12095        //    «a();
12096        ///    b();
12097        //!    c();ˇ»
12098        }
12099    "});
12100
12101    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12102
12103    cx.assert_editor_state(indoc! {"
12104        fn a() {
12105            «a();
12106            b();
12107            c();ˇ»
12108        }
12109    "});
12110}
12111
12112#[gpui::test]
12113async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12114    init_test(cx, |_| {});
12115
12116    let language = Arc::new(Language::new(
12117        LanguageConfig {
12118            line_comments: vec!["// ".into()],
12119            ..Default::default()
12120        },
12121        Some(tree_sitter_rust::LANGUAGE.into()),
12122    ));
12123
12124    let mut cx = EditorTestContext::new(cx).await;
12125
12126    cx.language_registry().add(language.clone());
12127    cx.update_buffer(|buffer, cx| {
12128        buffer.set_language(Some(language), cx);
12129    });
12130
12131    let toggle_comments = &ToggleComments {
12132        advance_downwards: true,
12133        ignore_indent: false,
12134    };
12135
12136    // Single cursor on one line -> advance
12137    // Cursor moves horizontally 3 characters as well on non-blank line
12138    cx.set_state(indoc!(
12139        "fn a() {
12140             ˇdog();
12141             cat();
12142        }"
12143    ));
12144    cx.update_editor(|editor, window, cx| {
12145        editor.toggle_comments(toggle_comments, window, cx);
12146    });
12147    cx.assert_editor_state(indoc!(
12148        "fn a() {
12149             // dog();
12150             catˇ();
12151        }"
12152    ));
12153
12154    // Single selection on one line -> don't advance
12155    cx.set_state(indoc!(
12156        "fn a() {
12157             «dog()ˇ»;
12158             cat();
12159        }"
12160    ));
12161    cx.update_editor(|editor, window, cx| {
12162        editor.toggle_comments(toggle_comments, window, cx);
12163    });
12164    cx.assert_editor_state(indoc!(
12165        "fn a() {
12166             // «dog()ˇ»;
12167             cat();
12168        }"
12169    ));
12170
12171    // Multiple cursors on one line -> advance
12172    cx.set_state(indoc!(
12173        "fn a() {
12174             ˇdˇog();
12175             cat();
12176        }"
12177    ));
12178    cx.update_editor(|editor, window, cx| {
12179        editor.toggle_comments(toggle_comments, window, cx);
12180    });
12181    cx.assert_editor_state(indoc!(
12182        "fn a() {
12183             // dog();
12184             catˇ(ˇ);
12185        }"
12186    ));
12187
12188    // Multiple cursors on one line, with selection -> don't advance
12189    cx.set_state(indoc!(
12190        "fn a() {
12191             ˇdˇog«()ˇ»;
12192             cat();
12193        }"
12194    ));
12195    cx.update_editor(|editor, window, cx| {
12196        editor.toggle_comments(toggle_comments, window, cx);
12197    });
12198    cx.assert_editor_state(indoc!(
12199        "fn a() {
12200             // ˇdˇog«()ˇ»;
12201             cat();
12202        }"
12203    ));
12204
12205    // Single cursor on one line -> advance
12206    // Cursor moves to column 0 on blank line
12207    cx.set_state(indoc!(
12208        "fn a() {
12209             ˇdog();
12210
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        ˇ
12221             cat();
12222        }"
12223    ));
12224
12225    // Single cursor on one line -> advance
12226    // Cursor starts and ends at column 0
12227    cx.set_state(indoc!(
12228        "fn a() {
12229         ˇ    dog();
12230             cat();
12231        }"
12232    ));
12233    cx.update_editor(|editor, window, cx| {
12234        editor.toggle_comments(toggle_comments, window, cx);
12235    });
12236    cx.assert_editor_state(indoc!(
12237        "fn a() {
12238             // dog();
12239         ˇ    cat();
12240        }"
12241    ));
12242}
12243
12244#[gpui::test]
12245async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12246    init_test(cx, |_| {});
12247
12248    let mut cx = EditorTestContext::new(cx).await;
12249
12250    let html_language = Arc::new(
12251        Language::new(
12252            LanguageConfig {
12253                name: "HTML".into(),
12254                block_comment: Some(("<!-- ".into(), " -->".into())),
12255                ..Default::default()
12256            },
12257            Some(tree_sitter_html::LANGUAGE.into()),
12258        )
12259        .with_injection_query(
12260            r#"
12261            (script_element
12262                (raw_text) @injection.content
12263                (#set! injection.language "javascript"))
12264            "#,
12265        )
12266        .unwrap(),
12267    );
12268
12269    let javascript_language = Arc::new(Language::new(
12270        LanguageConfig {
12271            name: "JavaScript".into(),
12272            line_comments: vec!["// ".into()],
12273            ..Default::default()
12274        },
12275        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12276    ));
12277
12278    cx.language_registry().add(html_language.clone());
12279    cx.language_registry().add(javascript_language.clone());
12280    cx.update_buffer(|buffer, cx| {
12281        buffer.set_language(Some(html_language), cx);
12282    });
12283
12284    // Toggle comments for empty selections
12285    cx.set_state(
12286        &r#"
12287            <p>A</p>ˇ
12288            <p>B</p>ˇ
12289            <p>C</p>ˇ
12290        "#
12291        .unindent(),
12292    );
12293    cx.update_editor(|editor, window, cx| {
12294        editor.toggle_comments(&ToggleComments::default(), window, cx)
12295    });
12296    cx.assert_editor_state(
12297        &r#"
12298            <!-- <p>A</p>ˇ -->
12299            <!-- <p>B</p>ˇ -->
12300            <!-- <p>C</p>ˇ -->
12301        "#
12302        .unindent(),
12303    );
12304    cx.update_editor(|editor, window, cx| {
12305        editor.toggle_comments(&ToggleComments::default(), window, cx)
12306    });
12307    cx.assert_editor_state(
12308        &r#"
12309            <p>A</p>ˇ
12310            <p>B</p>ˇ
12311            <p>C</p>ˇ
12312        "#
12313        .unindent(),
12314    );
12315
12316    // Toggle comments for mixture of empty and non-empty selections, where
12317    // multiple selections occupy a given line.
12318    cx.set_state(
12319        &r#"
12320            <p>A«</p>
12321            <p>ˇ»B</p>ˇ
12322            <p>C«</p>
12323            <p>ˇ»D</p>ˇ
12324        "#
12325        .unindent(),
12326    );
12327
12328    cx.update_editor(|editor, window, cx| {
12329        editor.toggle_comments(&ToggleComments::default(), window, cx)
12330    });
12331    cx.assert_editor_state(
12332        &r#"
12333            <!-- <p>A«</p>
12334            <p>ˇ»B</p>ˇ -->
12335            <!-- <p>C«</p>
12336            <p>ˇ»D</p>ˇ -->
12337        "#
12338        .unindent(),
12339    );
12340    cx.update_editor(|editor, window, cx| {
12341        editor.toggle_comments(&ToggleComments::default(), window, cx)
12342    });
12343    cx.assert_editor_state(
12344        &r#"
12345            <p>A«</p>
12346            <p>ˇ»B</p>ˇ
12347            <p>C«</p>
12348            <p>ˇ»D</p>ˇ
12349        "#
12350        .unindent(),
12351    );
12352
12353    // Toggle comments when different languages are active for different
12354    // selections.
12355    cx.set_state(
12356        &r#"
12357            ˇ<script>
12358                ˇvar x = new Y();
12359            ˇ</script>
12360        "#
12361        .unindent(),
12362    );
12363    cx.executor().run_until_parked();
12364    cx.update_editor(|editor, window, cx| {
12365        editor.toggle_comments(&ToggleComments::default(), window, cx)
12366    });
12367    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12368    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12369    cx.assert_editor_state(
12370        &r#"
12371            <!-- ˇ<script> -->
12372                // ˇvar x = new Y();
12373            <!-- ˇ</script> -->
12374        "#
12375        .unindent(),
12376    );
12377}
12378
12379#[gpui::test]
12380fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12381    init_test(cx, |_| {});
12382
12383    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12384    let multibuffer = cx.new(|cx| {
12385        let mut multibuffer = MultiBuffer::new(ReadWrite);
12386        multibuffer.push_excerpts(
12387            buffer.clone(),
12388            [
12389                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12390                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12391            ],
12392            cx,
12393        );
12394        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12395        multibuffer
12396    });
12397
12398    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12399    editor.update_in(cx, |editor, window, cx| {
12400        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12401        editor.change_selections(None, window, cx, |s| {
12402            s.select_ranges([
12403                Point::new(0, 0)..Point::new(0, 0),
12404                Point::new(1, 0)..Point::new(1, 0),
12405            ])
12406        });
12407
12408        editor.handle_input("X", window, cx);
12409        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12410        assert_eq!(
12411            editor.selections.ranges(cx),
12412            [
12413                Point::new(0, 1)..Point::new(0, 1),
12414                Point::new(1, 1)..Point::new(1, 1),
12415            ]
12416        );
12417
12418        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12419        editor.change_selections(None, window, cx, |s| {
12420            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12421        });
12422        editor.backspace(&Default::default(), window, cx);
12423        assert_eq!(editor.text(cx), "Xa\nbbb");
12424        assert_eq!(
12425            editor.selections.ranges(cx),
12426            [Point::new(1, 0)..Point::new(1, 0)]
12427        );
12428
12429        editor.change_selections(None, window, cx, |s| {
12430            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12431        });
12432        editor.backspace(&Default::default(), window, cx);
12433        assert_eq!(editor.text(cx), "X\nbb");
12434        assert_eq!(
12435            editor.selections.ranges(cx),
12436            [Point::new(0, 1)..Point::new(0, 1)]
12437        );
12438    });
12439}
12440
12441#[gpui::test]
12442fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12443    init_test(cx, |_| {});
12444
12445    let markers = vec![('[', ']').into(), ('(', ')').into()];
12446    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12447        indoc! {"
12448            [aaaa
12449            (bbbb]
12450            cccc)",
12451        },
12452        markers.clone(),
12453    );
12454    let excerpt_ranges = markers.into_iter().map(|marker| {
12455        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12456        ExcerptRange::new(context.clone())
12457    });
12458    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12459    let multibuffer = cx.new(|cx| {
12460        let mut multibuffer = MultiBuffer::new(ReadWrite);
12461        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12462        multibuffer
12463    });
12464
12465    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12466    editor.update_in(cx, |editor, window, cx| {
12467        let (expected_text, selection_ranges) = marked_text_ranges(
12468            indoc! {"
12469                aaaa
12470                bˇbbb
12471                bˇbbˇb
12472                cccc"
12473            },
12474            true,
12475        );
12476        assert_eq!(editor.text(cx), expected_text);
12477        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12478
12479        editor.handle_input("X", window, cx);
12480
12481        let (expected_text, expected_selections) = marked_text_ranges(
12482            indoc! {"
12483                aaaa
12484                bXˇbbXb
12485                bXˇbbXˇb
12486                cccc"
12487            },
12488            false,
12489        );
12490        assert_eq!(editor.text(cx), expected_text);
12491        assert_eq!(editor.selections.ranges(cx), expected_selections);
12492
12493        editor.newline(&Newline, window, cx);
12494        let (expected_text, expected_selections) = marked_text_ranges(
12495            indoc! {"
12496                aaaa
12497                bX
12498                ˇbbX
12499                b
12500                bX
12501                ˇbbX
12502                ˇb
12503                cccc"
12504            },
12505            false,
12506        );
12507        assert_eq!(editor.text(cx), expected_text);
12508        assert_eq!(editor.selections.ranges(cx), expected_selections);
12509    });
12510}
12511
12512#[gpui::test]
12513fn test_refresh_selections(cx: &mut TestAppContext) {
12514    init_test(cx, |_| {});
12515
12516    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12517    let mut excerpt1_id = None;
12518    let multibuffer = cx.new(|cx| {
12519        let mut multibuffer = MultiBuffer::new(ReadWrite);
12520        excerpt1_id = multibuffer
12521            .push_excerpts(
12522                buffer.clone(),
12523                [
12524                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12525                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12526                ],
12527                cx,
12528            )
12529            .into_iter()
12530            .next();
12531        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12532        multibuffer
12533    });
12534
12535    let editor = cx.add_window(|window, cx| {
12536        let mut editor = build_editor(multibuffer.clone(), window, cx);
12537        let snapshot = editor.snapshot(window, cx);
12538        editor.change_selections(None, window, cx, |s| {
12539            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12540        });
12541        editor.begin_selection(
12542            Point::new(2, 1).to_display_point(&snapshot),
12543            true,
12544            1,
12545            window,
12546            cx,
12547        );
12548        assert_eq!(
12549            editor.selections.ranges(cx),
12550            [
12551                Point::new(1, 3)..Point::new(1, 3),
12552                Point::new(2, 1)..Point::new(2, 1),
12553            ]
12554        );
12555        editor
12556    });
12557
12558    // Refreshing selections is a no-op when excerpts haven't changed.
12559    _ = editor.update(cx, |editor, window, cx| {
12560        editor.change_selections(None, window, cx, |s| s.refresh());
12561        assert_eq!(
12562            editor.selections.ranges(cx),
12563            [
12564                Point::new(1, 3)..Point::new(1, 3),
12565                Point::new(2, 1)..Point::new(2, 1),
12566            ]
12567        );
12568    });
12569
12570    multibuffer.update(cx, |multibuffer, cx| {
12571        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12572    });
12573    _ = editor.update(cx, |editor, window, cx| {
12574        // Removing an excerpt causes the first selection to become degenerate.
12575        assert_eq!(
12576            editor.selections.ranges(cx),
12577            [
12578                Point::new(0, 0)..Point::new(0, 0),
12579                Point::new(0, 1)..Point::new(0, 1)
12580            ]
12581        );
12582
12583        // Refreshing selections will relocate the first selection to the original buffer
12584        // location.
12585        editor.change_selections(None, window, cx, |s| s.refresh());
12586        assert_eq!(
12587            editor.selections.ranges(cx),
12588            [
12589                Point::new(0, 1)..Point::new(0, 1),
12590                Point::new(0, 3)..Point::new(0, 3)
12591            ]
12592        );
12593        assert!(editor.selections.pending_anchor().is_some());
12594    });
12595}
12596
12597#[gpui::test]
12598fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12599    init_test(cx, |_| {});
12600
12601    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12602    let mut excerpt1_id = None;
12603    let multibuffer = cx.new(|cx| {
12604        let mut multibuffer = MultiBuffer::new(ReadWrite);
12605        excerpt1_id = multibuffer
12606            .push_excerpts(
12607                buffer.clone(),
12608                [
12609                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12610                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12611                ],
12612                cx,
12613            )
12614            .into_iter()
12615            .next();
12616        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12617        multibuffer
12618    });
12619
12620    let editor = cx.add_window(|window, cx| {
12621        let mut editor = build_editor(multibuffer.clone(), window, cx);
12622        let snapshot = editor.snapshot(window, cx);
12623        editor.begin_selection(
12624            Point::new(1, 3).to_display_point(&snapshot),
12625            false,
12626            1,
12627            window,
12628            cx,
12629        );
12630        assert_eq!(
12631            editor.selections.ranges(cx),
12632            [Point::new(1, 3)..Point::new(1, 3)]
12633        );
12634        editor
12635    });
12636
12637    multibuffer.update(cx, |multibuffer, cx| {
12638        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12639    });
12640    _ = editor.update(cx, |editor, window, cx| {
12641        assert_eq!(
12642            editor.selections.ranges(cx),
12643            [Point::new(0, 0)..Point::new(0, 0)]
12644        );
12645
12646        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12647        editor.change_selections(None, window, cx, |s| s.refresh());
12648        assert_eq!(
12649            editor.selections.ranges(cx),
12650            [Point::new(0, 3)..Point::new(0, 3)]
12651        );
12652        assert!(editor.selections.pending_anchor().is_some());
12653    });
12654}
12655
12656#[gpui::test]
12657async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12658    init_test(cx, |_| {});
12659
12660    let language = Arc::new(
12661        Language::new(
12662            LanguageConfig {
12663                brackets: BracketPairConfig {
12664                    pairs: vec![
12665                        BracketPair {
12666                            start: "{".to_string(),
12667                            end: "}".to_string(),
12668                            close: true,
12669                            surround: true,
12670                            newline: true,
12671                        },
12672                        BracketPair {
12673                            start: "/* ".to_string(),
12674                            end: " */".to_string(),
12675                            close: true,
12676                            surround: true,
12677                            newline: true,
12678                        },
12679                    ],
12680                    ..Default::default()
12681                },
12682                ..Default::default()
12683            },
12684            Some(tree_sitter_rust::LANGUAGE.into()),
12685        )
12686        .with_indents_query("")
12687        .unwrap(),
12688    );
12689
12690    let text = concat!(
12691        "{   }\n",     //
12692        "  x\n",       //
12693        "  /*   */\n", //
12694        "x\n",         //
12695        "{{} }\n",     //
12696    );
12697
12698    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12699    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12700    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12701    editor
12702        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12703        .await;
12704
12705    editor.update_in(cx, |editor, window, cx| {
12706        editor.change_selections(None, window, cx, |s| {
12707            s.select_display_ranges([
12708                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12709                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12710                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12711            ])
12712        });
12713        editor.newline(&Newline, window, cx);
12714
12715        assert_eq!(
12716            editor.buffer().read(cx).read(cx).text(),
12717            concat!(
12718                "{ \n",    // Suppress rustfmt
12719                "\n",      //
12720                "}\n",     //
12721                "  x\n",   //
12722                "  /* \n", //
12723                "  \n",    //
12724                "  */\n",  //
12725                "x\n",     //
12726                "{{} \n",  //
12727                "}\n",     //
12728            )
12729        );
12730    });
12731}
12732
12733#[gpui::test]
12734fn test_highlighted_ranges(cx: &mut TestAppContext) {
12735    init_test(cx, |_| {});
12736
12737    let editor = cx.add_window(|window, cx| {
12738        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12739        build_editor(buffer.clone(), window, cx)
12740    });
12741
12742    _ = editor.update(cx, |editor, window, cx| {
12743        struct Type1;
12744        struct Type2;
12745
12746        let buffer = editor.buffer.read(cx).snapshot(cx);
12747
12748        let anchor_range =
12749            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12750
12751        editor.highlight_background::<Type1>(
12752            &[
12753                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12754                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12755                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12756                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12757            ],
12758            |_| Hsla::red(),
12759            cx,
12760        );
12761        editor.highlight_background::<Type2>(
12762            &[
12763                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12764                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12765                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12766                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12767            ],
12768            |_| Hsla::green(),
12769            cx,
12770        );
12771
12772        let snapshot = editor.snapshot(window, cx);
12773        let mut highlighted_ranges = editor.background_highlights_in_range(
12774            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12775            &snapshot,
12776            cx.theme().colors(),
12777        );
12778        // Enforce a consistent ordering based on color without relying on the ordering of the
12779        // highlight's `TypeId` which is non-executor.
12780        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12781        assert_eq!(
12782            highlighted_ranges,
12783            &[
12784                (
12785                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12786                    Hsla::red(),
12787                ),
12788                (
12789                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12790                    Hsla::red(),
12791                ),
12792                (
12793                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12794                    Hsla::green(),
12795                ),
12796                (
12797                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12798                    Hsla::green(),
12799                ),
12800            ]
12801        );
12802        assert_eq!(
12803            editor.background_highlights_in_range(
12804                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12805                &snapshot,
12806                cx.theme().colors(),
12807            ),
12808            &[(
12809                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12810                Hsla::red(),
12811            )]
12812        );
12813    });
12814}
12815
12816#[gpui::test]
12817async fn test_following(cx: &mut TestAppContext) {
12818    init_test(cx, |_| {});
12819
12820    let fs = FakeFs::new(cx.executor());
12821    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12822
12823    let buffer = project.update(cx, |project, cx| {
12824        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12825        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12826    });
12827    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12828    let follower = cx.update(|cx| {
12829        cx.open_window(
12830            WindowOptions {
12831                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12832                    gpui::Point::new(px(0.), px(0.)),
12833                    gpui::Point::new(px(10.), px(80.)),
12834                ))),
12835                ..Default::default()
12836            },
12837            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12838        )
12839        .unwrap()
12840    });
12841
12842    let is_still_following = Rc::new(RefCell::new(true));
12843    let follower_edit_event_count = Rc::new(RefCell::new(0));
12844    let pending_update = Rc::new(RefCell::new(None));
12845    let leader_entity = leader.root(cx).unwrap();
12846    let follower_entity = follower.root(cx).unwrap();
12847    _ = follower.update(cx, {
12848        let update = pending_update.clone();
12849        let is_still_following = is_still_following.clone();
12850        let follower_edit_event_count = follower_edit_event_count.clone();
12851        |_, window, cx| {
12852            cx.subscribe_in(
12853                &leader_entity,
12854                window,
12855                move |_, leader, event, window, cx| {
12856                    leader.read(cx).add_event_to_update_proto(
12857                        event,
12858                        &mut update.borrow_mut(),
12859                        window,
12860                        cx,
12861                    );
12862                },
12863            )
12864            .detach();
12865
12866            cx.subscribe_in(
12867                &follower_entity,
12868                window,
12869                move |_, _, event: &EditorEvent, _window, _cx| {
12870                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12871                        *is_still_following.borrow_mut() = false;
12872                    }
12873
12874                    if let EditorEvent::BufferEdited = event {
12875                        *follower_edit_event_count.borrow_mut() += 1;
12876                    }
12877                },
12878            )
12879            .detach();
12880        }
12881    });
12882
12883    // Update the selections only
12884    _ = leader.update(cx, |leader, window, cx| {
12885        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12886    });
12887    follower
12888        .update(cx, |follower, window, cx| {
12889            follower.apply_update_proto(
12890                &project,
12891                pending_update.borrow_mut().take().unwrap(),
12892                window,
12893                cx,
12894            )
12895        })
12896        .unwrap()
12897        .await
12898        .unwrap();
12899    _ = follower.update(cx, |follower, _, cx| {
12900        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12901    });
12902    assert!(*is_still_following.borrow());
12903    assert_eq!(*follower_edit_event_count.borrow(), 0);
12904
12905    // Update the scroll position only
12906    _ = leader.update(cx, |leader, window, cx| {
12907        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12908    });
12909    follower
12910        .update(cx, |follower, window, cx| {
12911            follower.apply_update_proto(
12912                &project,
12913                pending_update.borrow_mut().take().unwrap(),
12914                window,
12915                cx,
12916            )
12917        })
12918        .unwrap()
12919        .await
12920        .unwrap();
12921    assert_eq!(
12922        follower
12923            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12924            .unwrap(),
12925        gpui::Point::new(1.5, 3.5)
12926    );
12927    assert!(*is_still_following.borrow());
12928    assert_eq!(*follower_edit_event_count.borrow(), 0);
12929
12930    // Update the selections and scroll position. The follower's scroll position is updated
12931    // via autoscroll, not via the leader's exact scroll position.
12932    _ = leader.update(cx, |leader, window, cx| {
12933        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12934        leader.request_autoscroll(Autoscroll::newest(), cx);
12935        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12936    });
12937    follower
12938        .update(cx, |follower, window, cx| {
12939            follower.apply_update_proto(
12940                &project,
12941                pending_update.borrow_mut().take().unwrap(),
12942                window,
12943                cx,
12944            )
12945        })
12946        .unwrap()
12947        .await
12948        .unwrap();
12949    _ = follower.update(cx, |follower, _, cx| {
12950        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12951        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12952    });
12953    assert!(*is_still_following.borrow());
12954
12955    // Creating a pending selection that precedes another selection
12956    _ = leader.update(cx, |leader, window, cx| {
12957        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12958        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12959    });
12960    follower
12961        .update(cx, |follower, window, cx| {
12962            follower.apply_update_proto(
12963                &project,
12964                pending_update.borrow_mut().take().unwrap(),
12965                window,
12966                cx,
12967            )
12968        })
12969        .unwrap()
12970        .await
12971        .unwrap();
12972    _ = follower.update(cx, |follower, _, cx| {
12973        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12974    });
12975    assert!(*is_still_following.borrow());
12976
12977    // Extend the pending selection so that it surrounds another selection
12978    _ = leader.update(cx, |leader, window, cx| {
12979        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12980    });
12981    follower
12982        .update(cx, |follower, window, cx| {
12983            follower.apply_update_proto(
12984                &project,
12985                pending_update.borrow_mut().take().unwrap(),
12986                window,
12987                cx,
12988            )
12989        })
12990        .unwrap()
12991        .await
12992        .unwrap();
12993    _ = follower.update(cx, |follower, _, cx| {
12994        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12995    });
12996
12997    // Scrolling locally breaks the follow
12998    _ = follower.update(cx, |follower, window, cx| {
12999        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
13000        follower.set_scroll_anchor(
13001            ScrollAnchor {
13002                anchor: top_anchor,
13003                offset: gpui::Point::new(0.0, 0.5),
13004            },
13005            window,
13006            cx,
13007        );
13008    });
13009    assert!(!(*is_still_following.borrow()));
13010}
13011
13012#[gpui::test]
13013async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
13014    init_test(cx, |_| {});
13015
13016    let fs = FakeFs::new(cx.executor());
13017    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
13018    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13019    let pane = workspace
13020        .update(cx, |workspace, _, _| workspace.active_pane().clone())
13021        .unwrap();
13022
13023    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13024
13025    let leader = pane.update_in(cx, |_, window, cx| {
13026        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13027        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13028    });
13029
13030    // Start following the editor when it has no excerpts.
13031    let mut state_message =
13032        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13033    let workspace_entity = workspace.root(cx).unwrap();
13034    let follower_1 = cx
13035        .update_window(*workspace.deref(), |_, window, cx| {
13036            Editor::from_state_proto(
13037                workspace_entity,
13038                ViewId {
13039                    creator: CollaboratorId::PeerId(PeerId::default()),
13040                    id: 0,
13041                },
13042                &mut state_message,
13043                window,
13044                cx,
13045            )
13046        })
13047        .unwrap()
13048        .unwrap()
13049        .await
13050        .unwrap();
13051
13052    let update_message = Rc::new(RefCell::new(None));
13053    follower_1.update_in(cx, {
13054        let update = update_message.clone();
13055        |_, window, cx| {
13056            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13057                leader.read(cx).add_event_to_update_proto(
13058                    event,
13059                    &mut update.borrow_mut(),
13060                    window,
13061                    cx,
13062                );
13063            })
13064            .detach();
13065        }
13066    });
13067
13068    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13069        (
13070            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13071            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13072        )
13073    });
13074
13075    // Insert some excerpts.
13076    leader.update(cx, |leader, cx| {
13077        leader.buffer.update(cx, |multibuffer, cx| {
13078            multibuffer.set_excerpts_for_path(
13079                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13080                buffer_1.clone(),
13081                vec![
13082                    Point::row_range(0..3),
13083                    Point::row_range(1..6),
13084                    Point::row_range(12..15),
13085                ],
13086                0,
13087                cx,
13088            );
13089            multibuffer.set_excerpts_for_path(
13090                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13091                buffer_2.clone(),
13092                vec![Point::row_range(0..6), Point::row_range(8..12)],
13093                0,
13094                cx,
13095            );
13096        });
13097    });
13098
13099    // Apply the update of adding the excerpts.
13100    follower_1
13101        .update_in(cx, |follower, window, cx| {
13102            follower.apply_update_proto(
13103                &project,
13104                update_message.borrow().clone().unwrap(),
13105                window,
13106                cx,
13107            )
13108        })
13109        .await
13110        .unwrap();
13111    assert_eq!(
13112        follower_1.update(cx, |editor, cx| editor.text(cx)),
13113        leader.update(cx, |editor, cx| editor.text(cx))
13114    );
13115    update_message.borrow_mut().take();
13116
13117    // Start following separately after it already has excerpts.
13118    let mut state_message =
13119        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13120    let workspace_entity = workspace.root(cx).unwrap();
13121    let follower_2 = cx
13122        .update_window(*workspace.deref(), |_, window, cx| {
13123            Editor::from_state_proto(
13124                workspace_entity,
13125                ViewId {
13126                    creator: CollaboratorId::PeerId(PeerId::default()),
13127                    id: 0,
13128                },
13129                &mut state_message,
13130                window,
13131                cx,
13132            )
13133        })
13134        .unwrap()
13135        .unwrap()
13136        .await
13137        .unwrap();
13138    assert_eq!(
13139        follower_2.update(cx, |editor, cx| editor.text(cx)),
13140        leader.update(cx, |editor, cx| editor.text(cx))
13141    );
13142
13143    // Remove some excerpts.
13144    leader.update(cx, |leader, cx| {
13145        leader.buffer.update(cx, |multibuffer, cx| {
13146            let excerpt_ids = multibuffer.excerpt_ids();
13147            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13148            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13149        });
13150    });
13151
13152    // Apply the update of removing 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    follower_2
13165        .update_in(cx, |follower, window, cx| {
13166            follower.apply_update_proto(
13167                &project,
13168                update_message.borrow().clone().unwrap(),
13169                window,
13170                cx,
13171            )
13172        })
13173        .await
13174        .unwrap();
13175    update_message.borrow_mut().take();
13176    assert_eq!(
13177        follower_1.update(cx, |editor, cx| editor.text(cx)),
13178        leader.update(cx, |editor, cx| editor.text(cx))
13179    );
13180}
13181
13182#[gpui::test]
13183async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13184    init_test(cx, |_| {});
13185
13186    let mut cx = EditorTestContext::new(cx).await;
13187    let lsp_store =
13188        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13189
13190    cx.set_state(indoc! {"
13191        ˇfn func(abc def: i32) -> u32 {
13192        }
13193    "});
13194
13195    cx.update(|_, cx| {
13196        lsp_store.update(cx, |lsp_store, cx| {
13197            lsp_store
13198                .update_diagnostics(
13199                    LanguageServerId(0),
13200                    lsp::PublishDiagnosticsParams {
13201                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13202                        version: None,
13203                        diagnostics: vec![
13204                            lsp::Diagnostic {
13205                                range: lsp::Range::new(
13206                                    lsp::Position::new(0, 11),
13207                                    lsp::Position::new(0, 12),
13208                                ),
13209                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13210                                ..Default::default()
13211                            },
13212                            lsp::Diagnostic {
13213                                range: lsp::Range::new(
13214                                    lsp::Position::new(0, 12),
13215                                    lsp::Position::new(0, 15),
13216                                ),
13217                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13218                                ..Default::default()
13219                            },
13220                            lsp::Diagnostic {
13221                                range: lsp::Range::new(
13222                                    lsp::Position::new(0, 25),
13223                                    lsp::Position::new(0, 28),
13224                                ),
13225                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13226                                ..Default::default()
13227                            },
13228                        ],
13229                    },
13230                    &[],
13231                    cx,
13232                )
13233                .unwrap()
13234        });
13235    });
13236
13237    executor.run_until_parked();
13238
13239    cx.update_editor(|editor, window, cx| {
13240        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13241    });
13242
13243    cx.assert_editor_state(indoc! {"
13244        fn func(abc def: i32) -> ˇu32 {
13245        }
13246    "});
13247
13248    cx.update_editor(|editor, window, cx| {
13249        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13250    });
13251
13252    cx.assert_editor_state(indoc! {"
13253        fn func(abc ˇdef: i32) -> u32 {
13254        }
13255    "});
13256
13257    cx.update_editor(|editor, window, cx| {
13258        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13259    });
13260
13261    cx.assert_editor_state(indoc! {"
13262        fn func(abcˇ def: i32) -> u32 {
13263        }
13264    "});
13265
13266    cx.update_editor(|editor, window, cx| {
13267        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13268    });
13269
13270    cx.assert_editor_state(indoc! {"
13271        fn func(abc def: i32) -> ˇu32 {
13272        }
13273    "});
13274}
13275
13276#[gpui::test]
13277async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13278    init_test(cx, |_| {});
13279
13280    let mut cx = EditorTestContext::new(cx).await;
13281
13282    let diff_base = r#"
13283        use some::mod;
13284
13285        const A: u32 = 42;
13286
13287        fn main() {
13288            println!("hello");
13289
13290            println!("world");
13291        }
13292        "#
13293    .unindent();
13294
13295    // Edits are modified, removed, modified, added
13296    cx.set_state(
13297        &r#"
13298        use some::modified;
13299
13300        ˇ
13301        fn main() {
13302            println!("hello there");
13303
13304            println!("around the");
13305            println!("world");
13306        }
13307        "#
13308        .unindent(),
13309    );
13310
13311    cx.set_head_text(&diff_base);
13312    executor.run_until_parked();
13313
13314    cx.update_editor(|editor, window, cx| {
13315        //Wrap around the bottom of the buffer
13316        for _ in 0..3 {
13317            editor.go_to_next_hunk(&GoToHunk, window, cx);
13318        }
13319    });
13320
13321    cx.assert_editor_state(
13322        &r#"
13323        ˇuse some::modified;
13324
13325
13326        fn main() {
13327            println!("hello there");
13328
13329            println!("around the");
13330            println!("world");
13331        }
13332        "#
13333        .unindent(),
13334    );
13335
13336    cx.update_editor(|editor, window, cx| {
13337        //Wrap around the top of the buffer
13338        for _ in 0..2 {
13339            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13340        }
13341    });
13342
13343    cx.assert_editor_state(
13344        &r#"
13345        use some::modified;
13346
13347
13348        fn main() {
13349        ˇ    println!("hello there");
13350
13351            println!("around the");
13352            println!("world");
13353        }
13354        "#
13355        .unindent(),
13356    );
13357
13358    cx.update_editor(|editor, window, cx| {
13359        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13360    });
13361
13362    cx.assert_editor_state(
13363        &r#"
13364        use some::modified;
13365
13366        ˇ
13367        fn main() {
13368            println!("hello there");
13369
13370            println!("around the");
13371            println!("world");
13372        }
13373        "#
13374        .unindent(),
13375    );
13376
13377    cx.update_editor(|editor, window, cx| {
13378        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13379    });
13380
13381    cx.assert_editor_state(
13382        &r#"
13383        ˇuse some::modified;
13384
13385
13386        fn main() {
13387            println!("hello there");
13388
13389            println!("around the");
13390            println!("world");
13391        }
13392        "#
13393        .unindent(),
13394    );
13395
13396    cx.update_editor(|editor, window, cx| {
13397        for _ in 0..2 {
13398            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13399        }
13400    });
13401
13402    cx.assert_editor_state(
13403        &r#"
13404        use some::modified;
13405
13406
13407        fn main() {
13408        ˇ    println!("hello there");
13409
13410            println!("around the");
13411            println!("world");
13412        }
13413        "#
13414        .unindent(),
13415    );
13416
13417    cx.update_editor(|editor, window, cx| {
13418        editor.fold(&Fold, window, cx);
13419    });
13420
13421    cx.update_editor(|editor, window, cx| {
13422        editor.go_to_next_hunk(&GoToHunk, window, cx);
13423    });
13424
13425    cx.assert_editor_state(
13426        &r#"
13427        ˇuse some::modified;
13428
13429
13430        fn main() {
13431            println!("hello there");
13432
13433            println!("around the");
13434            println!("world");
13435        }
13436        "#
13437        .unindent(),
13438    );
13439}
13440
13441#[test]
13442fn test_split_words() {
13443    fn split(text: &str) -> Vec<&str> {
13444        split_words(text).collect()
13445    }
13446
13447    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13448    assert_eq!(split("hello_world"), &["hello_", "world"]);
13449    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13450    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13451    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13452    assert_eq!(split("helloworld"), &["helloworld"]);
13453
13454    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13455}
13456
13457#[gpui::test]
13458async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13459    init_test(cx, |_| {});
13460
13461    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13462    let mut assert = |before, after| {
13463        let _state_context = cx.set_state(before);
13464        cx.run_until_parked();
13465        cx.update_editor(|editor, window, cx| {
13466            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13467        });
13468        cx.run_until_parked();
13469        cx.assert_editor_state(after);
13470    };
13471
13472    // Outside bracket jumps to outside of matching bracket
13473    assert("console.logˇ(var);", "console.log(var)ˇ;");
13474    assert("console.log(var)ˇ;", "console.logˇ(var);");
13475
13476    // Inside bracket jumps to inside of matching bracket
13477    assert("console.log(ˇvar);", "console.log(varˇ);");
13478    assert("console.log(varˇ);", "console.log(ˇvar);");
13479
13480    // When outside a bracket and inside, favor jumping to the inside bracket
13481    assert(
13482        "console.log('foo', [1, 2, 3]ˇ);",
13483        "console.log(ˇ'foo', [1, 2, 3]);",
13484    );
13485    assert(
13486        "console.log(ˇ'foo', [1, 2, 3]);",
13487        "console.log('foo', [1, 2, 3]ˇ);",
13488    );
13489
13490    // Bias forward if two options are equally likely
13491    assert(
13492        "let result = curried_fun()ˇ();",
13493        "let result = curried_fun()()ˇ;",
13494    );
13495
13496    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13497    assert(
13498        indoc! {"
13499            function test() {
13500                console.log('test')ˇ
13501            }"},
13502        indoc! {"
13503            function test() {
13504                console.logˇ('test')
13505            }"},
13506    );
13507}
13508
13509#[gpui::test]
13510async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13511    init_test(cx, |_| {});
13512
13513    let fs = FakeFs::new(cx.executor());
13514    fs.insert_tree(
13515        path!("/a"),
13516        json!({
13517            "main.rs": "fn main() { let a = 5; }",
13518            "other.rs": "// Test file",
13519        }),
13520    )
13521    .await;
13522    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13523
13524    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13525    language_registry.add(Arc::new(Language::new(
13526        LanguageConfig {
13527            name: "Rust".into(),
13528            matcher: LanguageMatcher {
13529                path_suffixes: vec!["rs".to_string()],
13530                ..Default::default()
13531            },
13532            brackets: BracketPairConfig {
13533                pairs: vec![BracketPair {
13534                    start: "{".to_string(),
13535                    end: "}".to_string(),
13536                    close: true,
13537                    surround: true,
13538                    newline: true,
13539                }],
13540                disabled_scopes_by_bracket_ix: Vec::new(),
13541            },
13542            ..Default::default()
13543        },
13544        Some(tree_sitter_rust::LANGUAGE.into()),
13545    )));
13546    let mut fake_servers = language_registry.register_fake_lsp(
13547        "Rust",
13548        FakeLspAdapter {
13549            capabilities: lsp::ServerCapabilities {
13550                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13551                    first_trigger_character: "{".to_string(),
13552                    more_trigger_character: None,
13553                }),
13554                ..Default::default()
13555            },
13556            ..Default::default()
13557        },
13558    );
13559
13560    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13561
13562    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13563
13564    let worktree_id = workspace
13565        .update(cx, |workspace, _, cx| {
13566            workspace.project().update(cx, |project, cx| {
13567                project.worktrees(cx).next().unwrap().read(cx).id()
13568            })
13569        })
13570        .unwrap();
13571
13572    let buffer = project
13573        .update(cx, |project, cx| {
13574            project.open_local_buffer(path!("/a/main.rs"), cx)
13575        })
13576        .await
13577        .unwrap();
13578    let editor_handle = workspace
13579        .update(cx, |workspace, window, cx| {
13580            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13581        })
13582        .unwrap()
13583        .await
13584        .unwrap()
13585        .downcast::<Editor>()
13586        .unwrap();
13587
13588    cx.executor().start_waiting();
13589    let fake_server = fake_servers.next().await.unwrap();
13590
13591    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13592        |params, _| async move {
13593            assert_eq!(
13594                params.text_document_position.text_document.uri,
13595                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13596            );
13597            assert_eq!(
13598                params.text_document_position.position,
13599                lsp::Position::new(0, 21),
13600            );
13601
13602            Ok(Some(vec![lsp::TextEdit {
13603                new_text: "]".to_string(),
13604                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13605            }]))
13606        },
13607    );
13608
13609    editor_handle.update_in(cx, |editor, window, cx| {
13610        window.focus(&editor.focus_handle(cx));
13611        editor.change_selections(None, window, cx, |s| {
13612            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13613        });
13614        editor.handle_input("{", window, cx);
13615    });
13616
13617    cx.executor().run_until_parked();
13618
13619    buffer.update(cx, |buffer, _| {
13620        assert_eq!(
13621            buffer.text(),
13622            "fn main() { let a = {5}; }",
13623            "No extra braces from on type formatting should appear in the buffer"
13624        )
13625    });
13626}
13627
13628#[gpui::test]
13629async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13630    init_test(cx, |_| {});
13631
13632    let fs = FakeFs::new(cx.executor());
13633    fs.insert_tree(
13634        path!("/a"),
13635        json!({
13636            "main.rs": "fn main() { let a = 5; }",
13637            "other.rs": "// Test file",
13638        }),
13639    )
13640    .await;
13641
13642    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13643
13644    let server_restarts = Arc::new(AtomicUsize::new(0));
13645    let closure_restarts = Arc::clone(&server_restarts);
13646    let language_server_name = "test language server";
13647    let language_name: LanguageName = "Rust".into();
13648
13649    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13650    language_registry.add(Arc::new(Language::new(
13651        LanguageConfig {
13652            name: language_name.clone(),
13653            matcher: LanguageMatcher {
13654                path_suffixes: vec!["rs".to_string()],
13655                ..Default::default()
13656            },
13657            ..Default::default()
13658        },
13659        Some(tree_sitter_rust::LANGUAGE.into()),
13660    )));
13661    let mut fake_servers = language_registry.register_fake_lsp(
13662        "Rust",
13663        FakeLspAdapter {
13664            name: language_server_name,
13665            initialization_options: Some(json!({
13666                "testOptionValue": true
13667            })),
13668            initializer: Some(Box::new(move |fake_server| {
13669                let task_restarts = Arc::clone(&closure_restarts);
13670                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13671                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13672                    futures::future::ready(Ok(()))
13673                });
13674            })),
13675            ..Default::default()
13676        },
13677    );
13678
13679    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13680    let _buffer = project
13681        .update(cx, |project, cx| {
13682            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13683        })
13684        .await
13685        .unwrap();
13686    let _fake_server = fake_servers.next().await.unwrap();
13687    update_test_language_settings(cx, |language_settings| {
13688        language_settings.languages.insert(
13689            language_name.clone(),
13690            LanguageSettingsContent {
13691                tab_size: NonZeroU32::new(8),
13692                ..Default::default()
13693            },
13694        );
13695    });
13696    cx.executor().run_until_parked();
13697    assert_eq!(
13698        server_restarts.load(atomic::Ordering::Acquire),
13699        0,
13700        "Should not restart LSP server on an unrelated change"
13701    );
13702
13703    update_test_project_settings(cx, |project_settings| {
13704        project_settings.lsp.insert(
13705            "Some other server name".into(),
13706            LspSettings {
13707                binary: None,
13708                settings: None,
13709                initialization_options: Some(json!({
13710                    "some other init value": false
13711                })),
13712                enable_lsp_tasks: false,
13713            },
13714        );
13715    });
13716    cx.executor().run_until_parked();
13717    assert_eq!(
13718        server_restarts.load(atomic::Ordering::Acquire),
13719        0,
13720        "Should not restart LSP server on an unrelated LSP settings change"
13721    );
13722
13723    update_test_project_settings(cx, |project_settings| {
13724        project_settings.lsp.insert(
13725            language_server_name.into(),
13726            LspSettings {
13727                binary: None,
13728                settings: None,
13729                initialization_options: Some(json!({
13730                    "anotherInitValue": false
13731                })),
13732                enable_lsp_tasks: false,
13733            },
13734        );
13735    });
13736    cx.executor().run_until_parked();
13737    assert_eq!(
13738        server_restarts.load(atomic::Ordering::Acquire),
13739        1,
13740        "Should restart LSP server on a related LSP settings change"
13741    );
13742
13743    update_test_project_settings(cx, |project_settings| {
13744        project_settings.lsp.insert(
13745            language_server_name.into(),
13746            LspSettings {
13747                binary: None,
13748                settings: None,
13749                initialization_options: Some(json!({
13750                    "anotherInitValue": false
13751                })),
13752                enable_lsp_tasks: false,
13753            },
13754        );
13755    });
13756    cx.executor().run_until_parked();
13757    assert_eq!(
13758        server_restarts.load(atomic::Ordering::Acquire),
13759        1,
13760        "Should not restart LSP server on a related LSP settings change that is the same"
13761    );
13762
13763    update_test_project_settings(cx, |project_settings| {
13764        project_settings.lsp.insert(
13765            language_server_name.into(),
13766            LspSettings {
13767                binary: None,
13768                settings: None,
13769                initialization_options: None,
13770                enable_lsp_tasks: false,
13771            },
13772        );
13773    });
13774    cx.executor().run_until_parked();
13775    assert_eq!(
13776        server_restarts.load(atomic::Ordering::Acquire),
13777        2,
13778        "Should restart LSP server on another related LSP settings change"
13779    );
13780}
13781
13782#[gpui::test]
13783async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13784    init_test(cx, |_| {});
13785
13786    let mut cx = EditorLspTestContext::new_rust(
13787        lsp::ServerCapabilities {
13788            completion_provider: Some(lsp::CompletionOptions {
13789                trigger_characters: Some(vec![".".to_string()]),
13790                resolve_provider: Some(true),
13791                ..Default::default()
13792            }),
13793            ..Default::default()
13794        },
13795        cx,
13796    )
13797    .await;
13798
13799    cx.set_state("fn main() { let a = 2ˇ; }");
13800    cx.simulate_keystroke(".");
13801    let completion_item = lsp::CompletionItem {
13802        label: "some".into(),
13803        kind: Some(lsp::CompletionItemKind::SNIPPET),
13804        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13805        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13806            kind: lsp::MarkupKind::Markdown,
13807            value: "```rust\nSome(2)\n```".to_string(),
13808        })),
13809        deprecated: Some(false),
13810        sort_text: Some("fffffff2".to_string()),
13811        filter_text: Some("some".to_string()),
13812        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13813        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13814            range: lsp::Range {
13815                start: lsp::Position {
13816                    line: 0,
13817                    character: 22,
13818                },
13819                end: lsp::Position {
13820                    line: 0,
13821                    character: 22,
13822                },
13823            },
13824            new_text: "Some(2)".to_string(),
13825        })),
13826        additional_text_edits: Some(vec![lsp::TextEdit {
13827            range: lsp::Range {
13828                start: lsp::Position {
13829                    line: 0,
13830                    character: 20,
13831                },
13832                end: lsp::Position {
13833                    line: 0,
13834                    character: 22,
13835                },
13836            },
13837            new_text: "".to_string(),
13838        }]),
13839        ..Default::default()
13840    };
13841
13842    let closure_completion_item = completion_item.clone();
13843    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13844        let task_completion_item = closure_completion_item.clone();
13845        async move {
13846            Ok(Some(lsp::CompletionResponse::Array(vec![
13847                task_completion_item,
13848            ])))
13849        }
13850    });
13851
13852    request.next().await;
13853
13854    cx.condition(|editor, _| editor.context_menu_visible())
13855        .await;
13856    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13857        editor
13858            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13859            .unwrap()
13860    });
13861    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13862
13863    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13864        let task_completion_item = completion_item.clone();
13865        async move { Ok(task_completion_item) }
13866    })
13867    .next()
13868    .await
13869    .unwrap();
13870    apply_additional_edits.await.unwrap();
13871    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13872}
13873
13874#[gpui::test]
13875async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13876    init_test(cx, |_| {});
13877
13878    let mut cx = EditorLspTestContext::new_rust(
13879        lsp::ServerCapabilities {
13880            completion_provider: Some(lsp::CompletionOptions {
13881                trigger_characters: Some(vec![".".to_string()]),
13882                resolve_provider: Some(true),
13883                ..Default::default()
13884            }),
13885            ..Default::default()
13886        },
13887        cx,
13888    )
13889    .await;
13890
13891    cx.set_state("fn main() { let a = 2ˇ; }");
13892    cx.simulate_keystroke(".");
13893
13894    let item1 = lsp::CompletionItem {
13895        label: "method id()".to_string(),
13896        filter_text: Some("id".to_string()),
13897        detail: None,
13898        documentation: None,
13899        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13900            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13901            new_text: ".id".to_string(),
13902        })),
13903        ..lsp::CompletionItem::default()
13904    };
13905
13906    let item2 = lsp::CompletionItem {
13907        label: "other".to_string(),
13908        filter_text: Some("other".to_string()),
13909        detail: None,
13910        documentation: None,
13911        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13912            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13913            new_text: ".other".to_string(),
13914        })),
13915        ..lsp::CompletionItem::default()
13916    };
13917
13918    let item1 = item1.clone();
13919    cx.set_request_handler::<lsp::request::Completion, _, _>({
13920        let item1 = item1.clone();
13921        move |_, _, _| {
13922            let item1 = item1.clone();
13923            let item2 = item2.clone();
13924            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13925        }
13926    })
13927    .next()
13928    .await;
13929
13930    cx.condition(|editor, _| editor.context_menu_visible())
13931        .await;
13932    cx.update_editor(|editor, _, _| {
13933        let context_menu = editor.context_menu.borrow_mut();
13934        let context_menu = context_menu
13935            .as_ref()
13936            .expect("Should have the context menu deployed");
13937        match context_menu {
13938            CodeContextMenu::Completions(completions_menu) => {
13939                let completions = completions_menu.completions.borrow_mut();
13940                assert_eq!(
13941                    completions
13942                        .iter()
13943                        .map(|completion| &completion.label.text)
13944                        .collect::<Vec<_>>(),
13945                    vec!["method id()", "other"]
13946                )
13947            }
13948            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13949        }
13950    });
13951
13952    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13953        let item1 = item1.clone();
13954        move |_, item_to_resolve, _| {
13955            let item1 = item1.clone();
13956            async move {
13957                if item1 == item_to_resolve {
13958                    Ok(lsp::CompletionItem {
13959                        label: "method id()".to_string(),
13960                        filter_text: Some("id".to_string()),
13961                        detail: Some("Now resolved!".to_string()),
13962                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13963                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13964                            range: lsp::Range::new(
13965                                lsp::Position::new(0, 22),
13966                                lsp::Position::new(0, 22),
13967                            ),
13968                            new_text: ".id".to_string(),
13969                        })),
13970                        ..lsp::CompletionItem::default()
13971                    })
13972                } else {
13973                    Ok(item_to_resolve)
13974                }
13975            }
13976        }
13977    })
13978    .next()
13979    .await
13980    .unwrap();
13981    cx.run_until_parked();
13982
13983    cx.update_editor(|editor, window, cx| {
13984        editor.context_menu_next(&Default::default(), window, cx);
13985    });
13986
13987    cx.update_editor(|editor, _, _| {
13988        let context_menu = editor.context_menu.borrow_mut();
13989        let context_menu = context_menu
13990            .as_ref()
13991            .expect("Should have the context menu deployed");
13992        match context_menu {
13993            CodeContextMenu::Completions(completions_menu) => {
13994                let completions = completions_menu.completions.borrow_mut();
13995                assert_eq!(
13996                    completions
13997                        .iter()
13998                        .map(|completion| &completion.label.text)
13999                        .collect::<Vec<_>>(),
14000                    vec!["method id() Now resolved!", "other"],
14001                    "Should update first completion label, but not second as the filter text did not match."
14002                );
14003            }
14004            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14005        }
14006    });
14007}
14008
14009#[gpui::test]
14010async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
14011    init_test(cx, |_| {});
14012    let mut cx = EditorLspTestContext::new_rust(
14013        lsp::ServerCapabilities {
14014            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
14015            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
14016            completion_provider: Some(lsp::CompletionOptions {
14017                resolve_provider: Some(true),
14018                ..Default::default()
14019            }),
14020            ..Default::default()
14021        },
14022        cx,
14023    )
14024    .await;
14025    cx.set_state(indoc! {"
14026        struct TestStruct {
14027            field: i32
14028        }
14029
14030        fn mainˇ() {
14031            let unused_var = 42;
14032            let test_struct = TestStruct { field: 42 };
14033        }
14034    "});
14035    let symbol_range = cx.lsp_range(indoc! {"
14036        struct TestStruct {
14037            field: i32
14038        }
14039
14040        «fn main»() {
14041            let unused_var = 42;
14042            let test_struct = TestStruct { field: 42 };
14043        }
14044    "});
14045    let mut hover_requests =
14046        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14047            Ok(Some(lsp::Hover {
14048                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14049                    kind: lsp::MarkupKind::Markdown,
14050                    value: "Function documentation".to_string(),
14051                }),
14052                range: Some(symbol_range),
14053            }))
14054        });
14055
14056    // Case 1: Test that code action menu hide hover popover
14057    cx.dispatch_action(Hover);
14058    hover_requests.next().await;
14059    cx.condition(|editor, _| editor.hover_state.visible()).await;
14060    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14061        move |_, _, _| async move {
14062            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14063                lsp::CodeAction {
14064                    title: "Remove unused variable".to_string(),
14065                    kind: Some(CodeActionKind::QUICKFIX),
14066                    edit: Some(lsp::WorkspaceEdit {
14067                        changes: Some(
14068                            [(
14069                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14070                                vec![lsp::TextEdit {
14071                                    range: lsp::Range::new(
14072                                        lsp::Position::new(5, 4),
14073                                        lsp::Position::new(5, 27),
14074                                    ),
14075                                    new_text: "".to_string(),
14076                                }],
14077                            )]
14078                            .into_iter()
14079                            .collect(),
14080                        ),
14081                        ..Default::default()
14082                    }),
14083                    ..Default::default()
14084                },
14085            )]))
14086        },
14087    );
14088    cx.update_editor(|editor, window, cx| {
14089        editor.toggle_code_actions(
14090            &ToggleCodeActions {
14091                deployed_from_indicator: None,
14092                quick_launch: false,
14093            },
14094            window,
14095            cx,
14096        );
14097    });
14098    code_action_requests.next().await;
14099    cx.run_until_parked();
14100    cx.condition(|editor, _| editor.context_menu_visible())
14101        .await;
14102    cx.update_editor(|editor, _, _| {
14103        assert!(
14104            !editor.hover_state.visible(),
14105            "Hover popover should be hidden when code action menu is shown"
14106        );
14107        // Hide code actions
14108        editor.context_menu.take();
14109    });
14110
14111    // Case 2: Test that code completions hide hover popover
14112    cx.dispatch_action(Hover);
14113    hover_requests.next().await;
14114    cx.condition(|editor, _| editor.hover_state.visible()).await;
14115    let counter = Arc::new(AtomicUsize::new(0));
14116    let mut completion_requests =
14117        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14118            let counter = counter.clone();
14119            async move {
14120                counter.fetch_add(1, atomic::Ordering::Release);
14121                Ok(Some(lsp::CompletionResponse::Array(vec![
14122                    lsp::CompletionItem {
14123                        label: "main".into(),
14124                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14125                        detail: Some("() -> ()".to_string()),
14126                        ..Default::default()
14127                    },
14128                    lsp::CompletionItem {
14129                        label: "TestStruct".into(),
14130                        kind: Some(lsp::CompletionItemKind::STRUCT),
14131                        detail: Some("struct TestStruct".to_string()),
14132                        ..Default::default()
14133                    },
14134                ])))
14135            }
14136        });
14137    cx.update_editor(|editor, window, cx| {
14138        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14139    });
14140    completion_requests.next().await;
14141    cx.condition(|editor, _| editor.context_menu_visible())
14142        .await;
14143    cx.update_editor(|editor, _, _| {
14144        assert!(
14145            !editor.hover_state.visible(),
14146            "Hover popover should be hidden when completion menu is shown"
14147        );
14148    });
14149}
14150
14151#[gpui::test]
14152async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14153    init_test(cx, |_| {});
14154
14155    let mut cx = EditorLspTestContext::new_rust(
14156        lsp::ServerCapabilities {
14157            completion_provider: Some(lsp::CompletionOptions {
14158                trigger_characters: Some(vec![".".to_string()]),
14159                resolve_provider: Some(true),
14160                ..Default::default()
14161            }),
14162            ..Default::default()
14163        },
14164        cx,
14165    )
14166    .await;
14167
14168    cx.set_state("fn main() { let a = 2ˇ; }");
14169    cx.simulate_keystroke(".");
14170
14171    let unresolved_item_1 = lsp::CompletionItem {
14172        label: "id".to_string(),
14173        filter_text: Some("id".to_string()),
14174        detail: None,
14175        documentation: None,
14176        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14177            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14178            new_text: ".id".to_string(),
14179        })),
14180        ..lsp::CompletionItem::default()
14181    };
14182    let resolved_item_1 = lsp::CompletionItem {
14183        additional_text_edits: Some(vec![lsp::TextEdit {
14184            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14185            new_text: "!!".to_string(),
14186        }]),
14187        ..unresolved_item_1.clone()
14188    };
14189    let unresolved_item_2 = lsp::CompletionItem {
14190        label: "other".to_string(),
14191        filter_text: Some("other".to_string()),
14192        detail: None,
14193        documentation: None,
14194        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14195            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14196            new_text: ".other".to_string(),
14197        })),
14198        ..lsp::CompletionItem::default()
14199    };
14200    let resolved_item_2 = lsp::CompletionItem {
14201        additional_text_edits: Some(vec![lsp::TextEdit {
14202            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14203            new_text: "??".to_string(),
14204        }]),
14205        ..unresolved_item_2.clone()
14206    };
14207
14208    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14209    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14210    cx.lsp
14211        .server
14212        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14213            let unresolved_item_1 = unresolved_item_1.clone();
14214            let resolved_item_1 = resolved_item_1.clone();
14215            let unresolved_item_2 = unresolved_item_2.clone();
14216            let resolved_item_2 = resolved_item_2.clone();
14217            let resolve_requests_1 = resolve_requests_1.clone();
14218            let resolve_requests_2 = resolve_requests_2.clone();
14219            move |unresolved_request, _| {
14220                let unresolved_item_1 = unresolved_item_1.clone();
14221                let resolved_item_1 = resolved_item_1.clone();
14222                let unresolved_item_2 = unresolved_item_2.clone();
14223                let resolved_item_2 = resolved_item_2.clone();
14224                let resolve_requests_1 = resolve_requests_1.clone();
14225                let resolve_requests_2 = resolve_requests_2.clone();
14226                async move {
14227                    if unresolved_request == unresolved_item_1 {
14228                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14229                        Ok(resolved_item_1.clone())
14230                    } else if unresolved_request == unresolved_item_2 {
14231                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14232                        Ok(resolved_item_2.clone())
14233                    } else {
14234                        panic!("Unexpected completion item {unresolved_request:?}")
14235                    }
14236                }
14237            }
14238        })
14239        .detach();
14240
14241    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14242        let unresolved_item_1 = unresolved_item_1.clone();
14243        let unresolved_item_2 = unresolved_item_2.clone();
14244        async move {
14245            Ok(Some(lsp::CompletionResponse::Array(vec![
14246                unresolved_item_1,
14247                unresolved_item_2,
14248            ])))
14249        }
14250    })
14251    .next()
14252    .await;
14253
14254    cx.condition(|editor, _| editor.context_menu_visible())
14255        .await;
14256    cx.update_editor(|editor, _, _| {
14257        let context_menu = editor.context_menu.borrow_mut();
14258        let context_menu = context_menu
14259            .as_ref()
14260            .expect("Should have the context menu deployed");
14261        match context_menu {
14262            CodeContextMenu::Completions(completions_menu) => {
14263                let completions = completions_menu.completions.borrow_mut();
14264                assert_eq!(
14265                    completions
14266                        .iter()
14267                        .map(|completion| &completion.label.text)
14268                        .collect::<Vec<_>>(),
14269                    vec!["id", "other"]
14270                )
14271            }
14272            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14273        }
14274    });
14275    cx.run_until_parked();
14276
14277    cx.update_editor(|editor, window, cx| {
14278        editor.context_menu_next(&ContextMenuNext, window, cx);
14279    });
14280    cx.run_until_parked();
14281    cx.update_editor(|editor, window, cx| {
14282        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14283    });
14284    cx.run_until_parked();
14285    cx.update_editor(|editor, window, cx| {
14286        editor.context_menu_next(&ContextMenuNext, window, cx);
14287    });
14288    cx.run_until_parked();
14289    cx.update_editor(|editor, window, cx| {
14290        editor
14291            .compose_completion(&ComposeCompletion::default(), window, cx)
14292            .expect("No task returned")
14293    })
14294    .await
14295    .expect("Completion failed");
14296    cx.run_until_parked();
14297
14298    cx.update_editor(|editor, _, cx| {
14299        assert_eq!(
14300            resolve_requests_1.load(atomic::Ordering::Acquire),
14301            1,
14302            "Should always resolve once despite multiple selections"
14303        );
14304        assert_eq!(
14305            resolve_requests_2.load(atomic::Ordering::Acquire),
14306            1,
14307            "Should always resolve once after multiple selections and applying the completion"
14308        );
14309        assert_eq!(
14310            editor.text(cx),
14311            "fn main() { let a = ??.other; }",
14312            "Should use resolved data when applying the completion"
14313        );
14314    });
14315}
14316
14317#[gpui::test]
14318async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14319    init_test(cx, |_| {});
14320
14321    let item_0 = lsp::CompletionItem {
14322        label: "abs".into(),
14323        insert_text: Some("abs".into()),
14324        data: Some(json!({ "very": "special"})),
14325        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14326        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14327            lsp::InsertReplaceEdit {
14328                new_text: "abs".to_string(),
14329                insert: lsp::Range::default(),
14330                replace: lsp::Range::default(),
14331            },
14332        )),
14333        ..lsp::CompletionItem::default()
14334    };
14335    let items = iter::once(item_0.clone())
14336        .chain((11..51).map(|i| lsp::CompletionItem {
14337            label: format!("item_{}", i),
14338            insert_text: Some(format!("item_{}", i)),
14339            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14340            ..lsp::CompletionItem::default()
14341        }))
14342        .collect::<Vec<_>>();
14343
14344    let default_commit_characters = vec!["?".to_string()];
14345    let default_data = json!({ "default": "data"});
14346    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14347    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14348    let default_edit_range = lsp::Range {
14349        start: lsp::Position {
14350            line: 0,
14351            character: 5,
14352        },
14353        end: lsp::Position {
14354            line: 0,
14355            character: 5,
14356        },
14357    };
14358
14359    let mut cx = EditorLspTestContext::new_rust(
14360        lsp::ServerCapabilities {
14361            completion_provider: Some(lsp::CompletionOptions {
14362                trigger_characters: Some(vec![".".to_string()]),
14363                resolve_provider: Some(true),
14364                ..Default::default()
14365            }),
14366            ..Default::default()
14367        },
14368        cx,
14369    )
14370    .await;
14371
14372    cx.set_state("fn main() { let a = 2ˇ; }");
14373    cx.simulate_keystroke(".");
14374
14375    let completion_data = default_data.clone();
14376    let completion_characters = default_commit_characters.clone();
14377    let completion_items = items.clone();
14378    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14379        let default_data = completion_data.clone();
14380        let default_commit_characters = completion_characters.clone();
14381        let items = completion_items.clone();
14382        async move {
14383            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14384                items,
14385                item_defaults: Some(lsp::CompletionListItemDefaults {
14386                    data: Some(default_data.clone()),
14387                    commit_characters: Some(default_commit_characters.clone()),
14388                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14389                        default_edit_range,
14390                    )),
14391                    insert_text_format: Some(default_insert_text_format),
14392                    insert_text_mode: Some(default_insert_text_mode),
14393                }),
14394                ..lsp::CompletionList::default()
14395            })))
14396        }
14397    })
14398    .next()
14399    .await;
14400
14401    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14402    cx.lsp
14403        .server
14404        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14405            let closure_resolved_items = resolved_items.clone();
14406            move |item_to_resolve, _| {
14407                let closure_resolved_items = closure_resolved_items.clone();
14408                async move {
14409                    closure_resolved_items.lock().push(item_to_resolve.clone());
14410                    Ok(item_to_resolve)
14411                }
14412            }
14413        })
14414        .detach();
14415
14416    cx.condition(|editor, _| editor.context_menu_visible())
14417        .await;
14418    cx.run_until_parked();
14419    cx.update_editor(|editor, _, _| {
14420        let menu = editor.context_menu.borrow_mut();
14421        match menu.as_ref().expect("should have the completions menu") {
14422            CodeContextMenu::Completions(completions_menu) => {
14423                assert_eq!(
14424                    completions_menu
14425                        .entries
14426                        .borrow()
14427                        .iter()
14428                        .map(|mat| mat.string.clone())
14429                        .collect::<Vec<String>>(),
14430                    items
14431                        .iter()
14432                        .map(|completion| completion.label.clone())
14433                        .collect::<Vec<String>>()
14434                );
14435            }
14436            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14437        }
14438    });
14439    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14440    // with 4 from the end.
14441    assert_eq!(
14442        *resolved_items.lock(),
14443        [&items[0..16], &items[items.len() - 4..items.len()]]
14444            .concat()
14445            .iter()
14446            .cloned()
14447            .map(|mut item| {
14448                if item.data.is_none() {
14449                    item.data = Some(default_data.clone());
14450                }
14451                item
14452            })
14453            .collect::<Vec<lsp::CompletionItem>>(),
14454        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14455    );
14456    resolved_items.lock().clear();
14457
14458    cx.update_editor(|editor, window, cx| {
14459        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14460    });
14461    cx.run_until_parked();
14462    // Completions that have already been resolved are skipped.
14463    assert_eq!(
14464        *resolved_items.lock(),
14465        items[items.len() - 16..items.len() - 4]
14466            .iter()
14467            .cloned()
14468            .map(|mut item| {
14469                if item.data.is_none() {
14470                    item.data = Some(default_data.clone());
14471                }
14472                item
14473            })
14474            .collect::<Vec<lsp::CompletionItem>>()
14475    );
14476    resolved_items.lock().clear();
14477}
14478
14479#[gpui::test]
14480async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14481    init_test(cx, |_| {});
14482
14483    let mut cx = EditorLspTestContext::new(
14484        Language::new(
14485            LanguageConfig {
14486                matcher: LanguageMatcher {
14487                    path_suffixes: vec!["jsx".into()],
14488                    ..Default::default()
14489                },
14490                overrides: [(
14491                    "element".into(),
14492                    LanguageConfigOverride {
14493                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14494                        ..Default::default()
14495                    },
14496                )]
14497                .into_iter()
14498                .collect(),
14499                ..Default::default()
14500            },
14501            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14502        )
14503        .with_override_query("(jsx_self_closing_element) @element")
14504        .unwrap(),
14505        lsp::ServerCapabilities {
14506            completion_provider: Some(lsp::CompletionOptions {
14507                trigger_characters: Some(vec![":".to_string()]),
14508                ..Default::default()
14509            }),
14510            ..Default::default()
14511        },
14512        cx,
14513    )
14514    .await;
14515
14516    cx.lsp
14517        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14518            Ok(Some(lsp::CompletionResponse::Array(vec![
14519                lsp::CompletionItem {
14520                    label: "bg-blue".into(),
14521                    ..Default::default()
14522                },
14523                lsp::CompletionItem {
14524                    label: "bg-red".into(),
14525                    ..Default::default()
14526                },
14527                lsp::CompletionItem {
14528                    label: "bg-yellow".into(),
14529                    ..Default::default()
14530                },
14531            ])))
14532        });
14533
14534    cx.set_state(r#"<p class="bgˇ" />"#);
14535
14536    // Trigger completion when typing a dash, because the dash is an extra
14537    // word character in the 'element' scope, which contains the cursor.
14538    cx.simulate_keystroke("-");
14539    cx.executor().run_until_parked();
14540    cx.update_editor(|editor, _, _| {
14541        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14542        {
14543            assert_eq!(
14544                completion_menu_entries(&menu),
14545                &["bg-red", "bg-blue", "bg-yellow"]
14546            );
14547        } else {
14548            panic!("expected completion menu to be open");
14549        }
14550    });
14551
14552    cx.simulate_keystroke("l");
14553    cx.executor().run_until_parked();
14554    cx.update_editor(|editor, _, _| {
14555        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14556        {
14557            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14558        } else {
14559            panic!("expected completion menu to be open");
14560        }
14561    });
14562
14563    // When filtering completions, consider the character after the '-' to
14564    // be the start of a subword.
14565    cx.set_state(r#"<p class="yelˇ" />"#);
14566    cx.simulate_keystroke("l");
14567    cx.executor().run_until_parked();
14568    cx.update_editor(|editor, _, _| {
14569        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14570        {
14571            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14572        } else {
14573            panic!("expected completion menu to be open");
14574        }
14575    });
14576}
14577
14578fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14579    let entries = menu.entries.borrow();
14580    entries.iter().map(|mat| mat.string.clone()).collect()
14581}
14582
14583#[gpui::test]
14584async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14585    init_test(cx, |settings| {
14586        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14587            FormatterList(vec![Formatter::Prettier].into()),
14588        ))
14589    });
14590
14591    let fs = FakeFs::new(cx.executor());
14592    fs.insert_file(path!("/file.ts"), Default::default()).await;
14593
14594    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14595    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14596
14597    language_registry.add(Arc::new(Language::new(
14598        LanguageConfig {
14599            name: "TypeScript".into(),
14600            matcher: LanguageMatcher {
14601                path_suffixes: vec!["ts".to_string()],
14602                ..Default::default()
14603            },
14604            ..Default::default()
14605        },
14606        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14607    )));
14608    update_test_language_settings(cx, |settings| {
14609        settings.defaults.prettier = Some(PrettierSettings {
14610            allowed: true,
14611            ..PrettierSettings::default()
14612        });
14613    });
14614
14615    let test_plugin = "test_plugin";
14616    let _ = language_registry.register_fake_lsp(
14617        "TypeScript",
14618        FakeLspAdapter {
14619            prettier_plugins: vec![test_plugin],
14620            ..Default::default()
14621        },
14622    );
14623
14624    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14625    let buffer = project
14626        .update(cx, |project, cx| {
14627            project.open_local_buffer(path!("/file.ts"), cx)
14628        })
14629        .await
14630        .unwrap();
14631
14632    let buffer_text = "one\ntwo\nthree\n";
14633    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14634    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14635    editor.update_in(cx, |editor, window, cx| {
14636        editor.set_text(buffer_text, window, cx)
14637    });
14638
14639    editor
14640        .update_in(cx, |editor, window, cx| {
14641            editor.perform_format(
14642                project.clone(),
14643                FormatTrigger::Manual,
14644                FormatTarget::Buffers,
14645                window,
14646                cx,
14647            )
14648        })
14649        .unwrap()
14650        .await;
14651    assert_eq!(
14652        editor.update(cx, |editor, cx| editor.text(cx)),
14653        buffer_text.to_string() + prettier_format_suffix,
14654        "Test prettier formatting was not applied to the original buffer text",
14655    );
14656
14657    update_test_language_settings(cx, |settings| {
14658        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14659    });
14660    let format = editor.update_in(cx, |editor, window, cx| {
14661        editor.perform_format(
14662            project.clone(),
14663            FormatTrigger::Manual,
14664            FormatTarget::Buffers,
14665            window,
14666            cx,
14667        )
14668    });
14669    format.await.unwrap();
14670    assert_eq!(
14671        editor.update(cx, |editor, cx| editor.text(cx)),
14672        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14673        "Autoformatting (via test prettier) was not applied to the original buffer text",
14674    );
14675}
14676
14677#[gpui::test]
14678async fn test_addition_reverts(cx: &mut TestAppContext) {
14679    init_test(cx, |_| {});
14680    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14681    let base_text = indoc! {r#"
14682        struct Row;
14683        struct Row1;
14684        struct Row2;
14685
14686        struct Row4;
14687        struct Row5;
14688        struct Row6;
14689
14690        struct Row8;
14691        struct Row9;
14692        struct Row10;"#};
14693
14694    // When addition hunks are not adjacent to carets, no hunk revert is performed
14695    assert_hunk_revert(
14696        indoc! {r#"struct Row;
14697                   struct Row1;
14698                   struct Row1.1;
14699                   struct Row1.2;
14700                   struct Row2;ˇ
14701
14702                   struct Row4;
14703                   struct Row5;
14704                   struct Row6;
14705
14706                   struct Row8;
14707                   ˇstruct Row9;
14708                   struct Row9.1;
14709                   struct Row9.2;
14710                   struct Row9.3;
14711                   struct Row10;"#},
14712        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14713        indoc! {r#"struct Row;
14714                   struct Row1;
14715                   struct Row1.1;
14716                   struct Row1.2;
14717                   struct Row2;ˇ
14718
14719                   struct Row4;
14720                   struct Row5;
14721                   struct Row6;
14722
14723                   struct Row8;
14724                   ˇstruct Row9;
14725                   struct Row9.1;
14726                   struct Row9.2;
14727                   struct Row9.3;
14728                   struct Row10;"#},
14729        base_text,
14730        &mut cx,
14731    );
14732    // Same for selections
14733    assert_hunk_revert(
14734        indoc! {r#"struct Row;
14735                   struct Row1;
14736                   struct Row2;
14737                   struct Row2.1;
14738                   struct Row2.2;
14739                   «ˇ
14740                   struct Row4;
14741                   struct» Row5;
14742                   «struct Row6;
14743                   ˇ»
14744                   struct Row9.1;
14745                   struct Row9.2;
14746                   struct Row9.3;
14747                   struct Row8;
14748                   struct Row9;
14749                   struct Row10;"#},
14750        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14751        indoc! {r#"struct Row;
14752                   struct Row1;
14753                   struct Row2;
14754                   struct Row2.1;
14755                   struct Row2.2;
14756                   «ˇ
14757                   struct Row4;
14758                   struct» Row5;
14759                   «struct Row6;
14760                   ˇ»
14761                   struct Row9.1;
14762                   struct Row9.2;
14763                   struct Row9.3;
14764                   struct Row8;
14765                   struct Row9;
14766                   struct Row10;"#},
14767        base_text,
14768        &mut cx,
14769    );
14770
14771    // When carets and selections intersect the addition hunks, those are reverted.
14772    // Adjacent carets got merged.
14773    assert_hunk_revert(
14774        indoc! {r#"struct Row;
14775                   ˇ// something on the top
14776                   struct Row1;
14777                   struct Row2;
14778                   struct Roˇw3.1;
14779                   struct Row2.2;
14780                   struct Row2.3;ˇ
14781
14782                   struct Row4;
14783                   struct ˇRow5.1;
14784                   struct Row5.2;
14785                   struct «Rowˇ»5.3;
14786                   struct Row5;
14787                   struct Row6;
14788                   ˇ
14789                   struct Row9.1;
14790                   struct «Rowˇ»9.2;
14791                   struct «ˇRow»9.3;
14792                   struct Row8;
14793                   struct Row9;
14794                   «ˇ// something on bottom»
14795                   struct Row10;"#},
14796        vec![
14797            DiffHunkStatusKind::Added,
14798            DiffHunkStatusKind::Added,
14799            DiffHunkStatusKind::Added,
14800            DiffHunkStatusKind::Added,
14801            DiffHunkStatusKind::Added,
14802        ],
14803        indoc! {r#"struct Row;
14804                   ˇstruct Row1;
14805                   struct Row2;
14806                   ˇ
14807                   struct Row4;
14808                   ˇstruct Row5;
14809                   struct Row6;
14810                   ˇ
14811                   ˇstruct Row8;
14812                   struct Row9;
14813                   ˇstruct Row10;"#},
14814        base_text,
14815        &mut cx,
14816    );
14817}
14818
14819#[gpui::test]
14820async fn test_modification_reverts(cx: &mut TestAppContext) {
14821    init_test(cx, |_| {});
14822    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14823    let base_text = indoc! {r#"
14824        struct Row;
14825        struct Row1;
14826        struct Row2;
14827
14828        struct Row4;
14829        struct Row5;
14830        struct Row6;
14831
14832        struct Row8;
14833        struct Row9;
14834        struct Row10;"#};
14835
14836    // Modification hunks behave the same as the addition ones.
14837    assert_hunk_revert(
14838        indoc! {r#"struct Row;
14839                   struct Row1;
14840                   struct Row33;
14841                   ˇ
14842                   struct Row4;
14843                   struct Row5;
14844                   struct Row6;
14845                   ˇ
14846                   struct Row99;
14847                   struct Row9;
14848                   struct Row10;"#},
14849        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14850        indoc! {r#"struct Row;
14851                   struct Row1;
14852                   struct Row33;
14853                   ˇ
14854                   struct Row4;
14855                   struct Row5;
14856                   struct Row6;
14857                   ˇ
14858                   struct Row99;
14859                   struct Row9;
14860                   struct Row10;"#},
14861        base_text,
14862        &mut cx,
14863    );
14864    assert_hunk_revert(
14865        indoc! {r#"struct Row;
14866                   struct Row1;
14867                   struct Row33;
14868                   «ˇ
14869                   struct Row4;
14870                   struct» Row5;
14871                   «struct Row6;
14872                   ˇ»
14873                   struct Row99;
14874                   struct Row9;
14875                   struct Row10;"#},
14876        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14877        indoc! {r#"struct Row;
14878                   struct Row1;
14879                   struct Row33;
14880                   «ˇ
14881                   struct Row4;
14882                   struct» Row5;
14883                   «struct Row6;
14884                   ˇ»
14885                   struct Row99;
14886                   struct Row9;
14887                   struct Row10;"#},
14888        base_text,
14889        &mut cx,
14890    );
14891
14892    assert_hunk_revert(
14893        indoc! {r#"ˇstruct Row1.1;
14894                   struct Row1;
14895                   «ˇstr»uct Row22;
14896
14897                   struct ˇRow44;
14898                   struct Row5;
14899                   struct «Rˇ»ow66;ˇ
14900
14901                   «struˇ»ct Row88;
14902                   struct Row9;
14903                   struct Row1011;ˇ"#},
14904        vec![
14905            DiffHunkStatusKind::Modified,
14906            DiffHunkStatusKind::Modified,
14907            DiffHunkStatusKind::Modified,
14908            DiffHunkStatusKind::Modified,
14909            DiffHunkStatusKind::Modified,
14910            DiffHunkStatusKind::Modified,
14911        ],
14912        indoc! {r#"struct Row;
14913                   ˇstruct Row1;
14914                   struct Row2;
14915                   ˇ
14916                   struct Row4;
14917                   ˇstruct Row5;
14918                   struct Row6;
14919                   ˇ
14920                   struct Row8;
14921                   ˇstruct Row9;
14922                   struct Row10;ˇ"#},
14923        base_text,
14924        &mut cx,
14925    );
14926}
14927
14928#[gpui::test]
14929async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14930    init_test(cx, |_| {});
14931    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14932    let base_text = indoc! {r#"
14933        one
14934
14935        two
14936        three
14937        "#};
14938
14939    cx.set_head_text(base_text);
14940    cx.set_state("\nˇ\n");
14941    cx.executor().run_until_parked();
14942    cx.update_editor(|editor, _window, cx| {
14943        editor.expand_selected_diff_hunks(cx);
14944    });
14945    cx.executor().run_until_parked();
14946    cx.update_editor(|editor, window, cx| {
14947        editor.backspace(&Default::default(), window, cx);
14948    });
14949    cx.run_until_parked();
14950    cx.assert_state_with_diff(
14951        indoc! {r#"
14952
14953        - two
14954        - threeˇ
14955        +
14956        "#}
14957        .to_string(),
14958    );
14959}
14960
14961#[gpui::test]
14962async fn test_deletion_reverts(cx: &mut TestAppContext) {
14963    init_test(cx, |_| {});
14964    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14965    let base_text = indoc! {r#"struct Row;
14966struct Row1;
14967struct Row2;
14968
14969struct Row4;
14970struct Row5;
14971struct Row6;
14972
14973struct Row8;
14974struct Row9;
14975struct Row10;"#};
14976
14977    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14978    assert_hunk_revert(
14979        indoc! {r#"struct Row;
14980                   struct Row2;
14981
14982                   ˇstruct Row4;
14983                   struct Row5;
14984                   struct Row6;
14985                   ˇ
14986                   struct Row8;
14987                   struct Row10;"#},
14988        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14989        indoc! {r#"struct Row;
14990                   struct Row2;
14991
14992                   ˇstruct Row4;
14993                   struct Row5;
14994                   struct Row6;
14995                   ˇ
14996                   struct Row8;
14997                   struct Row10;"#},
14998        base_text,
14999        &mut cx,
15000    );
15001    assert_hunk_revert(
15002        indoc! {r#"struct Row;
15003                   struct Row2;
15004
15005                   «ˇstruct Row4;
15006                   struct» Row5;
15007                   «struct Row6;
15008                   ˇ»
15009                   struct Row8;
15010                   struct Row10;"#},
15011        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15012        indoc! {r#"struct Row;
15013                   struct Row2;
15014
15015                   «ˇstruct Row4;
15016                   struct» Row5;
15017                   «struct Row6;
15018                   ˇ»
15019                   struct Row8;
15020                   struct Row10;"#},
15021        base_text,
15022        &mut cx,
15023    );
15024
15025    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15026    assert_hunk_revert(
15027        indoc! {r#"struct Row;
15028                   ˇstruct Row2;
15029
15030                   struct Row4;
15031                   struct Row5;
15032                   struct Row6;
15033
15034                   struct Row8;ˇ
15035                   struct Row10;"#},
15036        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15037        indoc! {r#"struct Row;
15038                   struct Row1;
15039                   ˇstruct Row2;
15040
15041                   struct Row4;
15042                   struct Row5;
15043                   struct Row6;
15044
15045                   struct Row8;ˇ
15046                   struct Row9;
15047                   struct Row10;"#},
15048        base_text,
15049        &mut cx,
15050    );
15051    assert_hunk_revert(
15052        indoc! {r#"struct Row;
15053                   struct Row2«ˇ;
15054                   struct Row4;
15055                   struct» Row5;
15056                   «struct Row6;
15057
15058                   struct Row8;ˇ»
15059                   struct Row10;"#},
15060        vec![
15061            DiffHunkStatusKind::Deleted,
15062            DiffHunkStatusKind::Deleted,
15063            DiffHunkStatusKind::Deleted,
15064        ],
15065        indoc! {r#"struct Row;
15066                   struct Row1;
15067                   struct Row2«ˇ;
15068
15069                   struct Row4;
15070                   struct» Row5;
15071                   «struct Row6;
15072
15073                   struct Row8;ˇ»
15074                   struct Row9;
15075                   struct Row10;"#},
15076        base_text,
15077        &mut cx,
15078    );
15079}
15080
15081#[gpui::test]
15082async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15083    init_test(cx, |_| {});
15084
15085    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15086    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15087    let base_text_3 =
15088        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15089
15090    let text_1 = edit_first_char_of_every_line(base_text_1);
15091    let text_2 = edit_first_char_of_every_line(base_text_2);
15092    let text_3 = edit_first_char_of_every_line(base_text_3);
15093
15094    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15095    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15096    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15097
15098    let multibuffer = cx.new(|cx| {
15099        let mut multibuffer = MultiBuffer::new(ReadWrite);
15100        multibuffer.push_excerpts(
15101            buffer_1.clone(),
15102            [
15103                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15104                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15105                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15106            ],
15107            cx,
15108        );
15109        multibuffer.push_excerpts(
15110            buffer_2.clone(),
15111            [
15112                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15113                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15114                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15115            ],
15116            cx,
15117        );
15118        multibuffer.push_excerpts(
15119            buffer_3.clone(),
15120            [
15121                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15122                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15123                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15124            ],
15125            cx,
15126        );
15127        multibuffer
15128    });
15129
15130    let fs = FakeFs::new(cx.executor());
15131    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15132    let (editor, cx) = cx
15133        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15134    editor.update_in(cx, |editor, _window, cx| {
15135        for (buffer, diff_base) in [
15136            (buffer_1.clone(), base_text_1),
15137            (buffer_2.clone(), base_text_2),
15138            (buffer_3.clone(), base_text_3),
15139        ] {
15140            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15141            editor
15142                .buffer
15143                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15144        }
15145    });
15146    cx.executor().run_until_parked();
15147
15148    editor.update_in(cx, |editor, window, cx| {
15149        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}");
15150        editor.select_all(&SelectAll, window, cx);
15151        editor.git_restore(&Default::default(), window, cx);
15152    });
15153    cx.executor().run_until_parked();
15154
15155    // When all ranges are selected, all buffer hunks are reverted.
15156    editor.update(cx, |editor, cx| {
15157        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");
15158    });
15159    buffer_1.update(cx, |buffer, _| {
15160        assert_eq!(buffer.text(), base_text_1);
15161    });
15162    buffer_2.update(cx, |buffer, _| {
15163        assert_eq!(buffer.text(), base_text_2);
15164    });
15165    buffer_3.update(cx, |buffer, _| {
15166        assert_eq!(buffer.text(), base_text_3);
15167    });
15168
15169    editor.update_in(cx, |editor, window, cx| {
15170        editor.undo(&Default::default(), window, cx);
15171    });
15172
15173    editor.update_in(cx, |editor, window, cx| {
15174        editor.change_selections(None, window, cx, |s| {
15175            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15176        });
15177        editor.git_restore(&Default::default(), window, cx);
15178    });
15179
15180    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15181    // but not affect buffer_2 and its related excerpts.
15182    editor.update(cx, |editor, cx| {
15183        assert_eq!(
15184            editor.text(cx),
15185            "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}"
15186        );
15187    });
15188    buffer_1.update(cx, |buffer, _| {
15189        assert_eq!(buffer.text(), base_text_1);
15190    });
15191    buffer_2.update(cx, |buffer, _| {
15192        assert_eq!(
15193            buffer.text(),
15194            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15195        );
15196    });
15197    buffer_3.update(cx, |buffer, _| {
15198        assert_eq!(
15199            buffer.text(),
15200            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15201        );
15202    });
15203
15204    fn edit_first_char_of_every_line(text: &str) -> String {
15205        text.split('\n')
15206            .map(|line| format!("X{}", &line[1..]))
15207            .collect::<Vec<_>>()
15208            .join("\n")
15209    }
15210}
15211
15212#[gpui::test]
15213async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15214    init_test(cx, |_| {});
15215
15216    let cols = 4;
15217    let rows = 10;
15218    let sample_text_1 = sample_text(rows, cols, 'a');
15219    assert_eq!(
15220        sample_text_1,
15221        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15222    );
15223    let sample_text_2 = sample_text(rows, cols, 'l');
15224    assert_eq!(
15225        sample_text_2,
15226        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15227    );
15228    let sample_text_3 = sample_text(rows, cols, 'v');
15229    assert_eq!(
15230        sample_text_3,
15231        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15232    );
15233
15234    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15235    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15236    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15237
15238    let multi_buffer = cx.new(|cx| {
15239        let mut multibuffer = MultiBuffer::new(ReadWrite);
15240        multibuffer.push_excerpts(
15241            buffer_1.clone(),
15242            [
15243                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15244                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15245                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15246            ],
15247            cx,
15248        );
15249        multibuffer.push_excerpts(
15250            buffer_2.clone(),
15251            [
15252                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15253                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15254                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15255            ],
15256            cx,
15257        );
15258        multibuffer.push_excerpts(
15259            buffer_3.clone(),
15260            [
15261                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15262                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15263                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15264            ],
15265            cx,
15266        );
15267        multibuffer
15268    });
15269
15270    let fs = FakeFs::new(cx.executor());
15271    fs.insert_tree(
15272        "/a",
15273        json!({
15274            "main.rs": sample_text_1,
15275            "other.rs": sample_text_2,
15276            "lib.rs": sample_text_3,
15277        }),
15278    )
15279    .await;
15280    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15281    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15282    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15283    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15284        Editor::new(
15285            EditorMode::full(),
15286            multi_buffer,
15287            Some(project.clone()),
15288            window,
15289            cx,
15290        )
15291    });
15292    let multibuffer_item_id = workspace
15293        .update(cx, |workspace, window, cx| {
15294            assert!(
15295                workspace.active_item(cx).is_none(),
15296                "active item should be None before the first item is added"
15297            );
15298            workspace.add_item_to_active_pane(
15299                Box::new(multi_buffer_editor.clone()),
15300                None,
15301                true,
15302                window,
15303                cx,
15304            );
15305            let active_item = workspace
15306                .active_item(cx)
15307                .expect("should have an active item after adding the multi buffer");
15308            assert!(
15309                !active_item.is_singleton(cx),
15310                "A multi buffer was expected to active after adding"
15311            );
15312            active_item.item_id()
15313        })
15314        .unwrap();
15315    cx.executor().run_until_parked();
15316
15317    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15318        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15319            s.select_ranges(Some(1..2))
15320        });
15321        editor.open_excerpts(&OpenExcerpts, window, cx);
15322    });
15323    cx.executor().run_until_parked();
15324    let first_item_id = workspace
15325        .update(cx, |workspace, window, cx| {
15326            let active_item = workspace
15327                .active_item(cx)
15328                .expect("should have an active item after navigating into the 1st buffer");
15329            let first_item_id = active_item.item_id();
15330            assert_ne!(
15331                first_item_id, multibuffer_item_id,
15332                "Should navigate into the 1st buffer and activate it"
15333            );
15334            assert!(
15335                active_item.is_singleton(cx),
15336                "New active item should be a singleton buffer"
15337            );
15338            assert_eq!(
15339                active_item
15340                    .act_as::<Editor>(cx)
15341                    .expect("should have navigated into an editor for the 1st buffer")
15342                    .read(cx)
15343                    .text(cx),
15344                sample_text_1
15345            );
15346
15347            workspace
15348                .go_back(workspace.active_pane().downgrade(), window, cx)
15349                .detach_and_log_err(cx);
15350
15351            first_item_id
15352        })
15353        .unwrap();
15354    cx.executor().run_until_parked();
15355    workspace
15356        .update(cx, |workspace, _, cx| {
15357            let active_item = workspace
15358                .active_item(cx)
15359                .expect("should have an active item after navigating back");
15360            assert_eq!(
15361                active_item.item_id(),
15362                multibuffer_item_id,
15363                "Should navigate back to the multi buffer"
15364            );
15365            assert!(!active_item.is_singleton(cx));
15366        })
15367        .unwrap();
15368
15369    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15370        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15371            s.select_ranges(Some(39..40))
15372        });
15373        editor.open_excerpts(&OpenExcerpts, window, cx);
15374    });
15375    cx.executor().run_until_parked();
15376    let second_item_id = workspace
15377        .update(cx, |workspace, window, cx| {
15378            let active_item = workspace
15379                .active_item(cx)
15380                .expect("should have an active item after navigating into the 2nd buffer");
15381            let second_item_id = active_item.item_id();
15382            assert_ne!(
15383                second_item_id, multibuffer_item_id,
15384                "Should navigate away from the multibuffer"
15385            );
15386            assert_ne!(
15387                second_item_id, first_item_id,
15388                "Should navigate into the 2nd buffer and activate it"
15389            );
15390            assert!(
15391                active_item.is_singleton(cx),
15392                "New active item should be a singleton buffer"
15393            );
15394            assert_eq!(
15395                active_item
15396                    .act_as::<Editor>(cx)
15397                    .expect("should have navigated into an editor")
15398                    .read(cx)
15399                    .text(cx),
15400                sample_text_2
15401            );
15402
15403            workspace
15404                .go_back(workspace.active_pane().downgrade(), window, cx)
15405                .detach_and_log_err(cx);
15406
15407            second_item_id
15408        })
15409        .unwrap();
15410    cx.executor().run_until_parked();
15411    workspace
15412        .update(cx, |workspace, _, cx| {
15413            let active_item = workspace
15414                .active_item(cx)
15415                .expect("should have an active item after navigating back from the 2nd buffer");
15416            assert_eq!(
15417                active_item.item_id(),
15418                multibuffer_item_id,
15419                "Should navigate back from the 2nd buffer to the multi buffer"
15420            );
15421            assert!(!active_item.is_singleton(cx));
15422        })
15423        .unwrap();
15424
15425    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15426        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15427            s.select_ranges(Some(70..70))
15428        });
15429        editor.open_excerpts(&OpenExcerpts, window, cx);
15430    });
15431    cx.executor().run_until_parked();
15432    workspace
15433        .update(cx, |workspace, window, cx| {
15434            let active_item = workspace
15435                .active_item(cx)
15436                .expect("should have an active item after navigating into the 3rd buffer");
15437            let third_item_id = active_item.item_id();
15438            assert_ne!(
15439                third_item_id, multibuffer_item_id,
15440                "Should navigate into the 3rd buffer and activate it"
15441            );
15442            assert_ne!(third_item_id, first_item_id);
15443            assert_ne!(third_item_id, second_item_id);
15444            assert!(
15445                active_item.is_singleton(cx),
15446                "New active item should be a singleton buffer"
15447            );
15448            assert_eq!(
15449                active_item
15450                    .act_as::<Editor>(cx)
15451                    .expect("should have navigated into an editor")
15452                    .read(cx)
15453                    .text(cx),
15454                sample_text_3
15455            );
15456
15457            workspace
15458                .go_back(workspace.active_pane().downgrade(), window, cx)
15459                .detach_and_log_err(cx);
15460        })
15461        .unwrap();
15462    cx.executor().run_until_parked();
15463    workspace
15464        .update(cx, |workspace, _, cx| {
15465            let active_item = workspace
15466                .active_item(cx)
15467                .expect("should have an active item after navigating back from the 3rd buffer");
15468            assert_eq!(
15469                active_item.item_id(),
15470                multibuffer_item_id,
15471                "Should navigate back from the 3rd buffer to the multi buffer"
15472            );
15473            assert!(!active_item.is_singleton(cx));
15474        })
15475        .unwrap();
15476}
15477
15478#[gpui::test]
15479async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15480    init_test(cx, |_| {});
15481
15482    let mut cx = EditorTestContext::new(cx).await;
15483
15484    let diff_base = r#"
15485        use some::mod;
15486
15487        const A: u32 = 42;
15488
15489        fn main() {
15490            println!("hello");
15491
15492            println!("world");
15493        }
15494        "#
15495    .unindent();
15496
15497    cx.set_state(
15498        &r#"
15499        use some::modified;
15500
15501        ˇ
15502        fn main() {
15503            println!("hello there");
15504
15505            println!("around the");
15506            println!("world");
15507        }
15508        "#
15509        .unindent(),
15510    );
15511
15512    cx.set_head_text(&diff_base);
15513    executor.run_until_parked();
15514
15515    cx.update_editor(|editor, window, cx| {
15516        editor.go_to_next_hunk(&GoToHunk, window, cx);
15517        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15518    });
15519    executor.run_until_parked();
15520    cx.assert_state_with_diff(
15521        r#"
15522          use some::modified;
15523
15524
15525          fn main() {
15526        -     println!("hello");
15527        + ˇ    println!("hello there");
15528
15529              println!("around the");
15530              println!("world");
15531          }
15532        "#
15533        .unindent(),
15534    );
15535
15536    cx.update_editor(|editor, window, cx| {
15537        for _ in 0..2 {
15538            editor.go_to_next_hunk(&GoToHunk, window, cx);
15539            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15540        }
15541    });
15542    executor.run_until_parked();
15543    cx.assert_state_with_diff(
15544        r#"
15545        - use some::mod;
15546        + ˇuse some::modified;
15547
15548
15549          fn main() {
15550        -     println!("hello");
15551        +     println!("hello there");
15552
15553        +     println!("around the");
15554              println!("world");
15555          }
15556        "#
15557        .unindent(),
15558    );
15559
15560    cx.update_editor(|editor, window, cx| {
15561        editor.go_to_next_hunk(&GoToHunk, window, cx);
15562        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15563    });
15564    executor.run_until_parked();
15565    cx.assert_state_with_diff(
15566        r#"
15567        - use some::mod;
15568        + use some::modified;
15569
15570        - const A: u32 = 42;
15571          ˇ
15572          fn main() {
15573        -     println!("hello");
15574        +     println!("hello there");
15575
15576        +     println!("around the");
15577              println!("world");
15578          }
15579        "#
15580        .unindent(),
15581    );
15582
15583    cx.update_editor(|editor, window, cx| {
15584        editor.cancel(&Cancel, window, cx);
15585    });
15586
15587    cx.assert_state_with_diff(
15588        r#"
15589          use some::modified;
15590
15591          ˇ
15592          fn main() {
15593              println!("hello there");
15594
15595              println!("around the");
15596              println!("world");
15597          }
15598        "#
15599        .unindent(),
15600    );
15601}
15602
15603#[gpui::test]
15604async fn test_diff_base_change_with_expanded_diff_hunks(
15605    executor: BackgroundExecutor,
15606    cx: &mut TestAppContext,
15607) {
15608    init_test(cx, |_| {});
15609
15610    let mut cx = EditorTestContext::new(cx).await;
15611
15612    let diff_base = r#"
15613        use some::mod1;
15614        use some::mod2;
15615
15616        const A: u32 = 42;
15617        const B: u32 = 42;
15618        const C: u32 = 42;
15619
15620        fn main() {
15621            println!("hello");
15622
15623            println!("world");
15624        }
15625        "#
15626    .unindent();
15627
15628    cx.set_state(
15629        &r#"
15630        use some::mod2;
15631
15632        const A: u32 = 42;
15633        const C: u32 = 42;
15634
15635        fn main(ˇ) {
15636            //println!("hello");
15637
15638            println!("world");
15639            //
15640            //
15641        }
15642        "#
15643        .unindent(),
15644    );
15645
15646    cx.set_head_text(&diff_base);
15647    executor.run_until_parked();
15648
15649    cx.update_editor(|editor, window, cx| {
15650        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15651    });
15652    executor.run_until_parked();
15653    cx.assert_state_with_diff(
15654        r#"
15655        - use some::mod1;
15656          use some::mod2;
15657
15658          const A: u32 = 42;
15659        - const B: u32 = 42;
15660          const C: u32 = 42;
15661
15662          fn main(ˇ) {
15663        -     println!("hello");
15664        +     //println!("hello");
15665
15666              println!("world");
15667        +     //
15668        +     //
15669          }
15670        "#
15671        .unindent(),
15672    );
15673
15674    cx.set_head_text("new diff base!");
15675    executor.run_until_parked();
15676    cx.assert_state_with_diff(
15677        r#"
15678        - new diff base!
15679        + use some::mod2;
15680        +
15681        + const A: u32 = 42;
15682        + const C: u32 = 42;
15683        +
15684        + fn main(ˇ) {
15685        +     //println!("hello");
15686        +
15687        +     println!("world");
15688        +     //
15689        +     //
15690        + }
15691        "#
15692        .unindent(),
15693    );
15694}
15695
15696#[gpui::test]
15697async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15698    init_test(cx, |_| {});
15699
15700    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15701    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15702    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15703    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15704    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15705    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15706
15707    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15708    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15709    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15710
15711    let multi_buffer = cx.new(|cx| {
15712        let mut multibuffer = MultiBuffer::new(ReadWrite);
15713        multibuffer.push_excerpts(
15714            buffer_1.clone(),
15715            [
15716                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15717                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15718                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15719            ],
15720            cx,
15721        );
15722        multibuffer.push_excerpts(
15723            buffer_2.clone(),
15724            [
15725                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15726                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15727                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15728            ],
15729            cx,
15730        );
15731        multibuffer.push_excerpts(
15732            buffer_3.clone(),
15733            [
15734                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15735                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15736                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15737            ],
15738            cx,
15739        );
15740        multibuffer
15741    });
15742
15743    let editor =
15744        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15745    editor
15746        .update(cx, |editor, _window, cx| {
15747            for (buffer, diff_base) in [
15748                (buffer_1.clone(), file_1_old),
15749                (buffer_2.clone(), file_2_old),
15750                (buffer_3.clone(), file_3_old),
15751            ] {
15752                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15753                editor
15754                    .buffer
15755                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15756            }
15757        })
15758        .unwrap();
15759
15760    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15761    cx.run_until_parked();
15762
15763    cx.assert_editor_state(
15764        &"
15765            ˇaaa
15766            ccc
15767            ddd
15768
15769            ggg
15770            hhh
15771
15772
15773            lll
15774            mmm
15775            NNN
15776
15777            qqq
15778            rrr
15779
15780            uuu
15781            111
15782            222
15783            333
15784
15785            666
15786            777
15787
15788            000
15789            !!!"
15790        .unindent(),
15791    );
15792
15793    cx.update_editor(|editor, window, cx| {
15794        editor.select_all(&SelectAll, window, cx);
15795        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15796    });
15797    cx.executor().run_until_parked();
15798
15799    cx.assert_state_with_diff(
15800        "
15801            «aaa
15802          - bbb
15803            ccc
15804            ddd
15805
15806            ggg
15807            hhh
15808
15809
15810            lll
15811            mmm
15812          - nnn
15813          + NNN
15814
15815            qqq
15816            rrr
15817
15818            uuu
15819            111
15820            222
15821            333
15822
15823          + 666
15824            777
15825
15826            000
15827            !!!ˇ»"
15828            .unindent(),
15829    );
15830}
15831
15832#[gpui::test]
15833async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15834    init_test(cx, |_| {});
15835
15836    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15837    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15838
15839    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15840    let multi_buffer = cx.new(|cx| {
15841        let mut multibuffer = MultiBuffer::new(ReadWrite);
15842        multibuffer.push_excerpts(
15843            buffer.clone(),
15844            [
15845                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15846                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15847                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15848            ],
15849            cx,
15850        );
15851        multibuffer
15852    });
15853
15854    let editor =
15855        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15856    editor
15857        .update(cx, |editor, _window, cx| {
15858            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15859            editor
15860                .buffer
15861                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15862        })
15863        .unwrap();
15864
15865    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15866    cx.run_until_parked();
15867
15868    cx.update_editor(|editor, window, cx| {
15869        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15870    });
15871    cx.executor().run_until_parked();
15872
15873    // When the start of a hunk coincides with the start of its excerpt,
15874    // the hunk is expanded. When the start of a a hunk is earlier than
15875    // the start of its excerpt, the hunk is not expanded.
15876    cx.assert_state_with_diff(
15877        "
15878            ˇaaa
15879          - bbb
15880          + BBB
15881
15882          - ddd
15883          - eee
15884          + DDD
15885          + EEE
15886            fff
15887
15888            iii
15889        "
15890        .unindent(),
15891    );
15892}
15893
15894#[gpui::test]
15895async fn test_edits_around_expanded_insertion_hunks(
15896    executor: BackgroundExecutor,
15897    cx: &mut TestAppContext,
15898) {
15899    init_test(cx, |_| {});
15900
15901    let mut cx = EditorTestContext::new(cx).await;
15902
15903    let diff_base = r#"
15904        use some::mod1;
15905        use some::mod2;
15906
15907        const A: u32 = 42;
15908
15909        fn main() {
15910            println!("hello");
15911
15912            println!("world");
15913        }
15914        "#
15915    .unindent();
15916    executor.run_until_parked();
15917    cx.set_state(
15918        &r#"
15919        use some::mod1;
15920        use some::mod2;
15921
15922        const A: u32 = 42;
15923        const B: u32 = 42;
15924        const C: u32 = 42;
15925        ˇ
15926
15927        fn main() {
15928            println!("hello");
15929
15930            println!("world");
15931        }
15932        "#
15933        .unindent(),
15934    );
15935
15936    cx.set_head_text(&diff_base);
15937    executor.run_until_parked();
15938
15939    cx.update_editor(|editor, window, cx| {
15940        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15941    });
15942    executor.run_until_parked();
15943
15944    cx.assert_state_with_diff(
15945        r#"
15946        use some::mod1;
15947        use some::mod2;
15948
15949        const A: u32 = 42;
15950      + const B: u32 = 42;
15951      + const C: u32 = 42;
15952      + ˇ
15953
15954        fn main() {
15955            println!("hello");
15956
15957            println!("world");
15958        }
15959      "#
15960        .unindent(),
15961    );
15962
15963    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15964    executor.run_until_parked();
15965
15966    cx.assert_state_with_diff(
15967        r#"
15968        use some::mod1;
15969        use some::mod2;
15970
15971        const A: u32 = 42;
15972      + const B: u32 = 42;
15973      + const C: u32 = 42;
15974      + const D: u32 = 42;
15975      + ˇ
15976
15977        fn main() {
15978            println!("hello");
15979
15980            println!("world");
15981        }
15982      "#
15983        .unindent(),
15984    );
15985
15986    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15987    executor.run_until_parked();
15988
15989    cx.assert_state_with_diff(
15990        r#"
15991        use some::mod1;
15992        use some::mod2;
15993
15994        const A: u32 = 42;
15995      + const B: u32 = 42;
15996      + const C: u32 = 42;
15997      + const D: u32 = 42;
15998      + const E: u32 = 42;
15999      + ˇ
16000
16001        fn main() {
16002            println!("hello");
16003
16004            println!("world");
16005        }
16006      "#
16007        .unindent(),
16008    );
16009
16010    cx.update_editor(|editor, window, cx| {
16011        editor.delete_line(&DeleteLine, window, cx);
16012    });
16013    executor.run_until_parked();
16014
16015    cx.assert_state_with_diff(
16016        r#"
16017        use some::mod1;
16018        use some::mod2;
16019
16020        const A: u32 = 42;
16021      + const B: u32 = 42;
16022      + const C: u32 = 42;
16023      + const D: u32 = 42;
16024      + const E: u32 = 42;
16025        ˇ
16026        fn main() {
16027            println!("hello");
16028
16029            println!("world");
16030        }
16031      "#
16032        .unindent(),
16033    );
16034
16035    cx.update_editor(|editor, window, cx| {
16036        editor.move_up(&MoveUp, window, cx);
16037        editor.delete_line(&DeleteLine, window, cx);
16038        editor.move_up(&MoveUp, window, cx);
16039        editor.delete_line(&DeleteLine, window, cx);
16040        editor.move_up(&MoveUp, window, cx);
16041        editor.delete_line(&DeleteLine, window, cx);
16042    });
16043    executor.run_until_parked();
16044    cx.assert_state_with_diff(
16045        r#"
16046        use some::mod1;
16047        use some::mod2;
16048
16049        const A: u32 = 42;
16050      + const B: u32 = 42;
16051        ˇ
16052        fn main() {
16053            println!("hello");
16054
16055            println!("world");
16056        }
16057      "#
16058        .unindent(),
16059    );
16060
16061    cx.update_editor(|editor, window, cx| {
16062        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16063        editor.delete_line(&DeleteLine, window, cx);
16064    });
16065    executor.run_until_parked();
16066    cx.assert_state_with_diff(
16067        r#"
16068        ˇ
16069        fn main() {
16070            println!("hello");
16071
16072            println!("world");
16073        }
16074      "#
16075        .unindent(),
16076    );
16077}
16078
16079#[gpui::test]
16080async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16081    init_test(cx, |_| {});
16082
16083    let mut cx = EditorTestContext::new(cx).await;
16084    cx.set_head_text(indoc! { "
16085        one
16086        two
16087        three
16088        four
16089        five
16090        "
16091    });
16092    cx.set_state(indoc! { "
16093        one
16094        ˇthree
16095        five
16096    "});
16097    cx.run_until_parked();
16098    cx.update_editor(|editor, window, cx| {
16099        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16100    });
16101    cx.assert_state_with_diff(
16102        indoc! { "
16103        one
16104      - two
16105        ˇthree
16106      - four
16107        five
16108    "}
16109        .to_string(),
16110    );
16111    cx.update_editor(|editor, window, cx| {
16112        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16113    });
16114
16115    cx.assert_state_with_diff(
16116        indoc! { "
16117        one
16118        ˇthree
16119        five
16120    "}
16121        .to_string(),
16122    );
16123
16124    cx.set_state(indoc! { "
16125        one
16126        ˇTWO
16127        three
16128        four
16129        five
16130    "});
16131    cx.run_until_parked();
16132    cx.update_editor(|editor, window, cx| {
16133        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16134    });
16135
16136    cx.assert_state_with_diff(
16137        indoc! { "
16138            one
16139          - two
16140          + ˇTWO
16141            three
16142            four
16143            five
16144        "}
16145        .to_string(),
16146    );
16147    cx.update_editor(|editor, window, cx| {
16148        editor.move_up(&Default::default(), window, cx);
16149        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16150    });
16151    cx.assert_state_with_diff(
16152        indoc! { "
16153            one
16154            ˇTWO
16155            three
16156            four
16157            five
16158        "}
16159        .to_string(),
16160    );
16161}
16162
16163#[gpui::test]
16164async fn test_edits_around_expanded_deletion_hunks(
16165    executor: BackgroundExecutor,
16166    cx: &mut TestAppContext,
16167) {
16168    init_test(cx, |_| {});
16169
16170    let mut cx = EditorTestContext::new(cx).await;
16171
16172    let diff_base = r#"
16173        use some::mod1;
16174        use some::mod2;
16175
16176        const A: u32 = 42;
16177        const B: u32 = 42;
16178        const C: u32 = 42;
16179
16180
16181        fn main() {
16182            println!("hello");
16183
16184            println!("world");
16185        }
16186    "#
16187    .unindent();
16188    executor.run_until_parked();
16189    cx.set_state(
16190        &r#"
16191        use some::mod1;
16192        use some::mod2;
16193
16194        ˇconst B: u32 = 42;
16195        const C: u32 = 42;
16196
16197
16198        fn main() {
16199            println!("hello");
16200
16201            println!("world");
16202        }
16203        "#
16204        .unindent(),
16205    );
16206
16207    cx.set_head_text(&diff_base);
16208    executor.run_until_parked();
16209
16210    cx.update_editor(|editor, window, cx| {
16211        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16212    });
16213    executor.run_until_parked();
16214
16215    cx.assert_state_with_diff(
16216        r#"
16217        use some::mod1;
16218        use some::mod2;
16219
16220      - const A: u32 = 42;
16221        ˇconst B: u32 = 42;
16222        const C: u32 = 42;
16223
16224
16225        fn main() {
16226            println!("hello");
16227
16228            println!("world");
16229        }
16230      "#
16231        .unindent(),
16232    );
16233
16234    cx.update_editor(|editor, window, cx| {
16235        editor.delete_line(&DeleteLine, window, cx);
16236    });
16237    executor.run_until_parked();
16238    cx.assert_state_with_diff(
16239        r#"
16240        use some::mod1;
16241        use some::mod2;
16242
16243      - const A: u32 = 42;
16244      - const B: u32 = 42;
16245        ˇconst C: u32 = 42;
16246
16247
16248        fn main() {
16249            println!("hello");
16250
16251            println!("world");
16252        }
16253      "#
16254        .unindent(),
16255    );
16256
16257    cx.update_editor(|editor, window, cx| {
16258        editor.delete_line(&DeleteLine, window, cx);
16259    });
16260    executor.run_until_parked();
16261    cx.assert_state_with_diff(
16262        r#"
16263        use some::mod1;
16264        use some::mod2;
16265
16266      - const A: u32 = 42;
16267      - const B: u32 = 42;
16268      - const C: u32 = 42;
16269        ˇ
16270
16271        fn main() {
16272            println!("hello");
16273
16274            println!("world");
16275        }
16276      "#
16277        .unindent(),
16278    );
16279
16280    cx.update_editor(|editor, window, cx| {
16281        editor.handle_input("replacement", window, cx);
16282    });
16283    executor.run_until_parked();
16284    cx.assert_state_with_diff(
16285        r#"
16286        use some::mod1;
16287        use some::mod2;
16288
16289      - const A: u32 = 42;
16290      - const B: u32 = 42;
16291      - const C: u32 = 42;
16292      -
16293      + replacementˇ
16294
16295        fn main() {
16296            println!("hello");
16297
16298            println!("world");
16299        }
16300      "#
16301        .unindent(),
16302    );
16303}
16304
16305#[gpui::test]
16306async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16307    init_test(cx, |_| {});
16308
16309    let mut cx = EditorTestContext::new(cx).await;
16310
16311    let base_text = r#"
16312        one
16313        two
16314        three
16315        four
16316        five
16317    "#
16318    .unindent();
16319    executor.run_until_parked();
16320    cx.set_state(
16321        &r#"
16322        one
16323        two
16324        fˇour
16325        five
16326        "#
16327        .unindent(),
16328    );
16329
16330    cx.set_head_text(&base_text);
16331    executor.run_until_parked();
16332
16333    cx.update_editor(|editor, window, cx| {
16334        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16335    });
16336    executor.run_until_parked();
16337
16338    cx.assert_state_with_diff(
16339        r#"
16340          one
16341          two
16342        - three
16343          fˇour
16344          five
16345        "#
16346        .unindent(),
16347    );
16348
16349    cx.update_editor(|editor, window, cx| {
16350        editor.backspace(&Backspace, window, cx);
16351        editor.backspace(&Backspace, window, cx);
16352    });
16353    executor.run_until_parked();
16354    cx.assert_state_with_diff(
16355        r#"
16356          one
16357          two
16358        - threeˇ
16359        - four
16360        + our
16361          five
16362        "#
16363        .unindent(),
16364    );
16365}
16366
16367#[gpui::test]
16368async fn test_edit_after_expanded_modification_hunk(
16369    executor: BackgroundExecutor,
16370    cx: &mut TestAppContext,
16371) {
16372    init_test(cx, |_| {});
16373
16374    let mut cx = EditorTestContext::new(cx).await;
16375
16376    let diff_base = r#"
16377        use some::mod1;
16378        use some::mod2;
16379
16380        const A: u32 = 42;
16381        const B: u32 = 42;
16382        const C: u32 = 42;
16383        const D: u32 = 42;
16384
16385
16386        fn main() {
16387            println!("hello");
16388
16389            println!("world");
16390        }"#
16391    .unindent();
16392
16393    cx.set_state(
16394        &r#"
16395        use some::mod1;
16396        use some::mod2;
16397
16398        const A: u32 = 42;
16399        const B: u32 = 42;
16400        const C: u32 = 43ˇ
16401        const D: u32 = 42;
16402
16403
16404        fn main() {
16405            println!("hello");
16406
16407            println!("world");
16408        }"#
16409        .unindent(),
16410    );
16411
16412    cx.set_head_text(&diff_base);
16413    executor.run_until_parked();
16414    cx.update_editor(|editor, window, cx| {
16415        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16416    });
16417    executor.run_until_parked();
16418
16419    cx.assert_state_with_diff(
16420        r#"
16421        use some::mod1;
16422        use some::mod2;
16423
16424        const A: u32 = 42;
16425        const B: u32 = 42;
16426      - const C: u32 = 42;
16427      + const C: u32 = 43ˇ
16428        const D: u32 = 42;
16429
16430
16431        fn main() {
16432            println!("hello");
16433
16434            println!("world");
16435        }"#
16436        .unindent(),
16437    );
16438
16439    cx.update_editor(|editor, window, cx| {
16440        editor.handle_input("\nnew_line\n", window, cx);
16441    });
16442    executor.run_until_parked();
16443
16444    cx.assert_state_with_diff(
16445        r#"
16446        use some::mod1;
16447        use some::mod2;
16448
16449        const A: u32 = 42;
16450        const B: u32 = 42;
16451      - const C: u32 = 42;
16452      + const C: u32 = 43
16453      + new_line
16454      + ˇ
16455        const D: u32 = 42;
16456
16457
16458        fn main() {
16459            println!("hello");
16460
16461            println!("world");
16462        }"#
16463        .unindent(),
16464    );
16465}
16466
16467#[gpui::test]
16468async fn test_stage_and_unstage_added_file_hunk(
16469    executor: BackgroundExecutor,
16470    cx: &mut TestAppContext,
16471) {
16472    init_test(cx, |_| {});
16473
16474    let mut cx = EditorTestContext::new(cx).await;
16475    cx.update_editor(|editor, _, cx| {
16476        editor.set_expand_all_diff_hunks(cx);
16477    });
16478
16479    let working_copy = r#"
16480            ˇfn main() {
16481                println!("hello, world!");
16482            }
16483        "#
16484    .unindent();
16485
16486    cx.set_state(&working_copy);
16487    executor.run_until_parked();
16488
16489    cx.assert_state_with_diff(
16490        r#"
16491            + ˇfn main() {
16492            +     println!("hello, world!");
16493            + }
16494        "#
16495        .unindent(),
16496    );
16497    cx.assert_index_text(None);
16498
16499    cx.update_editor(|editor, window, cx| {
16500        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16501    });
16502    executor.run_until_parked();
16503    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16504    cx.assert_state_with_diff(
16505        r#"
16506            + ˇfn main() {
16507            +     println!("hello, world!");
16508            + }
16509        "#
16510        .unindent(),
16511    );
16512
16513    cx.update_editor(|editor, window, cx| {
16514        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16515    });
16516    executor.run_until_parked();
16517    cx.assert_index_text(None);
16518}
16519
16520async fn setup_indent_guides_editor(
16521    text: &str,
16522    cx: &mut TestAppContext,
16523) -> (BufferId, EditorTestContext) {
16524    init_test(cx, |_| {});
16525
16526    let mut cx = EditorTestContext::new(cx).await;
16527
16528    let buffer_id = cx.update_editor(|editor, window, cx| {
16529        editor.set_text(text, window, cx);
16530        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16531
16532        buffer_ids[0]
16533    });
16534
16535    (buffer_id, cx)
16536}
16537
16538fn assert_indent_guides(
16539    range: Range<u32>,
16540    expected: Vec<IndentGuide>,
16541    active_indices: Option<Vec<usize>>,
16542    cx: &mut EditorTestContext,
16543) {
16544    let indent_guides = cx.update_editor(|editor, window, cx| {
16545        let snapshot = editor.snapshot(window, cx).display_snapshot;
16546        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16547            editor,
16548            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16549            true,
16550            &snapshot,
16551            cx,
16552        );
16553
16554        indent_guides.sort_by(|a, b| {
16555            a.depth.cmp(&b.depth).then(
16556                a.start_row
16557                    .cmp(&b.start_row)
16558                    .then(a.end_row.cmp(&b.end_row)),
16559            )
16560        });
16561        indent_guides
16562    });
16563
16564    if let Some(expected) = active_indices {
16565        let active_indices = cx.update_editor(|editor, window, cx| {
16566            let snapshot = editor.snapshot(window, cx).display_snapshot;
16567            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16568        });
16569
16570        assert_eq!(
16571            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16572            expected,
16573            "Active indent guide indices do not match"
16574        );
16575    }
16576
16577    assert_eq!(indent_guides, expected, "Indent guides do not match");
16578}
16579
16580fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16581    IndentGuide {
16582        buffer_id,
16583        start_row: MultiBufferRow(start_row),
16584        end_row: MultiBufferRow(end_row),
16585        depth,
16586        tab_size: 4,
16587        settings: IndentGuideSettings {
16588            enabled: true,
16589            line_width: 1,
16590            active_line_width: 1,
16591            ..Default::default()
16592        },
16593    }
16594}
16595
16596#[gpui::test]
16597async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16598    let (buffer_id, mut cx) = setup_indent_guides_editor(
16599        &"
16600    fn main() {
16601        let a = 1;
16602    }"
16603        .unindent(),
16604        cx,
16605    )
16606    .await;
16607
16608    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16609}
16610
16611#[gpui::test]
16612async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16613    let (buffer_id, mut cx) = setup_indent_guides_editor(
16614        &"
16615    fn main() {
16616        let a = 1;
16617        let b = 2;
16618    }"
16619        .unindent(),
16620        cx,
16621    )
16622    .await;
16623
16624    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16625}
16626
16627#[gpui::test]
16628async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16629    let (buffer_id, mut cx) = setup_indent_guides_editor(
16630        &"
16631    fn main() {
16632        let a = 1;
16633        if a == 3 {
16634            let b = 2;
16635        } else {
16636            let c = 3;
16637        }
16638    }"
16639        .unindent(),
16640        cx,
16641    )
16642    .await;
16643
16644    assert_indent_guides(
16645        0..8,
16646        vec![
16647            indent_guide(buffer_id, 1, 6, 0),
16648            indent_guide(buffer_id, 3, 3, 1),
16649            indent_guide(buffer_id, 5, 5, 1),
16650        ],
16651        None,
16652        &mut cx,
16653    );
16654}
16655
16656#[gpui::test]
16657async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16658    let (buffer_id, mut cx) = setup_indent_guides_editor(
16659        &"
16660    fn main() {
16661        let a = 1;
16662            let b = 2;
16663        let c = 3;
16664    }"
16665        .unindent(),
16666        cx,
16667    )
16668    .await;
16669
16670    assert_indent_guides(
16671        0..5,
16672        vec![
16673            indent_guide(buffer_id, 1, 3, 0),
16674            indent_guide(buffer_id, 2, 2, 1),
16675        ],
16676        None,
16677        &mut cx,
16678    );
16679}
16680
16681#[gpui::test]
16682async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16683    let (buffer_id, mut cx) = setup_indent_guides_editor(
16684        &"
16685        fn main() {
16686            let a = 1;
16687
16688            let c = 3;
16689        }"
16690        .unindent(),
16691        cx,
16692    )
16693    .await;
16694
16695    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16696}
16697
16698#[gpui::test]
16699async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16700    let (buffer_id, mut cx) = setup_indent_guides_editor(
16701        &"
16702        fn main() {
16703            let a = 1;
16704
16705            let c = 3;
16706
16707            if a == 3 {
16708                let b = 2;
16709            } else {
16710                let c = 3;
16711            }
16712        }"
16713        .unindent(),
16714        cx,
16715    )
16716    .await;
16717
16718    assert_indent_guides(
16719        0..11,
16720        vec![
16721            indent_guide(buffer_id, 1, 9, 0),
16722            indent_guide(buffer_id, 6, 6, 1),
16723            indent_guide(buffer_id, 8, 8, 1),
16724        ],
16725        None,
16726        &mut cx,
16727    );
16728}
16729
16730#[gpui::test]
16731async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16732    let (buffer_id, mut cx) = setup_indent_guides_editor(
16733        &"
16734        fn main() {
16735            let a = 1;
16736
16737            let c = 3;
16738
16739            if a == 3 {
16740                let b = 2;
16741            } else {
16742                let c = 3;
16743            }
16744        }"
16745        .unindent(),
16746        cx,
16747    )
16748    .await;
16749
16750    assert_indent_guides(
16751        1..11,
16752        vec![
16753            indent_guide(buffer_id, 1, 9, 0),
16754            indent_guide(buffer_id, 6, 6, 1),
16755            indent_guide(buffer_id, 8, 8, 1),
16756        ],
16757        None,
16758        &mut cx,
16759    );
16760}
16761
16762#[gpui::test]
16763async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16764    let (buffer_id, mut cx) = setup_indent_guides_editor(
16765        &"
16766        fn main() {
16767            let a = 1;
16768
16769            let c = 3;
16770
16771            if a == 3 {
16772                let b = 2;
16773            } else {
16774                let c = 3;
16775            }
16776        }"
16777        .unindent(),
16778        cx,
16779    )
16780    .await;
16781
16782    assert_indent_guides(
16783        1..10,
16784        vec![
16785            indent_guide(buffer_id, 1, 9, 0),
16786            indent_guide(buffer_id, 6, 6, 1),
16787            indent_guide(buffer_id, 8, 8, 1),
16788        ],
16789        None,
16790        &mut cx,
16791    );
16792}
16793
16794#[gpui::test]
16795async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16796    let (buffer_id, mut cx) = setup_indent_guides_editor(
16797        &"
16798        block1
16799            block2
16800                block3
16801                    block4
16802            block2
16803        block1
16804        block1"
16805            .unindent(),
16806        cx,
16807    )
16808    .await;
16809
16810    assert_indent_guides(
16811        1..10,
16812        vec![
16813            indent_guide(buffer_id, 1, 4, 0),
16814            indent_guide(buffer_id, 2, 3, 1),
16815            indent_guide(buffer_id, 3, 3, 2),
16816        ],
16817        None,
16818        &mut cx,
16819    );
16820}
16821
16822#[gpui::test]
16823async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16824    let (buffer_id, mut cx) = setup_indent_guides_editor(
16825        &"
16826        block1
16827            block2
16828                block3
16829
16830        block1
16831        block1"
16832            .unindent(),
16833        cx,
16834    )
16835    .await;
16836
16837    assert_indent_guides(
16838        0..6,
16839        vec![
16840            indent_guide(buffer_id, 1, 2, 0),
16841            indent_guide(buffer_id, 2, 2, 1),
16842        ],
16843        None,
16844        &mut cx,
16845    );
16846}
16847
16848#[gpui::test]
16849async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16850    let (buffer_id, mut cx) = setup_indent_guides_editor(
16851        &"
16852        block1
16853
16854
16855
16856            block2
16857        "
16858        .unindent(),
16859        cx,
16860    )
16861    .await;
16862
16863    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16864}
16865
16866#[gpui::test]
16867async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16868    let (buffer_id, mut cx) = setup_indent_guides_editor(
16869        &"
16870        def a:
16871        \tb = 3
16872        \tif True:
16873        \t\tc = 4
16874        \t\td = 5
16875        \tprint(b)
16876        "
16877        .unindent(),
16878        cx,
16879    )
16880    .await;
16881
16882    assert_indent_guides(
16883        0..6,
16884        vec![
16885            indent_guide(buffer_id, 1, 5, 0),
16886            indent_guide(buffer_id, 3, 4, 1),
16887        ],
16888        None,
16889        &mut cx,
16890    );
16891}
16892
16893#[gpui::test]
16894async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16895    let (buffer_id, mut cx) = setup_indent_guides_editor(
16896        &"
16897    fn main() {
16898        let a = 1;
16899    }"
16900        .unindent(),
16901        cx,
16902    )
16903    .await;
16904
16905    cx.update_editor(|editor, window, cx| {
16906        editor.change_selections(None, window, cx, |s| {
16907            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16908        });
16909    });
16910
16911    assert_indent_guides(
16912        0..3,
16913        vec![indent_guide(buffer_id, 1, 1, 0)],
16914        Some(vec![0]),
16915        &mut cx,
16916    );
16917}
16918
16919#[gpui::test]
16920async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16921    let (buffer_id, mut cx) = setup_indent_guides_editor(
16922        &"
16923    fn main() {
16924        if 1 == 2 {
16925            let a = 1;
16926        }
16927    }"
16928        .unindent(),
16929        cx,
16930    )
16931    .await;
16932
16933    cx.update_editor(|editor, window, cx| {
16934        editor.change_selections(None, window, cx, |s| {
16935            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16936        });
16937    });
16938
16939    assert_indent_guides(
16940        0..4,
16941        vec![
16942            indent_guide(buffer_id, 1, 3, 0),
16943            indent_guide(buffer_id, 2, 2, 1),
16944        ],
16945        Some(vec![1]),
16946        &mut cx,
16947    );
16948
16949    cx.update_editor(|editor, window, cx| {
16950        editor.change_selections(None, window, cx, |s| {
16951            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16952        });
16953    });
16954
16955    assert_indent_guides(
16956        0..4,
16957        vec![
16958            indent_guide(buffer_id, 1, 3, 0),
16959            indent_guide(buffer_id, 2, 2, 1),
16960        ],
16961        Some(vec![1]),
16962        &mut cx,
16963    );
16964
16965    cx.update_editor(|editor, window, cx| {
16966        editor.change_selections(None, window, cx, |s| {
16967            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16968        });
16969    });
16970
16971    assert_indent_guides(
16972        0..4,
16973        vec![
16974            indent_guide(buffer_id, 1, 3, 0),
16975            indent_guide(buffer_id, 2, 2, 1),
16976        ],
16977        Some(vec![0]),
16978        &mut cx,
16979    );
16980}
16981
16982#[gpui::test]
16983async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16984    let (buffer_id, mut cx) = setup_indent_guides_editor(
16985        &"
16986    fn main() {
16987        let a = 1;
16988
16989        let b = 2;
16990    }"
16991        .unindent(),
16992        cx,
16993    )
16994    .await;
16995
16996    cx.update_editor(|editor, window, cx| {
16997        editor.change_selections(None, window, cx, |s| {
16998            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16999        });
17000    });
17001
17002    assert_indent_guides(
17003        0..5,
17004        vec![indent_guide(buffer_id, 1, 3, 0)],
17005        Some(vec![0]),
17006        &mut cx,
17007    );
17008}
17009
17010#[gpui::test]
17011async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
17012    let (buffer_id, mut cx) = setup_indent_guides_editor(
17013        &"
17014    def m:
17015        a = 1
17016        pass"
17017            .unindent(),
17018        cx,
17019    )
17020    .await;
17021
17022    cx.update_editor(|editor, window, cx| {
17023        editor.change_selections(None, window, cx, |s| {
17024            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
17025        });
17026    });
17027
17028    assert_indent_guides(
17029        0..3,
17030        vec![indent_guide(buffer_id, 1, 2, 0)],
17031        Some(vec![0]),
17032        &mut cx,
17033    );
17034}
17035
17036#[gpui::test]
17037async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17038    init_test(cx, |_| {});
17039    let mut cx = EditorTestContext::new(cx).await;
17040    let text = indoc! {
17041        "
17042        impl A {
17043            fn b() {
17044                0;
17045                3;
17046                5;
17047                6;
17048                7;
17049            }
17050        }
17051        "
17052    };
17053    let base_text = indoc! {
17054        "
17055        impl A {
17056            fn b() {
17057                0;
17058                1;
17059                2;
17060                3;
17061                4;
17062            }
17063            fn c() {
17064                5;
17065                6;
17066                7;
17067            }
17068        }
17069        "
17070    };
17071
17072    cx.update_editor(|editor, window, cx| {
17073        editor.set_text(text, window, cx);
17074
17075        editor.buffer().update(cx, |multibuffer, cx| {
17076            let buffer = multibuffer.as_singleton().unwrap();
17077            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17078
17079            multibuffer.set_all_diff_hunks_expanded(cx);
17080            multibuffer.add_diff(diff, cx);
17081
17082            buffer.read(cx).remote_id()
17083        })
17084    });
17085    cx.run_until_parked();
17086
17087    cx.assert_state_with_diff(
17088        indoc! { "
17089          impl A {
17090              fn b() {
17091                  0;
17092        -         1;
17093        -         2;
17094                  3;
17095        -         4;
17096        -     }
17097        -     fn c() {
17098                  5;
17099                  6;
17100                  7;
17101              }
17102          }
17103          ˇ"
17104        }
17105        .to_string(),
17106    );
17107
17108    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17109        editor
17110            .snapshot(window, cx)
17111            .buffer_snapshot
17112            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17113            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17114            .collect::<Vec<_>>()
17115    });
17116    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17117    assert_eq!(
17118        actual_guides,
17119        vec![
17120            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17121            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17122            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17123        ]
17124    );
17125}
17126
17127#[gpui::test]
17128async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17129    init_test(cx, |_| {});
17130    let mut cx = EditorTestContext::new(cx).await;
17131
17132    let diff_base = r#"
17133        a
17134        b
17135        c
17136        "#
17137    .unindent();
17138
17139    cx.set_state(
17140        &r#"
17141        ˇA
17142        b
17143        C
17144        "#
17145        .unindent(),
17146    );
17147    cx.set_head_text(&diff_base);
17148    cx.update_editor(|editor, window, cx| {
17149        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17150    });
17151    executor.run_until_parked();
17152
17153    let both_hunks_expanded = r#"
17154        - a
17155        + ˇA
17156          b
17157        - c
17158        + C
17159        "#
17160    .unindent();
17161
17162    cx.assert_state_with_diff(both_hunks_expanded.clone());
17163
17164    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17165        let snapshot = editor.snapshot(window, cx);
17166        let hunks = editor
17167            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17168            .collect::<Vec<_>>();
17169        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17170        let buffer_id = hunks[0].buffer_id;
17171        hunks
17172            .into_iter()
17173            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17174            .collect::<Vec<_>>()
17175    });
17176    assert_eq!(hunk_ranges.len(), 2);
17177
17178    cx.update_editor(|editor, _, cx| {
17179        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17180    });
17181    executor.run_until_parked();
17182
17183    let second_hunk_expanded = r#"
17184          ˇA
17185          b
17186        - c
17187        + C
17188        "#
17189    .unindent();
17190
17191    cx.assert_state_with_diff(second_hunk_expanded);
17192
17193    cx.update_editor(|editor, _, cx| {
17194        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17195    });
17196    executor.run_until_parked();
17197
17198    cx.assert_state_with_diff(both_hunks_expanded.clone());
17199
17200    cx.update_editor(|editor, _, cx| {
17201        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17202    });
17203    executor.run_until_parked();
17204
17205    let first_hunk_expanded = r#"
17206        - a
17207        + ˇA
17208          b
17209          C
17210        "#
17211    .unindent();
17212
17213    cx.assert_state_with_diff(first_hunk_expanded);
17214
17215    cx.update_editor(|editor, _, cx| {
17216        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17217    });
17218    executor.run_until_parked();
17219
17220    cx.assert_state_with_diff(both_hunks_expanded);
17221
17222    cx.set_state(
17223        &r#"
17224        ˇA
17225        b
17226        "#
17227        .unindent(),
17228    );
17229    cx.run_until_parked();
17230
17231    // TODO this cursor position seems bad
17232    cx.assert_state_with_diff(
17233        r#"
17234        - ˇa
17235        + A
17236          b
17237        "#
17238        .unindent(),
17239    );
17240
17241    cx.update_editor(|editor, window, cx| {
17242        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17243    });
17244
17245    cx.assert_state_with_diff(
17246        r#"
17247            - ˇa
17248            + A
17249              b
17250            - c
17251            "#
17252        .unindent(),
17253    );
17254
17255    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17256        let snapshot = editor.snapshot(window, cx);
17257        let hunks = editor
17258            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17259            .collect::<Vec<_>>();
17260        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17261        let buffer_id = hunks[0].buffer_id;
17262        hunks
17263            .into_iter()
17264            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17265            .collect::<Vec<_>>()
17266    });
17267    assert_eq!(hunk_ranges.len(), 2);
17268
17269    cx.update_editor(|editor, _, cx| {
17270        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17271    });
17272    executor.run_until_parked();
17273
17274    cx.assert_state_with_diff(
17275        r#"
17276        - ˇa
17277        + A
17278          b
17279        "#
17280        .unindent(),
17281    );
17282}
17283
17284#[gpui::test]
17285async fn test_toggle_deletion_hunk_at_start_of_file(
17286    executor: BackgroundExecutor,
17287    cx: &mut TestAppContext,
17288) {
17289    init_test(cx, |_| {});
17290    let mut cx = EditorTestContext::new(cx).await;
17291
17292    let diff_base = r#"
17293        a
17294        b
17295        c
17296        "#
17297    .unindent();
17298
17299    cx.set_state(
17300        &r#"
17301        ˇb
17302        c
17303        "#
17304        .unindent(),
17305    );
17306    cx.set_head_text(&diff_base);
17307    cx.update_editor(|editor, window, cx| {
17308        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17309    });
17310    executor.run_until_parked();
17311
17312    let hunk_expanded = r#"
17313        - a
17314          ˇb
17315          c
17316        "#
17317    .unindent();
17318
17319    cx.assert_state_with_diff(hunk_expanded.clone());
17320
17321    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17322        let snapshot = editor.snapshot(window, cx);
17323        let hunks = editor
17324            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17325            .collect::<Vec<_>>();
17326        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17327        let buffer_id = hunks[0].buffer_id;
17328        hunks
17329            .into_iter()
17330            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17331            .collect::<Vec<_>>()
17332    });
17333    assert_eq!(hunk_ranges.len(), 1);
17334
17335    cx.update_editor(|editor, _, cx| {
17336        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17337    });
17338    executor.run_until_parked();
17339
17340    let hunk_collapsed = r#"
17341          ˇb
17342          c
17343        "#
17344    .unindent();
17345
17346    cx.assert_state_with_diff(hunk_collapsed);
17347
17348    cx.update_editor(|editor, _, cx| {
17349        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17350    });
17351    executor.run_until_parked();
17352
17353    cx.assert_state_with_diff(hunk_expanded.clone());
17354}
17355
17356#[gpui::test]
17357async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17358    init_test(cx, |_| {});
17359
17360    let fs = FakeFs::new(cx.executor());
17361    fs.insert_tree(
17362        path!("/test"),
17363        json!({
17364            ".git": {},
17365            "file-1": "ONE\n",
17366            "file-2": "TWO\n",
17367            "file-3": "THREE\n",
17368        }),
17369    )
17370    .await;
17371
17372    fs.set_head_for_repo(
17373        path!("/test/.git").as_ref(),
17374        &[
17375            ("file-1".into(), "one\n".into()),
17376            ("file-2".into(), "two\n".into()),
17377            ("file-3".into(), "three\n".into()),
17378        ],
17379    );
17380
17381    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17382    let mut buffers = vec![];
17383    for i in 1..=3 {
17384        let buffer = project
17385            .update(cx, |project, cx| {
17386                let path = format!(path!("/test/file-{}"), i);
17387                project.open_local_buffer(path, cx)
17388            })
17389            .await
17390            .unwrap();
17391        buffers.push(buffer);
17392    }
17393
17394    let multibuffer = cx.new(|cx| {
17395        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17396        multibuffer.set_all_diff_hunks_expanded(cx);
17397        for buffer in &buffers {
17398            let snapshot = buffer.read(cx).snapshot();
17399            multibuffer.set_excerpts_for_path(
17400                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17401                buffer.clone(),
17402                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17403                DEFAULT_MULTIBUFFER_CONTEXT,
17404                cx,
17405            );
17406        }
17407        multibuffer
17408    });
17409
17410    let editor = cx.add_window(|window, cx| {
17411        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17412    });
17413    cx.run_until_parked();
17414
17415    let snapshot = editor
17416        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17417        .unwrap();
17418    let hunks = snapshot
17419        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17420        .map(|hunk| match hunk {
17421            DisplayDiffHunk::Unfolded {
17422                display_row_range, ..
17423            } => display_row_range,
17424            DisplayDiffHunk::Folded { .. } => unreachable!(),
17425        })
17426        .collect::<Vec<_>>();
17427    assert_eq!(
17428        hunks,
17429        [
17430            DisplayRow(2)..DisplayRow(4),
17431            DisplayRow(7)..DisplayRow(9),
17432            DisplayRow(12)..DisplayRow(14),
17433        ]
17434    );
17435}
17436
17437#[gpui::test]
17438async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17439    init_test(cx, |_| {});
17440
17441    let mut cx = EditorTestContext::new(cx).await;
17442    cx.set_head_text(indoc! { "
17443        one
17444        two
17445        three
17446        four
17447        five
17448        "
17449    });
17450    cx.set_index_text(indoc! { "
17451        one
17452        two
17453        three
17454        four
17455        five
17456        "
17457    });
17458    cx.set_state(indoc! {"
17459        one
17460        TWO
17461        ˇTHREE
17462        FOUR
17463        five
17464    "});
17465    cx.run_until_parked();
17466    cx.update_editor(|editor, window, cx| {
17467        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17468    });
17469    cx.run_until_parked();
17470    cx.assert_index_text(Some(indoc! {"
17471        one
17472        TWO
17473        THREE
17474        FOUR
17475        five
17476    "}));
17477    cx.set_state(indoc! { "
17478        one
17479        TWO
17480        ˇTHREE-HUNDRED
17481        FOUR
17482        five
17483    "});
17484    cx.run_until_parked();
17485    cx.update_editor(|editor, window, cx| {
17486        let snapshot = editor.snapshot(window, cx);
17487        let hunks = editor
17488            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17489            .collect::<Vec<_>>();
17490        assert_eq!(hunks.len(), 1);
17491        assert_eq!(
17492            hunks[0].status(),
17493            DiffHunkStatus {
17494                kind: DiffHunkStatusKind::Modified,
17495                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17496            }
17497        );
17498
17499        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17500    });
17501    cx.run_until_parked();
17502    cx.assert_index_text(Some(indoc! {"
17503        one
17504        TWO
17505        THREE-HUNDRED
17506        FOUR
17507        five
17508    "}));
17509}
17510
17511#[gpui::test]
17512fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17513    init_test(cx, |_| {});
17514
17515    let editor = cx.add_window(|window, cx| {
17516        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17517        build_editor(buffer, window, cx)
17518    });
17519
17520    let render_args = Arc::new(Mutex::new(None));
17521    let snapshot = editor
17522        .update(cx, |editor, window, cx| {
17523            let snapshot = editor.buffer().read(cx).snapshot(cx);
17524            let range =
17525                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17526
17527            struct RenderArgs {
17528                row: MultiBufferRow,
17529                folded: bool,
17530                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17531            }
17532
17533            let crease = Crease::inline(
17534                range,
17535                FoldPlaceholder::test(),
17536                {
17537                    let toggle_callback = render_args.clone();
17538                    move |row, folded, callback, _window, _cx| {
17539                        *toggle_callback.lock() = Some(RenderArgs {
17540                            row,
17541                            folded,
17542                            callback,
17543                        });
17544                        div()
17545                    }
17546                },
17547                |_row, _folded, _window, _cx| div(),
17548            );
17549
17550            editor.insert_creases(Some(crease), cx);
17551            let snapshot = editor.snapshot(window, cx);
17552            let _div = snapshot.render_crease_toggle(
17553                MultiBufferRow(1),
17554                false,
17555                cx.entity().clone(),
17556                window,
17557                cx,
17558            );
17559            snapshot
17560        })
17561        .unwrap();
17562
17563    let render_args = render_args.lock().take().unwrap();
17564    assert_eq!(render_args.row, MultiBufferRow(1));
17565    assert!(!render_args.folded);
17566    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17567
17568    cx.update_window(*editor, |_, window, cx| {
17569        (render_args.callback)(true, window, cx)
17570    })
17571    .unwrap();
17572    let snapshot = editor
17573        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17574        .unwrap();
17575    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17576
17577    cx.update_window(*editor, |_, window, cx| {
17578        (render_args.callback)(false, window, cx)
17579    })
17580    .unwrap();
17581    let snapshot = editor
17582        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17583        .unwrap();
17584    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17585}
17586
17587#[gpui::test]
17588async fn test_input_text(cx: &mut TestAppContext) {
17589    init_test(cx, |_| {});
17590    let mut cx = EditorTestContext::new(cx).await;
17591
17592    cx.set_state(
17593        &r#"ˇone
17594        two
17595
17596        three
17597        fourˇ
17598        five
17599
17600        siˇx"#
17601            .unindent(),
17602    );
17603
17604    cx.dispatch_action(HandleInput(String::new()));
17605    cx.assert_editor_state(
17606        &r#"ˇone
17607        two
17608
17609        three
17610        fourˇ
17611        five
17612
17613        siˇx"#
17614            .unindent(),
17615    );
17616
17617    cx.dispatch_action(HandleInput("AAAA".to_string()));
17618    cx.assert_editor_state(
17619        &r#"AAAAˇone
17620        two
17621
17622        three
17623        fourAAAAˇ
17624        five
17625
17626        siAAAAˇx"#
17627            .unindent(),
17628    );
17629}
17630
17631#[gpui::test]
17632async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17633    init_test(cx, |_| {});
17634
17635    let mut cx = EditorTestContext::new(cx).await;
17636    cx.set_state(
17637        r#"let foo = 1;
17638let foo = 2;
17639let foo = 3;
17640let fooˇ = 4;
17641let foo = 5;
17642let foo = 6;
17643let foo = 7;
17644let foo = 8;
17645let foo = 9;
17646let foo = 10;
17647let foo = 11;
17648let foo = 12;
17649let foo = 13;
17650let foo = 14;
17651let foo = 15;"#,
17652    );
17653
17654    cx.update_editor(|e, window, cx| {
17655        assert_eq!(
17656            e.next_scroll_position,
17657            NextScrollCursorCenterTopBottom::Center,
17658            "Default next scroll direction is center",
17659        );
17660
17661        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17662        assert_eq!(
17663            e.next_scroll_position,
17664            NextScrollCursorCenterTopBottom::Top,
17665            "After center, next scroll direction should be top",
17666        );
17667
17668        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17669        assert_eq!(
17670            e.next_scroll_position,
17671            NextScrollCursorCenterTopBottom::Bottom,
17672            "After top, next scroll direction should be bottom",
17673        );
17674
17675        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17676        assert_eq!(
17677            e.next_scroll_position,
17678            NextScrollCursorCenterTopBottom::Center,
17679            "After bottom, scrolling should start over",
17680        );
17681
17682        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17683        assert_eq!(
17684            e.next_scroll_position,
17685            NextScrollCursorCenterTopBottom::Top,
17686            "Scrolling continues if retriggered fast enough"
17687        );
17688    });
17689
17690    cx.executor()
17691        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17692    cx.executor().run_until_parked();
17693    cx.update_editor(|e, _, _| {
17694        assert_eq!(
17695            e.next_scroll_position,
17696            NextScrollCursorCenterTopBottom::Center,
17697            "If scrolling is not triggered fast enough, it should reset"
17698        );
17699    });
17700}
17701
17702#[gpui::test]
17703async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17704    init_test(cx, |_| {});
17705    let mut cx = EditorLspTestContext::new_rust(
17706        lsp::ServerCapabilities {
17707            definition_provider: Some(lsp::OneOf::Left(true)),
17708            references_provider: Some(lsp::OneOf::Left(true)),
17709            ..lsp::ServerCapabilities::default()
17710        },
17711        cx,
17712    )
17713    .await;
17714
17715    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17716        let go_to_definition = cx
17717            .lsp
17718            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17719                move |params, _| async move {
17720                    if empty_go_to_definition {
17721                        Ok(None)
17722                    } else {
17723                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17724                            uri: params.text_document_position_params.text_document.uri,
17725                            range: lsp::Range::new(
17726                                lsp::Position::new(4, 3),
17727                                lsp::Position::new(4, 6),
17728                            ),
17729                        })))
17730                    }
17731                },
17732            );
17733        let references = cx
17734            .lsp
17735            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17736                Ok(Some(vec![lsp::Location {
17737                    uri: params.text_document_position.text_document.uri,
17738                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17739                }]))
17740            });
17741        (go_to_definition, references)
17742    };
17743
17744    cx.set_state(
17745        &r#"fn one() {
17746            let mut a = ˇtwo();
17747        }
17748
17749        fn two() {}"#
17750            .unindent(),
17751    );
17752    set_up_lsp_handlers(false, &mut cx);
17753    let navigated = cx
17754        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17755        .await
17756        .expect("Failed to navigate to definition");
17757    assert_eq!(
17758        navigated,
17759        Navigated::Yes,
17760        "Should have navigated to definition from the GetDefinition response"
17761    );
17762    cx.assert_editor_state(
17763        &r#"fn one() {
17764            let mut a = two();
17765        }
17766
17767        fn «twoˇ»() {}"#
17768            .unindent(),
17769    );
17770
17771    let editors = cx.update_workspace(|workspace, _, cx| {
17772        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17773    });
17774    cx.update_editor(|_, _, test_editor_cx| {
17775        assert_eq!(
17776            editors.len(),
17777            1,
17778            "Initially, only one, test, editor should be open in the workspace"
17779        );
17780        assert_eq!(
17781            test_editor_cx.entity(),
17782            editors.last().expect("Asserted len is 1").clone()
17783        );
17784    });
17785
17786    set_up_lsp_handlers(true, &mut cx);
17787    let navigated = cx
17788        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17789        .await
17790        .expect("Failed to navigate to lookup references");
17791    assert_eq!(
17792        navigated,
17793        Navigated::Yes,
17794        "Should have navigated to references as a fallback after empty GoToDefinition response"
17795    );
17796    // We should not change the selections in the existing file,
17797    // if opening another milti buffer with the references
17798    cx.assert_editor_state(
17799        &r#"fn one() {
17800            let mut a = two();
17801        }
17802
17803        fn «twoˇ»() {}"#
17804            .unindent(),
17805    );
17806    let editors = cx.update_workspace(|workspace, _, cx| {
17807        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17808    });
17809    cx.update_editor(|_, _, test_editor_cx| {
17810        assert_eq!(
17811            editors.len(),
17812            2,
17813            "After falling back to references search, we open a new editor with the results"
17814        );
17815        let references_fallback_text = editors
17816            .into_iter()
17817            .find(|new_editor| *new_editor != test_editor_cx.entity())
17818            .expect("Should have one non-test editor now")
17819            .read(test_editor_cx)
17820            .text(test_editor_cx);
17821        assert_eq!(
17822            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17823            "Should use the range from the references response and not the GoToDefinition one"
17824        );
17825    });
17826}
17827
17828#[gpui::test]
17829async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17830    init_test(cx, |_| {});
17831    cx.update(|cx| {
17832        let mut editor_settings = EditorSettings::get_global(cx).clone();
17833        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17834        EditorSettings::override_global(editor_settings, cx);
17835    });
17836    let mut cx = EditorLspTestContext::new_rust(
17837        lsp::ServerCapabilities {
17838            definition_provider: Some(lsp::OneOf::Left(true)),
17839            references_provider: Some(lsp::OneOf::Left(true)),
17840            ..lsp::ServerCapabilities::default()
17841        },
17842        cx,
17843    )
17844    .await;
17845    let original_state = r#"fn one() {
17846        let mut a = ˇtwo();
17847    }
17848
17849    fn two() {}"#
17850        .unindent();
17851    cx.set_state(&original_state);
17852
17853    let mut go_to_definition = cx
17854        .lsp
17855        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17856            move |_, _| async move { Ok(None) },
17857        );
17858    let _references = cx
17859        .lsp
17860        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17861            panic!("Should not call for references with no go to definition fallback")
17862        });
17863
17864    let navigated = cx
17865        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17866        .await
17867        .expect("Failed to navigate to lookup references");
17868    go_to_definition
17869        .next()
17870        .await
17871        .expect("Should have called the go_to_definition handler");
17872
17873    assert_eq!(
17874        navigated,
17875        Navigated::No,
17876        "Should have navigated to references as a fallback after empty GoToDefinition response"
17877    );
17878    cx.assert_editor_state(&original_state);
17879    let editors = cx.update_workspace(|workspace, _, cx| {
17880        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17881    });
17882    cx.update_editor(|_, _, _| {
17883        assert_eq!(
17884            editors.len(),
17885            1,
17886            "After unsuccessful fallback, no other editor should have been opened"
17887        );
17888    });
17889}
17890
17891#[gpui::test]
17892async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17893    init_test(cx, |_| {});
17894
17895    let language = Arc::new(Language::new(
17896        LanguageConfig::default(),
17897        Some(tree_sitter_rust::LANGUAGE.into()),
17898    ));
17899
17900    let text = r#"
17901        #[cfg(test)]
17902        mod tests() {
17903            #[test]
17904            fn runnable_1() {
17905                let a = 1;
17906            }
17907
17908            #[test]
17909            fn runnable_2() {
17910                let a = 1;
17911                let b = 2;
17912            }
17913        }
17914    "#
17915    .unindent();
17916
17917    let fs = FakeFs::new(cx.executor());
17918    fs.insert_file("/file.rs", Default::default()).await;
17919
17920    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17921    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17922    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17923    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17924    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17925
17926    let editor = cx.new_window_entity(|window, cx| {
17927        Editor::new(
17928            EditorMode::full(),
17929            multi_buffer,
17930            Some(project.clone()),
17931            window,
17932            cx,
17933        )
17934    });
17935
17936    editor.update_in(cx, |editor, window, cx| {
17937        let snapshot = editor.buffer().read(cx).snapshot(cx);
17938        editor.tasks.insert(
17939            (buffer.read(cx).remote_id(), 3),
17940            RunnableTasks {
17941                templates: vec![],
17942                offset: snapshot.anchor_before(43),
17943                column: 0,
17944                extra_variables: HashMap::default(),
17945                context_range: BufferOffset(43)..BufferOffset(85),
17946            },
17947        );
17948        editor.tasks.insert(
17949            (buffer.read(cx).remote_id(), 8),
17950            RunnableTasks {
17951                templates: vec![],
17952                offset: snapshot.anchor_before(86),
17953                column: 0,
17954                extra_variables: HashMap::default(),
17955                context_range: BufferOffset(86)..BufferOffset(191),
17956            },
17957        );
17958
17959        // Test finding task when cursor is inside function body
17960        editor.change_selections(None, window, cx, |s| {
17961            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17962        });
17963        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17964        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17965
17966        // Test finding task when cursor is on function name
17967        editor.change_selections(None, window, cx, |s| {
17968            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17969        });
17970        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17971        assert_eq!(row, 8, "Should find task when cursor is on function name");
17972    });
17973}
17974
17975#[gpui::test]
17976async fn test_folding_buffers(cx: &mut TestAppContext) {
17977    init_test(cx, |_| {});
17978
17979    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17980    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17981    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17982
17983    let fs = FakeFs::new(cx.executor());
17984    fs.insert_tree(
17985        path!("/a"),
17986        json!({
17987            "first.rs": sample_text_1,
17988            "second.rs": sample_text_2,
17989            "third.rs": sample_text_3,
17990        }),
17991    )
17992    .await;
17993    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17994    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17995    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17996    let worktree = project.update(cx, |project, cx| {
17997        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17998        assert_eq!(worktrees.len(), 1);
17999        worktrees.pop().unwrap()
18000    });
18001    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18002
18003    let buffer_1 = project
18004        .update(cx, |project, cx| {
18005            project.open_buffer((worktree_id, "first.rs"), cx)
18006        })
18007        .await
18008        .unwrap();
18009    let buffer_2 = project
18010        .update(cx, |project, cx| {
18011            project.open_buffer((worktree_id, "second.rs"), cx)
18012        })
18013        .await
18014        .unwrap();
18015    let buffer_3 = project
18016        .update(cx, |project, cx| {
18017            project.open_buffer((worktree_id, "third.rs"), cx)
18018        })
18019        .await
18020        .unwrap();
18021
18022    let multi_buffer = cx.new(|cx| {
18023        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18024        multi_buffer.push_excerpts(
18025            buffer_1.clone(),
18026            [
18027                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18028                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18029                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18030            ],
18031            cx,
18032        );
18033        multi_buffer.push_excerpts(
18034            buffer_2.clone(),
18035            [
18036                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18037                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18038                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18039            ],
18040            cx,
18041        );
18042        multi_buffer.push_excerpts(
18043            buffer_3.clone(),
18044            [
18045                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18046                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18047                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18048            ],
18049            cx,
18050        );
18051        multi_buffer
18052    });
18053    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18054        Editor::new(
18055            EditorMode::full(),
18056            multi_buffer.clone(),
18057            Some(project.clone()),
18058            window,
18059            cx,
18060        )
18061    });
18062
18063    assert_eq!(
18064        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18065        "\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",
18066    );
18067
18068    multi_buffer_editor.update(cx, |editor, cx| {
18069        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18070    });
18071    assert_eq!(
18072        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18073        "\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",
18074        "After folding the first buffer, its text should not be displayed"
18075    );
18076
18077    multi_buffer_editor.update(cx, |editor, cx| {
18078        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18079    });
18080    assert_eq!(
18081        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18082        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18083        "After folding the second buffer, its text should not be displayed"
18084    );
18085
18086    multi_buffer_editor.update(cx, |editor, cx| {
18087        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18088    });
18089    assert_eq!(
18090        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18091        "\n\n\n\n\n",
18092        "After folding the third buffer, its text should not be displayed"
18093    );
18094
18095    // Emulate selection inside the fold logic, that should work
18096    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18097        editor
18098            .snapshot(window, cx)
18099            .next_line_boundary(Point::new(0, 4));
18100    });
18101
18102    multi_buffer_editor.update(cx, |editor, cx| {
18103        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18104    });
18105    assert_eq!(
18106        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18107        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18108        "After unfolding the second buffer, its text should be displayed"
18109    );
18110
18111    // Typing inside of buffer 1 causes that buffer to be unfolded.
18112    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18113        assert_eq!(
18114            multi_buffer
18115                .read(cx)
18116                .snapshot(cx)
18117                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18118                .collect::<String>(),
18119            "bbbb"
18120        );
18121        editor.change_selections(None, window, cx, |selections| {
18122            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18123        });
18124        editor.handle_input("B", window, cx);
18125    });
18126
18127    assert_eq!(
18128        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18129        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18130        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18131    );
18132
18133    multi_buffer_editor.update(cx, |editor, cx| {
18134        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18135    });
18136    assert_eq!(
18137        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18138        "\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",
18139        "After unfolding the all buffers, all original text should be displayed"
18140    );
18141}
18142
18143#[gpui::test]
18144async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18145    init_test(cx, |_| {});
18146
18147    let sample_text_1 = "1111\n2222\n3333".to_string();
18148    let sample_text_2 = "4444\n5555\n6666".to_string();
18149    let sample_text_3 = "7777\n8888\n9999".to_string();
18150
18151    let fs = FakeFs::new(cx.executor());
18152    fs.insert_tree(
18153        path!("/a"),
18154        json!({
18155            "first.rs": sample_text_1,
18156            "second.rs": sample_text_2,
18157            "third.rs": sample_text_3,
18158        }),
18159    )
18160    .await;
18161    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18162    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18163    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18164    let worktree = project.update(cx, |project, cx| {
18165        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18166        assert_eq!(worktrees.len(), 1);
18167        worktrees.pop().unwrap()
18168    });
18169    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18170
18171    let buffer_1 = project
18172        .update(cx, |project, cx| {
18173            project.open_buffer((worktree_id, "first.rs"), cx)
18174        })
18175        .await
18176        .unwrap();
18177    let buffer_2 = project
18178        .update(cx, |project, cx| {
18179            project.open_buffer((worktree_id, "second.rs"), cx)
18180        })
18181        .await
18182        .unwrap();
18183    let buffer_3 = project
18184        .update(cx, |project, cx| {
18185            project.open_buffer((worktree_id, "third.rs"), cx)
18186        })
18187        .await
18188        .unwrap();
18189
18190    let multi_buffer = cx.new(|cx| {
18191        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18192        multi_buffer.push_excerpts(
18193            buffer_1.clone(),
18194            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18195            cx,
18196        );
18197        multi_buffer.push_excerpts(
18198            buffer_2.clone(),
18199            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18200            cx,
18201        );
18202        multi_buffer.push_excerpts(
18203            buffer_3.clone(),
18204            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18205            cx,
18206        );
18207        multi_buffer
18208    });
18209
18210    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18211        Editor::new(
18212            EditorMode::full(),
18213            multi_buffer,
18214            Some(project.clone()),
18215            window,
18216            cx,
18217        )
18218    });
18219
18220    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18221    assert_eq!(
18222        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18223        full_text,
18224    );
18225
18226    multi_buffer_editor.update(cx, |editor, cx| {
18227        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18228    });
18229    assert_eq!(
18230        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18231        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18232        "After folding the first buffer, its text should not be displayed"
18233    );
18234
18235    multi_buffer_editor.update(cx, |editor, cx| {
18236        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18237    });
18238
18239    assert_eq!(
18240        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18241        "\n\n\n\n\n\n7777\n8888\n9999",
18242        "After folding the second buffer, its text should not be displayed"
18243    );
18244
18245    multi_buffer_editor.update(cx, |editor, cx| {
18246        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18247    });
18248    assert_eq!(
18249        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18250        "\n\n\n\n\n",
18251        "After folding the third buffer, its text should not be displayed"
18252    );
18253
18254    multi_buffer_editor.update(cx, |editor, cx| {
18255        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18256    });
18257    assert_eq!(
18258        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18259        "\n\n\n\n4444\n5555\n6666\n\n",
18260        "After unfolding the second buffer, its text should be displayed"
18261    );
18262
18263    multi_buffer_editor.update(cx, |editor, cx| {
18264        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18265    });
18266    assert_eq!(
18267        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18268        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18269        "After unfolding the first buffer, its text should be displayed"
18270    );
18271
18272    multi_buffer_editor.update(cx, |editor, cx| {
18273        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18274    });
18275    assert_eq!(
18276        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18277        full_text,
18278        "After unfolding all buffers, all original text should be displayed"
18279    );
18280}
18281
18282#[gpui::test]
18283async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18284    init_test(cx, |_| {});
18285
18286    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18287
18288    let fs = FakeFs::new(cx.executor());
18289    fs.insert_tree(
18290        path!("/a"),
18291        json!({
18292            "main.rs": sample_text,
18293        }),
18294    )
18295    .await;
18296    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18297    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18298    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18299    let worktree = project.update(cx, |project, cx| {
18300        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18301        assert_eq!(worktrees.len(), 1);
18302        worktrees.pop().unwrap()
18303    });
18304    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18305
18306    let buffer_1 = project
18307        .update(cx, |project, cx| {
18308            project.open_buffer((worktree_id, "main.rs"), cx)
18309        })
18310        .await
18311        .unwrap();
18312
18313    let multi_buffer = cx.new(|cx| {
18314        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18315        multi_buffer.push_excerpts(
18316            buffer_1.clone(),
18317            [ExcerptRange::new(
18318                Point::new(0, 0)
18319                    ..Point::new(
18320                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18321                        0,
18322                    ),
18323            )],
18324            cx,
18325        );
18326        multi_buffer
18327    });
18328    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18329        Editor::new(
18330            EditorMode::full(),
18331            multi_buffer,
18332            Some(project.clone()),
18333            window,
18334            cx,
18335        )
18336    });
18337
18338    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18339    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18340        enum TestHighlight {}
18341        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18342        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18343        editor.highlight_text::<TestHighlight>(
18344            vec![highlight_range.clone()],
18345            HighlightStyle::color(Hsla::green()),
18346            cx,
18347        );
18348        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18349    });
18350
18351    let full_text = format!("\n\n{sample_text}");
18352    assert_eq!(
18353        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18354        full_text,
18355    );
18356}
18357
18358#[gpui::test]
18359async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18360    init_test(cx, |_| {});
18361    cx.update(|cx| {
18362        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18363            "keymaps/default-linux.json",
18364            cx,
18365        )
18366        .unwrap();
18367        cx.bind_keys(default_key_bindings);
18368    });
18369
18370    let (editor, cx) = cx.add_window_view(|window, cx| {
18371        let multi_buffer = MultiBuffer::build_multi(
18372            [
18373                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18374                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18375                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18376                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18377            ],
18378            cx,
18379        );
18380        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18381
18382        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18383        // fold all but the second buffer, so that we test navigating between two
18384        // adjacent folded buffers, as well as folded buffers at the start and
18385        // end the multibuffer
18386        editor.fold_buffer(buffer_ids[0], cx);
18387        editor.fold_buffer(buffer_ids[2], cx);
18388        editor.fold_buffer(buffer_ids[3], cx);
18389
18390        editor
18391    });
18392    cx.simulate_resize(size(px(1000.), px(1000.)));
18393
18394    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18395    cx.assert_excerpts_with_selections(indoc! {"
18396        [EXCERPT]
18397        ˇ[FOLDED]
18398        [EXCERPT]
18399        a1
18400        b1
18401        [EXCERPT]
18402        [FOLDED]
18403        [EXCERPT]
18404        [FOLDED]
18405        "
18406    });
18407    cx.simulate_keystroke("down");
18408    cx.assert_excerpts_with_selections(indoc! {"
18409        [EXCERPT]
18410        [FOLDED]
18411        [EXCERPT]
18412        ˇa1
18413        b1
18414        [EXCERPT]
18415        [FOLDED]
18416        [EXCERPT]
18417        [FOLDED]
18418        "
18419    });
18420    cx.simulate_keystroke("down");
18421    cx.assert_excerpts_with_selections(indoc! {"
18422        [EXCERPT]
18423        [FOLDED]
18424        [EXCERPT]
18425        a1
18426        ˇb1
18427        [EXCERPT]
18428        [FOLDED]
18429        [EXCERPT]
18430        [FOLDED]
18431        "
18432    });
18433    cx.simulate_keystroke("down");
18434    cx.assert_excerpts_with_selections(indoc! {"
18435        [EXCERPT]
18436        [FOLDED]
18437        [EXCERPT]
18438        a1
18439        b1
18440        ˇ[EXCERPT]
18441        [FOLDED]
18442        [EXCERPT]
18443        [FOLDED]
18444        "
18445    });
18446    cx.simulate_keystroke("down");
18447    cx.assert_excerpts_with_selections(indoc! {"
18448        [EXCERPT]
18449        [FOLDED]
18450        [EXCERPT]
18451        a1
18452        b1
18453        [EXCERPT]
18454        ˇ[FOLDED]
18455        [EXCERPT]
18456        [FOLDED]
18457        "
18458    });
18459    for _ in 0..5 {
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    }
18474
18475    cx.simulate_keystroke("up");
18476    cx.assert_excerpts_with_selections(indoc! {"
18477        [EXCERPT]
18478        [FOLDED]
18479        [EXCERPT]
18480        a1
18481        b1
18482        [EXCERPT]
18483        ˇ[FOLDED]
18484        [EXCERPT]
18485        [FOLDED]
18486        "
18487    });
18488    cx.simulate_keystroke("up");
18489    cx.assert_excerpts_with_selections(indoc! {"
18490        [EXCERPT]
18491        [FOLDED]
18492        [EXCERPT]
18493        a1
18494        b1
18495        ˇ[EXCERPT]
18496        [FOLDED]
18497        [EXCERPT]
18498        [FOLDED]
18499        "
18500    });
18501    cx.simulate_keystroke("up");
18502    cx.assert_excerpts_with_selections(indoc! {"
18503        [EXCERPT]
18504        [FOLDED]
18505        [EXCERPT]
18506        a1
18507        ˇb1
18508        [EXCERPT]
18509        [FOLDED]
18510        [EXCERPT]
18511        [FOLDED]
18512        "
18513    });
18514    cx.simulate_keystroke("up");
18515    cx.assert_excerpts_with_selections(indoc! {"
18516        [EXCERPT]
18517        [FOLDED]
18518        [EXCERPT]
18519        ˇa1
18520        b1
18521        [EXCERPT]
18522        [FOLDED]
18523        [EXCERPT]
18524        [FOLDED]
18525        "
18526    });
18527    for _ in 0..5 {
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    }
18542}
18543
18544#[gpui::test]
18545async fn test_inline_completion_text(cx: &mut TestAppContext) {
18546    init_test(cx, |_| {});
18547
18548    // Simple insertion
18549    assert_highlighted_edits(
18550        "Hello, world!",
18551        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18552        true,
18553        cx,
18554        |highlighted_edits, cx| {
18555            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18556            assert_eq!(highlighted_edits.highlights.len(), 1);
18557            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18558            assert_eq!(
18559                highlighted_edits.highlights[0].1.background_color,
18560                Some(cx.theme().status().created_background)
18561            );
18562        },
18563    )
18564    .await;
18565
18566    // Replacement
18567    assert_highlighted_edits(
18568        "This is a test.",
18569        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18570        false,
18571        cx,
18572        |highlighted_edits, cx| {
18573            assert_eq!(highlighted_edits.text, "That is a test.");
18574            assert_eq!(highlighted_edits.highlights.len(), 1);
18575            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18576            assert_eq!(
18577                highlighted_edits.highlights[0].1.background_color,
18578                Some(cx.theme().status().created_background)
18579            );
18580        },
18581    )
18582    .await;
18583
18584    // Multiple edits
18585    assert_highlighted_edits(
18586        "Hello, world!",
18587        vec![
18588            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18589            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18590        ],
18591        false,
18592        cx,
18593        |highlighted_edits, cx| {
18594            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18595            assert_eq!(highlighted_edits.highlights.len(), 2);
18596            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18597            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18598            assert_eq!(
18599                highlighted_edits.highlights[0].1.background_color,
18600                Some(cx.theme().status().created_background)
18601            );
18602            assert_eq!(
18603                highlighted_edits.highlights[1].1.background_color,
18604                Some(cx.theme().status().created_background)
18605            );
18606        },
18607    )
18608    .await;
18609
18610    // Multiple lines with edits
18611    assert_highlighted_edits(
18612        "First line\nSecond line\nThird line\nFourth line",
18613        vec![
18614            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18615            (
18616                Point::new(2, 0)..Point::new(2, 10),
18617                "New third line".to_string(),
18618            ),
18619            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18620        ],
18621        false,
18622        cx,
18623        |highlighted_edits, cx| {
18624            assert_eq!(
18625                highlighted_edits.text,
18626                "Second modified\nNew third line\nFourth updated line"
18627            );
18628            assert_eq!(highlighted_edits.highlights.len(), 3);
18629            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18630            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18631            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18632            for highlight in &highlighted_edits.highlights {
18633                assert_eq!(
18634                    highlight.1.background_color,
18635                    Some(cx.theme().status().created_background)
18636                );
18637            }
18638        },
18639    )
18640    .await;
18641}
18642
18643#[gpui::test]
18644async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18645    init_test(cx, |_| {});
18646
18647    // Deletion
18648    assert_highlighted_edits(
18649        "Hello, world!",
18650        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18651        true,
18652        cx,
18653        |highlighted_edits, cx| {
18654            assert_eq!(highlighted_edits.text, "Hello, world!");
18655            assert_eq!(highlighted_edits.highlights.len(), 1);
18656            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18657            assert_eq!(
18658                highlighted_edits.highlights[0].1.background_color,
18659                Some(cx.theme().status().deleted_background)
18660            );
18661        },
18662    )
18663    .await;
18664
18665    // Insertion
18666    assert_highlighted_edits(
18667        "Hello, world!",
18668        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18669        true,
18670        cx,
18671        |highlighted_edits, cx| {
18672            assert_eq!(highlighted_edits.highlights.len(), 1);
18673            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18674            assert_eq!(
18675                highlighted_edits.highlights[0].1.background_color,
18676                Some(cx.theme().status().created_background)
18677            );
18678        },
18679    )
18680    .await;
18681}
18682
18683async fn assert_highlighted_edits(
18684    text: &str,
18685    edits: Vec<(Range<Point>, String)>,
18686    include_deletions: bool,
18687    cx: &mut TestAppContext,
18688    assertion_fn: impl Fn(HighlightedText, &App),
18689) {
18690    let window = cx.add_window(|window, cx| {
18691        let buffer = MultiBuffer::build_simple(text, cx);
18692        Editor::new(EditorMode::full(), buffer, None, window, cx)
18693    });
18694    let cx = &mut VisualTestContext::from_window(*window, cx);
18695
18696    let (buffer, snapshot) = window
18697        .update(cx, |editor, _window, cx| {
18698            (
18699                editor.buffer().clone(),
18700                editor.buffer().read(cx).snapshot(cx),
18701            )
18702        })
18703        .unwrap();
18704
18705    let edits = edits
18706        .into_iter()
18707        .map(|(range, edit)| {
18708            (
18709                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18710                edit,
18711            )
18712        })
18713        .collect::<Vec<_>>();
18714
18715    let text_anchor_edits = edits
18716        .clone()
18717        .into_iter()
18718        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18719        .collect::<Vec<_>>();
18720
18721    let edit_preview = window
18722        .update(cx, |_, _window, cx| {
18723            buffer
18724                .read(cx)
18725                .as_singleton()
18726                .unwrap()
18727                .read(cx)
18728                .preview_edits(text_anchor_edits.into(), cx)
18729        })
18730        .unwrap()
18731        .await;
18732
18733    cx.update(|_window, cx| {
18734        let highlighted_edits = inline_completion_edit_text(
18735            &snapshot.as_singleton().unwrap().2,
18736            &edits,
18737            &edit_preview,
18738            include_deletions,
18739            cx,
18740        );
18741        assertion_fn(highlighted_edits, cx)
18742    });
18743}
18744
18745#[track_caller]
18746fn assert_breakpoint(
18747    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18748    path: &Arc<Path>,
18749    expected: Vec<(u32, Breakpoint)>,
18750) {
18751    if expected.len() == 0usize {
18752        assert!(!breakpoints.contains_key(path), "{}", path.display());
18753    } else {
18754        let mut breakpoint = breakpoints
18755            .get(path)
18756            .unwrap()
18757            .into_iter()
18758            .map(|breakpoint| {
18759                (
18760                    breakpoint.row,
18761                    Breakpoint {
18762                        message: breakpoint.message.clone(),
18763                        state: breakpoint.state,
18764                        condition: breakpoint.condition.clone(),
18765                        hit_condition: breakpoint.hit_condition.clone(),
18766                    },
18767                )
18768            })
18769            .collect::<Vec<_>>();
18770
18771        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18772
18773        assert_eq!(expected, breakpoint);
18774    }
18775}
18776
18777fn add_log_breakpoint_at_cursor(
18778    editor: &mut Editor,
18779    log_message: &str,
18780    window: &mut Window,
18781    cx: &mut Context<Editor>,
18782) {
18783    let (anchor, bp) = editor
18784        .breakpoints_at_cursors(window, cx)
18785        .first()
18786        .and_then(|(anchor, bp)| {
18787            if let Some(bp) = bp {
18788                Some((*anchor, bp.clone()))
18789            } else {
18790                None
18791            }
18792        })
18793        .unwrap_or_else(|| {
18794            let cursor_position: Point = editor.selections.newest(cx).head();
18795
18796            let breakpoint_position = editor
18797                .snapshot(window, cx)
18798                .display_snapshot
18799                .buffer_snapshot
18800                .anchor_before(Point::new(cursor_position.row, 0));
18801
18802            (breakpoint_position, Breakpoint::new_log(&log_message))
18803        });
18804
18805    editor.edit_breakpoint_at_anchor(
18806        anchor,
18807        bp,
18808        BreakpointEditAction::EditLogMessage(log_message.into()),
18809        cx,
18810    );
18811}
18812
18813#[gpui::test]
18814async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18815    init_test(cx, |_| {});
18816
18817    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18818    let fs = FakeFs::new(cx.executor());
18819    fs.insert_tree(
18820        path!("/a"),
18821        json!({
18822            "main.rs": sample_text,
18823        }),
18824    )
18825    .await;
18826    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18827    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18828    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18829
18830    let fs = FakeFs::new(cx.executor());
18831    fs.insert_tree(
18832        path!("/a"),
18833        json!({
18834            "main.rs": sample_text,
18835        }),
18836    )
18837    .await;
18838    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18839    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18840    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18841    let worktree_id = workspace
18842        .update(cx, |workspace, _window, cx| {
18843            workspace.project().update(cx, |project, cx| {
18844                project.worktrees(cx).next().unwrap().read(cx).id()
18845            })
18846        })
18847        .unwrap();
18848
18849    let buffer = project
18850        .update(cx, |project, cx| {
18851            project.open_buffer((worktree_id, "main.rs"), cx)
18852        })
18853        .await
18854        .unwrap();
18855
18856    let (editor, cx) = cx.add_window_view(|window, cx| {
18857        Editor::new(
18858            EditorMode::full(),
18859            MultiBuffer::build_from_buffer(buffer, cx),
18860            Some(project.clone()),
18861            window,
18862            cx,
18863        )
18864    });
18865
18866    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18867    let abs_path = project.read_with(cx, |project, cx| {
18868        project
18869            .absolute_path(&project_path, cx)
18870            .map(|path_buf| Arc::from(path_buf.to_owned()))
18871            .unwrap()
18872    });
18873
18874    // assert we can add breakpoint on the first line
18875    editor.update_in(cx, |editor, window, cx| {
18876        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18877        editor.move_to_end(&MoveToEnd, window, cx);
18878        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18879    });
18880
18881    let breakpoints = editor.update(cx, |editor, cx| {
18882        editor
18883            .breakpoint_store()
18884            .as_ref()
18885            .unwrap()
18886            .read(cx)
18887            .all_source_breakpoints(cx)
18888            .clone()
18889    });
18890
18891    assert_eq!(1, breakpoints.len());
18892    assert_breakpoint(
18893        &breakpoints,
18894        &abs_path,
18895        vec![
18896            (0, Breakpoint::new_standard()),
18897            (3, Breakpoint::new_standard()),
18898        ],
18899    );
18900
18901    editor.update_in(cx, |editor, window, cx| {
18902        editor.move_to_beginning(&MoveToBeginning, window, cx);
18903        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18904    });
18905
18906    let breakpoints = editor.update(cx, |editor, cx| {
18907        editor
18908            .breakpoint_store()
18909            .as_ref()
18910            .unwrap()
18911            .read(cx)
18912            .all_source_breakpoints(cx)
18913            .clone()
18914    });
18915
18916    assert_eq!(1, breakpoints.len());
18917    assert_breakpoint(
18918        &breakpoints,
18919        &abs_path,
18920        vec![(3, Breakpoint::new_standard())],
18921    );
18922
18923    editor.update_in(cx, |editor, window, cx| {
18924        editor.move_to_end(&MoveToEnd, window, cx);
18925        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18926    });
18927
18928    let breakpoints = editor.update(cx, |editor, cx| {
18929        editor
18930            .breakpoint_store()
18931            .as_ref()
18932            .unwrap()
18933            .read(cx)
18934            .all_source_breakpoints(cx)
18935            .clone()
18936    });
18937
18938    assert_eq!(0, breakpoints.len());
18939    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18940}
18941
18942#[gpui::test]
18943async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18944    init_test(cx, |_| {});
18945
18946    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18947
18948    let fs = FakeFs::new(cx.executor());
18949    fs.insert_tree(
18950        path!("/a"),
18951        json!({
18952            "main.rs": sample_text,
18953        }),
18954    )
18955    .await;
18956    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18957    let (workspace, cx) =
18958        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18959
18960    let worktree_id = workspace.update(cx, |workspace, cx| {
18961        workspace.project().update(cx, |project, cx| {
18962            project.worktrees(cx).next().unwrap().read(cx).id()
18963        })
18964    });
18965
18966    let buffer = project
18967        .update(cx, |project, cx| {
18968            project.open_buffer((worktree_id, "main.rs"), cx)
18969        })
18970        .await
18971        .unwrap();
18972
18973    let (editor, cx) = cx.add_window_view(|window, cx| {
18974        Editor::new(
18975            EditorMode::full(),
18976            MultiBuffer::build_from_buffer(buffer, cx),
18977            Some(project.clone()),
18978            window,
18979            cx,
18980        )
18981    });
18982
18983    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18984    let abs_path = project.read_with(cx, |project, cx| {
18985        project
18986            .absolute_path(&project_path, cx)
18987            .map(|path_buf| Arc::from(path_buf.to_owned()))
18988            .unwrap()
18989    });
18990
18991    editor.update_in(cx, |editor, window, cx| {
18992        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18993    });
18994
18995    let breakpoints = editor.update(cx, |editor, cx| {
18996        editor
18997            .breakpoint_store()
18998            .as_ref()
18999            .unwrap()
19000            .read(cx)
19001            .all_source_breakpoints(cx)
19002            .clone()
19003    });
19004
19005    assert_breakpoint(
19006        &breakpoints,
19007        &abs_path,
19008        vec![(0, Breakpoint::new_log("hello world"))],
19009    );
19010
19011    // Removing a log message from a log breakpoint should remove it
19012    editor.update_in(cx, |editor, window, cx| {
19013        add_log_breakpoint_at_cursor(editor, "", window, cx);
19014    });
19015
19016    let breakpoints = editor.update(cx, |editor, cx| {
19017        editor
19018            .breakpoint_store()
19019            .as_ref()
19020            .unwrap()
19021            .read(cx)
19022            .all_source_breakpoints(cx)
19023            .clone()
19024    });
19025
19026    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19027
19028    editor.update_in(cx, |editor, window, cx| {
19029        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19030        editor.move_to_end(&MoveToEnd, window, cx);
19031        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19032        // Not adding a log message to a standard breakpoint shouldn't remove it
19033        add_log_breakpoint_at_cursor(editor, "", window, cx);
19034    });
19035
19036    let breakpoints = editor.update(cx, |editor, cx| {
19037        editor
19038            .breakpoint_store()
19039            .as_ref()
19040            .unwrap()
19041            .read(cx)
19042            .all_source_breakpoints(cx)
19043            .clone()
19044    });
19045
19046    assert_breakpoint(
19047        &breakpoints,
19048        &abs_path,
19049        vec![
19050            (0, Breakpoint::new_standard()),
19051            (3, Breakpoint::new_standard()),
19052        ],
19053    );
19054
19055    editor.update_in(cx, |editor, window, cx| {
19056        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19057    });
19058
19059    let breakpoints = editor.update(cx, |editor, cx| {
19060        editor
19061            .breakpoint_store()
19062            .as_ref()
19063            .unwrap()
19064            .read(cx)
19065            .all_source_breakpoints(cx)
19066            .clone()
19067    });
19068
19069    assert_breakpoint(
19070        &breakpoints,
19071        &abs_path,
19072        vec![
19073            (0, Breakpoint::new_standard()),
19074            (3, Breakpoint::new_log("hello world")),
19075        ],
19076    );
19077
19078    editor.update_in(cx, |editor, window, cx| {
19079        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19080    });
19081
19082    let breakpoints = editor.update(cx, |editor, cx| {
19083        editor
19084            .breakpoint_store()
19085            .as_ref()
19086            .unwrap()
19087            .read(cx)
19088            .all_source_breakpoints(cx)
19089            .clone()
19090    });
19091
19092    assert_breakpoint(
19093        &breakpoints,
19094        &abs_path,
19095        vec![
19096            (0, Breakpoint::new_standard()),
19097            (3, Breakpoint::new_log("hello Earth!!")),
19098        ],
19099    );
19100}
19101
19102/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19103/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19104/// or when breakpoints were placed out of order. This tests for a regression too
19105#[gpui::test]
19106async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19107    init_test(cx, |_| {});
19108
19109    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19110    let fs = FakeFs::new(cx.executor());
19111    fs.insert_tree(
19112        path!("/a"),
19113        json!({
19114            "main.rs": sample_text,
19115        }),
19116    )
19117    .await;
19118    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19119    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19120    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19121
19122    let fs = FakeFs::new(cx.executor());
19123    fs.insert_tree(
19124        path!("/a"),
19125        json!({
19126            "main.rs": sample_text,
19127        }),
19128    )
19129    .await;
19130    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19131    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19132    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19133    let worktree_id = workspace
19134        .update(cx, |workspace, _window, cx| {
19135            workspace.project().update(cx, |project, cx| {
19136                project.worktrees(cx).next().unwrap().read(cx).id()
19137            })
19138        })
19139        .unwrap();
19140
19141    let buffer = project
19142        .update(cx, |project, cx| {
19143            project.open_buffer((worktree_id, "main.rs"), cx)
19144        })
19145        .await
19146        .unwrap();
19147
19148    let (editor, cx) = cx.add_window_view(|window, cx| {
19149        Editor::new(
19150            EditorMode::full(),
19151            MultiBuffer::build_from_buffer(buffer, cx),
19152            Some(project.clone()),
19153            window,
19154            cx,
19155        )
19156    });
19157
19158    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19159    let abs_path = project.read_with(cx, |project, cx| {
19160        project
19161            .absolute_path(&project_path, cx)
19162            .map(|path_buf| Arc::from(path_buf.to_owned()))
19163            .unwrap()
19164    });
19165
19166    // assert we can add breakpoint on the first line
19167    editor.update_in(cx, |editor, window, cx| {
19168        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19169        editor.move_to_end(&MoveToEnd, window, cx);
19170        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19171        editor.move_up(&MoveUp, window, cx);
19172        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19173    });
19174
19175    let breakpoints = editor.update(cx, |editor, cx| {
19176        editor
19177            .breakpoint_store()
19178            .as_ref()
19179            .unwrap()
19180            .read(cx)
19181            .all_source_breakpoints(cx)
19182            .clone()
19183    });
19184
19185    assert_eq!(1, breakpoints.len());
19186    assert_breakpoint(
19187        &breakpoints,
19188        &abs_path,
19189        vec![
19190            (0, Breakpoint::new_standard()),
19191            (2, Breakpoint::new_standard()),
19192            (3, Breakpoint::new_standard()),
19193        ],
19194    );
19195
19196    editor.update_in(cx, |editor, window, cx| {
19197        editor.move_to_beginning(&MoveToBeginning, window, cx);
19198        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19199        editor.move_to_end(&MoveToEnd, window, cx);
19200        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19201        // Disabling a breakpoint that doesn't exist should do nothing
19202        editor.move_up(&MoveUp, window, cx);
19203        editor.move_up(&MoveUp, window, cx);
19204        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19205    });
19206
19207    let breakpoints = editor.update(cx, |editor, cx| {
19208        editor
19209            .breakpoint_store()
19210            .as_ref()
19211            .unwrap()
19212            .read(cx)
19213            .all_source_breakpoints(cx)
19214            .clone()
19215    });
19216
19217    let disable_breakpoint = {
19218        let mut bp = Breakpoint::new_standard();
19219        bp.state = BreakpointState::Disabled;
19220        bp
19221    };
19222
19223    assert_eq!(1, breakpoints.len());
19224    assert_breakpoint(
19225        &breakpoints,
19226        &abs_path,
19227        vec![
19228            (0, disable_breakpoint.clone()),
19229            (2, Breakpoint::new_standard()),
19230            (3, disable_breakpoint.clone()),
19231        ],
19232    );
19233
19234    editor.update_in(cx, |editor, window, cx| {
19235        editor.move_to_beginning(&MoveToBeginning, window, cx);
19236        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19237        editor.move_to_end(&MoveToEnd, window, cx);
19238        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19239        editor.move_up(&MoveUp, window, cx);
19240        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19241    });
19242
19243    let breakpoints = editor.update(cx, |editor, cx| {
19244        editor
19245            .breakpoint_store()
19246            .as_ref()
19247            .unwrap()
19248            .read(cx)
19249            .all_source_breakpoints(cx)
19250            .clone()
19251    });
19252
19253    assert_eq!(1, breakpoints.len());
19254    assert_breakpoint(
19255        &breakpoints,
19256        &abs_path,
19257        vec![
19258            (0, Breakpoint::new_standard()),
19259            (2, disable_breakpoint),
19260            (3, Breakpoint::new_standard()),
19261        ],
19262    );
19263}
19264
19265#[gpui::test]
19266async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19267    init_test(cx, |_| {});
19268    let capabilities = lsp::ServerCapabilities {
19269        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19270            prepare_provider: Some(true),
19271            work_done_progress_options: Default::default(),
19272        })),
19273        ..Default::default()
19274    };
19275    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19276
19277    cx.set_state(indoc! {"
19278        struct Fˇoo {}
19279    "});
19280
19281    cx.update_editor(|editor, _, cx| {
19282        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19283        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19284        editor.highlight_background::<DocumentHighlightRead>(
19285            &[highlight_range],
19286            |c| c.editor_document_highlight_read_background,
19287            cx,
19288        );
19289    });
19290
19291    let mut prepare_rename_handler = cx
19292        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19293            move |_, _, _| async move {
19294                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19295                    start: lsp::Position {
19296                        line: 0,
19297                        character: 7,
19298                    },
19299                    end: lsp::Position {
19300                        line: 0,
19301                        character: 10,
19302                    },
19303                })))
19304            },
19305        );
19306    let prepare_rename_task = cx
19307        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19308        .expect("Prepare rename was not started");
19309    prepare_rename_handler.next().await.unwrap();
19310    prepare_rename_task.await.expect("Prepare rename failed");
19311
19312    let mut rename_handler =
19313        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19314            let edit = lsp::TextEdit {
19315                range: lsp::Range {
19316                    start: lsp::Position {
19317                        line: 0,
19318                        character: 7,
19319                    },
19320                    end: lsp::Position {
19321                        line: 0,
19322                        character: 10,
19323                    },
19324                },
19325                new_text: "FooRenamed".to_string(),
19326            };
19327            Ok(Some(lsp::WorkspaceEdit::new(
19328                // Specify the same edit twice
19329                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19330            )))
19331        });
19332    let rename_task = cx
19333        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19334        .expect("Confirm rename was not started");
19335    rename_handler.next().await.unwrap();
19336    rename_task.await.expect("Confirm rename failed");
19337    cx.run_until_parked();
19338
19339    // Despite two edits, only one is actually applied as those are identical
19340    cx.assert_editor_state(indoc! {"
19341        struct FooRenamedˇ {}
19342    "});
19343}
19344
19345#[gpui::test]
19346async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19347    init_test(cx, |_| {});
19348    // These capabilities indicate that the server does not support prepare rename.
19349    let capabilities = lsp::ServerCapabilities {
19350        rename_provider: Some(lsp::OneOf::Left(true)),
19351        ..Default::default()
19352    };
19353    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19354
19355    cx.set_state(indoc! {"
19356        struct Fˇoo {}
19357    "});
19358
19359    cx.update_editor(|editor, _window, cx| {
19360        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19361        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19362        editor.highlight_background::<DocumentHighlightRead>(
19363            &[highlight_range],
19364            |c| c.editor_document_highlight_read_background,
19365            cx,
19366        );
19367    });
19368
19369    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19370        .expect("Prepare rename was not started")
19371        .await
19372        .expect("Prepare rename failed");
19373
19374    let mut rename_handler =
19375        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19376            let edit = lsp::TextEdit {
19377                range: lsp::Range {
19378                    start: lsp::Position {
19379                        line: 0,
19380                        character: 7,
19381                    },
19382                    end: lsp::Position {
19383                        line: 0,
19384                        character: 10,
19385                    },
19386                },
19387                new_text: "FooRenamed".to_string(),
19388            };
19389            Ok(Some(lsp::WorkspaceEdit::new(
19390                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19391            )))
19392        });
19393    let rename_task = cx
19394        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19395        .expect("Confirm rename was not started");
19396    rename_handler.next().await.unwrap();
19397    rename_task.await.expect("Confirm rename failed");
19398    cx.run_until_parked();
19399
19400    // Correct range is renamed, as `surrounding_word` is used to find it.
19401    cx.assert_editor_state(indoc! {"
19402        struct FooRenamedˇ {}
19403    "});
19404}
19405
19406#[gpui::test]
19407async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19408    init_test(cx, |_| {});
19409    let mut cx = EditorTestContext::new(cx).await;
19410
19411    let language = Arc::new(
19412        Language::new(
19413            LanguageConfig::default(),
19414            Some(tree_sitter_html::LANGUAGE.into()),
19415        )
19416        .with_brackets_query(
19417            r#"
19418            ("<" @open "/>" @close)
19419            ("</" @open ">" @close)
19420            ("<" @open ">" @close)
19421            ("\"" @open "\"" @close)
19422            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19423        "#,
19424        )
19425        .unwrap(),
19426    );
19427    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19428
19429    cx.set_state(indoc! {"
19430        <span>ˇ</span>
19431    "});
19432    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19433    cx.assert_editor_state(indoc! {"
19434        <span>
19435        ˇ
19436        </span>
19437    "});
19438
19439    cx.set_state(indoc! {"
19440        <span><span></span>ˇ</span>
19441    "});
19442    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19443    cx.assert_editor_state(indoc! {"
19444        <span><span></span>
19445        ˇ</span>
19446    "});
19447
19448    cx.set_state(indoc! {"
19449        <span>ˇ
19450        </span>
19451    "});
19452    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19453    cx.assert_editor_state(indoc! {"
19454        <span>
19455        ˇ
19456        </span>
19457    "});
19458}
19459
19460#[gpui::test(iterations = 10)]
19461async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19462    init_test(cx, |_| {});
19463
19464    let fs = FakeFs::new(cx.executor());
19465    fs.insert_tree(
19466        path!("/dir"),
19467        json!({
19468            "a.ts": "a",
19469        }),
19470    )
19471    .await;
19472
19473    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19474    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19475    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19476
19477    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19478    language_registry.add(Arc::new(Language::new(
19479        LanguageConfig {
19480            name: "TypeScript".into(),
19481            matcher: LanguageMatcher {
19482                path_suffixes: vec!["ts".to_string()],
19483                ..Default::default()
19484            },
19485            ..Default::default()
19486        },
19487        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19488    )));
19489    let mut fake_language_servers = language_registry.register_fake_lsp(
19490        "TypeScript",
19491        FakeLspAdapter {
19492            capabilities: lsp::ServerCapabilities {
19493                code_lens_provider: Some(lsp::CodeLensOptions {
19494                    resolve_provider: Some(true),
19495                }),
19496                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19497                    commands: vec!["_the/command".to_string()],
19498                    ..lsp::ExecuteCommandOptions::default()
19499                }),
19500                ..lsp::ServerCapabilities::default()
19501            },
19502            ..FakeLspAdapter::default()
19503        },
19504    );
19505
19506    let (buffer, _handle) = project
19507        .update(cx, |p, cx| {
19508            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19509        })
19510        .await
19511        .unwrap();
19512    cx.executor().run_until_parked();
19513
19514    let fake_server = fake_language_servers.next().await.unwrap();
19515
19516    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19517    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19518    drop(buffer_snapshot);
19519    let actions = cx
19520        .update_window(*workspace, |_, window, cx| {
19521            project.code_actions(&buffer, anchor..anchor, window, cx)
19522        })
19523        .unwrap();
19524
19525    fake_server
19526        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19527            Ok(Some(vec![
19528                lsp::CodeLens {
19529                    range: lsp::Range::default(),
19530                    command: Some(lsp::Command {
19531                        title: "Code lens command".to_owned(),
19532                        command: "_the/command".to_owned(),
19533                        arguments: None,
19534                    }),
19535                    data: None,
19536                },
19537                lsp::CodeLens {
19538                    range: lsp::Range::default(),
19539                    command: Some(lsp::Command {
19540                        title: "Command not in capabilities".to_owned(),
19541                        command: "not in capabilities".to_owned(),
19542                        arguments: None,
19543                    }),
19544                    data: None,
19545                },
19546                lsp::CodeLens {
19547                    range: lsp::Range {
19548                        start: lsp::Position {
19549                            line: 1,
19550                            character: 1,
19551                        },
19552                        end: lsp::Position {
19553                            line: 1,
19554                            character: 1,
19555                        },
19556                    },
19557                    command: Some(lsp::Command {
19558                        title: "Command not in range".to_owned(),
19559                        command: "_the/command".to_owned(),
19560                        arguments: None,
19561                    }),
19562                    data: None,
19563                },
19564            ]))
19565        })
19566        .next()
19567        .await;
19568
19569    let actions = actions.await.unwrap();
19570    assert_eq!(
19571        actions.len(),
19572        1,
19573        "Should have only one valid action for the 0..0 range"
19574    );
19575    let action = actions[0].clone();
19576    let apply = project.update(cx, |project, cx| {
19577        project.apply_code_action(buffer.clone(), action, true, cx)
19578    });
19579
19580    // Resolving the code action does not populate its edits. In absence of
19581    // edits, we must execute the given command.
19582    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19583        |mut lens, _| async move {
19584            let lens_command = lens.command.as_mut().expect("should have a command");
19585            assert_eq!(lens_command.title, "Code lens command");
19586            lens_command.arguments = Some(vec![json!("the-argument")]);
19587            Ok(lens)
19588        },
19589    );
19590
19591    // While executing the command, the language server sends the editor
19592    // a `workspaceEdit` request.
19593    fake_server
19594        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19595            let fake = fake_server.clone();
19596            move |params, _| {
19597                assert_eq!(params.command, "_the/command");
19598                let fake = fake.clone();
19599                async move {
19600                    fake.server
19601                        .request::<lsp::request::ApplyWorkspaceEdit>(
19602                            lsp::ApplyWorkspaceEditParams {
19603                                label: None,
19604                                edit: lsp::WorkspaceEdit {
19605                                    changes: Some(
19606                                        [(
19607                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19608                                            vec![lsp::TextEdit {
19609                                                range: lsp::Range::new(
19610                                                    lsp::Position::new(0, 0),
19611                                                    lsp::Position::new(0, 0),
19612                                                ),
19613                                                new_text: "X".into(),
19614                                            }],
19615                                        )]
19616                                        .into_iter()
19617                                        .collect(),
19618                                    ),
19619                                    ..Default::default()
19620                                },
19621                            },
19622                        )
19623                        .await
19624                        .into_response()
19625                        .unwrap();
19626                    Ok(Some(json!(null)))
19627                }
19628            }
19629        })
19630        .next()
19631        .await;
19632
19633    // Applying the code lens command returns a project transaction containing the edits
19634    // sent by the language server in its `workspaceEdit` request.
19635    let transaction = apply.await.unwrap();
19636    assert!(transaction.0.contains_key(&buffer));
19637    buffer.update(cx, |buffer, cx| {
19638        assert_eq!(buffer.text(), "Xa");
19639        buffer.undo(cx);
19640        assert_eq!(buffer.text(), "a");
19641    });
19642}
19643
19644#[gpui::test]
19645async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19646    init_test(cx, |_| {});
19647
19648    let fs = FakeFs::new(cx.executor());
19649    let main_text = r#"fn main() {
19650println!("1");
19651println!("2");
19652println!("3");
19653println!("4");
19654println!("5");
19655}"#;
19656    let lib_text = "mod foo {}";
19657    fs.insert_tree(
19658        path!("/a"),
19659        json!({
19660            "lib.rs": lib_text,
19661            "main.rs": main_text,
19662        }),
19663    )
19664    .await;
19665
19666    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19667    let (workspace, cx) =
19668        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19669    let worktree_id = workspace.update(cx, |workspace, cx| {
19670        workspace.project().update(cx, |project, cx| {
19671            project.worktrees(cx).next().unwrap().read(cx).id()
19672        })
19673    });
19674
19675    let expected_ranges = vec![
19676        Point::new(0, 0)..Point::new(0, 0),
19677        Point::new(1, 0)..Point::new(1, 1),
19678        Point::new(2, 0)..Point::new(2, 2),
19679        Point::new(3, 0)..Point::new(3, 3),
19680    ];
19681
19682    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19683    let editor_1 = workspace
19684        .update_in(cx, |workspace, window, cx| {
19685            workspace.open_path(
19686                (worktree_id, "main.rs"),
19687                Some(pane_1.downgrade()),
19688                true,
19689                window,
19690                cx,
19691            )
19692        })
19693        .unwrap()
19694        .await
19695        .downcast::<Editor>()
19696        .unwrap();
19697    pane_1.update(cx, |pane, cx| {
19698        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19699        open_editor.update(cx, |editor, cx| {
19700            assert_eq!(
19701                editor.display_text(cx),
19702                main_text,
19703                "Original main.rs text on initial open",
19704            );
19705            assert_eq!(
19706                editor
19707                    .selections
19708                    .all::<Point>(cx)
19709                    .into_iter()
19710                    .map(|s| s.range())
19711                    .collect::<Vec<_>>(),
19712                vec![Point::zero()..Point::zero()],
19713                "Default selections on initial open",
19714            );
19715        })
19716    });
19717    editor_1.update_in(cx, |editor, window, cx| {
19718        editor.change_selections(None, window, cx, |s| {
19719            s.select_ranges(expected_ranges.clone());
19720        });
19721    });
19722
19723    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19724        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19725    });
19726    let editor_2 = workspace
19727        .update_in(cx, |workspace, window, cx| {
19728            workspace.open_path(
19729                (worktree_id, "main.rs"),
19730                Some(pane_2.downgrade()),
19731                true,
19732                window,
19733                cx,
19734            )
19735        })
19736        .unwrap()
19737        .await
19738        .downcast::<Editor>()
19739        .unwrap();
19740    pane_2.update(cx, |pane, cx| {
19741        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19742        open_editor.update(cx, |editor, cx| {
19743            assert_eq!(
19744                editor.display_text(cx),
19745                main_text,
19746                "Original main.rs text on initial open in another panel",
19747            );
19748            assert_eq!(
19749                editor
19750                    .selections
19751                    .all::<Point>(cx)
19752                    .into_iter()
19753                    .map(|s| s.range())
19754                    .collect::<Vec<_>>(),
19755                vec![Point::zero()..Point::zero()],
19756                "Default selections on initial open in another panel",
19757            );
19758        })
19759    });
19760
19761    editor_2.update_in(cx, |editor, window, cx| {
19762        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19763    });
19764
19765    let _other_editor_1 = workspace
19766        .update_in(cx, |workspace, window, cx| {
19767            workspace.open_path(
19768                (worktree_id, "lib.rs"),
19769                Some(pane_1.downgrade()),
19770                true,
19771                window,
19772                cx,
19773            )
19774        })
19775        .unwrap()
19776        .await
19777        .downcast::<Editor>()
19778        .unwrap();
19779    pane_1
19780        .update_in(cx, |pane, window, cx| {
19781            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19782                .unwrap()
19783        })
19784        .await
19785        .unwrap();
19786    drop(editor_1);
19787    pane_1.update(cx, |pane, cx| {
19788        pane.active_item()
19789            .unwrap()
19790            .downcast::<Editor>()
19791            .unwrap()
19792            .update(cx, |editor, cx| {
19793                assert_eq!(
19794                    editor.display_text(cx),
19795                    lib_text,
19796                    "Other file should be open and active",
19797                );
19798            });
19799        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19800    });
19801
19802    let _other_editor_2 = workspace
19803        .update_in(cx, |workspace, window, cx| {
19804            workspace.open_path(
19805                (worktree_id, "lib.rs"),
19806                Some(pane_2.downgrade()),
19807                true,
19808                window,
19809                cx,
19810            )
19811        })
19812        .unwrap()
19813        .await
19814        .downcast::<Editor>()
19815        .unwrap();
19816    pane_2
19817        .update_in(cx, |pane, window, cx| {
19818            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19819                .unwrap()
19820        })
19821        .await
19822        .unwrap();
19823    drop(editor_2);
19824    pane_2.update(cx, |pane, cx| {
19825        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19826        open_editor.update(cx, |editor, cx| {
19827            assert_eq!(
19828                editor.display_text(cx),
19829                lib_text,
19830                "Other file should be open and active in another panel too",
19831            );
19832        });
19833        assert_eq!(
19834            pane.items().count(),
19835            1,
19836            "No other editors should be open in another pane",
19837        );
19838    });
19839
19840    let _editor_1_reopened = workspace
19841        .update_in(cx, |workspace, window, cx| {
19842            workspace.open_path(
19843                (worktree_id, "main.rs"),
19844                Some(pane_1.downgrade()),
19845                true,
19846                window,
19847                cx,
19848            )
19849        })
19850        .unwrap()
19851        .await
19852        .downcast::<Editor>()
19853        .unwrap();
19854    let _editor_2_reopened = workspace
19855        .update_in(cx, |workspace, window, cx| {
19856            workspace.open_path(
19857                (worktree_id, "main.rs"),
19858                Some(pane_2.downgrade()),
19859                true,
19860                window,
19861                cx,
19862            )
19863        })
19864        .unwrap()
19865        .await
19866        .downcast::<Editor>()
19867        .unwrap();
19868    pane_1.update(cx, |pane, cx| {
19869        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19870        open_editor.update(cx, |editor, cx| {
19871            assert_eq!(
19872                editor.display_text(cx),
19873                main_text,
19874                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19875            );
19876            assert_eq!(
19877                editor
19878                    .selections
19879                    .all::<Point>(cx)
19880                    .into_iter()
19881                    .map(|s| s.range())
19882                    .collect::<Vec<_>>(),
19883                expected_ranges,
19884                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19885            );
19886        })
19887    });
19888    pane_2.update(cx, |pane, cx| {
19889        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19890        open_editor.update(cx, |editor, cx| {
19891            assert_eq!(
19892                editor.display_text(cx),
19893                r#"fn main() {
19894⋯rintln!("1");
19895⋯intln!("2");
19896⋯ntln!("3");
19897println!("4");
19898println!("5");
19899}"#,
19900                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19901            );
19902            assert_eq!(
19903                editor
19904                    .selections
19905                    .all::<Point>(cx)
19906                    .into_iter()
19907                    .map(|s| s.range())
19908                    .collect::<Vec<_>>(),
19909                vec![Point::zero()..Point::zero()],
19910                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19911            );
19912        })
19913    });
19914}
19915
19916#[gpui::test]
19917async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19918    init_test(cx, |_| {});
19919
19920    let fs = FakeFs::new(cx.executor());
19921    let main_text = r#"fn main() {
19922println!("1");
19923println!("2");
19924println!("3");
19925println!("4");
19926println!("5");
19927}"#;
19928    let lib_text = "mod foo {}";
19929    fs.insert_tree(
19930        path!("/a"),
19931        json!({
19932            "lib.rs": lib_text,
19933            "main.rs": main_text,
19934        }),
19935    )
19936    .await;
19937
19938    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19939    let (workspace, cx) =
19940        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19941    let worktree_id = workspace.update(cx, |workspace, cx| {
19942        workspace.project().update(cx, |project, cx| {
19943            project.worktrees(cx).next().unwrap().read(cx).id()
19944        })
19945    });
19946
19947    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19948    let editor = workspace
19949        .update_in(cx, |workspace, window, cx| {
19950            workspace.open_path(
19951                (worktree_id, "main.rs"),
19952                Some(pane.downgrade()),
19953                true,
19954                window,
19955                cx,
19956            )
19957        })
19958        .unwrap()
19959        .await
19960        .downcast::<Editor>()
19961        .unwrap();
19962    pane.update(cx, |pane, cx| {
19963        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19964        open_editor.update(cx, |editor, cx| {
19965            assert_eq!(
19966                editor.display_text(cx),
19967                main_text,
19968                "Original main.rs text on initial open",
19969            );
19970        })
19971    });
19972    editor.update_in(cx, |editor, window, cx| {
19973        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19974    });
19975
19976    cx.update_global(|store: &mut SettingsStore, cx| {
19977        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19978            s.restore_on_file_reopen = Some(false);
19979        });
19980    });
19981    editor.update_in(cx, |editor, window, cx| {
19982        editor.fold_ranges(
19983            vec![
19984                Point::new(1, 0)..Point::new(1, 1),
19985                Point::new(2, 0)..Point::new(2, 2),
19986                Point::new(3, 0)..Point::new(3, 3),
19987            ],
19988            false,
19989            window,
19990            cx,
19991        );
19992    });
19993    pane.update_in(cx, |pane, window, cx| {
19994        pane.close_all_items(&CloseAllItems::default(), window, cx)
19995            .unwrap()
19996    })
19997    .await
19998    .unwrap();
19999    pane.update(cx, |pane, _| {
20000        assert!(pane.active_item().is_none());
20001    });
20002    cx.update_global(|store: &mut SettingsStore, cx| {
20003        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
20004            s.restore_on_file_reopen = Some(true);
20005        });
20006    });
20007
20008    let _editor_reopened = workspace
20009        .update_in(cx, |workspace, window, cx| {
20010            workspace.open_path(
20011                (worktree_id, "main.rs"),
20012                Some(pane.downgrade()),
20013                true,
20014                window,
20015                cx,
20016            )
20017        })
20018        .unwrap()
20019        .await
20020        .downcast::<Editor>()
20021        .unwrap();
20022    pane.update(cx, |pane, cx| {
20023        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20024        open_editor.update(cx, |editor, cx| {
20025            assert_eq!(
20026                editor.display_text(cx),
20027                main_text,
20028                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20029            );
20030        })
20031    });
20032}
20033
20034#[gpui::test]
20035async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20036    struct EmptyModalView {
20037        focus_handle: gpui::FocusHandle,
20038    }
20039    impl EventEmitter<DismissEvent> for EmptyModalView {}
20040    impl Render for EmptyModalView {
20041        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20042            div()
20043        }
20044    }
20045    impl Focusable for EmptyModalView {
20046        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20047            self.focus_handle.clone()
20048        }
20049    }
20050    impl workspace::ModalView for EmptyModalView {}
20051    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20052        EmptyModalView {
20053            focus_handle: cx.focus_handle(),
20054        }
20055    }
20056
20057    init_test(cx, |_| {});
20058
20059    let fs = FakeFs::new(cx.executor());
20060    let project = Project::test(fs, [], cx).await;
20061    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20062    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20063    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20064    let editor = cx.new_window_entity(|window, cx| {
20065        Editor::new(
20066            EditorMode::full(),
20067            buffer,
20068            Some(project.clone()),
20069            window,
20070            cx,
20071        )
20072    });
20073    workspace
20074        .update(cx, |workspace, window, cx| {
20075            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20076        })
20077        .unwrap();
20078    editor.update_in(cx, |editor, window, cx| {
20079        editor.open_context_menu(&OpenContextMenu, window, cx);
20080        assert!(editor.mouse_context_menu.is_some());
20081    });
20082    workspace
20083        .update(cx, |workspace, window, cx| {
20084            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20085        })
20086        .unwrap();
20087    cx.read(|cx| {
20088        assert!(editor.read(cx).mouse_context_menu.is_none());
20089    });
20090}
20091
20092#[gpui::test]
20093async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20094    init_test(cx, |_| {});
20095
20096    let fs = FakeFs::new(cx.executor());
20097    fs.insert_file(path!("/file.html"), Default::default())
20098        .await;
20099
20100    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20101
20102    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20103    let html_language = Arc::new(Language::new(
20104        LanguageConfig {
20105            name: "HTML".into(),
20106            matcher: LanguageMatcher {
20107                path_suffixes: vec!["html".to_string()],
20108                ..LanguageMatcher::default()
20109            },
20110            brackets: BracketPairConfig {
20111                pairs: vec![BracketPair {
20112                    start: "<".into(),
20113                    end: ">".into(),
20114                    close: true,
20115                    ..Default::default()
20116                }],
20117                ..Default::default()
20118            },
20119            ..Default::default()
20120        },
20121        Some(tree_sitter_html::LANGUAGE.into()),
20122    ));
20123    language_registry.add(html_language);
20124    let mut fake_servers = language_registry.register_fake_lsp(
20125        "HTML",
20126        FakeLspAdapter {
20127            capabilities: lsp::ServerCapabilities {
20128                completion_provider: Some(lsp::CompletionOptions {
20129                    resolve_provider: Some(true),
20130                    ..Default::default()
20131                }),
20132                ..Default::default()
20133            },
20134            ..Default::default()
20135        },
20136    );
20137
20138    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20139    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20140
20141    let worktree_id = workspace
20142        .update(cx, |workspace, _window, cx| {
20143            workspace.project().update(cx, |project, cx| {
20144                project.worktrees(cx).next().unwrap().read(cx).id()
20145            })
20146        })
20147        .unwrap();
20148    project
20149        .update(cx, |project, cx| {
20150            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20151        })
20152        .await
20153        .unwrap();
20154    let editor = workspace
20155        .update(cx, |workspace, window, cx| {
20156            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20157        })
20158        .unwrap()
20159        .await
20160        .unwrap()
20161        .downcast::<Editor>()
20162        .unwrap();
20163
20164    let fake_server = fake_servers.next().await.unwrap();
20165    editor.update_in(cx, |editor, window, cx| {
20166        editor.set_text("<ad></ad>", window, cx);
20167        editor.change_selections(None, window, cx, |selections| {
20168            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20169        });
20170        let Some((buffer, _)) = editor
20171            .buffer
20172            .read(cx)
20173            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20174        else {
20175            panic!("Failed to get buffer for selection position");
20176        };
20177        let buffer = buffer.read(cx);
20178        let buffer_id = buffer.remote_id();
20179        let opening_range =
20180            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20181        let closing_range =
20182            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20183        let mut linked_ranges = HashMap::default();
20184        linked_ranges.insert(
20185            buffer_id,
20186            vec![(opening_range.clone(), vec![closing_range.clone()])],
20187        );
20188        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20189    });
20190    let mut completion_handle =
20191        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20192            Ok(Some(lsp::CompletionResponse::Array(vec![
20193                lsp::CompletionItem {
20194                    label: "head".to_string(),
20195                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20196                        lsp::InsertReplaceEdit {
20197                            new_text: "head".to_string(),
20198                            insert: lsp::Range::new(
20199                                lsp::Position::new(0, 1),
20200                                lsp::Position::new(0, 3),
20201                            ),
20202                            replace: lsp::Range::new(
20203                                lsp::Position::new(0, 1),
20204                                lsp::Position::new(0, 3),
20205                            ),
20206                        },
20207                    )),
20208                    ..Default::default()
20209                },
20210            ])))
20211        });
20212    editor.update_in(cx, |editor, window, cx| {
20213        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20214    });
20215    cx.run_until_parked();
20216    completion_handle.next().await.unwrap();
20217    editor.update(cx, |editor, _| {
20218        assert!(
20219            editor.context_menu_visible(),
20220            "Completion menu should be visible"
20221        );
20222    });
20223    editor.update_in(cx, |editor, window, cx| {
20224        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20225    });
20226    cx.executor().run_until_parked();
20227    editor.update(cx, |editor, cx| {
20228        assert_eq!(editor.text(cx), "<head></head>");
20229    });
20230}
20231
20232#[gpui::test]
20233async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20234    init_test(cx, |_| {});
20235
20236    let fs = FakeFs::new(cx.executor());
20237    fs.insert_tree(
20238        path!("/root"),
20239        json!({
20240            "a": {
20241                "main.rs": "fn main() {}",
20242            },
20243            "foo": {
20244                "bar": {
20245                    "external_file.rs": "pub mod external {}",
20246                }
20247            }
20248        }),
20249    )
20250    .await;
20251
20252    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20253    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20254    language_registry.add(rust_lang());
20255    let _fake_servers = language_registry.register_fake_lsp(
20256        "Rust",
20257        FakeLspAdapter {
20258            ..FakeLspAdapter::default()
20259        },
20260    );
20261    let (workspace, cx) =
20262        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20263    let worktree_id = workspace.update(cx, |workspace, cx| {
20264        workspace.project().update(cx, |project, cx| {
20265            project.worktrees(cx).next().unwrap().read(cx).id()
20266        })
20267    });
20268
20269    let assert_language_servers_count =
20270        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20271            project.update(cx, |project, cx| {
20272                let current = project
20273                    .lsp_store()
20274                    .read(cx)
20275                    .as_local()
20276                    .unwrap()
20277                    .language_servers
20278                    .len();
20279                assert_eq!(expected, current, "{context}");
20280            });
20281        };
20282
20283    assert_language_servers_count(
20284        0,
20285        "No servers should be running before any file is open",
20286        cx,
20287    );
20288    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20289    let main_editor = workspace
20290        .update_in(cx, |workspace, window, cx| {
20291            workspace.open_path(
20292                (worktree_id, "main.rs"),
20293                Some(pane.downgrade()),
20294                true,
20295                window,
20296                cx,
20297            )
20298        })
20299        .unwrap()
20300        .await
20301        .downcast::<Editor>()
20302        .unwrap();
20303    pane.update(cx, |pane, cx| {
20304        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20305        open_editor.update(cx, |editor, cx| {
20306            assert_eq!(
20307                editor.display_text(cx),
20308                "fn main() {}",
20309                "Original main.rs text on initial open",
20310            );
20311        });
20312        assert_eq!(open_editor, main_editor);
20313    });
20314    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20315
20316    let external_editor = workspace
20317        .update_in(cx, |workspace, window, cx| {
20318            workspace.open_abs_path(
20319                PathBuf::from("/root/foo/bar/external_file.rs"),
20320                OpenOptions::default(),
20321                window,
20322                cx,
20323            )
20324        })
20325        .await
20326        .expect("opening external file")
20327        .downcast::<Editor>()
20328        .expect("downcasted external file's open element to editor");
20329    pane.update(cx, |pane, cx| {
20330        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20331        open_editor.update(cx, |editor, cx| {
20332            assert_eq!(
20333                editor.display_text(cx),
20334                "pub mod external {}",
20335                "External file is open now",
20336            );
20337        });
20338        assert_eq!(open_editor, external_editor);
20339    });
20340    assert_language_servers_count(
20341        1,
20342        "Second, external, *.rs file should join the existing server",
20343        cx,
20344    );
20345
20346    pane.update_in(cx, |pane, window, cx| {
20347        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20348    })
20349    .unwrap()
20350    .await
20351    .unwrap();
20352    pane.update_in(cx, |pane, window, cx| {
20353        pane.navigate_backward(window, cx);
20354    });
20355    cx.run_until_parked();
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                "pub mod external {}",
20362                "External file is open now",
20363            );
20364        });
20365    });
20366    assert_language_servers_count(
20367        1,
20368        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20369        cx,
20370    );
20371
20372    cx.update(|_, cx| {
20373        workspace::reload(&workspace::Reload::default(), cx);
20374    });
20375    assert_language_servers_count(
20376        1,
20377        "After reloading the worktree with local and external files opened, only one project should be started",
20378        cx,
20379    );
20380}
20381
20382#[gpui::test]
20383async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20384    init_test(cx, |_| {});
20385
20386    let mut cx = EditorTestContext::new(cx).await;
20387    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20388    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20389
20390    // test cursor move to start of each line on tab
20391    // for `if`, `elif`, `else`, `while`, `with` and `for`
20392    cx.set_state(indoc! {"
20393        def main():
20394        ˇ    for item in items:
20395        ˇ        while item.active:
20396        ˇ            if item.value > 10:
20397        ˇ                continue
20398        ˇ            elif item.value < 0:
20399        ˇ                break
20400        ˇ            else:
20401        ˇ                with item.context() as ctx:
20402        ˇ                    yield count
20403        ˇ        else:
20404        ˇ            log('while else')
20405        ˇ    else:
20406        ˇ        log('for else')
20407    "});
20408    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20409    cx.assert_editor_state(indoc! {"
20410        def main():
20411            ˇfor item in items:
20412                ˇwhile item.active:
20413                    ˇif item.value > 10:
20414                        ˇcontinue
20415                    ˇelif item.value < 0:
20416                        ˇbreak
20417                    ˇelse:
20418                        ˇwith item.context() as ctx:
20419                            ˇyield count
20420                ˇelse:
20421                    ˇlog('while else')
20422            ˇelse:
20423                ˇlog('for else')
20424    "});
20425    // test relative indent is preserved when tab
20426    // for `if`, `elif`, `else`, `while`, `with` and `for`
20427    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20428    cx.assert_editor_state(indoc! {"
20429        def main():
20430                ˇfor item in items:
20431                    ˇwhile item.active:
20432                        ˇif item.value > 10:
20433                            ˇcontinue
20434                        ˇelif item.value < 0:
20435                            ˇbreak
20436                        ˇelse:
20437                            ˇwith item.context() as ctx:
20438                                ˇyield count
20439                    ˇelse:
20440                        ˇlog('while else')
20441                ˇelse:
20442                    ˇlog('for else')
20443    "});
20444
20445    // test cursor move to start of each line on tab
20446    // for `try`, `except`, `else`, `finally`, `match` and `def`
20447    cx.set_state(indoc! {"
20448        def main():
20449        ˇ    try:
20450        ˇ       fetch()
20451        ˇ    except ValueError:
20452        ˇ       handle_error()
20453        ˇ    else:
20454        ˇ        match value:
20455        ˇ            case _:
20456        ˇ    finally:
20457        ˇ        def status():
20458        ˇ            return 0
20459    "});
20460    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20461    cx.assert_editor_state(indoc! {"
20462        def main():
20463            ˇtry:
20464                ˇfetch()
20465            ˇexcept ValueError:
20466                ˇhandle_error()
20467            ˇelse:
20468                ˇmatch value:
20469                    ˇcase _:
20470            ˇfinally:
20471                ˇdef status():
20472                    ˇreturn 0
20473    "});
20474    // test relative indent is preserved when tab
20475    // for `try`, `except`, `else`, `finally`, `match` and `def`
20476    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20477    cx.assert_editor_state(indoc! {"
20478        def main():
20479                ˇtry:
20480                    ˇfetch()
20481                ˇexcept ValueError:
20482                    ˇhandle_error()
20483                ˇelse:
20484                    ˇmatch value:
20485                        ˇcase _:
20486                ˇfinally:
20487                    ˇdef status():
20488                        ˇreturn 0
20489    "});
20490}
20491
20492#[gpui::test]
20493async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20494    init_test(cx, |_| {});
20495
20496    let mut cx = EditorTestContext::new(cx).await;
20497    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20498    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20499
20500    // test `else` auto outdents when typed inside `if` block
20501    cx.set_state(indoc! {"
20502        def main():
20503            if i == 2:
20504                return
20505                ˇ
20506    "});
20507    cx.update_editor(|editor, window, cx| {
20508        editor.handle_input("else:", window, cx);
20509    });
20510    cx.assert_editor_state(indoc! {"
20511        def main():
20512            if i == 2:
20513                return
20514            else:ˇ
20515    "});
20516
20517    // test `except` auto outdents when typed inside `try` block
20518    cx.set_state(indoc! {"
20519        def main():
20520            try:
20521                i = 2
20522                ˇ
20523    "});
20524    cx.update_editor(|editor, window, cx| {
20525        editor.handle_input("except:", window, cx);
20526    });
20527    cx.assert_editor_state(indoc! {"
20528        def main():
20529            try:
20530                i = 2
20531            except:ˇ
20532    "});
20533
20534    // test `else` auto outdents when typed inside `except` block
20535    cx.set_state(indoc! {"
20536        def main():
20537            try:
20538                i = 2
20539            except:
20540                j = 2
20541                ˇ
20542    "});
20543    cx.update_editor(|editor, window, cx| {
20544        editor.handle_input("else:", window, cx);
20545    });
20546    cx.assert_editor_state(indoc! {"
20547        def main():
20548            try:
20549                i = 2
20550            except:
20551                j = 2
20552            else:ˇ
20553    "});
20554
20555    // test `finally` auto outdents when typed inside `else` block
20556    cx.set_state(indoc! {"
20557        def main():
20558            try:
20559                i = 2
20560            except:
20561                j = 2
20562            else:
20563                k = 2
20564                ˇ
20565    "});
20566    cx.update_editor(|editor, window, cx| {
20567        editor.handle_input("finally:", window, cx);
20568    });
20569    cx.assert_editor_state(indoc! {"
20570        def main():
20571            try:
20572                i = 2
20573            except:
20574                j = 2
20575            else:
20576                k = 2
20577            finally:ˇ
20578    "});
20579
20580    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20581    // cx.set_state(indoc! {"
20582    //     def main():
20583    //         try:
20584    //             for i in range(n):
20585    //                 pass
20586    //             ˇ
20587    // "});
20588    // cx.update_editor(|editor, window, cx| {
20589    //     editor.handle_input("except:", window, cx);
20590    // });
20591    // cx.assert_editor_state(indoc! {"
20592    //     def main():
20593    //         try:
20594    //             for i in range(n):
20595    //                 pass
20596    //         except:ˇ
20597    // "});
20598
20599    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20600    // cx.set_state(indoc! {"
20601    //     def main():
20602    //         try:
20603    //             i = 2
20604    //         except:
20605    //             for i in range(n):
20606    //                 pass
20607    //             ˇ
20608    // "});
20609    // cx.update_editor(|editor, window, cx| {
20610    //     editor.handle_input("else:", window, cx);
20611    // });
20612    // cx.assert_editor_state(indoc! {"
20613    //     def main():
20614    //         try:
20615    //             i = 2
20616    //         except:
20617    //             for i in range(n):
20618    //                 pass
20619    //         else:ˇ
20620    // "});
20621
20622    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20623    // cx.set_state(indoc! {"
20624    //     def main():
20625    //         try:
20626    //             i = 2
20627    //         except:
20628    //             j = 2
20629    //         else:
20630    //             for i in range(n):
20631    //                 pass
20632    //             ˇ
20633    // "});
20634    // cx.update_editor(|editor, window, cx| {
20635    //     editor.handle_input("finally:", window, cx);
20636    // });
20637    // cx.assert_editor_state(indoc! {"
20638    //     def main():
20639    //         try:
20640    //             i = 2
20641    //         except:
20642    //             j = 2
20643    //         else:
20644    //             for i in range(n):
20645    //                 pass
20646    //         finally:ˇ
20647    // "});
20648
20649    // test `else` stays at correct indent when typed after `for` block
20650    cx.set_state(indoc! {"
20651        def main():
20652            for i in range(10):
20653                if i == 3:
20654                    break
20655            ˇ
20656    "});
20657    cx.update_editor(|editor, window, cx| {
20658        editor.handle_input("else:", window, cx);
20659    });
20660    cx.assert_editor_state(indoc! {"
20661        def main():
20662            for i in range(10):
20663                if i == 3:
20664                    break
20665            else:ˇ
20666    "});
20667}
20668
20669#[gpui::test]
20670async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
20671    init_test(cx, |_| {});
20672    update_test_language_settings(cx, |settings| {
20673        settings.defaults.extend_comment_on_newline = Some(false);
20674    });
20675    let mut cx = EditorTestContext::new(cx).await;
20676    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20677    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20678
20679    // test correct indent after newline on comment
20680    cx.set_state(indoc! {"
20681        # COMMENT:ˇ
20682    "});
20683    cx.update_editor(|editor, window, cx| {
20684        editor.newline(&Newline, window, cx);
20685    });
20686    cx.assert_editor_state(indoc! {"
20687        # COMMENT:
20688        ˇ
20689    "});
20690
20691    // test correct indent after newline in curly brackets
20692    cx.set_state(indoc! {"
20693        {ˇ}
20694    "});
20695    cx.update_editor(|editor, window, cx| {
20696        editor.newline(&Newline, window, cx);
20697    });
20698    cx.run_until_parked();
20699    cx.assert_editor_state(indoc! {"
20700        {
20701            ˇ
20702        }
20703    "});
20704}
20705
20706fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20707    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20708    point..point
20709}
20710
20711fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20712    let (text, ranges) = marked_text_ranges(marked_text, true);
20713    assert_eq!(editor.text(cx), text);
20714    assert_eq!(
20715        editor.selections.ranges(cx),
20716        ranges,
20717        "Assert selections are {}",
20718        marked_text
20719    );
20720}
20721
20722pub fn handle_signature_help_request(
20723    cx: &mut EditorLspTestContext,
20724    mocked_response: lsp::SignatureHelp,
20725) -> impl Future<Output = ()> + use<> {
20726    let mut request =
20727        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20728            let mocked_response = mocked_response.clone();
20729            async move { Ok(Some(mocked_response)) }
20730        });
20731
20732    async move {
20733        request.next().await;
20734    }
20735}
20736
20737/// Handle completion request passing a marked string specifying where the completion
20738/// should be triggered from using '|' character, what range should be replaced, and what completions
20739/// should be returned using '<' and '>' to delimit the range.
20740///
20741/// Also see `handle_completion_request_with_insert_and_replace`.
20742#[track_caller]
20743pub fn handle_completion_request(
20744    cx: &mut EditorLspTestContext,
20745    marked_string: &str,
20746    completions: Vec<&'static str>,
20747    counter: Arc<AtomicUsize>,
20748) -> impl Future<Output = ()> {
20749    let complete_from_marker: TextRangeMarker = '|'.into();
20750    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20751    let (_, mut marked_ranges) = marked_text_ranges_by(
20752        marked_string,
20753        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20754    );
20755
20756    let complete_from_position =
20757        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20758    let replace_range =
20759        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20760
20761    let mut request =
20762        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20763            let completions = completions.clone();
20764            counter.fetch_add(1, atomic::Ordering::Release);
20765            async move {
20766                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20767                assert_eq!(
20768                    params.text_document_position.position,
20769                    complete_from_position
20770                );
20771                Ok(Some(lsp::CompletionResponse::Array(
20772                    completions
20773                        .iter()
20774                        .map(|completion_text| lsp::CompletionItem {
20775                            label: completion_text.to_string(),
20776                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20777                                range: replace_range,
20778                                new_text: completion_text.to_string(),
20779                            })),
20780                            ..Default::default()
20781                        })
20782                        .collect(),
20783                )))
20784            }
20785        });
20786
20787    async move {
20788        request.next().await;
20789    }
20790}
20791
20792/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20793/// given instead, which also contains an `insert` range.
20794///
20795/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20796/// that is, `replace_range.start..cursor_pos`.
20797pub fn handle_completion_request_with_insert_and_replace(
20798    cx: &mut EditorLspTestContext,
20799    marked_string: &str,
20800    completions: Vec<&'static str>,
20801    counter: Arc<AtomicUsize>,
20802) -> impl Future<Output = ()> {
20803    let complete_from_marker: TextRangeMarker = '|'.into();
20804    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20805    let (_, mut marked_ranges) = marked_text_ranges_by(
20806        marked_string,
20807        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20808    );
20809
20810    let complete_from_position =
20811        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20812    let replace_range =
20813        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20814
20815    let mut request =
20816        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20817            let completions = completions.clone();
20818            counter.fetch_add(1, atomic::Ordering::Release);
20819            async move {
20820                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20821                assert_eq!(
20822                    params.text_document_position.position, complete_from_position,
20823                    "marker `|` position doesn't match",
20824                );
20825                Ok(Some(lsp::CompletionResponse::Array(
20826                    completions
20827                        .iter()
20828                        .map(|completion_text| lsp::CompletionItem {
20829                            label: completion_text.to_string(),
20830                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20831                                lsp::InsertReplaceEdit {
20832                                    insert: lsp::Range {
20833                                        start: replace_range.start,
20834                                        end: complete_from_position,
20835                                    },
20836                                    replace: replace_range,
20837                                    new_text: completion_text.to_string(),
20838                                },
20839                            )),
20840                            ..Default::default()
20841                        })
20842                        .collect(),
20843                )))
20844            }
20845        });
20846
20847    async move {
20848        request.next().await;
20849    }
20850}
20851
20852fn handle_resolve_completion_request(
20853    cx: &mut EditorLspTestContext,
20854    edits: Option<Vec<(&'static str, &'static str)>>,
20855) -> impl Future<Output = ()> {
20856    let edits = edits.map(|edits| {
20857        edits
20858            .iter()
20859            .map(|(marked_string, new_text)| {
20860                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20861                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20862                lsp::TextEdit::new(replace_range, new_text.to_string())
20863            })
20864            .collect::<Vec<_>>()
20865    });
20866
20867    let mut request =
20868        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20869            let edits = edits.clone();
20870            async move {
20871                Ok(lsp::CompletionItem {
20872                    additional_text_edits: edits,
20873                    ..Default::default()
20874                })
20875            }
20876        });
20877
20878    async move {
20879        request.next().await;
20880    }
20881}
20882
20883pub(crate) fn update_test_language_settings(
20884    cx: &mut TestAppContext,
20885    f: impl Fn(&mut AllLanguageSettingsContent),
20886) {
20887    cx.update(|cx| {
20888        SettingsStore::update_global(cx, |store, cx| {
20889            store.update_user_settings::<AllLanguageSettings>(cx, f);
20890        });
20891    });
20892}
20893
20894pub(crate) fn update_test_project_settings(
20895    cx: &mut TestAppContext,
20896    f: impl Fn(&mut ProjectSettings),
20897) {
20898    cx.update(|cx| {
20899        SettingsStore::update_global(cx, |store, cx| {
20900            store.update_user_settings::<ProjectSettings>(cx, f);
20901        });
20902    });
20903}
20904
20905pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
20906    cx.update(|cx| {
20907        assets::Assets.load_test_fonts(cx);
20908        let store = SettingsStore::test(cx);
20909        cx.set_global(store);
20910        theme::init(theme::LoadThemes::JustBase, cx);
20911        release_channel::init(SemanticVersion::default(), cx);
20912        client::init_settings(cx);
20913        language::init(cx);
20914        Project::init_settings(cx);
20915        workspace::init_settings(cx);
20916        crate::init(cx);
20917    });
20918
20919    update_test_language_settings(cx, f);
20920}
20921
20922#[track_caller]
20923fn assert_hunk_revert(
20924    not_reverted_text_with_selections: &str,
20925    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
20926    expected_reverted_text_with_selections: &str,
20927    base_text: &str,
20928    cx: &mut EditorLspTestContext,
20929) {
20930    cx.set_state(not_reverted_text_with_selections);
20931    cx.set_head_text(base_text);
20932    cx.executor().run_until_parked();
20933
20934    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
20935        let snapshot = editor.snapshot(window, cx);
20936        let reverted_hunk_statuses = snapshot
20937            .buffer_snapshot
20938            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
20939            .map(|hunk| hunk.status().kind)
20940            .collect::<Vec<_>>();
20941
20942        editor.git_restore(&Default::default(), window, cx);
20943        reverted_hunk_statuses
20944    });
20945    cx.executor().run_until_parked();
20946    cx.assert_editor_state(expected_reverted_text_with_selections);
20947    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20948}