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           nlmˇo
 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           nlmˇo
 5984           "#
 5985    ));
 5986
 5987    // change selections
 5988    cx.set_state(indoc!(
 5989        r#"abc
 5990           def«ˇg»hi
 5991
 5992           jk
 5993           nlmo
 5994           "#
 5995    ));
 5996
 5997    cx.update_editor(|editor, window, cx| {
 5998        editor.add_selection_below(&Default::default(), window, cx);
 5999    });
 6000
 6001    cx.assert_editor_state(indoc!(
 6002        r#"abc
 6003           def«ˇg»hi
 6004
 6005           jk
 6006           nlm«ˇo»
 6007           "#
 6008    ));
 6009
 6010    cx.update_editor(|editor, window, cx| {
 6011        editor.add_selection_below(&Default::default(), window, cx);
 6012    });
 6013
 6014    cx.assert_editor_state(indoc!(
 6015        r#"abc
 6016           def«ˇg»hi
 6017
 6018           jk
 6019           nlm«ˇo»
 6020           "#
 6021    ));
 6022
 6023    cx.update_editor(|editor, window, cx| {
 6024        editor.add_selection_above(&Default::default(), window, cx);
 6025    });
 6026
 6027    cx.assert_editor_state(indoc!(
 6028        r#"abc
 6029           def«ˇg»hi
 6030
 6031           jk
 6032           nlmo
 6033           "#
 6034    ));
 6035
 6036    cx.update_editor(|editor, window, cx| {
 6037        editor.add_selection_above(&Default::default(), window, cx);
 6038    });
 6039
 6040    cx.assert_editor_state(indoc!(
 6041        r#"abc
 6042           def«ˇg»hi
 6043
 6044           jk
 6045           nlmo
 6046           "#
 6047    ));
 6048
 6049    // Change selections again
 6050    cx.set_state(indoc!(
 6051        r#"a«bc
 6052           defgˇ»hi
 6053
 6054           jk
 6055           nlmo
 6056           "#
 6057    ));
 6058
 6059    cx.update_editor(|editor, window, cx| {
 6060        editor.add_selection_below(&Default::default(), window, cx);
 6061    });
 6062
 6063    cx.assert_editor_state(indoc!(
 6064        r#"a«bcˇ»
 6065           d«efgˇ»hi
 6066
 6067           j«kˇ»
 6068           nlmo
 6069           "#
 6070    ));
 6071
 6072    cx.update_editor(|editor, window, cx| {
 6073        editor.add_selection_below(&Default::default(), window, cx);
 6074    });
 6075    cx.assert_editor_state(indoc!(
 6076        r#"a«bcˇ»
 6077           d«efgˇ»hi
 6078
 6079           j«kˇ»
 6080           n«lmoˇ»
 6081           "#
 6082    ));
 6083    cx.update_editor(|editor, window, cx| {
 6084        editor.add_selection_above(&Default::default(), window, cx);
 6085    });
 6086
 6087    cx.assert_editor_state(indoc!(
 6088        r#"a«bcˇ»
 6089           d«efgˇ»hi
 6090
 6091           j«kˇ»
 6092           nlmo
 6093           "#
 6094    ));
 6095
 6096    // Change selections again
 6097    cx.set_state(indoc!(
 6098        r#"abc
 6099           d«ˇefghi
 6100
 6101           jk
 6102           nlm»o
 6103           "#
 6104    ));
 6105
 6106    cx.update_editor(|editor, window, cx| {
 6107        editor.add_selection_above(&Default::default(), window, cx);
 6108    });
 6109
 6110    cx.assert_editor_state(indoc!(
 6111        r#"a«ˇbc»
 6112           d«ˇef»ghi
 6113
 6114           j«ˇk»
 6115           n«ˇlm»o
 6116           "#
 6117    ));
 6118
 6119    cx.update_editor(|editor, window, cx| {
 6120        editor.add_selection_below(&Default::default(), window, cx);
 6121    });
 6122
 6123    cx.assert_editor_state(indoc!(
 6124        r#"abc
 6125           d«ˇef»ghi
 6126
 6127           j«ˇk»
 6128           n«ˇlm»o
 6129           "#
 6130    ));
 6131}
 6132
 6133#[gpui::test]
 6134async fn test_select_next(cx: &mut TestAppContext) {
 6135    init_test(cx, |_| {});
 6136
 6137    let mut cx = EditorTestContext::new(cx).await;
 6138    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6139
 6140    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6141        .unwrap();
 6142    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6143
 6144    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6145        .unwrap();
 6146    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6147
 6148    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6149    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6150
 6151    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6152    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6153
 6154    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6155        .unwrap();
 6156    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6157
 6158    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6159        .unwrap();
 6160    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6161
 6162    // Test selection direction should be preserved
 6163    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6164
 6165    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6166        .unwrap();
 6167    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6168}
 6169
 6170#[gpui::test]
 6171async fn test_select_all_matches(cx: &mut TestAppContext) {
 6172    init_test(cx, |_| {});
 6173
 6174    let mut cx = EditorTestContext::new(cx).await;
 6175
 6176    // Test caret-only selections
 6177    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6178    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6179        .unwrap();
 6180    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6181
 6182    // Test left-to-right selections
 6183    cx.set_state("abc\n«abcˇ»\nabc");
 6184    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6185        .unwrap();
 6186    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6187
 6188    // Test right-to-left selections
 6189    cx.set_state("abc\n«ˇabc»\nabc");
 6190    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6191        .unwrap();
 6192    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6193
 6194    // Test selecting whitespace with caret selection
 6195    cx.set_state("abc\nˇ   abc\nabc");
 6196    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6197        .unwrap();
 6198    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6199
 6200    // Test selecting whitespace with left-to-right selection
 6201    cx.set_state("abc\n«ˇ  »abc\nabc");
 6202    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6203        .unwrap();
 6204    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6205
 6206    // Test no matches with right-to-left selection
 6207    cx.set_state("abc\n«  ˇ»abc\nabc");
 6208    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6209        .unwrap();
 6210    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6211}
 6212
 6213#[gpui::test]
 6214async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6215    init_test(cx, |_| {});
 6216
 6217    let mut cx = EditorTestContext::new(cx).await;
 6218
 6219    let large_body_1 = "\nd".repeat(200);
 6220    let large_body_2 = "\ne".repeat(200);
 6221
 6222    cx.set_state(&format!(
 6223        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6224    ));
 6225    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6226        let scroll_position = editor.scroll_position(cx);
 6227        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6228        scroll_position
 6229    });
 6230
 6231    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6232        .unwrap();
 6233    cx.assert_editor_state(&format!(
 6234        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6235    ));
 6236    let scroll_position_after_selection =
 6237        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6238    assert_eq!(
 6239        initial_scroll_position, scroll_position_after_selection,
 6240        "Scroll position should not change after selecting all matches"
 6241    );
 6242}
 6243
 6244#[gpui::test]
 6245async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6246    init_test(cx, |_| {});
 6247
 6248    let mut cx = EditorLspTestContext::new_rust(
 6249        lsp::ServerCapabilities {
 6250            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6251            ..Default::default()
 6252        },
 6253        cx,
 6254    )
 6255    .await;
 6256
 6257    cx.set_state(indoc! {"
 6258        line 1
 6259        line 2
 6260        linˇe 3
 6261        line 4
 6262        line 5
 6263    "});
 6264
 6265    // Make an edit
 6266    cx.update_editor(|editor, window, cx| {
 6267        editor.handle_input("X", window, cx);
 6268    });
 6269
 6270    // Move cursor to a different position
 6271    cx.update_editor(|editor, window, cx| {
 6272        editor.change_selections(None, window, cx, |s| {
 6273            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6274        });
 6275    });
 6276
 6277    cx.assert_editor_state(indoc! {"
 6278        line 1
 6279        line 2
 6280        linXe 3
 6281        line 4
 6282        liˇne 5
 6283    "});
 6284
 6285    cx.lsp
 6286        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6287            Ok(Some(vec![lsp::TextEdit::new(
 6288                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6289                "PREFIX ".to_string(),
 6290            )]))
 6291        });
 6292
 6293    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6294        .unwrap()
 6295        .await
 6296        .unwrap();
 6297
 6298    cx.assert_editor_state(indoc! {"
 6299        PREFIX line 1
 6300        line 2
 6301        linXe 3
 6302        line 4
 6303        liˇne 5
 6304    "});
 6305
 6306    // Undo formatting
 6307    cx.update_editor(|editor, window, cx| {
 6308        editor.undo(&Default::default(), window, cx);
 6309    });
 6310
 6311    // Verify cursor moved back to position after edit
 6312    cx.assert_editor_state(indoc! {"
 6313        line 1
 6314        line 2
 6315        linXˇe 3
 6316        line 4
 6317        line 5
 6318    "});
 6319}
 6320
 6321#[gpui::test]
 6322async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6323    init_test(cx, |_| {});
 6324
 6325    let mut cx = EditorTestContext::new(cx).await;
 6326    cx.set_state(
 6327        r#"let foo = 2;
 6328lˇet foo = 2;
 6329let fooˇ = 2;
 6330let foo = 2;
 6331let foo = ˇ2;"#,
 6332    );
 6333
 6334    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6335        .unwrap();
 6336    cx.assert_editor_state(
 6337        r#"let foo = 2;
 6338«letˇ» foo = 2;
 6339let «fooˇ» = 2;
 6340let foo = 2;
 6341let foo = «2ˇ»;"#,
 6342    );
 6343
 6344    // noop for multiple selections with different contents
 6345    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6346        .unwrap();
 6347    cx.assert_editor_state(
 6348        r#"let foo = 2;
 6349«letˇ» foo = 2;
 6350let «fooˇ» = 2;
 6351let foo = 2;
 6352let foo = «2ˇ»;"#,
 6353    );
 6354
 6355    // Test last selection direction should be preserved
 6356    cx.set_state(
 6357        r#"let foo = 2;
 6358let foo = 2;
 6359let «fooˇ» = 2;
 6360let «ˇfoo» = 2;
 6361let foo = 2;"#,
 6362    );
 6363
 6364    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6365        .unwrap();
 6366    cx.assert_editor_state(
 6367        r#"let foo = 2;
 6368let foo = 2;
 6369let «fooˇ» = 2;
 6370let «ˇfoo» = 2;
 6371let «ˇfoo» = 2;"#,
 6372    );
 6373}
 6374
 6375#[gpui::test]
 6376async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6377    init_test(cx, |_| {});
 6378
 6379    let mut cx =
 6380        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6381
 6382    cx.assert_editor_state(indoc! {"
 6383        ˇbbb
 6384        ccc
 6385
 6386        bbb
 6387        ccc
 6388        "});
 6389    cx.dispatch_action(SelectPrevious::default());
 6390    cx.assert_editor_state(indoc! {"
 6391                «bbbˇ»
 6392                ccc
 6393
 6394                bbb
 6395                ccc
 6396                "});
 6397    cx.dispatch_action(SelectPrevious::default());
 6398    cx.assert_editor_state(indoc! {"
 6399                «bbbˇ»
 6400                ccc
 6401
 6402                «bbbˇ»
 6403                ccc
 6404                "});
 6405}
 6406
 6407#[gpui::test]
 6408async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6409    init_test(cx, |_| {});
 6410
 6411    let mut cx = EditorTestContext::new(cx).await;
 6412    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6413
 6414    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6415        .unwrap();
 6416    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6417
 6418    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6419        .unwrap();
 6420    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6421
 6422    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6423    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6424
 6425    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6426    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6427
 6428    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6429        .unwrap();
 6430    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6431
 6432    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6433        .unwrap();
 6434    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6435}
 6436
 6437#[gpui::test]
 6438async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6439    init_test(cx, |_| {});
 6440
 6441    let mut cx = EditorTestContext::new(cx).await;
 6442    cx.set_state("");
 6443
 6444    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6445        .unwrap();
 6446    cx.assert_editor_state("«aˇ»");
 6447    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6448        .unwrap();
 6449    cx.assert_editor_state("«aˇ»");
 6450}
 6451
 6452#[gpui::test]
 6453async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6454    init_test(cx, |_| {});
 6455
 6456    let mut cx = EditorTestContext::new(cx).await;
 6457    cx.set_state(
 6458        r#"let foo = 2;
 6459lˇet foo = 2;
 6460let fooˇ = 2;
 6461let foo = 2;
 6462let foo = ˇ2;"#,
 6463    );
 6464
 6465    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6466        .unwrap();
 6467    cx.assert_editor_state(
 6468        r#"let foo = 2;
 6469«letˇ» foo = 2;
 6470let «fooˇ» = 2;
 6471let foo = 2;
 6472let foo = «2ˇ»;"#,
 6473    );
 6474
 6475    // noop for multiple selections with different contents
 6476    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6477        .unwrap();
 6478    cx.assert_editor_state(
 6479        r#"let foo = 2;
 6480«letˇ» foo = 2;
 6481let «fooˇ» = 2;
 6482let foo = 2;
 6483let foo = «2ˇ»;"#,
 6484    );
 6485}
 6486
 6487#[gpui::test]
 6488async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6489    init_test(cx, |_| {});
 6490
 6491    let mut cx = EditorTestContext::new(cx).await;
 6492    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6493
 6494    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6495        .unwrap();
 6496    // selection direction is preserved
 6497    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6498
 6499    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6500        .unwrap();
 6501    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6502
 6503    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6504    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6505
 6506    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6507    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6508
 6509    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6510        .unwrap();
 6511    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6512
 6513    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6514        .unwrap();
 6515    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6516}
 6517
 6518#[gpui::test]
 6519async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6520    init_test(cx, |_| {});
 6521
 6522    let language = Arc::new(Language::new(
 6523        LanguageConfig::default(),
 6524        Some(tree_sitter_rust::LANGUAGE.into()),
 6525    ));
 6526
 6527    let text = r#"
 6528        use mod1::mod2::{mod3, mod4};
 6529
 6530        fn fn_1(param1: bool, param2: &str) {
 6531            let var1 = "text";
 6532        }
 6533    "#
 6534    .unindent();
 6535
 6536    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6537    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6538    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6539
 6540    editor
 6541        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6542        .await;
 6543
 6544    editor.update_in(cx, |editor, window, cx| {
 6545        editor.change_selections(None, window, cx, |s| {
 6546            s.select_display_ranges([
 6547                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6548                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6549                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6550            ]);
 6551        });
 6552        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6553    });
 6554    editor.update(cx, |editor, cx| {
 6555        assert_text_with_selections(
 6556            editor,
 6557            indoc! {r#"
 6558                use mod1::mod2::{mod3, «mod4ˇ»};
 6559
 6560                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6561                    let var1 = "«ˇtext»";
 6562                }
 6563            "#},
 6564            cx,
 6565        );
 6566    });
 6567
 6568    editor.update_in(cx, |editor, window, cx| {
 6569        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6570    });
 6571    editor.update(cx, |editor, cx| {
 6572        assert_text_with_selections(
 6573            editor,
 6574            indoc! {r#"
 6575                use mod1::mod2::«{mod3, mod4}ˇ»;
 6576
 6577                «ˇfn fn_1(param1: bool, param2: &str) {
 6578                    let var1 = "text";
 6579 6580            "#},
 6581            cx,
 6582        );
 6583    });
 6584
 6585    editor.update_in(cx, |editor, window, cx| {
 6586        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6587    });
 6588    assert_eq!(
 6589        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6590        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6591    );
 6592
 6593    // Trying to expand the selected syntax node one more time has no effect.
 6594    editor.update_in(cx, |editor, window, cx| {
 6595        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6596    });
 6597    assert_eq!(
 6598        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6599        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6600    );
 6601
 6602    editor.update_in(cx, |editor, window, cx| {
 6603        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6604    });
 6605    editor.update(cx, |editor, cx| {
 6606        assert_text_with_selections(
 6607            editor,
 6608            indoc! {r#"
 6609                use mod1::mod2::«{mod3, mod4}ˇ»;
 6610
 6611                «ˇfn fn_1(param1: bool, param2: &str) {
 6612                    let var1 = "text";
 6613 6614            "#},
 6615            cx,
 6616        );
 6617    });
 6618
 6619    editor.update_in(cx, |editor, window, cx| {
 6620        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6621    });
 6622    editor.update(cx, |editor, cx| {
 6623        assert_text_with_selections(
 6624            editor,
 6625            indoc! {r#"
 6626                use mod1::mod2::{mod3, «mod4ˇ»};
 6627
 6628                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6629                    let var1 = "«ˇtext»";
 6630                }
 6631            "#},
 6632            cx,
 6633        );
 6634    });
 6635
 6636    editor.update_in(cx, |editor, window, cx| {
 6637        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6638    });
 6639    editor.update(cx, |editor, cx| {
 6640        assert_text_with_selections(
 6641            editor,
 6642            indoc! {r#"
 6643                use mod1::mod2::{mod3, mo«ˇ»d4};
 6644
 6645                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6646                    let var1 = "te«ˇ»xt";
 6647                }
 6648            "#},
 6649            cx,
 6650        );
 6651    });
 6652
 6653    // Trying to shrink the selected syntax node one more time has no effect.
 6654    editor.update_in(cx, |editor, window, cx| {
 6655        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6656    });
 6657    editor.update_in(cx, |editor, _, cx| {
 6658        assert_text_with_selections(
 6659            editor,
 6660            indoc! {r#"
 6661                use mod1::mod2::{mod3, mo«ˇ»d4};
 6662
 6663                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6664                    let var1 = "te«ˇ»xt";
 6665                }
 6666            "#},
 6667            cx,
 6668        );
 6669    });
 6670
 6671    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6672    // a fold.
 6673    editor.update_in(cx, |editor, window, cx| {
 6674        editor.fold_creases(
 6675            vec![
 6676                Crease::simple(
 6677                    Point::new(0, 21)..Point::new(0, 24),
 6678                    FoldPlaceholder::test(),
 6679                ),
 6680                Crease::simple(
 6681                    Point::new(3, 20)..Point::new(3, 22),
 6682                    FoldPlaceholder::test(),
 6683                ),
 6684            ],
 6685            true,
 6686            window,
 6687            cx,
 6688        );
 6689        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6690    });
 6691    editor.update(cx, |editor, cx| {
 6692        assert_text_with_selections(
 6693            editor,
 6694            indoc! {r#"
 6695                use mod1::mod2::«{mod3, mod4}ˇ»;
 6696
 6697                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6698                    let var1 = "«ˇtext»";
 6699                }
 6700            "#},
 6701            cx,
 6702        );
 6703    });
 6704}
 6705
 6706#[gpui::test]
 6707async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6708    init_test(cx, |_| {});
 6709
 6710    let language = Arc::new(Language::new(
 6711        LanguageConfig::default(),
 6712        Some(tree_sitter_rust::LANGUAGE.into()),
 6713    ));
 6714
 6715    let text = "let a = 2;";
 6716
 6717    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6718    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6719    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6720
 6721    editor
 6722        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6723        .await;
 6724
 6725    // Test case 1: Cursor at end of word
 6726    editor.update_in(cx, |editor, window, cx| {
 6727        editor.change_selections(None, window, cx, |s| {
 6728            s.select_display_ranges([
 6729                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6730            ]);
 6731        });
 6732    });
 6733    editor.update(cx, |editor, cx| {
 6734        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6735    });
 6736    editor.update_in(cx, |editor, window, cx| {
 6737        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6738    });
 6739    editor.update(cx, |editor, cx| {
 6740        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6741    });
 6742    editor.update_in(cx, |editor, window, cx| {
 6743        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6744    });
 6745    editor.update(cx, |editor, cx| {
 6746        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6747    });
 6748
 6749    // Test case 2: Cursor at end of statement
 6750    editor.update_in(cx, |editor, window, cx| {
 6751        editor.change_selections(None, window, cx, |s| {
 6752            s.select_display_ranges([
 6753                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6754            ]);
 6755        });
 6756    });
 6757    editor.update(cx, |editor, cx| {
 6758        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6759    });
 6760    editor.update_in(cx, |editor, window, cx| {
 6761        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6762    });
 6763    editor.update(cx, |editor, cx| {
 6764        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6765    });
 6766}
 6767
 6768#[gpui::test]
 6769async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6770    init_test(cx, |_| {});
 6771
 6772    let language = Arc::new(Language::new(
 6773        LanguageConfig::default(),
 6774        Some(tree_sitter_rust::LANGUAGE.into()),
 6775    ));
 6776
 6777    let text = r#"
 6778        use mod1::mod2::{mod3, mod4};
 6779
 6780        fn fn_1(param1: bool, param2: &str) {
 6781            let var1 = "hello world";
 6782        }
 6783    "#
 6784    .unindent();
 6785
 6786    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6787    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6788    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6789
 6790    editor
 6791        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6792        .await;
 6793
 6794    // Test 1: Cursor on a letter of a string word
 6795    editor.update_in(cx, |editor, window, cx| {
 6796        editor.change_selections(None, window, cx, |s| {
 6797            s.select_display_ranges([
 6798                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6799            ]);
 6800        });
 6801    });
 6802    editor.update_in(cx, |editor, window, cx| {
 6803        assert_text_with_selections(
 6804            editor,
 6805            indoc! {r#"
 6806                use mod1::mod2::{mod3, mod4};
 6807
 6808                fn fn_1(param1: bool, param2: &str) {
 6809                    let var1 = "hˇello world";
 6810                }
 6811            "#},
 6812            cx,
 6813        );
 6814        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6815        assert_text_with_selections(
 6816            editor,
 6817            indoc! {r#"
 6818                use mod1::mod2::{mod3, mod4};
 6819
 6820                fn fn_1(param1: bool, param2: &str) {
 6821                    let var1 = "«ˇhello» world";
 6822                }
 6823            "#},
 6824            cx,
 6825        );
 6826    });
 6827
 6828    // Test 2: Partial selection within a word
 6829    editor.update_in(cx, |editor, window, cx| {
 6830        editor.change_selections(None, window, cx, |s| {
 6831            s.select_display_ranges([
 6832                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6833            ]);
 6834        });
 6835    });
 6836    editor.update_in(cx, |editor, window, cx| {
 6837        assert_text_with_selections(
 6838            editor,
 6839            indoc! {r#"
 6840                use mod1::mod2::{mod3, mod4};
 6841
 6842                fn fn_1(param1: bool, param2: &str) {
 6843                    let var1 = "h«elˇ»lo world";
 6844                }
 6845            "#},
 6846            cx,
 6847        );
 6848        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6849        assert_text_with_selections(
 6850            editor,
 6851            indoc! {r#"
 6852                use mod1::mod2::{mod3, mod4};
 6853
 6854                fn fn_1(param1: bool, param2: &str) {
 6855                    let var1 = "«ˇhello» world";
 6856                }
 6857            "#},
 6858            cx,
 6859        );
 6860    });
 6861
 6862    // Test 3: Complete word already selected
 6863    editor.update_in(cx, |editor, window, cx| {
 6864        editor.change_selections(None, window, cx, |s| {
 6865            s.select_display_ranges([
 6866                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6867            ]);
 6868        });
 6869    });
 6870    editor.update_in(cx, |editor, window, cx| {
 6871        assert_text_with_selections(
 6872            editor,
 6873            indoc! {r#"
 6874                use mod1::mod2::{mod3, mod4};
 6875
 6876                fn fn_1(param1: bool, param2: &str) {
 6877                    let var1 = "«helloˇ» world";
 6878                }
 6879            "#},
 6880            cx,
 6881        );
 6882        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6883        assert_text_with_selections(
 6884            editor,
 6885            indoc! {r#"
 6886                use mod1::mod2::{mod3, mod4};
 6887
 6888                fn fn_1(param1: bool, param2: &str) {
 6889                    let var1 = "«hello worldˇ»";
 6890                }
 6891            "#},
 6892            cx,
 6893        );
 6894    });
 6895
 6896    // Test 4: Selection spanning across words
 6897    editor.update_in(cx, |editor, window, cx| {
 6898        editor.change_selections(None, window, cx, |s| {
 6899            s.select_display_ranges([
 6900                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6901            ]);
 6902        });
 6903    });
 6904    editor.update_in(cx, |editor, window, cx| {
 6905        assert_text_with_selections(
 6906            editor,
 6907            indoc! {r#"
 6908                use mod1::mod2::{mod3, mod4};
 6909
 6910                fn fn_1(param1: bool, param2: &str) {
 6911                    let var1 = "hel«lo woˇ»rld";
 6912                }
 6913            "#},
 6914            cx,
 6915        );
 6916        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6917        assert_text_with_selections(
 6918            editor,
 6919            indoc! {r#"
 6920                use mod1::mod2::{mod3, mod4};
 6921
 6922                fn fn_1(param1: bool, param2: &str) {
 6923                    let var1 = "«ˇhello world»";
 6924                }
 6925            "#},
 6926            cx,
 6927        );
 6928    });
 6929
 6930    // Test 5: Expansion beyond string
 6931    editor.update_in(cx, |editor, window, cx| {
 6932        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6933        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6934        assert_text_with_selections(
 6935            editor,
 6936            indoc! {r#"
 6937                use mod1::mod2::{mod3, mod4};
 6938
 6939                fn fn_1(param1: bool, param2: &str) {
 6940                    «ˇlet var1 = "hello world";»
 6941                }
 6942            "#},
 6943            cx,
 6944        );
 6945    });
 6946}
 6947
 6948#[gpui::test]
 6949async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6950    init_test(cx, |_| {});
 6951
 6952    let base_text = r#"
 6953        impl A {
 6954            // this is an uncommitted comment
 6955
 6956            fn b() {
 6957                c();
 6958            }
 6959
 6960            // this is another uncommitted comment
 6961
 6962            fn d() {
 6963                // e
 6964                // f
 6965            }
 6966        }
 6967
 6968        fn g() {
 6969            // h
 6970        }
 6971    "#
 6972    .unindent();
 6973
 6974    let text = r#"
 6975        ˇimpl A {
 6976
 6977            fn b() {
 6978                c();
 6979            }
 6980
 6981            fn d() {
 6982                // e
 6983                // f
 6984            }
 6985        }
 6986
 6987        fn g() {
 6988            // h
 6989        }
 6990    "#
 6991    .unindent();
 6992
 6993    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6994    cx.set_state(&text);
 6995    cx.set_head_text(&base_text);
 6996    cx.update_editor(|editor, window, cx| {
 6997        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6998    });
 6999
 7000    cx.assert_state_with_diff(
 7001        "
 7002        ˇimpl A {
 7003      -     // this is an uncommitted comment
 7004
 7005            fn b() {
 7006                c();
 7007            }
 7008
 7009      -     // this is another uncommitted comment
 7010      -
 7011            fn d() {
 7012                // e
 7013                // f
 7014            }
 7015        }
 7016
 7017        fn g() {
 7018            // h
 7019        }
 7020    "
 7021        .unindent(),
 7022    );
 7023
 7024    let expected_display_text = "
 7025        impl A {
 7026            // this is an uncommitted comment
 7027
 7028            fn b() {
 7029 7030            }
 7031
 7032            // this is another uncommitted comment
 7033
 7034            fn d() {
 7035 7036            }
 7037        }
 7038
 7039        fn g() {
 7040 7041        }
 7042        "
 7043    .unindent();
 7044
 7045    cx.update_editor(|editor, window, cx| {
 7046        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 7047        assert_eq!(editor.display_text(cx), expected_display_text);
 7048    });
 7049}
 7050
 7051#[gpui::test]
 7052async fn test_autoindent(cx: &mut TestAppContext) {
 7053    init_test(cx, |_| {});
 7054
 7055    let language = Arc::new(
 7056        Language::new(
 7057            LanguageConfig {
 7058                brackets: BracketPairConfig {
 7059                    pairs: vec![
 7060                        BracketPair {
 7061                            start: "{".to_string(),
 7062                            end: "}".to_string(),
 7063                            close: false,
 7064                            surround: false,
 7065                            newline: true,
 7066                        },
 7067                        BracketPair {
 7068                            start: "(".to_string(),
 7069                            end: ")".to_string(),
 7070                            close: false,
 7071                            surround: false,
 7072                            newline: true,
 7073                        },
 7074                    ],
 7075                    ..Default::default()
 7076                },
 7077                ..Default::default()
 7078            },
 7079            Some(tree_sitter_rust::LANGUAGE.into()),
 7080        )
 7081        .with_indents_query(
 7082            r#"
 7083                (_ "(" ")" @end) @indent
 7084                (_ "{" "}" @end) @indent
 7085            "#,
 7086        )
 7087        .unwrap(),
 7088    );
 7089
 7090    let text = "fn a() {}";
 7091
 7092    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7093    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7094    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7095    editor
 7096        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7097        .await;
 7098
 7099    editor.update_in(cx, |editor, window, cx| {
 7100        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7101        editor.newline(&Newline, window, cx);
 7102        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7103        assert_eq!(
 7104            editor.selections.ranges(cx),
 7105            &[
 7106                Point::new(1, 4)..Point::new(1, 4),
 7107                Point::new(3, 4)..Point::new(3, 4),
 7108                Point::new(5, 0)..Point::new(5, 0)
 7109            ]
 7110        );
 7111    });
 7112}
 7113
 7114#[gpui::test]
 7115async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7116    init_test(cx, |_| {});
 7117
 7118    {
 7119        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7120        cx.set_state(indoc! {"
 7121            impl A {
 7122
 7123                fn b() {}
 7124
 7125            «fn c() {
 7126
 7127            }ˇ»
 7128            }
 7129        "});
 7130
 7131        cx.update_editor(|editor, window, cx| {
 7132            editor.autoindent(&Default::default(), window, cx);
 7133        });
 7134
 7135        cx.assert_editor_state(indoc! {"
 7136            impl A {
 7137
 7138                fn b() {}
 7139
 7140                «fn c() {
 7141
 7142                }ˇ»
 7143            }
 7144        "});
 7145    }
 7146
 7147    {
 7148        let mut cx = EditorTestContext::new_multibuffer(
 7149            cx,
 7150            [indoc! { "
 7151                impl A {
 7152                «
 7153                // a
 7154                fn b(){}
 7155                »
 7156                «
 7157                    }
 7158                    fn c(){}
 7159                »
 7160            "}],
 7161        );
 7162
 7163        let buffer = cx.update_editor(|editor, _, cx| {
 7164            let buffer = editor.buffer().update(cx, |buffer, _| {
 7165                buffer.all_buffers().iter().next().unwrap().clone()
 7166            });
 7167            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7168            buffer
 7169        });
 7170
 7171        cx.run_until_parked();
 7172        cx.update_editor(|editor, window, cx| {
 7173            editor.select_all(&Default::default(), window, cx);
 7174            editor.autoindent(&Default::default(), window, cx)
 7175        });
 7176        cx.run_until_parked();
 7177
 7178        cx.update(|_, cx| {
 7179            assert_eq!(
 7180                buffer.read(cx).text(),
 7181                indoc! { "
 7182                    impl A {
 7183
 7184                        // a
 7185                        fn b(){}
 7186
 7187
 7188                    }
 7189                    fn c(){}
 7190
 7191                " }
 7192            )
 7193        });
 7194    }
 7195}
 7196
 7197#[gpui::test]
 7198async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7199    init_test(cx, |_| {});
 7200
 7201    let mut cx = EditorTestContext::new(cx).await;
 7202
 7203    let language = Arc::new(Language::new(
 7204        LanguageConfig {
 7205            brackets: BracketPairConfig {
 7206                pairs: vec![
 7207                    BracketPair {
 7208                        start: "{".to_string(),
 7209                        end: "}".to_string(),
 7210                        close: true,
 7211                        surround: true,
 7212                        newline: true,
 7213                    },
 7214                    BracketPair {
 7215                        start: "(".to_string(),
 7216                        end: ")".to_string(),
 7217                        close: true,
 7218                        surround: true,
 7219                        newline: true,
 7220                    },
 7221                    BracketPair {
 7222                        start: "/*".to_string(),
 7223                        end: " */".to_string(),
 7224                        close: true,
 7225                        surround: true,
 7226                        newline: true,
 7227                    },
 7228                    BracketPair {
 7229                        start: "[".to_string(),
 7230                        end: "]".to_string(),
 7231                        close: false,
 7232                        surround: false,
 7233                        newline: true,
 7234                    },
 7235                    BracketPair {
 7236                        start: "\"".to_string(),
 7237                        end: "\"".to_string(),
 7238                        close: true,
 7239                        surround: true,
 7240                        newline: false,
 7241                    },
 7242                    BracketPair {
 7243                        start: "<".to_string(),
 7244                        end: ">".to_string(),
 7245                        close: false,
 7246                        surround: true,
 7247                        newline: true,
 7248                    },
 7249                ],
 7250                ..Default::default()
 7251            },
 7252            autoclose_before: "})]".to_string(),
 7253            ..Default::default()
 7254        },
 7255        Some(tree_sitter_rust::LANGUAGE.into()),
 7256    ));
 7257
 7258    cx.language_registry().add(language.clone());
 7259    cx.update_buffer(|buffer, cx| {
 7260        buffer.set_language(Some(language), cx);
 7261    });
 7262
 7263    cx.set_state(
 7264        &r#"
 7265            🏀ˇ
 7266            εˇ
 7267            ❤️ˇ
 7268        "#
 7269        .unindent(),
 7270    );
 7271
 7272    // autoclose multiple nested brackets at multiple cursors
 7273    cx.update_editor(|editor, window, cx| {
 7274        editor.handle_input("{", window, cx);
 7275        editor.handle_input("{", window, cx);
 7276        editor.handle_input("{", window, cx);
 7277    });
 7278    cx.assert_editor_state(
 7279        &"
 7280            🏀{{{ˇ}}}
 7281            ε{{{ˇ}}}
 7282            ❤️{{{ˇ}}}
 7283        "
 7284        .unindent(),
 7285    );
 7286
 7287    // insert a different closing bracket
 7288    cx.update_editor(|editor, window, cx| {
 7289        editor.handle_input(")", window, cx);
 7290    });
 7291    cx.assert_editor_state(
 7292        &"
 7293            🏀{{{)ˇ}}}
 7294            ε{{{)ˇ}}}
 7295            ❤️{{{)ˇ}}}
 7296        "
 7297        .unindent(),
 7298    );
 7299
 7300    // skip over the auto-closed brackets when typing a closing bracket
 7301    cx.update_editor(|editor, window, cx| {
 7302        editor.move_right(&MoveRight, window, cx);
 7303        editor.handle_input("}", window, cx);
 7304        editor.handle_input("}", window, cx);
 7305        editor.handle_input("}", window, cx);
 7306    });
 7307    cx.assert_editor_state(
 7308        &"
 7309            🏀{{{)}}}}ˇ
 7310            ε{{{)}}}}ˇ
 7311            ❤️{{{)}}}}ˇ
 7312        "
 7313        .unindent(),
 7314    );
 7315
 7316    // autoclose multi-character pairs
 7317    cx.set_state(
 7318        &"
 7319            ˇ
 7320            ˇ
 7321        "
 7322        .unindent(),
 7323    );
 7324    cx.update_editor(|editor, window, cx| {
 7325        editor.handle_input("/", window, cx);
 7326        editor.handle_input("*", window, cx);
 7327    });
 7328    cx.assert_editor_state(
 7329        &"
 7330            /*ˇ */
 7331            /*ˇ */
 7332        "
 7333        .unindent(),
 7334    );
 7335
 7336    // one cursor autocloses a multi-character pair, one cursor
 7337    // does not autoclose.
 7338    cx.set_state(
 7339        &"
 7340 7341            ˇ
 7342        "
 7343        .unindent(),
 7344    );
 7345    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7346    cx.assert_editor_state(
 7347        &"
 7348            /*ˇ */
 7349 7350        "
 7351        .unindent(),
 7352    );
 7353
 7354    // Don't autoclose if the next character isn't whitespace and isn't
 7355    // listed in the language's "autoclose_before" section.
 7356    cx.set_state("ˇa b");
 7357    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7358    cx.assert_editor_state("{ˇa b");
 7359
 7360    // Don't autoclose if `close` is false for the bracket pair
 7361    cx.set_state("ˇ");
 7362    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7363    cx.assert_editor_state("");
 7364
 7365    // Surround with brackets if text is selected
 7366    cx.set_state("«aˇ» b");
 7367    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7368    cx.assert_editor_state("{«aˇ»} b");
 7369
 7370    // Autoclose when not immediately after a word character
 7371    cx.set_state("a ˇ");
 7372    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7373    cx.assert_editor_state("a \"ˇ\"");
 7374
 7375    // Autoclose pair where the start and end characters are the same
 7376    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7377    cx.assert_editor_state("a \"\"ˇ");
 7378
 7379    // Don't autoclose when immediately after a word character
 7380    cx.set_state("");
 7381    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7382    cx.assert_editor_state("a\"ˇ");
 7383
 7384    // Do autoclose when after a non-word character
 7385    cx.set_state("");
 7386    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7387    cx.assert_editor_state("{\"ˇ\"");
 7388
 7389    // Non identical pairs autoclose regardless of preceding character
 7390    cx.set_state("");
 7391    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7392    cx.assert_editor_state("a{ˇ}");
 7393
 7394    // Don't autoclose pair if autoclose is disabled
 7395    cx.set_state("ˇ");
 7396    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7397    cx.assert_editor_state("");
 7398
 7399    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7400    cx.set_state("«aˇ» b");
 7401    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7402    cx.assert_editor_state("<«aˇ»> b");
 7403}
 7404
 7405#[gpui::test]
 7406async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7407    init_test(cx, |settings| {
 7408        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7409    });
 7410
 7411    let mut cx = EditorTestContext::new(cx).await;
 7412
 7413    let language = Arc::new(Language::new(
 7414        LanguageConfig {
 7415            brackets: BracketPairConfig {
 7416                pairs: vec![
 7417                    BracketPair {
 7418                        start: "{".to_string(),
 7419                        end: "}".to_string(),
 7420                        close: true,
 7421                        surround: true,
 7422                        newline: true,
 7423                    },
 7424                    BracketPair {
 7425                        start: "(".to_string(),
 7426                        end: ")".to_string(),
 7427                        close: true,
 7428                        surround: true,
 7429                        newline: true,
 7430                    },
 7431                    BracketPair {
 7432                        start: "[".to_string(),
 7433                        end: "]".to_string(),
 7434                        close: false,
 7435                        surround: false,
 7436                        newline: true,
 7437                    },
 7438                ],
 7439                ..Default::default()
 7440            },
 7441            autoclose_before: "})]".to_string(),
 7442            ..Default::default()
 7443        },
 7444        Some(tree_sitter_rust::LANGUAGE.into()),
 7445    ));
 7446
 7447    cx.language_registry().add(language.clone());
 7448    cx.update_buffer(|buffer, cx| {
 7449        buffer.set_language(Some(language), cx);
 7450    });
 7451
 7452    cx.set_state(
 7453        &"
 7454            ˇ
 7455            ˇ
 7456            ˇ
 7457        "
 7458        .unindent(),
 7459    );
 7460
 7461    // ensure only matching closing brackets are skipped over
 7462    cx.update_editor(|editor, window, cx| {
 7463        editor.handle_input("}", window, cx);
 7464        editor.move_left(&MoveLeft, window, cx);
 7465        editor.handle_input(")", window, cx);
 7466        editor.move_left(&MoveLeft, window, cx);
 7467    });
 7468    cx.assert_editor_state(
 7469        &"
 7470            ˇ)}
 7471            ˇ)}
 7472            ˇ)}
 7473        "
 7474        .unindent(),
 7475    );
 7476
 7477    // skip-over closing brackets at multiple cursors
 7478    cx.update_editor(|editor, window, cx| {
 7479        editor.handle_input(")", window, cx);
 7480        editor.handle_input("}", window, cx);
 7481    });
 7482    cx.assert_editor_state(
 7483        &"
 7484            )}ˇ
 7485            )}ˇ
 7486            )}ˇ
 7487        "
 7488        .unindent(),
 7489    );
 7490
 7491    // ignore non-close brackets
 7492    cx.update_editor(|editor, window, cx| {
 7493        editor.handle_input("]", window, cx);
 7494        editor.move_left(&MoveLeft, window, cx);
 7495        editor.handle_input("]", window, cx);
 7496    });
 7497    cx.assert_editor_state(
 7498        &"
 7499            )}]ˇ]
 7500            )}]ˇ]
 7501            )}]ˇ]
 7502        "
 7503        .unindent(),
 7504    );
 7505}
 7506
 7507#[gpui::test]
 7508async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7509    init_test(cx, |_| {});
 7510
 7511    let mut cx = EditorTestContext::new(cx).await;
 7512
 7513    let html_language = Arc::new(
 7514        Language::new(
 7515            LanguageConfig {
 7516                name: "HTML".into(),
 7517                brackets: BracketPairConfig {
 7518                    pairs: vec![
 7519                        BracketPair {
 7520                            start: "<".into(),
 7521                            end: ">".into(),
 7522                            close: true,
 7523                            ..Default::default()
 7524                        },
 7525                        BracketPair {
 7526                            start: "{".into(),
 7527                            end: "}".into(),
 7528                            close: true,
 7529                            ..Default::default()
 7530                        },
 7531                        BracketPair {
 7532                            start: "(".into(),
 7533                            end: ")".into(),
 7534                            close: true,
 7535                            ..Default::default()
 7536                        },
 7537                    ],
 7538                    ..Default::default()
 7539                },
 7540                autoclose_before: "})]>".into(),
 7541                ..Default::default()
 7542            },
 7543            Some(tree_sitter_html::LANGUAGE.into()),
 7544        )
 7545        .with_injection_query(
 7546            r#"
 7547            (script_element
 7548                (raw_text) @injection.content
 7549                (#set! injection.language "javascript"))
 7550            "#,
 7551        )
 7552        .unwrap(),
 7553    );
 7554
 7555    let javascript_language = Arc::new(Language::new(
 7556        LanguageConfig {
 7557            name: "JavaScript".into(),
 7558            brackets: BracketPairConfig {
 7559                pairs: vec![
 7560                    BracketPair {
 7561                        start: "/*".into(),
 7562                        end: " */".into(),
 7563                        close: true,
 7564                        ..Default::default()
 7565                    },
 7566                    BracketPair {
 7567                        start: "{".into(),
 7568                        end: "}".into(),
 7569                        close: true,
 7570                        ..Default::default()
 7571                    },
 7572                    BracketPair {
 7573                        start: "(".into(),
 7574                        end: ")".into(),
 7575                        close: true,
 7576                        ..Default::default()
 7577                    },
 7578                ],
 7579                ..Default::default()
 7580            },
 7581            autoclose_before: "})]>".into(),
 7582            ..Default::default()
 7583        },
 7584        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7585    ));
 7586
 7587    cx.language_registry().add(html_language.clone());
 7588    cx.language_registry().add(javascript_language.clone());
 7589
 7590    cx.update_buffer(|buffer, cx| {
 7591        buffer.set_language(Some(html_language), cx);
 7592    });
 7593
 7594    cx.set_state(
 7595        &r#"
 7596            <body>ˇ
 7597                <script>
 7598                    var x = 1;ˇ
 7599                </script>
 7600            </body>ˇ
 7601        "#
 7602        .unindent(),
 7603    );
 7604
 7605    // Precondition: different languages are active at different locations.
 7606    cx.update_editor(|editor, window, cx| {
 7607        let snapshot = editor.snapshot(window, cx);
 7608        let cursors = editor.selections.ranges::<usize>(cx);
 7609        let languages = cursors
 7610            .iter()
 7611            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7612            .collect::<Vec<_>>();
 7613        assert_eq!(
 7614            languages,
 7615            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7616        );
 7617    });
 7618
 7619    // Angle brackets autoclose in HTML, but not JavaScript.
 7620    cx.update_editor(|editor, window, cx| {
 7621        editor.handle_input("<", window, cx);
 7622        editor.handle_input("a", window, cx);
 7623    });
 7624    cx.assert_editor_state(
 7625        &r#"
 7626            <body><aˇ>
 7627                <script>
 7628                    var x = 1;<aˇ
 7629                </script>
 7630            </body><aˇ>
 7631        "#
 7632        .unindent(),
 7633    );
 7634
 7635    // Curly braces and parens autoclose in both HTML and JavaScript.
 7636    cx.update_editor(|editor, window, cx| {
 7637        editor.handle_input(" b=", window, cx);
 7638        editor.handle_input("{", window, cx);
 7639        editor.handle_input("c", window, cx);
 7640        editor.handle_input("(", window, cx);
 7641    });
 7642    cx.assert_editor_state(
 7643        &r#"
 7644            <body><a b={c(ˇ)}>
 7645                <script>
 7646                    var x = 1;<a b={c(ˇ)}
 7647                </script>
 7648            </body><a b={c(ˇ)}>
 7649        "#
 7650        .unindent(),
 7651    );
 7652
 7653    // Brackets that were already autoclosed are skipped.
 7654    cx.update_editor(|editor, window, cx| {
 7655        editor.handle_input(")", window, cx);
 7656        editor.handle_input("d", window, cx);
 7657        editor.handle_input("}", window, cx);
 7658    });
 7659    cx.assert_editor_state(
 7660        &r#"
 7661            <body><a b={c()d}ˇ>
 7662                <script>
 7663                    var x = 1;<a b={c()d}ˇ
 7664                </script>
 7665            </body><a b={c()d}ˇ>
 7666        "#
 7667        .unindent(),
 7668    );
 7669    cx.update_editor(|editor, window, cx| {
 7670        editor.handle_input(">", window, cx);
 7671    });
 7672    cx.assert_editor_state(
 7673        &r#"
 7674            <body><a b={c()d}>ˇ
 7675                <script>
 7676                    var x = 1;<a b={c()d}>ˇ
 7677                </script>
 7678            </body><a b={c()d}>ˇ
 7679        "#
 7680        .unindent(),
 7681    );
 7682
 7683    // Reset
 7684    cx.set_state(
 7685        &r#"
 7686            <body>ˇ
 7687                <script>
 7688                    var x = 1;ˇ
 7689                </script>
 7690            </body>ˇ
 7691        "#
 7692        .unindent(),
 7693    );
 7694
 7695    cx.update_editor(|editor, window, cx| {
 7696        editor.handle_input("<", window, cx);
 7697    });
 7698    cx.assert_editor_state(
 7699        &r#"
 7700            <body><ˇ>
 7701                <script>
 7702                    var x = 1;<ˇ
 7703                </script>
 7704            </body><ˇ>
 7705        "#
 7706        .unindent(),
 7707    );
 7708
 7709    // When backspacing, the closing angle brackets are removed.
 7710    cx.update_editor(|editor, window, cx| {
 7711        editor.backspace(&Backspace, window, cx);
 7712    });
 7713    cx.assert_editor_state(
 7714        &r#"
 7715            <body>ˇ
 7716                <script>
 7717                    var x = 1;ˇ
 7718                </script>
 7719            </body>ˇ
 7720        "#
 7721        .unindent(),
 7722    );
 7723
 7724    // Block comments autoclose in JavaScript, but not HTML.
 7725    cx.update_editor(|editor, window, cx| {
 7726        editor.handle_input("/", window, cx);
 7727        editor.handle_input("*", window, cx);
 7728    });
 7729    cx.assert_editor_state(
 7730        &r#"
 7731            <body>/*ˇ
 7732                <script>
 7733                    var x = 1;/*ˇ */
 7734                </script>
 7735            </body>/*ˇ
 7736        "#
 7737        .unindent(),
 7738    );
 7739}
 7740
 7741#[gpui::test]
 7742async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7743    init_test(cx, |_| {});
 7744
 7745    let mut cx = EditorTestContext::new(cx).await;
 7746
 7747    let rust_language = Arc::new(
 7748        Language::new(
 7749            LanguageConfig {
 7750                name: "Rust".into(),
 7751                brackets: serde_json::from_value(json!([
 7752                    { "start": "{", "end": "}", "close": true, "newline": true },
 7753                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7754                ]))
 7755                .unwrap(),
 7756                autoclose_before: "})]>".into(),
 7757                ..Default::default()
 7758            },
 7759            Some(tree_sitter_rust::LANGUAGE.into()),
 7760        )
 7761        .with_override_query("(string_literal) @string")
 7762        .unwrap(),
 7763    );
 7764
 7765    cx.language_registry().add(rust_language.clone());
 7766    cx.update_buffer(|buffer, cx| {
 7767        buffer.set_language(Some(rust_language), cx);
 7768    });
 7769
 7770    cx.set_state(
 7771        &r#"
 7772            let x = ˇ
 7773        "#
 7774        .unindent(),
 7775    );
 7776
 7777    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7778    cx.update_editor(|editor, window, cx| {
 7779        editor.handle_input("\"", window, cx);
 7780    });
 7781    cx.assert_editor_state(
 7782        &r#"
 7783            let x = "ˇ"
 7784        "#
 7785        .unindent(),
 7786    );
 7787
 7788    // Inserting another quotation mark. The cursor moves across the existing
 7789    // automatically-inserted quotation mark.
 7790    cx.update_editor(|editor, window, cx| {
 7791        editor.handle_input("\"", window, cx);
 7792    });
 7793    cx.assert_editor_state(
 7794        &r#"
 7795            let x = ""ˇ
 7796        "#
 7797        .unindent(),
 7798    );
 7799
 7800    // Reset
 7801    cx.set_state(
 7802        &r#"
 7803            let x = ˇ
 7804        "#
 7805        .unindent(),
 7806    );
 7807
 7808    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7809    cx.update_editor(|editor, window, cx| {
 7810        editor.handle_input("\"", window, cx);
 7811        editor.handle_input(" ", window, cx);
 7812        editor.move_left(&Default::default(), window, cx);
 7813        editor.handle_input("\\", window, cx);
 7814        editor.handle_input("\"", window, cx);
 7815    });
 7816    cx.assert_editor_state(
 7817        &r#"
 7818            let x = "\"ˇ "
 7819        "#
 7820        .unindent(),
 7821    );
 7822
 7823    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7824    // mark. Nothing is inserted.
 7825    cx.update_editor(|editor, window, cx| {
 7826        editor.move_right(&Default::default(), window, cx);
 7827        editor.handle_input("\"", window, cx);
 7828    });
 7829    cx.assert_editor_state(
 7830        &r#"
 7831            let x = "\" "ˇ
 7832        "#
 7833        .unindent(),
 7834    );
 7835}
 7836
 7837#[gpui::test]
 7838async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7839    init_test(cx, |_| {});
 7840
 7841    let language = Arc::new(Language::new(
 7842        LanguageConfig {
 7843            brackets: BracketPairConfig {
 7844                pairs: vec![
 7845                    BracketPair {
 7846                        start: "{".to_string(),
 7847                        end: "}".to_string(),
 7848                        close: true,
 7849                        surround: true,
 7850                        newline: true,
 7851                    },
 7852                    BracketPair {
 7853                        start: "/* ".to_string(),
 7854                        end: "*/".to_string(),
 7855                        close: true,
 7856                        surround: true,
 7857                        ..Default::default()
 7858                    },
 7859                ],
 7860                ..Default::default()
 7861            },
 7862            ..Default::default()
 7863        },
 7864        Some(tree_sitter_rust::LANGUAGE.into()),
 7865    ));
 7866
 7867    let text = r#"
 7868        a
 7869        b
 7870        c
 7871    "#
 7872    .unindent();
 7873
 7874    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7875    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7876    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7877    editor
 7878        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7879        .await;
 7880
 7881    editor.update_in(cx, |editor, window, cx| {
 7882        editor.change_selections(None, window, cx, |s| {
 7883            s.select_display_ranges([
 7884                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7885                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7886                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7887            ])
 7888        });
 7889
 7890        editor.handle_input("{", window, cx);
 7891        editor.handle_input("{", window, cx);
 7892        editor.handle_input("{", window, cx);
 7893        assert_eq!(
 7894            editor.text(cx),
 7895            "
 7896                {{{a}}}
 7897                {{{b}}}
 7898                {{{c}}}
 7899            "
 7900            .unindent()
 7901        );
 7902        assert_eq!(
 7903            editor.selections.display_ranges(cx),
 7904            [
 7905                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7906                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7907                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7908            ]
 7909        );
 7910
 7911        editor.undo(&Undo, window, cx);
 7912        editor.undo(&Undo, window, cx);
 7913        editor.undo(&Undo, window, cx);
 7914        assert_eq!(
 7915            editor.text(cx),
 7916            "
 7917                a
 7918                b
 7919                c
 7920            "
 7921            .unindent()
 7922        );
 7923        assert_eq!(
 7924            editor.selections.display_ranges(cx),
 7925            [
 7926                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7927                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7928                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7929            ]
 7930        );
 7931
 7932        // Ensure inserting the first character of a multi-byte bracket pair
 7933        // doesn't surround the selections with the bracket.
 7934        editor.handle_input("/", window, cx);
 7935        assert_eq!(
 7936            editor.text(cx),
 7937            "
 7938                /
 7939                /
 7940                /
 7941            "
 7942            .unindent()
 7943        );
 7944        assert_eq!(
 7945            editor.selections.display_ranges(cx),
 7946            [
 7947                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7948                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7949                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7950            ]
 7951        );
 7952
 7953        editor.undo(&Undo, window, cx);
 7954        assert_eq!(
 7955            editor.text(cx),
 7956            "
 7957                a
 7958                b
 7959                c
 7960            "
 7961            .unindent()
 7962        );
 7963        assert_eq!(
 7964            editor.selections.display_ranges(cx),
 7965            [
 7966                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7967                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7968                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7969            ]
 7970        );
 7971
 7972        // Ensure inserting the last character of a multi-byte bracket pair
 7973        // doesn't surround the selections with the bracket.
 7974        editor.handle_input("*", window, cx);
 7975        assert_eq!(
 7976            editor.text(cx),
 7977            "
 7978                *
 7979                *
 7980                *
 7981            "
 7982            .unindent()
 7983        );
 7984        assert_eq!(
 7985            editor.selections.display_ranges(cx),
 7986            [
 7987                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7988                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7989                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7990            ]
 7991        );
 7992    });
 7993}
 7994
 7995#[gpui::test]
 7996async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7997    init_test(cx, |_| {});
 7998
 7999    let language = Arc::new(Language::new(
 8000        LanguageConfig {
 8001            brackets: BracketPairConfig {
 8002                pairs: vec![BracketPair {
 8003                    start: "{".to_string(),
 8004                    end: "}".to_string(),
 8005                    close: true,
 8006                    surround: true,
 8007                    newline: true,
 8008                }],
 8009                ..Default::default()
 8010            },
 8011            autoclose_before: "}".to_string(),
 8012            ..Default::default()
 8013        },
 8014        Some(tree_sitter_rust::LANGUAGE.into()),
 8015    ));
 8016
 8017    let text = r#"
 8018        a
 8019        b
 8020        c
 8021    "#
 8022    .unindent();
 8023
 8024    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 8025    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8026    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8027    editor
 8028        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8029        .await;
 8030
 8031    editor.update_in(cx, |editor, window, cx| {
 8032        editor.change_selections(None, window, cx, |s| {
 8033            s.select_ranges([
 8034                Point::new(0, 1)..Point::new(0, 1),
 8035                Point::new(1, 1)..Point::new(1, 1),
 8036                Point::new(2, 1)..Point::new(2, 1),
 8037            ])
 8038        });
 8039
 8040        editor.handle_input("{", window, cx);
 8041        editor.handle_input("{", window, cx);
 8042        editor.handle_input("_", window, cx);
 8043        assert_eq!(
 8044            editor.text(cx),
 8045            "
 8046                a{{_}}
 8047                b{{_}}
 8048                c{{_}}
 8049            "
 8050            .unindent()
 8051        );
 8052        assert_eq!(
 8053            editor.selections.ranges::<Point>(cx),
 8054            [
 8055                Point::new(0, 4)..Point::new(0, 4),
 8056                Point::new(1, 4)..Point::new(1, 4),
 8057                Point::new(2, 4)..Point::new(2, 4)
 8058            ]
 8059        );
 8060
 8061        editor.backspace(&Default::default(), window, cx);
 8062        editor.backspace(&Default::default(), window, cx);
 8063        assert_eq!(
 8064            editor.text(cx),
 8065            "
 8066                a{}
 8067                b{}
 8068                c{}
 8069            "
 8070            .unindent()
 8071        );
 8072        assert_eq!(
 8073            editor.selections.ranges::<Point>(cx),
 8074            [
 8075                Point::new(0, 2)..Point::new(0, 2),
 8076                Point::new(1, 2)..Point::new(1, 2),
 8077                Point::new(2, 2)..Point::new(2, 2)
 8078            ]
 8079        );
 8080
 8081        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 8082        assert_eq!(
 8083            editor.text(cx),
 8084            "
 8085                a
 8086                b
 8087                c
 8088            "
 8089            .unindent()
 8090        );
 8091        assert_eq!(
 8092            editor.selections.ranges::<Point>(cx),
 8093            [
 8094                Point::new(0, 1)..Point::new(0, 1),
 8095                Point::new(1, 1)..Point::new(1, 1),
 8096                Point::new(2, 1)..Point::new(2, 1)
 8097            ]
 8098        );
 8099    });
 8100}
 8101
 8102#[gpui::test]
 8103async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8104    init_test(cx, |settings| {
 8105        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8106    });
 8107
 8108    let mut cx = EditorTestContext::new(cx).await;
 8109
 8110    let language = Arc::new(Language::new(
 8111        LanguageConfig {
 8112            brackets: BracketPairConfig {
 8113                pairs: vec![
 8114                    BracketPair {
 8115                        start: "{".to_string(),
 8116                        end: "}".to_string(),
 8117                        close: true,
 8118                        surround: true,
 8119                        newline: true,
 8120                    },
 8121                    BracketPair {
 8122                        start: "(".to_string(),
 8123                        end: ")".to_string(),
 8124                        close: true,
 8125                        surround: true,
 8126                        newline: true,
 8127                    },
 8128                    BracketPair {
 8129                        start: "[".to_string(),
 8130                        end: "]".to_string(),
 8131                        close: false,
 8132                        surround: true,
 8133                        newline: true,
 8134                    },
 8135                ],
 8136                ..Default::default()
 8137            },
 8138            autoclose_before: "})]".to_string(),
 8139            ..Default::default()
 8140        },
 8141        Some(tree_sitter_rust::LANGUAGE.into()),
 8142    ));
 8143
 8144    cx.language_registry().add(language.clone());
 8145    cx.update_buffer(|buffer, cx| {
 8146        buffer.set_language(Some(language), cx);
 8147    });
 8148
 8149    cx.set_state(
 8150        &"
 8151            {(ˇ)}
 8152            [[ˇ]]
 8153            {(ˇ)}
 8154        "
 8155        .unindent(),
 8156    );
 8157
 8158    cx.update_editor(|editor, window, cx| {
 8159        editor.backspace(&Default::default(), window, cx);
 8160        editor.backspace(&Default::default(), window, cx);
 8161    });
 8162
 8163    cx.assert_editor_state(
 8164        &"
 8165            ˇ
 8166            ˇ]]
 8167            ˇ
 8168        "
 8169        .unindent(),
 8170    );
 8171
 8172    cx.update_editor(|editor, window, cx| {
 8173        editor.handle_input("{", window, cx);
 8174        editor.handle_input("{", window, cx);
 8175        editor.move_right(&MoveRight, window, cx);
 8176        editor.move_right(&MoveRight, window, cx);
 8177        editor.move_left(&MoveLeft, window, cx);
 8178        editor.move_left(&MoveLeft, window, cx);
 8179        editor.backspace(&Default::default(), window, cx);
 8180    });
 8181
 8182    cx.assert_editor_state(
 8183        &"
 8184            {ˇ}
 8185            {ˇ}]]
 8186            {ˇ}
 8187        "
 8188        .unindent(),
 8189    );
 8190
 8191    cx.update_editor(|editor, window, cx| {
 8192        editor.backspace(&Default::default(), window, cx);
 8193    });
 8194
 8195    cx.assert_editor_state(
 8196        &"
 8197            ˇ
 8198            ˇ]]
 8199            ˇ
 8200        "
 8201        .unindent(),
 8202    );
 8203}
 8204
 8205#[gpui::test]
 8206async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8207    init_test(cx, |_| {});
 8208
 8209    let language = Arc::new(Language::new(
 8210        LanguageConfig::default(),
 8211        Some(tree_sitter_rust::LANGUAGE.into()),
 8212    ));
 8213
 8214    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8215    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8216    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8217    editor
 8218        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8219        .await;
 8220
 8221    editor.update_in(cx, |editor, window, cx| {
 8222        editor.set_auto_replace_emoji_shortcode(true);
 8223
 8224        editor.handle_input("Hello ", window, cx);
 8225        editor.handle_input(":wave", window, cx);
 8226        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8227
 8228        editor.handle_input(":", window, cx);
 8229        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8230
 8231        editor.handle_input(" :smile", window, cx);
 8232        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8233
 8234        editor.handle_input(":", window, cx);
 8235        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8236
 8237        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8238        editor.handle_input(":wave", window, cx);
 8239        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8240
 8241        editor.handle_input(":", window, cx);
 8242        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8243
 8244        editor.handle_input(":1", window, cx);
 8245        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8246
 8247        editor.handle_input(":", window, cx);
 8248        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8249
 8250        // Ensure shortcode does not get replaced when it is part of a word
 8251        editor.handle_input(" Test:wave", window, cx);
 8252        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8253
 8254        editor.handle_input(":", window, cx);
 8255        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8256
 8257        editor.set_auto_replace_emoji_shortcode(false);
 8258
 8259        // Ensure shortcode does not get replaced when auto replace is off
 8260        editor.handle_input(" :wave", window, cx);
 8261        assert_eq!(
 8262            editor.text(cx),
 8263            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8264        );
 8265
 8266        editor.handle_input(":", window, cx);
 8267        assert_eq!(
 8268            editor.text(cx),
 8269            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8270        );
 8271    });
 8272}
 8273
 8274#[gpui::test]
 8275async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8276    init_test(cx, |_| {});
 8277
 8278    let (text, insertion_ranges) = marked_text_ranges(
 8279        indoc! {"
 8280            ˇ
 8281        "},
 8282        false,
 8283    );
 8284
 8285    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8286    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8287
 8288    _ = editor.update_in(cx, |editor, window, cx| {
 8289        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8290
 8291        editor
 8292            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8293            .unwrap();
 8294
 8295        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8296            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8297            assert_eq!(editor.text(cx), expected_text);
 8298            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8299        }
 8300
 8301        assert(
 8302            editor,
 8303            cx,
 8304            indoc! {"
 8305            type «» =•
 8306            "},
 8307        );
 8308
 8309        assert!(editor.context_menu_visible(), "There should be a matches");
 8310    });
 8311}
 8312
 8313#[gpui::test]
 8314async fn test_snippets(cx: &mut TestAppContext) {
 8315    init_test(cx, |_| {});
 8316
 8317    let (text, insertion_ranges) = marked_text_ranges(
 8318        indoc! {"
 8319            a.ˇ b
 8320            a.ˇ b
 8321            a.ˇ b
 8322        "},
 8323        false,
 8324    );
 8325
 8326    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8327    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8328
 8329    editor.update_in(cx, |editor, window, cx| {
 8330        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8331
 8332        editor
 8333            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8334            .unwrap();
 8335
 8336        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8337            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8338            assert_eq!(editor.text(cx), expected_text);
 8339            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8340        }
 8341
 8342        assert(
 8343            editor,
 8344            cx,
 8345            indoc! {"
 8346                a.f(«one», two, «three») b
 8347                a.f(«one», two, «three») b
 8348                a.f(«one», two, «three») b
 8349            "},
 8350        );
 8351
 8352        // Can't move earlier than the first tab stop
 8353        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8354        assert(
 8355            editor,
 8356            cx,
 8357            indoc! {"
 8358                a.f(«one», two, «three») b
 8359                a.f(«one», two, «three») b
 8360                a.f(«one», two, «three») b
 8361            "},
 8362        );
 8363
 8364        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8365        assert(
 8366            editor,
 8367            cx,
 8368            indoc! {"
 8369                a.f(one, «two», three) b
 8370                a.f(one, «two», three) b
 8371                a.f(one, «two», three) b
 8372            "},
 8373        );
 8374
 8375        editor.move_to_prev_snippet_tabstop(window, cx);
 8376        assert(
 8377            editor,
 8378            cx,
 8379            indoc! {"
 8380                a.f(«one», two, «three») b
 8381                a.f(«one», two, «three») b
 8382                a.f(«one», two, «three») b
 8383            "},
 8384        );
 8385
 8386        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8387        assert(
 8388            editor,
 8389            cx,
 8390            indoc! {"
 8391                a.f(one, «two», three) b
 8392                a.f(one, «two», three) b
 8393                a.f(one, «two», three) b
 8394            "},
 8395        );
 8396        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8397        assert(
 8398            editor,
 8399            cx,
 8400            indoc! {"
 8401                a.f(one, two, three)ˇ b
 8402                a.f(one, two, three)ˇ b
 8403                a.f(one, two, three)ˇ b
 8404            "},
 8405        );
 8406
 8407        // As soon as the last tab stop is reached, snippet state is gone
 8408        editor.move_to_prev_snippet_tabstop(window, cx);
 8409        assert(
 8410            editor,
 8411            cx,
 8412            indoc! {"
 8413                a.f(one, two, three)ˇ b
 8414                a.f(one, two, three)ˇ b
 8415                a.f(one, two, three)ˇ b
 8416            "},
 8417        );
 8418    });
 8419}
 8420
 8421#[gpui::test]
 8422async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8423    init_test(cx, |_| {});
 8424
 8425    let fs = FakeFs::new(cx.executor());
 8426    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8427
 8428    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8429
 8430    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8431    language_registry.add(rust_lang());
 8432    let mut fake_servers = language_registry.register_fake_lsp(
 8433        "Rust",
 8434        FakeLspAdapter {
 8435            capabilities: lsp::ServerCapabilities {
 8436                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8437                ..Default::default()
 8438            },
 8439            ..Default::default()
 8440        },
 8441    );
 8442
 8443    let buffer = project
 8444        .update(cx, |project, cx| {
 8445            project.open_local_buffer(path!("/file.rs"), cx)
 8446        })
 8447        .await
 8448        .unwrap();
 8449
 8450    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8451    let (editor, cx) = cx.add_window_view(|window, cx| {
 8452        build_editor_with_project(project.clone(), buffer, window, cx)
 8453    });
 8454    editor.update_in(cx, |editor, window, cx| {
 8455        editor.set_text("one\ntwo\nthree\n", window, cx)
 8456    });
 8457    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8458
 8459    cx.executor().start_waiting();
 8460    let fake_server = fake_servers.next().await.unwrap();
 8461
 8462    {
 8463        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8464            move |params, _| async move {
 8465                assert_eq!(
 8466                    params.text_document.uri,
 8467                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8468                );
 8469                assert_eq!(params.options.tab_size, 4);
 8470                Ok(Some(vec![lsp::TextEdit::new(
 8471                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8472                    ", ".to_string(),
 8473                )]))
 8474            },
 8475        );
 8476        let save = editor
 8477            .update_in(cx, |editor, window, cx| {
 8478                editor.save(true, project.clone(), window, cx)
 8479            })
 8480            .unwrap();
 8481        cx.executor().start_waiting();
 8482        save.await;
 8483
 8484        assert_eq!(
 8485            editor.update(cx, |editor, cx| editor.text(cx)),
 8486            "one, two\nthree\n"
 8487        );
 8488        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8489    }
 8490
 8491    {
 8492        editor.update_in(cx, |editor, window, cx| {
 8493            editor.set_text("one\ntwo\nthree\n", window, cx)
 8494        });
 8495        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8496
 8497        // Ensure we can still save even if formatting hangs.
 8498        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8499            move |params, _| async move {
 8500                assert_eq!(
 8501                    params.text_document.uri,
 8502                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8503                );
 8504                futures::future::pending::<()>().await;
 8505                unreachable!()
 8506            },
 8507        );
 8508        let save = editor
 8509            .update_in(cx, |editor, window, cx| {
 8510                editor.save(true, project.clone(), window, cx)
 8511            })
 8512            .unwrap();
 8513        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8514        cx.executor().start_waiting();
 8515        save.await;
 8516        assert_eq!(
 8517            editor.update(cx, |editor, cx| editor.text(cx)),
 8518            "one\ntwo\nthree\n"
 8519        );
 8520    }
 8521
 8522    // For non-dirty buffer, no formatting request should be sent
 8523    {
 8524        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8525
 8526        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8527            panic!("Should not be invoked on non-dirty buffer");
 8528        });
 8529        let save = editor
 8530            .update_in(cx, |editor, window, cx| {
 8531                editor.save(true, project.clone(), window, cx)
 8532            })
 8533            .unwrap();
 8534        cx.executor().start_waiting();
 8535        save.await;
 8536    }
 8537
 8538    // Set rust language override and assert overridden tabsize is sent to language server
 8539    update_test_language_settings(cx, |settings| {
 8540        settings.languages.insert(
 8541            "Rust".into(),
 8542            LanguageSettingsContent {
 8543                tab_size: NonZeroU32::new(8),
 8544                ..Default::default()
 8545            },
 8546        );
 8547    });
 8548
 8549    {
 8550        editor.update_in(cx, |editor, window, cx| {
 8551            editor.set_text("somehting_new\n", window, cx)
 8552        });
 8553        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8554        let _formatting_request_signal = fake_server
 8555            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8556                assert_eq!(
 8557                    params.text_document.uri,
 8558                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8559                );
 8560                assert_eq!(params.options.tab_size, 8);
 8561                Ok(Some(vec![]))
 8562            });
 8563        let save = editor
 8564            .update_in(cx, |editor, window, cx| {
 8565                editor.save(true, project.clone(), window, cx)
 8566            })
 8567            .unwrap();
 8568        cx.executor().start_waiting();
 8569        save.await;
 8570    }
 8571}
 8572
 8573#[gpui::test]
 8574async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8575    init_test(cx, |_| {});
 8576
 8577    let cols = 4;
 8578    let rows = 10;
 8579    let sample_text_1 = sample_text(rows, cols, 'a');
 8580    assert_eq!(
 8581        sample_text_1,
 8582        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8583    );
 8584    let sample_text_2 = sample_text(rows, cols, 'l');
 8585    assert_eq!(
 8586        sample_text_2,
 8587        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8588    );
 8589    let sample_text_3 = sample_text(rows, cols, 'v');
 8590    assert_eq!(
 8591        sample_text_3,
 8592        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8593    );
 8594
 8595    let fs = FakeFs::new(cx.executor());
 8596    fs.insert_tree(
 8597        path!("/a"),
 8598        json!({
 8599            "main.rs": sample_text_1,
 8600            "other.rs": sample_text_2,
 8601            "lib.rs": sample_text_3,
 8602        }),
 8603    )
 8604    .await;
 8605
 8606    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8607    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8608    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8609
 8610    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8611    language_registry.add(rust_lang());
 8612    let mut fake_servers = language_registry.register_fake_lsp(
 8613        "Rust",
 8614        FakeLspAdapter {
 8615            capabilities: lsp::ServerCapabilities {
 8616                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8617                ..Default::default()
 8618            },
 8619            ..Default::default()
 8620        },
 8621    );
 8622
 8623    let worktree = project.update(cx, |project, cx| {
 8624        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8625        assert_eq!(worktrees.len(), 1);
 8626        worktrees.pop().unwrap()
 8627    });
 8628    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8629
 8630    let buffer_1 = project
 8631        .update(cx, |project, cx| {
 8632            project.open_buffer((worktree_id, "main.rs"), cx)
 8633        })
 8634        .await
 8635        .unwrap();
 8636    let buffer_2 = project
 8637        .update(cx, |project, cx| {
 8638            project.open_buffer((worktree_id, "other.rs"), cx)
 8639        })
 8640        .await
 8641        .unwrap();
 8642    let buffer_3 = project
 8643        .update(cx, |project, cx| {
 8644            project.open_buffer((worktree_id, "lib.rs"), cx)
 8645        })
 8646        .await
 8647        .unwrap();
 8648
 8649    let multi_buffer = cx.new(|cx| {
 8650        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8651        multi_buffer.push_excerpts(
 8652            buffer_1.clone(),
 8653            [
 8654                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8655                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8656                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8657            ],
 8658            cx,
 8659        );
 8660        multi_buffer.push_excerpts(
 8661            buffer_2.clone(),
 8662            [
 8663                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8664                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8665                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8666            ],
 8667            cx,
 8668        );
 8669        multi_buffer.push_excerpts(
 8670            buffer_3.clone(),
 8671            [
 8672                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8673                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8674                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8675            ],
 8676            cx,
 8677        );
 8678        multi_buffer
 8679    });
 8680    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8681        Editor::new(
 8682            EditorMode::full(),
 8683            multi_buffer,
 8684            Some(project.clone()),
 8685            window,
 8686            cx,
 8687        )
 8688    });
 8689
 8690    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8691        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8692            s.select_ranges(Some(1..2))
 8693        });
 8694        editor.insert("|one|two|three|", window, cx);
 8695    });
 8696    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8697    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8698        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8699            s.select_ranges(Some(60..70))
 8700        });
 8701        editor.insert("|four|five|six|", window, cx);
 8702    });
 8703    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8704
 8705    // First two buffers should be edited, but not the third one.
 8706    assert_eq!(
 8707        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8708        "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}",
 8709    );
 8710    buffer_1.update(cx, |buffer, _| {
 8711        assert!(buffer.is_dirty());
 8712        assert_eq!(
 8713            buffer.text(),
 8714            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8715        )
 8716    });
 8717    buffer_2.update(cx, |buffer, _| {
 8718        assert!(buffer.is_dirty());
 8719        assert_eq!(
 8720            buffer.text(),
 8721            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8722        )
 8723    });
 8724    buffer_3.update(cx, |buffer, _| {
 8725        assert!(!buffer.is_dirty());
 8726        assert_eq!(buffer.text(), sample_text_3,)
 8727    });
 8728    cx.executor().run_until_parked();
 8729
 8730    cx.executor().start_waiting();
 8731    let save = multi_buffer_editor
 8732        .update_in(cx, |editor, window, cx| {
 8733            editor.save(true, project.clone(), window, cx)
 8734        })
 8735        .unwrap();
 8736
 8737    let fake_server = fake_servers.next().await.unwrap();
 8738    fake_server
 8739        .server
 8740        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8741            Ok(Some(vec![lsp::TextEdit::new(
 8742                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8743                format!("[{} formatted]", params.text_document.uri),
 8744            )]))
 8745        })
 8746        .detach();
 8747    save.await;
 8748
 8749    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8750    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8751    assert_eq!(
 8752        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8753        uri!(
 8754            "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}"
 8755        ),
 8756    );
 8757    buffer_1.update(cx, |buffer, _| {
 8758        assert!(!buffer.is_dirty());
 8759        assert_eq!(
 8760            buffer.text(),
 8761            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8762        )
 8763    });
 8764    buffer_2.update(cx, |buffer, _| {
 8765        assert!(!buffer.is_dirty());
 8766        assert_eq!(
 8767            buffer.text(),
 8768            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8769        )
 8770    });
 8771    buffer_3.update(cx, |buffer, _| {
 8772        assert!(!buffer.is_dirty());
 8773        assert_eq!(buffer.text(), sample_text_3,)
 8774    });
 8775}
 8776
 8777#[gpui::test]
 8778async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8779    init_test(cx, |_| {});
 8780
 8781    let fs = FakeFs::new(cx.executor());
 8782    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8783
 8784    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8785
 8786    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8787    language_registry.add(rust_lang());
 8788    let mut fake_servers = language_registry.register_fake_lsp(
 8789        "Rust",
 8790        FakeLspAdapter {
 8791            capabilities: lsp::ServerCapabilities {
 8792                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8793                ..Default::default()
 8794            },
 8795            ..Default::default()
 8796        },
 8797    );
 8798
 8799    let buffer = project
 8800        .update(cx, |project, cx| {
 8801            project.open_local_buffer(path!("/file.rs"), cx)
 8802        })
 8803        .await
 8804        .unwrap();
 8805
 8806    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8807    let (editor, cx) = cx.add_window_view(|window, cx| {
 8808        build_editor_with_project(project.clone(), buffer, window, cx)
 8809    });
 8810    editor.update_in(cx, |editor, window, cx| {
 8811        editor.set_text("one\ntwo\nthree\n", window, cx)
 8812    });
 8813    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8814
 8815    cx.executor().start_waiting();
 8816    let fake_server = fake_servers.next().await.unwrap();
 8817
 8818    let save = editor
 8819        .update_in(cx, |editor, window, cx| {
 8820            editor.save(true, project.clone(), window, cx)
 8821        })
 8822        .unwrap();
 8823    fake_server
 8824        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8825            assert_eq!(
 8826                params.text_document.uri,
 8827                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8828            );
 8829            assert_eq!(params.options.tab_size, 4);
 8830            Ok(Some(vec![lsp::TextEdit::new(
 8831                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8832                ", ".to_string(),
 8833            )]))
 8834        })
 8835        .next()
 8836        .await;
 8837    cx.executor().start_waiting();
 8838    save.await;
 8839    assert_eq!(
 8840        editor.update(cx, |editor, cx| editor.text(cx)),
 8841        "one, two\nthree\n"
 8842    );
 8843    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8844
 8845    editor.update_in(cx, |editor, window, cx| {
 8846        editor.set_text("one\ntwo\nthree\n", window, cx)
 8847    });
 8848    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8849
 8850    // Ensure we can still save even if formatting hangs.
 8851    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8852        move |params, _| async move {
 8853            assert_eq!(
 8854                params.text_document.uri,
 8855                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8856            );
 8857            futures::future::pending::<()>().await;
 8858            unreachable!()
 8859        },
 8860    );
 8861    let save = editor
 8862        .update_in(cx, |editor, window, cx| {
 8863            editor.save(true, project.clone(), window, cx)
 8864        })
 8865        .unwrap();
 8866    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8867    cx.executor().start_waiting();
 8868    save.await;
 8869    assert_eq!(
 8870        editor.update(cx, |editor, cx| editor.text(cx)),
 8871        "one\ntwo\nthree\n"
 8872    );
 8873    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8874
 8875    // For non-dirty buffer, no formatting request should be sent
 8876    let save = editor
 8877        .update_in(cx, |editor, window, cx| {
 8878            editor.save(true, project.clone(), window, cx)
 8879        })
 8880        .unwrap();
 8881    let _pending_format_request = fake_server
 8882        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8883            panic!("Should not be invoked on non-dirty buffer");
 8884        })
 8885        .next();
 8886    cx.executor().start_waiting();
 8887    save.await;
 8888
 8889    // Set Rust language override and assert overridden tabsize is sent to language server
 8890    update_test_language_settings(cx, |settings| {
 8891        settings.languages.insert(
 8892            "Rust".into(),
 8893            LanguageSettingsContent {
 8894                tab_size: NonZeroU32::new(8),
 8895                ..Default::default()
 8896            },
 8897        );
 8898    });
 8899
 8900    editor.update_in(cx, |editor, window, cx| {
 8901        editor.set_text("somehting_new\n", window, cx)
 8902    });
 8903    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8904    let save = editor
 8905        .update_in(cx, |editor, window, cx| {
 8906            editor.save(true, project.clone(), window, cx)
 8907        })
 8908        .unwrap();
 8909    fake_server
 8910        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8911            assert_eq!(
 8912                params.text_document.uri,
 8913                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8914            );
 8915            assert_eq!(params.options.tab_size, 8);
 8916            Ok(Some(vec![]))
 8917        })
 8918        .next()
 8919        .await;
 8920    cx.executor().start_waiting();
 8921    save.await;
 8922}
 8923
 8924#[gpui::test]
 8925async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8926    init_test(cx, |settings| {
 8927        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8928            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8929        ))
 8930    });
 8931
 8932    let fs = FakeFs::new(cx.executor());
 8933    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8934
 8935    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8936
 8937    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8938    language_registry.add(Arc::new(Language::new(
 8939        LanguageConfig {
 8940            name: "Rust".into(),
 8941            matcher: LanguageMatcher {
 8942                path_suffixes: vec!["rs".to_string()],
 8943                ..Default::default()
 8944            },
 8945            ..LanguageConfig::default()
 8946        },
 8947        Some(tree_sitter_rust::LANGUAGE.into()),
 8948    )));
 8949    update_test_language_settings(cx, |settings| {
 8950        // Enable Prettier formatting for the same buffer, and ensure
 8951        // LSP is called instead of Prettier.
 8952        settings.defaults.prettier = Some(PrettierSettings {
 8953            allowed: true,
 8954            ..PrettierSettings::default()
 8955        });
 8956    });
 8957    let mut fake_servers = language_registry.register_fake_lsp(
 8958        "Rust",
 8959        FakeLspAdapter {
 8960            capabilities: lsp::ServerCapabilities {
 8961                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8962                ..Default::default()
 8963            },
 8964            ..Default::default()
 8965        },
 8966    );
 8967
 8968    let buffer = project
 8969        .update(cx, |project, cx| {
 8970            project.open_local_buffer(path!("/file.rs"), cx)
 8971        })
 8972        .await
 8973        .unwrap();
 8974
 8975    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8976    let (editor, cx) = cx.add_window_view(|window, cx| {
 8977        build_editor_with_project(project.clone(), buffer, window, cx)
 8978    });
 8979    editor.update_in(cx, |editor, window, cx| {
 8980        editor.set_text("one\ntwo\nthree\n", window, cx)
 8981    });
 8982
 8983    cx.executor().start_waiting();
 8984    let fake_server = fake_servers.next().await.unwrap();
 8985
 8986    let format = editor
 8987        .update_in(cx, |editor, window, cx| {
 8988            editor.perform_format(
 8989                project.clone(),
 8990                FormatTrigger::Manual,
 8991                FormatTarget::Buffers,
 8992                window,
 8993                cx,
 8994            )
 8995        })
 8996        .unwrap();
 8997    fake_server
 8998        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8999            assert_eq!(
 9000                params.text_document.uri,
 9001                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9002            );
 9003            assert_eq!(params.options.tab_size, 4);
 9004            Ok(Some(vec![lsp::TextEdit::new(
 9005                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 9006                ", ".to_string(),
 9007            )]))
 9008        })
 9009        .next()
 9010        .await;
 9011    cx.executor().start_waiting();
 9012    format.await;
 9013    assert_eq!(
 9014        editor.update(cx, |editor, cx| editor.text(cx)),
 9015        "one, two\nthree\n"
 9016    );
 9017
 9018    editor.update_in(cx, |editor, window, cx| {
 9019        editor.set_text("one\ntwo\nthree\n", window, cx)
 9020    });
 9021    // Ensure we don't lock if formatting hangs.
 9022    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9023        move |params, _| async move {
 9024            assert_eq!(
 9025                params.text_document.uri,
 9026                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 9027            );
 9028            futures::future::pending::<()>().await;
 9029            unreachable!()
 9030        },
 9031    );
 9032    let format = editor
 9033        .update_in(cx, |editor, window, cx| {
 9034            editor.perform_format(
 9035                project,
 9036                FormatTrigger::Manual,
 9037                FormatTarget::Buffers,
 9038                window,
 9039                cx,
 9040            )
 9041        })
 9042        .unwrap();
 9043    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 9044    cx.executor().start_waiting();
 9045    format.await;
 9046    assert_eq!(
 9047        editor.update(cx, |editor, cx| editor.text(cx)),
 9048        "one\ntwo\nthree\n"
 9049    );
 9050}
 9051
 9052#[gpui::test]
 9053async fn test_multiple_formatters(cx: &mut TestAppContext) {
 9054    init_test(cx, |settings| {
 9055        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 9056        settings.defaults.formatter =
 9057            Some(language_settings::SelectedFormatter::List(FormatterList(
 9058                vec![
 9059                    Formatter::LanguageServer { name: None },
 9060                    Formatter::CodeActions(
 9061                        [
 9062                            ("code-action-1".into(), true),
 9063                            ("code-action-2".into(), true),
 9064                        ]
 9065                        .into_iter()
 9066                        .collect(),
 9067                    ),
 9068                ]
 9069                .into(),
 9070            )))
 9071    });
 9072
 9073    let fs = FakeFs::new(cx.executor());
 9074    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 9075        .await;
 9076
 9077    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9078    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9079    language_registry.add(rust_lang());
 9080
 9081    let mut fake_servers = language_registry.register_fake_lsp(
 9082        "Rust",
 9083        FakeLspAdapter {
 9084            capabilities: lsp::ServerCapabilities {
 9085                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9086                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 9087                    commands: vec!["the-command-for-code-action-1".into()],
 9088                    ..Default::default()
 9089                }),
 9090                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9091                ..Default::default()
 9092            },
 9093            ..Default::default()
 9094        },
 9095    );
 9096
 9097    let buffer = project
 9098        .update(cx, |project, cx| {
 9099            project.open_local_buffer(path!("/file.rs"), cx)
 9100        })
 9101        .await
 9102        .unwrap();
 9103
 9104    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9105    let (editor, cx) = cx.add_window_view(|window, cx| {
 9106        build_editor_with_project(project.clone(), buffer, window, cx)
 9107    });
 9108
 9109    cx.executor().start_waiting();
 9110
 9111    let fake_server = fake_servers.next().await.unwrap();
 9112    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9113        move |_params, _| async move {
 9114            Ok(Some(vec![lsp::TextEdit::new(
 9115                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9116                "applied-formatting\n".to_string(),
 9117            )]))
 9118        },
 9119    );
 9120    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9121        move |params, _| async move {
 9122            assert_eq!(
 9123                params.context.only,
 9124                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9125            );
 9126            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9127            Ok(Some(vec![
 9128                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9129                    kind: Some("code-action-1".into()),
 9130                    edit: Some(lsp::WorkspaceEdit::new(
 9131                        [(
 9132                            uri.clone(),
 9133                            vec![lsp::TextEdit::new(
 9134                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9135                                "applied-code-action-1-edit\n".to_string(),
 9136                            )],
 9137                        )]
 9138                        .into_iter()
 9139                        .collect(),
 9140                    )),
 9141                    command: Some(lsp::Command {
 9142                        command: "the-command-for-code-action-1".into(),
 9143                        ..Default::default()
 9144                    }),
 9145                    ..Default::default()
 9146                }),
 9147                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9148                    kind: Some("code-action-2".into()),
 9149                    edit: Some(lsp::WorkspaceEdit::new(
 9150                        [(
 9151                            uri.clone(),
 9152                            vec![lsp::TextEdit::new(
 9153                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9154                                "applied-code-action-2-edit\n".to_string(),
 9155                            )],
 9156                        )]
 9157                        .into_iter()
 9158                        .collect(),
 9159                    )),
 9160                    ..Default::default()
 9161                }),
 9162            ]))
 9163        },
 9164    );
 9165
 9166    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9167        move |params, _| async move { Ok(params) }
 9168    });
 9169
 9170    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9171    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9172        let fake = fake_server.clone();
 9173        let lock = command_lock.clone();
 9174        move |params, _| {
 9175            assert_eq!(params.command, "the-command-for-code-action-1");
 9176            let fake = fake.clone();
 9177            let lock = lock.clone();
 9178            async move {
 9179                lock.lock().await;
 9180                fake.server
 9181                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9182                        label: None,
 9183                        edit: lsp::WorkspaceEdit {
 9184                            changes: Some(
 9185                                [(
 9186                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9187                                    vec![lsp::TextEdit {
 9188                                        range: lsp::Range::new(
 9189                                            lsp::Position::new(0, 0),
 9190                                            lsp::Position::new(0, 0),
 9191                                        ),
 9192                                        new_text: "applied-code-action-1-command\n".into(),
 9193                                    }],
 9194                                )]
 9195                                .into_iter()
 9196                                .collect(),
 9197                            ),
 9198                            ..Default::default()
 9199                        },
 9200                    })
 9201                    .await
 9202                    .into_response()
 9203                    .unwrap();
 9204                Ok(Some(json!(null)))
 9205            }
 9206        }
 9207    });
 9208
 9209    cx.executor().start_waiting();
 9210    editor
 9211        .update_in(cx, |editor, window, cx| {
 9212            editor.perform_format(
 9213                project.clone(),
 9214                FormatTrigger::Manual,
 9215                FormatTarget::Buffers,
 9216                window,
 9217                cx,
 9218            )
 9219        })
 9220        .unwrap()
 9221        .await;
 9222    editor.update(cx, |editor, cx| {
 9223        assert_eq!(
 9224            editor.text(cx),
 9225            r#"
 9226                applied-code-action-2-edit
 9227                applied-code-action-1-command
 9228                applied-code-action-1-edit
 9229                applied-formatting
 9230                one
 9231                two
 9232                three
 9233            "#
 9234            .unindent()
 9235        );
 9236    });
 9237
 9238    editor.update_in(cx, |editor, window, cx| {
 9239        editor.undo(&Default::default(), window, cx);
 9240        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9241    });
 9242
 9243    // Perform a manual edit while waiting for an LSP command
 9244    // that's being run as part of a formatting code action.
 9245    let lock_guard = command_lock.lock().await;
 9246    let format = editor
 9247        .update_in(cx, |editor, window, cx| {
 9248            editor.perform_format(
 9249                project.clone(),
 9250                FormatTrigger::Manual,
 9251                FormatTarget::Buffers,
 9252                window,
 9253                cx,
 9254            )
 9255        })
 9256        .unwrap();
 9257    cx.run_until_parked();
 9258    editor.update(cx, |editor, cx| {
 9259        assert_eq!(
 9260            editor.text(cx),
 9261            r#"
 9262                applied-code-action-1-edit
 9263                applied-formatting
 9264                one
 9265                two
 9266                three
 9267            "#
 9268            .unindent()
 9269        );
 9270
 9271        editor.buffer.update(cx, |buffer, cx| {
 9272            let ix = buffer.len(cx);
 9273            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9274        });
 9275    });
 9276
 9277    // Allow the LSP command to proceed. Because the buffer was edited,
 9278    // the second code action will not be run.
 9279    drop(lock_guard);
 9280    format.await;
 9281    editor.update_in(cx, |editor, window, cx| {
 9282        assert_eq!(
 9283            editor.text(cx),
 9284            r#"
 9285                applied-code-action-1-command
 9286                applied-code-action-1-edit
 9287                applied-formatting
 9288                one
 9289                two
 9290                three
 9291                edited
 9292            "#
 9293            .unindent()
 9294        );
 9295
 9296        // The manual edit is undone first, because it is the last thing the user did
 9297        // (even though the command completed afterwards).
 9298        editor.undo(&Default::default(), window, cx);
 9299        assert_eq!(
 9300            editor.text(cx),
 9301            r#"
 9302                applied-code-action-1-command
 9303                applied-code-action-1-edit
 9304                applied-formatting
 9305                one
 9306                two
 9307                three
 9308            "#
 9309            .unindent()
 9310        );
 9311
 9312        // All the formatting (including the command, which completed after the manual edit)
 9313        // is undone together.
 9314        editor.undo(&Default::default(), window, cx);
 9315        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9316    });
 9317}
 9318
 9319#[gpui::test]
 9320async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9321    init_test(cx, |settings| {
 9322        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9323            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9324        ))
 9325    });
 9326
 9327    let fs = FakeFs::new(cx.executor());
 9328    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9329
 9330    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9331
 9332    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9333    language_registry.add(Arc::new(Language::new(
 9334        LanguageConfig {
 9335            name: "TypeScript".into(),
 9336            matcher: LanguageMatcher {
 9337                path_suffixes: vec!["ts".to_string()],
 9338                ..Default::default()
 9339            },
 9340            ..LanguageConfig::default()
 9341        },
 9342        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9343    )));
 9344    update_test_language_settings(cx, |settings| {
 9345        settings.defaults.prettier = Some(PrettierSettings {
 9346            allowed: true,
 9347            ..PrettierSettings::default()
 9348        });
 9349    });
 9350    let mut fake_servers = language_registry.register_fake_lsp(
 9351        "TypeScript",
 9352        FakeLspAdapter {
 9353            capabilities: lsp::ServerCapabilities {
 9354                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9355                ..Default::default()
 9356            },
 9357            ..Default::default()
 9358        },
 9359    );
 9360
 9361    let buffer = project
 9362        .update(cx, |project, cx| {
 9363            project.open_local_buffer(path!("/file.ts"), cx)
 9364        })
 9365        .await
 9366        .unwrap();
 9367
 9368    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9369    let (editor, cx) = cx.add_window_view(|window, cx| {
 9370        build_editor_with_project(project.clone(), buffer, window, cx)
 9371    });
 9372    editor.update_in(cx, |editor, window, cx| {
 9373        editor.set_text(
 9374            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9375            window,
 9376            cx,
 9377        )
 9378    });
 9379
 9380    cx.executor().start_waiting();
 9381    let fake_server = fake_servers.next().await.unwrap();
 9382
 9383    let format = editor
 9384        .update_in(cx, |editor, window, cx| {
 9385            editor.perform_code_action_kind(
 9386                project.clone(),
 9387                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9388                window,
 9389                cx,
 9390            )
 9391        })
 9392        .unwrap();
 9393    fake_server
 9394        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9395            assert_eq!(
 9396                params.text_document.uri,
 9397                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9398            );
 9399            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9400                lsp::CodeAction {
 9401                    title: "Organize Imports".to_string(),
 9402                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9403                    edit: Some(lsp::WorkspaceEdit {
 9404                        changes: Some(
 9405                            [(
 9406                                params.text_document.uri.clone(),
 9407                                vec![lsp::TextEdit::new(
 9408                                    lsp::Range::new(
 9409                                        lsp::Position::new(1, 0),
 9410                                        lsp::Position::new(2, 0),
 9411                                    ),
 9412                                    "".to_string(),
 9413                                )],
 9414                            )]
 9415                            .into_iter()
 9416                            .collect(),
 9417                        ),
 9418                        ..Default::default()
 9419                    }),
 9420                    ..Default::default()
 9421                },
 9422            )]))
 9423        })
 9424        .next()
 9425        .await;
 9426    cx.executor().start_waiting();
 9427    format.await;
 9428    assert_eq!(
 9429        editor.update(cx, |editor, cx| editor.text(cx)),
 9430        "import { a } from 'module';\n\nconst x = a;\n"
 9431    );
 9432
 9433    editor.update_in(cx, |editor, window, cx| {
 9434        editor.set_text(
 9435            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9436            window,
 9437            cx,
 9438        )
 9439    });
 9440    // Ensure we don't lock if code action hangs.
 9441    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9442        move |params, _| async move {
 9443            assert_eq!(
 9444                params.text_document.uri,
 9445                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9446            );
 9447            futures::future::pending::<()>().await;
 9448            unreachable!()
 9449        },
 9450    );
 9451    let format = editor
 9452        .update_in(cx, |editor, window, cx| {
 9453            editor.perform_code_action_kind(
 9454                project,
 9455                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9456                window,
 9457                cx,
 9458            )
 9459        })
 9460        .unwrap();
 9461    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9462    cx.executor().start_waiting();
 9463    format.await;
 9464    assert_eq!(
 9465        editor.update(cx, |editor, cx| editor.text(cx)),
 9466        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9467    );
 9468}
 9469
 9470#[gpui::test]
 9471async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9472    init_test(cx, |_| {});
 9473
 9474    let mut cx = EditorLspTestContext::new_rust(
 9475        lsp::ServerCapabilities {
 9476            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9477            ..Default::default()
 9478        },
 9479        cx,
 9480    )
 9481    .await;
 9482
 9483    cx.set_state(indoc! {"
 9484        one.twoˇ
 9485    "});
 9486
 9487    // The format request takes a long time. When it completes, it inserts
 9488    // a newline and an indent before the `.`
 9489    cx.lsp
 9490        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9491            let executor = cx.background_executor().clone();
 9492            async move {
 9493                executor.timer(Duration::from_millis(100)).await;
 9494                Ok(Some(vec![lsp::TextEdit {
 9495                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9496                    new_text: "\n    ".into(),
 9497                }]))
 9498            }
 9499        });
 9500
 9501    // Submit a format request.
 9502    let format_1 = cx
 9503        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9504        .unwrap();
 9505    cx.executor().run_until_parked();
 9506
 9507    // Submit a second format request.
 9508    let format_2 = cx
 9509        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9510        .unwrap();
 9511    cx.executor().run_until_parked();
 9512
 9513    // Wait for both format requests to complete
 9514    cx.executor().advance_clock(Duration::from_millis(200));
 9515    cx.executor().start_waiting();
 9516    format_1.await.unwrap();
 9517    cx.executor().start_waiting();
 9518    format_2.await.unwrap();
 9519
 9520    // The formatting edits only happens once.
 9521    cx.assert_editor_state(indoc! {"
 9522        one
 9523            .twoˇ
 9524    "});
 9525}
 9526
 9527#[gpui::test]
 9528async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9529    init_test(cx, |settings| {
 9530        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9531    });
 9532
 9533    let mut cx = EditorLspTestContext::new_rust(
 9534        lsp::ServerCapabilities {
 9535            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9536            ..Default::default()
 9537        },
 9538        cx,
 9539    )
 9540    .await;
 9541
 9542    // Set up a buffer white some trailing whitespace and no trailing newline.
 9543    cx.set_state(
 9544        &[
 9545            "one ",   //
 9546            "twoˇ",   //
 9547            "three ", //
 9548            "four",   //
 9549        ]
 9550        .join("\n"),
 9551    );
 9552
 9553    // Submit a format request.
 9554    let format = cx
 9555        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9556        .unwrap();
 9557
 9558    // Record which buffer changes have been sent to the language server
 9559    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9560    cx.lsp
 9561        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9562            let buffer_changes = buffer_changes.clone();
 9563            move |params, _| {
 9564                buffer_changes.lock().extend(
 9565                    params
 9566                        .content_changes
 9567                        .into_iter()
 9568                        .map(|e| (e.range.unwrap(), e.text)),
 9569                );
 9570            }
 9571        });
 9572
 9573    // Handle formatting requests to the language server.
 9574    cx.lsp
 9575        .set_request_handler::<lsp::request::Formatting, _, _>({
 9576            let buffer_changes = buffer_changes.clone();
 9577            move |_, _| {
 9578                // When formatting is requested, trailing whitespace has already been stripped,
 9579                // and the trailing newline has already been added.
 9580                assert_eq!(
 9581                    &buffer_changes.lock()[1..],
 9582                    &[
 9583                        (
 9584                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9585                            "".into()
 9586                        ),
 9587                        (
 9588                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9589                            "".into()
 9590                        ),
 9591                        (
 9592                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9593                            "\n".into()
 9594                        ),
 9595                    ]
 9596                );
 9597
 9598                // Insert blank lines between each line of the buffer.
 9599                async move {
 9600                    Ok(Some(vec![
 9601                        lsp::TextEdit {
 9602                            range: lsp::Range::new(
 9603                                lsp::Position::new(1, 0),
 9604                                lsp::Position::new(1, 0),
 9605                            ),
 9606                            new_text: "\n".into(),
 9607                        },
 9608                        lsp::TextEdit {
 9609                            range: lsp::Range::new(
 9610                                lsp::Position::new(2, 0),
 9611                                lsp::Position::new(2, 0),
 9612                            ),
 9613                            new_text: "\n".into(),
 9614                        },
 9615                    ]))
 9616                }
 9617            }
 9618        });
 9619
 9620    // After formatting the buffer, the trailing whitespace is stripped,
 9621    // a newline is appended, and the edits provided by the language server
 9622    // have been applied.
 9623    format.await.unwrap();
 9624    cx.assert_editor_state(
 9625        &[
 9626            "one",   //
 9627            "",      //
 9628            "twoˇ",  //
 9629            "",      //
 9630            "three", //
 9631            "four",  //
 9632            "",      //
 9633        ]
 9634        .join("\n"),
 9635    );
 9636
 9637    // Undoing the formatting undoes the trailing whitespace removal, the
 9638    // trailing newline, and the LSP edits.
 9639    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9640    cx.assert_editor_state(
 9641        &[
 9642            "one ",   //
 9643            "twoˇ",   //
 9644            "three ", //
 9645            "four",   //
 9646        ]
 9647        .join("\n"),
 9648    );
 9649}
 9650
 9651#[gpui::test]
 9652async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9653    cx: &mut TestAppContext,
 9654) {
 9655    init_test(cx, |_| {});
 9656
 9657    cx.update(|cx| {
 9658        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9659            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9660                settings.auto_signature_help = Some(true);
 9661            });
 9662        });
 9663    });
 9664
 9665    let mut cx = EditorLspTestContext::new_rust(
 9666        lsp::ServerCapabilities {
 9667            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9668                ..Default::default()
 9669            }),
 9670            ..Default::default()
 9671        },
 9672        cx,
 9673    )
 9674    .await;
 9675
 9676    let language = Language::new(
 9677        LanguageConfig {
 9678            name: "Rust".into(),
 9679            brackets: BracketPairConfig {
 9680                pairs: vec![
 9681                    BracketPair {
 9682                        start: "{".to_string(),
 9683                        end: "}".to_string(),
 9684                        close: true,
 9685                        surround: true,
 9686                        newline: true,
 9687                    },
 9688                    BracketPair {
 9689                        start: "(".to_string(),
 9690                        end: ")".to_string(),
 9691                        close: true,
 9692                        surround: true,
 9693                        newline: true,
 9694                    },
 9695                    BracketPair {
 9696                        start: "/*".to_string(),
 9697                        end: " */".to_string(),
 9698                        close: true,
 9699                        surround: true,
 9700                        newline: true,
 9701                    },
 9702                    BracketPair {
 9703                        start: "[".to_string(),
 9704                        end: "]".to_string(),
 9705                        close: false,
 9706                        surround: false,
 9707                        newline: true,
 9708                    },
 9709                    BracketPair {
 9710                        start: "\"".to_string(),
 9711                        end: "\"".to_string(),
 9712                        close: true,
 9713                        surround: true,
 9714                        newline: false,
 9715                    },
 9716                    BracketPair {
 9717                        start: "<".to_string(),
 9718                        end: ">".to_string(),
 9719                        close: false,
 9720                        surround: true,
 9721                        newline: true,
 9722                    },
 9723                ],
 9724                ..Default::default()
 9725            },
 9726            autoclose_before: "})]".to_string(),
 9727            ..Default::default()
 9728        },
 9729        Some(tree_sitter_rust::LANGUAGE.into()),
 9730    );
 9731    let language = Arc::new(language);
 9732
 9733    cx.language_registry().add(language.clone());
 9734    cx.update_buffer(|buffer, cx| {
 9735        buffer.set_language(Some(language), cx);
 9736    });
 9737
 9738    cx.set_state(
 9739        &r#"
 9740            fn main() {
 9741                sampleˇ
 9742            }
 9743        "#
 9744        .unindent(),
 9745    );
 9746
 9747    cx.update_editor(|editor, window, cx| {
 9748        editor.handle_input("(", window, cx);
 9749    });
 9750    cx.assert_editor_state(
 9751        &"
 9752            fn main() {
 9753                sample(ˇ)
 9754            }
 9755        "
 9756        .unindent(),
 9757    );
 9758
 9759    let mocked_response = lsp::SignatureHelp {
 9760        signatures: vec![lsp::SignatureInformation {
 9761            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9762            documentation: None,
 9763            parameters: Some(vec![
 9764                lsp::ParameterInformation {
 9765                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9766                    documentation: None,
 9767                },
 9768                lsp::ParameterInformation {
 9769                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9770                    documentation: None,
 9771                },
 9772            ]),
 9773            active_parameter: None,
 9774        }],
 9775        active_signature: Some(0),
 9776        active_parameter: Some(0),
 9777    };
 9778    handle_signature_help_request(&mut cx, mocked_response).await;
 9779
 9780    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9781        .await;
 9782
 9783    cx.editor(|editor, _, _| {
 9784        let signature_help_state = editor.signature_help_state.popover().cloned();
 9785        assert_eq!(
 9786            signature_help_state.unwrap().label,
 9787            "param1: u8, param2: u8"
 9788        );
 9789    });
 9790}
 9791
 9792#[gpui::test]
 9793async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9794    init_test(cx, |_| {});
 9795
 9796    cx.update(|cx| {
 9797        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9798            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9799                settings.auto_signature_help = Some(false);
 9800                settings.show_signature_help_after_edits = Some(false);
 9801            });
 9802        });
 9803    });
 9804
 9805    let mut cx = EditorLspTestContext::new_rust(
 9806        lsp::ServerCapabilities {
 9807            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9808                ..Default::default()
 9809            }),
 9810            ..Default::default()
 9811        },
 9812        cx,
 9813    )
 9814    .await;
 9815
 9816    let language = Language::new(
 9817        LanguageConfig {
 9818            name: "Rust".into(),
 9819            brackets: BracketPairConfig {
 9820                pairs: vec![
 9821                    BracketPair {
 9822                        start: "{".to_string(),
 9823                        end: "}".to_string(),
 9824                        close: true,
 9825                        surround: true,
 9826                        newline: true,
 9827                    },
 9828                    BracketPair {
 9829                        start: "(".to_string(),
 9830                        end: ")".to_string(),
 9831                        close: true,
 9832                        surround: true,
 9833                        newline: true,
 9834                    },
 9835                    BracketPair {
 9836                        start: "/*".to_string(),
 9837                        end: " */".to_string(),
 9838                        close: true,
 9839                        surround: true,
 9840                        newline: true,
 9841                    },
 9842                    BracketPair {
 9843                        start: "[".to_string(),
 9844                        end: "]".to_string(),
 9845                        close: false,
 9846                        surround: false,
 9847                        newline: true,
 9848                    },
 9849                    BracketPair {
 9850                        start: "\"".to_string(),
 9851                        end: "\"".to_string(),
 9852                        close: true,
 9853                        surround: true,
 9854                        newline: false,
 9855                    },
 9856                    BracketPair {
 9857                        start: "<".to_string(),
 9858                        end: ">".to_string(),
 9859                        close: false,
 9860                        surround: true,
 9861                        newline: true,
 9862                    },
 9863                ],
 9864                ..Default::default()
 9865            },
 9866            autoclose_before: "})]".to_string(),
 9867            ..Default::default()
 9868        },
 9869        Some(tree_sitter_rust::LANGUAGE.into()),
 9870    );
 9871    let language = Arc::new(language);
 9872
 9873    cx.language_registry().add(language.clone());
 9874    cx.update_buffer(|buffer, cx| {
 9875        buffer.set_language(Some(language), cx);
 9876    });
 9877
 9878    // Ensure that signature_help is not called when no signature help is enabled.
 9879    cx.set_state(
 9880        &r#"
 9881            fn main() {
 9882                sampleˇ
 9883            }
 9884        "#
 9885        .unindent(),
 9886    );
 9887    cx.update_editor(|editor, window, cx| {
 9888        editor.handle_input("(", window, cx);
 9889    });
 9890    cx.assert_editor_state(
 9891        &"
 9892            fn main() {
 9893                sample(ˇ)
 9894            }
 9895        "
 9896        .unindent(),
 9897    );
 9898    cx.editor(|editor, _, _| {
 9899        assert!(editor.signature_help_state.task().is_none());
 9900    });
 9901
 9902    let mocked_response = lsp::SignatureHelp {
 9903        signatures: vec![lsp::SignatureInformation {
 9904            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9905            documentation: None,
 9906            parameters: Some(vec![
 9907                lsp::ParameterInformation {
 9908                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9909                    documentation: None,
 9910                },
 9911                lsp::ParameterInformation {
 9912                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9913                    documentation: None,
 9914                },
 9915            ]),
 9916            active_parameter: None,
 9917        }],
 9918        active_signature: Some(0),
 9919        active_parameter: Some(0),
 9920    };
 9921
 9922    // Ensure that signature_help is called when enabled afte edits
 9923    cx.update(|_, cx| {
 9924        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9925            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9926                settings.auto_signature_help = Some(false);
 9927                settings.show_signature_help_after_edits = Some(true);
 9928            });
 9929        });
 9930    });
 9931    cx.set_state(
 9932        &r#"
 9933            fn main() {
 9934                sampleˇ
 9935            }
 9936        "#
 9937        .unindent(),
 9938    );
 9939    cx.update_editor(|editor, window, cx| {
 9940        editor.handle_input("(", window, cx);
 9941    });
 9942    cx.assert_editor_state(
 9943        &"
 9944            fn main() {
 9945                sample(ˇ)
 9946            }
 9947        "
 9948        .unindent(),
 9949    );
 9950    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9951    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9952        .await;
 9953    cx.update_editor(|editor, _, _| {
 9954        let signature_help_state = editor.signature_help_state.popover().cloned();
 9955        assert!(signature_help_state.is_some());
 9956        assert_eq!(
 9957            signature_help_state.unwrap().label,
 9958            "param1: u8, param2: u8"
 9959        );
 9960        editor.signature_help_state = SignatureHelpState::default();
 9961    });
 9962
 9963    // Ensure that signature_help is called when auto signature help override is enabled
 9964    cx.update(|_, cx| {
 9965        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9966            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9967                settings.auto_signature_help = Some(true);
 9968                settings.show_signature_help_after_edits = Some(false);
 9969            });
 9970        });
 9971    });
 9972    cx.set_state(
 9973        &r#"
 9974            fn main() {
 9975                sampleˇ
 9976            }
 9977        "#
 9978        .unindent(),
 9979    );
 9980    cx.update_editor(|editor, window, cx| {
 9981        editor.handle_input("(", window, cx);
 9982    });
 9983    cx.assert_editor_state(
 9984        &"
 9985            fn main() {
 9986                sample(ˇ)
 9987            }
 9988        "
 9989        .unindent(),
 9990    );
 9991    handle_signature_help_request(&mut cx, mocked_response).await;
 9992    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9993        .await;
 9994    cx.editor(|editor, _, _| {
 9995        let signature_help_state = editor.signature_help_state.popover().cloned();
 9996        assert!(signature_help_state.is_some());
 9997        assert_eq!(
 9998            signature_help_state.unwrap().label,
 9999            "param1: u8, param2: u8"
10000        );
10001    });
10002}
10003
10004#[gpui::test]
10005async fn test_signature_help(cx: &mut TestAppContext) {
10006    init_test(cx, |_| {});
10007    cx.update(|cx| {
10008        cx.update_global::<SettingsStore, _>(|settings, cx| {
10009            settings.update_user_settings::<EditorSettings>(cx, |settings| {
10010                settings.auto_signature_help = Some(true);
10011            });
10012        });
10013    });
10014
10015    let mut cx = EditorLspTestContext::new_rust(
10016        lsp::ServerCapabilities {
10017            signature_help_provider: Some(lsp::SignatureHelpOptions {
10018                ..Default::default()
10019            }),
10020            ..Default::default()
10021        },
10022        cx,
10023    )
10024    .await;
10025
10026    // A test that directly calls `show_signature_help`
10027    cx.update_editor(|editor, window, cx| {
10028        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10029    });
10030
10031    let mocked_response = lsp::SignatureHelp {
10032        signatures: vec![lsp::SignatureInformation {
10033            label: "fn sample(param1: u8, param2: u8)".to_string(),
10034            documentation: None,
10035            parameters: Some(vec![
10036                lsp::ParameterInformation {
10037                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10038                    documentation: None,
10039                },
10040                lsp::ParameterInformation {
10041                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10042                    documentation: None,
10043                },
10044            ]),
10045            active_parameter: None,
10046        }],
10047        active_signature: Some(0),
10048        active_parameter: Some(0),
10049    };
10050    handle_signature_help_request(&mut cx, mocked_response).await;
10051
10052    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10053        .await;
10054
10055    cx.editor(|editor, _, _| {
10056        let signature_help_state = editor.signature_help_state.popover().cloned();
10057        assert!(signature_help_state.is_some());
10058        assert_eq!(
10059            signature_help_state.unwrap().label,
10060            "param1: u8, param2: u8"
10061        );
10062    });
10063
10064    // When exiting outside from inside the brackets, `signature_help` is closed.
10065    cx.set_state(indoc! {"
10066        fn main() {
10067            sample(ˇ);
10068        }
10069
10070        fn sample(param1: u8, param2: u8) {}
10071    "});
10072
10073    cx.update_editor(|editor, window, cx| {
10074        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10075    });
10076
10077    let mocked_response = lsp::SignatureHelp {
10078        signatures: Vec::new(),
10079        active_signature: None,
10080        active_parameter: None,
10081    };
10082    handle_signature_help_request(&mut cx, mocked_response).await;
10083
10084    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10085        .await;
10086
10087    cx.editor(|editor, _, _| {
10088        assert!(!editor.signature_help_state.is_shown());
10089    });
10090
10091    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
10092    cx.set_state(indoc! {"
10093        fn main() {
10094            sample(ˇ);
10095        }
10096
10097        fn sample(param1: u8, param2: u8) {}
10098    "});
10099
10100    let mocked_response = lsp::SignatureHelp {
10101        signatures: vec![lsp::SignatureInformation {
10102            label: "fn sample(param1: u8, param2: u8)".to_string(),
10103            documentation: None,
10104            parameters: Some(vec![
10105                lsp::ParameterInformation {
10106                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10107                    documentation: None,
10108                },
10109                lsp::ParameterInformation {
10110                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10111                    documentation: None,
10112                },
10113            ]),
10114            active_parameter: None,
10115        }],
10116        active_signature: Some(0),
10117        active_parameter: Some(0),
10118    };
10119    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10120    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10121        .await;
10122    cx.editor(|editor, _, _| {
10123        assert!(editor.signature_help_state.is_shown());
10124    });
10125
10126    // Restore the popover with more parameter input
10127    cx.set_state(indoc! {"
10128        fn main() {
10129            sample(param1, param2ˇ);
10130        }
10131
10132        fn sample(param1: u8, param2: u8) {}
10133    "});
10134
10135    let mocked_response = lsp::SignatureHelp {
10136        signatures: vec![lsp::SignatureInformation {
10137            label: "fn sample(param1: u8, param2: u8)".to_string(),
10138            documentation: None,
10139            parameters: Some(vec![
10140                lsp::ParameterInformation {
10141                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10142                    documentation: None,
10143                },
10144                lsp::ParameterInformation {
10145                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10146                    documentation: None,
10147                },
10148            ]),
10149            active_parameter: None,
10150        }],
10151        active_signature: Some(0),
10152        active_parameter: Some(1),
10153    };
10154    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10155    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10156        .await;
10157
10158    // When selecting a range, the popover is gone.
10159    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10160    cx.update_editor(|editor, window, cx| {
10161        editor.change_selections(None, window, cx, |s| {
10162            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10163        })
10164    });
10165    cx.assert_editor_state(indoc! {"
10166        fn main() {
10167            sample(param1, «ˇparam2»);
10168        }
10169
10170        fn sample(param1: u8, param2: u8) {}
10171    "});
10172    cx.editor(|editor, _, _| {
10173        assert!(!editor.signature_help_state.is_shown());
10174    });
10175
10176    // When unselecting again, the popover is back if within the brackets.
10177    cx.update_editor(|editor, window, cx| {
10178        editor.change_selections(None, window, cx, |s| {
10179            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10180        })
10181    });
10182    cx.assert_editor_state(indoc! {"
10183        fn main() {
10184            sample(param1, ˇparam2);
10185        }
10186
10187        fn sample(param1: u8, param2: u8) {}
10188    "});
10189    handle_signature_help_request(&mut cx, mocked_response).await;
10190    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10191        .await;
10192    cx.editor(|editor, _, _| {
10193        assert!(editor.signature_help_state.is_shown());
10194    });
10195
10196    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10197    cx.update_editor(|editor, window, cx| {
10198        editor.change_selections(None, window, cx, |s| {
10199            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10200            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10201        })
10202    });
10203    cx.assert_editor_state(indoc! {"
10204        fn main() {
10205            sample(param1, ˇparam2);
10206        }
10207
10208        fn sample(param1: u8, param2: u8) {}
10209    "});
10210
10211    let mocked_response = lsp::SignatureHelp {
10212        signatures: vec![lsp::SignatureInformation {
10213            label: "fn sample(param1: u8, param2: u8)".to_string(),
10214            documentation: None,
10215            parameters: Some(vec![
10216                lsp::ParameterInformation {
10217                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10218                    documentation: None,
10219                },
10220                lsp::ParameterInformation {
10221                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10222                    documentation: None,
10223                },
10224            ]),
10225            active_parameter: None,
10226        }],
10227        active_signature: Some(0),
10228        active_parameter: Some(1),
10229    };
10230    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10231    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10232        .await;
10233    cx.update_editor(|editor, _, cx| {
10234        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10235    });
10236    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10237        .await;
10238    cx.update_editor(|editor, window, cx| {
10239        editor.change_selections(None, window, cx, |s| {
10240            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10241        })
10242    });
10243    cx.assert_editor_state(indoc! {"
10244        fn main() {
10245            sample(param1, «ˇparam2»);
10246        }
10247
10248        fn sample(param1: u8, param2: u8) {}
10249    "});
10250    cx.update_editor(|editor, window, cx| {
10251        editor.change_selections(None, window, cx, |s| {
10252            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10253        })
10254    });
10255    cx.assert_editor_state(indoc! {"
10256        fn main() {
10257            sample(param1, ˇparam2);
10258        }
10259
10260        fn sample(param1: u8, param2: u8) {}
10261    "});
10262    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10263        .await;
10264}
10265
10266#[gpui::test]
10267async fn test_completion_mode(cx: &mut TestAppContext) {
10268    init_test(cx, |_| {});
10269    let mut cx = EditorLspTestContext::new_rust(
10270        lsp::ServerCapabilities {
10271            completion_provider: Some(lsp::CompletionOptions {
10272                resolve_provider: Some(true),
10273                ..Default::default()
10274            }),
10275            ..Default::default()
10276        },
10277        cx,
10278    )
10279    .await;
10280
10281    struct Run {
10282        run_description: &'static str,
10283        initial_state: String,
10284        buffer_marked_text: String,
10285        completion_text: &'static str,
10286        expected_with_insert_mode: String,
10287        expected_with_replace_mode: String,
10288        expected_with_replace_subsequence_mode: String,
10289        expected_with_replace_suffix_mode: String,
10290    }
10291
10292    let runs = [
10293        Run {
10294            run_description: "Start of word matches completion text",
10295            initial_state: "before ediˇ after".into(),
10296            buffer_marked_text: "before <edi|> after".into(),
10297            completion_text: "editor",
10298            expected_with_insert_mode: "before editorˇ after".into(),
10299            expected_with_replace_mode: "before editorˇ after".into(),
10300            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10301            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10302        },
10303        Run {
10304            run_description: "Accept same text at the middle of the word",
10305            initial_state: "before ediˇtor after".into(),
10306            buffer_marked_text: "before <edi|tor> after".into(),
10307            completion_text: "editor",
10308            expected_with_insert_mode: "before editorˇtor after".into(),
10309            expected_with_replace_mode: "before editorˇ after".into(),
10310            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10311            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10312        },
10313        Run {
10314            run_description: "End of word matches completion text -- cursor at end",
10315            initial_state: "before torˇ after".into(),
10316            buffer_marked_text: "before <tor|> after".into(),
10317            completion_text: "editor",
10318            expected_with_insert_mode: "before editorˇ after".into(),
10319            expected_with_replace_mode: "before editorˇ after".into(),
10320            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10321            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10322        },
10323        Run {
10324            run_description: "End of word matches completion text -- cursor at start",
10325            initial_state: "before ˇtor after".into(),
10326            buffer_marked_text: "before <|tor> after".into(),
10327            completion_text: "editor",
10328            expected_with_insert_mode: "before editorˇtor after".into(),
10329            expected_with_replace_mode: "before editorˇ after".into(),
10330            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10331            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10332        },
10333        Run {
10334            run_description: "Prepend text containing whitespace",
10335            initial_state: "pˇfield: bool".into(),
10336            buffer_marked_text: "<p|field>: bool".into(),
10337            completion_text: "pub ",
10338            expected_with_insert_mode: "pub ˇfield: bool".into(),
10339            expected_with_replace_mode: "pub ˇ: bool".into(),
10340            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10341            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10342        },
10343        Run {
10344            run_description: "Add element to start of list",
10345            initial_state: "[element_ˇelement_2]".into(),
10346            buffer_marked_text: "[<element_|element_2>]".into(),
10347            completion_text: "element_1",
10348            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10349            expected_with_replace_mode: "[element_1ˇ]".into(),
10350            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10351            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10352        },
10353        Run {
10354            run_description: "Add element to start of list -- first and second elements are equal",
10355            initial_state: "[elˇelement]".into(),
10356            buffer_marked_text: "[<el|element>]".into(),
10357            completion_text: "element",
10358            expected_with_insert_mode: "[elementˇelement]".into(),
10359            expected_with_replace_mode: "[elementˇ]".into(),
10360            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10361            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10362        },
10363        Run {
10364            run_description: "Ends with matching suffix",
10365            initial_state: "SubˇError".into(),
10366            buffer_marked_text: "<Sub|Error>".into(),
10367            completion_text: "SubscriptionError",
10368            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10369            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10370            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10371            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10372        },
10373        Run {
10374            run_description: "Suffix is a subsequence -- contiguous",
10375            initial_state: "SubˇErr".into(),
10376            buffer_marked_text: "<Sub|Err>".into(),
10377            completion_text: "SubscriptionError",
10378            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10379            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10380            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10381            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10382        },
10383        Run {
10384            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10385            initial_state: "Suˇscrirr".into(),
10386            buffer_marked_text: "<Su|scrirr>".into(),
10387            completion_text: "SubscriptionError",
10388            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10389            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10390            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10391            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10392        },
10393        Run {
10394            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10395            initial_state: "foo(indˇix)".into(),
10396            buffer_marked_text: "foo(<ind|ix>)".into(),
10397            completion_text: "node_index",
10398            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10399            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10400            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10401            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10402        },
10403    ];
10404
10405    for run in runs {
10406        let run_variations = [
10407            (LspInsertMode::Insert, run.expected_with_insert_mode),
10408            (LspInsertMode::Replace, run.expected_with_replace_mode),
10409            (
10410                LspInsertMode::ReplaceSubsequence,
10411                run.expected_with_replace_subsequence_mode,
10412            ),
10413            (
10414                LspInsertMode::ReplaceSuffix,
10415                run.expected_with_replace_suffix_mode,
10416            ),
10417        ];
10418
10419        for (lsp_insert_mode, expected_text) in run_variations {
10420            eprintln!(
10421                "run = {:?}, mode = {lsp_insert_mode:.?}",
10422                run.run_description,
10423            );
10424
10425            update_test_language_settings(&mut cx, |settings| {
10426                settings.defaults.completions = Some(CompletionSettings {
10427                    lsp_insert_mode,
10428                    words: WordsCompletionMode::Disabled,
10429                    lsp: true,
10430                    lsp_fetch_timeout_ms: 0,
10431                });
10432            });
10433
10434            cx.set_state(&run.initial_state);
10435            cx.update_editor(|editor, window, cx| {
10436                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10437            });
10438
10439            let counter = Arc::new(AtomicUsize::new(0));
10440            handle_completion_request_with_insert_and_replace(
10441                &mut cx,
10442                &run.buffer_marked_text,
10443                vec![run.completion_text],
10444                counter.clone(),
10445            )
10446            .await;
10447            cx.condition(|editor, _| editor.context_menu_visible())
10448                .await;
10449            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10450
10451            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10452                editor
10453                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10454                    .unwrap()
10455            });
10456            cx.assert_editor_state(&expected_text);
10457            handle_resolve_completion_request(&mut cx, None).await;
10458            apply_additional_edits.await.unwrap();
10459        }
10460    }
10461}
10462
10463#[gpui::test]
10464async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10465    init_test(cx, |_| {});
10466    let mut cx = EditorLspTestContext::new_rust(
10467        lsp::ServerCapabilities {
10468            completion_provider: Some(lsp::CompletionOptions {
10469                resolve_provider: Some(true),
10470                ..Default::default()
10471            }),
10472            ..Default::default()
10473        },
10474        cx,
10475    )
10476    .await;
10477
10478    let initial_state = "SubˇError";
10479    let buffer_marked_text = "<Sub|Error>";
10480    let completion_text = "SubscriptionError";
10481    let expected_with_insert_mode = "SubscriptionErrorˇError";
10482    let expected_with_replace_mode = "SubscriptionErrorˇ";
10483
10484    update_test_language_settings(&mut cx, |settings| {
10485        settings.defaults.completions = Some(CompletionSettings {
10486            words: WordsCompletionMode::Disabled,
10487            // set the opposite here to ensure that the action is overriding the default behavior
10488            lsp_insert_mode: LspInsertMode::Insert,
10489            lsp: true,
10490            lsp_fetch_timeout_ms: 0,
10491        });
10492    });
10493
10494    cx.set_state(initial_state);
10495    cx.update_editor(|editor, window, cx| {
10496        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10497    });
10498
10499    let counter = Arc::new(AtomicUsize::new(0));
10500    handle_completion_request_with_insert_and_replace(
10501        &mut cx,
10502        &buffer_marked_text,
10503        vec![completion_text],
10504        counter.clone(),
10505    )
10506    .await;
10507    cx.condition(|editor, _| editor.context_menu_visible())
10508        .await;
10509    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10510
10511    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10512        editor
10513            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10514            .unwrap()
10515    });
10516    cx.assert_editor_state(&expected_with_replace_mode);
10517    handle_resolve_completion_request(&mut cx, None).await;
10518    apply_additional_edits.await.unwrap();
10519
10520    update_test_language_settings(&mut cx, |settings| {
10521        settings.defaults.completions = Some(CompletionSettings {
10522            words: WordsCompletionMode::Disabled,
10523            // set the opposite here to ensure that the action is overriding the default behavior
10524            lsp_insert_mode: LspInsertMode::Replace,
10525            lsp: true,
10526            lsp_fetch_timeout_ms: 0,
10527        });
10528    });
10529
10530    cx.set_state(initial_state);
10531    cx.update_editor(|editor, window, cx| {
10532        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10533    });
10534    handle_completion_request_with_insert_and_replace(
10535        &mut cx,
10536        &buffer_marked_text,
10537        vec![completion_text],
10538        counter.clone(),
10539    )
10540    .await;
10541    cx.condition(|editor, _| editor.context_menu_visible())
10542        .await;
10543    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10544
10545    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10546        editor
10547            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10548            .unwrap()
10549    });
10550    cx.assert_editor_state(&expected_with_insert_mode);
10551    handle_resolve_completion_request(&mut cx, None).await;
10552    apply_additional_edits.await.unwrap();
10553}
10554
10555#[gpui::test]
10556async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10557    init_test(cx, |_| {});
10558    let mut cx = EditorLspTestContext::new_rust(
10559        lsp::ServerCapabilities {
10560            completion_provider: Some(lsp::CompletionOptions {
10561                resolve_provider: Some(true),
10562                ..Default::default()
10563            }),
10564            ..Default::default()
10565        },
10566        cx,
10567    )
10568    .await;
10569
10570    // scenario: surrounding text matches completion text
10571    let completion_text = "to_offset";
10572    let initial_state = indoc! {"
10573        1. buf.to_offˇsuffix
10574        2. buf.to_offˇsuf
10575        3. buf.to_offˇfix
10576        4. buf.to_offˇ
10577        5. into_offˇensive
10578        6. ˇsuffix
10579        7. let ˇ //
10580        8. aaˇzz
10581        9. buf.to_off«zzzzzˇ»suffix
10582        10. buf.«ˇzzzzz»suffix
10583        11. to_off«ˇzzzzz»
10584
10585        buf.to_offˇsuffix  // newest cursor
10586    "};
10587    let completion_marked_buffer = indoc! {"
10588        1. buf.to_offsuffix
10589        2. buf.to_offsuf
10590        3. buf.to_offfix
10591        4. buf.to_off
10592        5. into_offensive
10593        6. suffix
10594        7. let  //
10595        8. aazz
10596        9. buf.to_offzzzzzsuffix
10597        10. buf.zzzzzsuffix
10598        11. to_offzzzzz
10599
10600        buf.<to_off|suffix>  // newest cursor
10601    "};
10602    let expected = indoc! {"
10603        1. buf.to_offsetˇ
10604        2. buf.to_offsetˇsuf
10605        3. buf.to_offsetˇfix
10606        4. buf.to_offsetˇ
10607        5. into_offsetˇensive
10608        6. to_offsetˇsuffix
10609        7. let to_offsetˇ //
10610        8. aato_offsetˇzz
10611        9. buf.to_offsetˇ
10612        10. buf.to_offsetˇsuffix
10613        11. to_offsetˇ
10614
10615        buf.to_offsetˇ  // newest cursor
10616    "};
10617    cx.set_state(initial_state);
10618    cx.update_editor(|editor, window, cx| {
10619        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10620    });
10621    handle_completion_request_with_insert_and_replace(
10622        &mut cx,
10623        completion_marked_buffer,
10624        vec![completion_text],
10625        Arc::new(AtomicUsize::new(0)),
10626    )
10627    .await;
10628    cx.condition(|editor, _| editor.context_menu_visible())
10629        .await;
10630    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10631        editor
10632            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10633            .unwrap()
10634    });
10635    cx.assert_editor_state(expected);
10636    handle_resolve_completion_request(&mut cx, None).await;
10637    apply_additional_edits.await.unwrap();
10638
10639    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10640    let completion_text = "foo_and_bar";
10641    let initial_state = indoc! {"
10642        1. ooanbˇ
10643        2. zooanbˇ
10644        3. ooanbˇz
10645        4. zooanbˇz
10646        5. ooanˇ
10647        6. oanbˇ
10648
10649        ooanbˇ
10650    "};
10651    let completion_marked_buffer = indoc! {"
10652        1. ooanb
10653        2. zooanb
10654        3. ooanbz
10655        4. zooanbz
10656        5. ooan
10657        6. oanb
10658
10659        <ooanb|>
10660    "};
10661    let expected = indoc! {"
10662        1. foo_and_barˇ
10663        2. zfoo_and_barˇ
10664        3. foo_and_barˇz
10665        4. zfoo_and_barˇz
10666        5. ooanfoo_and_barˇ
10667        6. oanbfoo_and_barˇ
10668
10669        foo_and_barˇ
10670    "};
10671    cx.set_state(initial_state);
10672    cx.update_editor(|editor, window, cx| {
10673        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10674    });
10675    handle_completion_request_with_insert_and_replace(
10676        &mut cx,
10677        completion_marked_buffer,
10678        vec![completion_text],
10679        Arc::new(AtomicUsize::new(0)),
10680    )
10681    .await;
10682    cx.condition(|editor, _| editor.context_menu_visible())
10683        .await;
10684    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10685        editor
10686            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10687            .unwrap()
10688    });
10689    cx.assert_editor_state(expected);
10690    handle_resolve_completion_request(&mut cx, None).await;
10691    apply_additional_edits.await.unwrap();
10692
10693    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10694    // (expects the same as if it was inserted at the end)
10695    let completion_text = "foo_and_bar";
10696    let initial_state = indoc! {"
10697        1. ooˇanb
10698        2. zooˇanb
10699        3. ooˇanbz
10700        4. zooˇanbz
10701
10702        ooˇanb
10703    "};
10704    let completion_marked_buffer = indoc! {"
10705        1. ooanb
10706        2. zooanb
10707        3. ooanbz
10708        4. zooanbz
10709
10710        <oo|anb>
10711    "};
10712    let expected = indoc! {"
10713        1. foo_and_barˇ
10714        2. zfoo_and_barˇ
10715        3. foo_and_barˇz
10716        4. zfoo_and_barˇz
10717
10718        foo_and_barˇ
10719    "};
10720    cx.set_state(initial_state);
10721    cx.update_editor(|editor, window, cx| {
10722        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10723    });
10724    handle_completion_request_with_insert_and_replace(
10725        &mut cx,
10726        completion_marked_buffer,
10727        vec![completion_text],
10728        Arc::new(AtomicUsize::new(0)),
10729    )
10730    .await;
10731    cx.condition(|editor, _| editor.context_menu_visible())
10732        .await;
10733    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10734        editor
10735            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10736            .unwrap()
10737    });
10738    cx.assert_editor_state(expected);
10739    handle_resolve_completion_request(&mut cx, None).await;
10740    apply_additional_edits.await.unwrap();
10741}
10742
10743// This used to crash
10744#[gpui::test]
10745async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10746    init_test(cx, |_| {});
10747
10748    let buffer_text = indoc! {"
10749        fn main() {
10750            10.satu;
10751
10752            //
10753            // separate cursors so they open in different excerpts (manually reproducible)
10754            //
10755
10756            10.satu20;
10757        }
10758    "};
10759    let multibuffer_text_with_selections = indoc! {"
10760        fn main() {
10761            10.satuˇ;
10762
10763            //
10764
10765            //
10766
10767            10.satuˇ20;
10768        }
10769    "};
10770    let expected_multibuffer = indoc! {"
10771        fn main() {
10772            10.saturating_sub()ˇ;
10773
10774            //
10775
10776            //
10777
10778            10.saturating_sub()ˇ;
10779        }
10780    "};
10781
10782    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10783    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10784
10785    let fs = FakeFs::new(cx.executor());
10786    fs.insert_tree(
10787        path!("/a"),
10788        json!({
10789            "main.rs": buffer_text,
10790        }),
10791    )
10792    .await;
10793
10794    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10795    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10796    language_registry.add(rust_lang());
10797    let mut fake_servers = language_registry.register_fake_lsp(
10798        "Rust",
10799        FakeLspAdapter {
10800            capabilities: lsp::ServerCapabilities {
10801                completion_provider: Some(lsp::CompletionOptions {
10802                    resolve_provider: None,
10803                    ..lsp::CompletionOptions::default()
10804                }),
10805                ..lsp::ServerCapabilities::default()
10806            },
10807            ..FakeLspAdapter::default()
10808        },
10809    );
10810    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10811    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10812    let buffer = project
10813        .update(cx, |project, cx| {
10814            project.open_local_buffer(path!("/a/main.rs"), cx)
10815        })
10816        .await
10817        .unwrap();
10818
10819    let multi_buffer = cx.new(|cx| {
10820        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10821        multi_buffer.push_excerpts(
10822            buffer.clone(),
10823            [ExcerptRange::new(0..first_excerpt_end)],
10824            cx,
10825        );
10826        multi_buffer.push_excerpts(
10827            buffer.clone(),
10828            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10829            cx,
10830        );
10831        multi_buffer
10832    });
10833
10834    let editor = workspace
10835        .update(cx, |_, window, cx| {
10836            cx.new(|cx| {
10837                Editor::new(
10838                    EditorMode::Full {
10839                        scale_ui_elements_with_buffer_font_size: false,
10840                        show_active_line_background: false,
10841                        sized_by_content: false,
10842                    },
10843                    multi_buffer.clone(),
10844                    Some(project.clone()),
10845                    window,
10846                    cx,
10847                )
10848            })
10849        })
10850        .unwrap();
10851
10852    let pane = workspace
10853        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10854        .unwrap();
10855    pane.update_in(cx, |pane, window, cx| {
10856        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10857    });
10858
10859    let fake_server = fake_servers.next().await.unwrap();
10860
10861    editor.update_in(cx, |editor, window, cx| {
10862        editor.change_selections(None, window, cx, |s| {
10863            s.select_ranges([
10864                Point::new(1, 11)..Point::new(1, 11),
10865                Point::new(7, 11)..Point::new(7, 11),
10866            ])
10867        });
10868
10869        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10870    });
10871
10872    editor.update_in(cx, |editor, window, cx| {
10873        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10874    });
10875
10876    fake_server
10877        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10878            let completion_item = lsp::CompletionItem {
10879                label: "saturating_sub()".into(),
10880                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10881                    lsp::InsertReplaceEdit {
10882                        new_text: "saturating_sub()".to_owned(),
10883                        insert: lsp::Range::new(
10884                            lsp::Position::new(7, 7),
10885                            lsp::Position::new(7, 11),
10886                        ),
10887                        replace: lsp::Range::new(
10888                            lsp::Position::new(7, 7),
10889                            lsp::Position::new(7, 13),
10890                        ),
10891                    },
10892                )),
10893                ..lsp::CompletionItem::default()
10894            };
10895
10896            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10897        })
10898        .next()
10899        .await
10900        .unwrap();
10901
10902    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10903        .await;
10904
10905    editor
10906        .update_in(cx, |editor, window, cx| {
10907            editor
10908                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10909                .unwrap()
10910        })
10911        .await
10912        .unwrap();
10913
10914    editor.update(cx, |editor, cx| {
10915        assert_text_with_selections(editor, expected_multibuffer, cx);
10916    })
10917}
10918
10919#[gpui::test]
10920async fn test_completion(cx: &mut TestAppContext) {
10921    init_test(cx, |_| {});
10922
10923    let mut cx = EditorLspTestContext::new_rust(
10924        lsp::ServerCapabilities {
10925            completion_provider: Some(lsp::CompletionOptions {
10926                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10927                resolve_provider: Some(true),
10928                ..Default::default()
10929            }),
10930            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10931            ..Default::default()
10932        },
10933        cx,
10934    )
10935    .await;
10936    let counter = Arc::new(AtomicUsize::new(0));
10937
10938    cx.set_state(indoc! {"
10939        oneˇ
10940        two
10941        three
10942    "});
10943    cx.simulate_keystroke(".");
10944    handle_completion_request(
10945        &mut cx,
10946        indoc! {"
10947            one.|<>
10948            two
10949            three
10950        "},
10951        vec!["first_completion", "second_completion"],
10952        counter.clone(),
10953    )
10954    .await;
10955    cx.condition(|editor, _| editor.context_menu_visible())
10956        .await;
10957    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10958
10959    let _handler = handle_signature_help_request(
10960        &mut cx,
10961        lsp::SignatureHelp {
10962            signatures: vec![lsp::SignatureInformation {
10963                label: "test signature".to_string(),
10964                documentation: None,
10965                parameters: Some(vec![lsp::ParameterInformation {
10966                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10967                    documentation: None,
10968                }]),
10969                active_parameter: None,
10970            }],
10971            active_signature: None,
10972            active_parameter: None,
10973        },
10974    );
10975    cx.update_editor(|editor, window, cx| {
10976        assert!(
10977            !editor.signature_help_state.is_shown(),
10978            "No signature help was called for"
10979        );
10980        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10981    });
10982    cx.run_until_parked();
10983    cx.update_editor(|editor, _, _| {
10984        assert!(
10985            !editor.signature_help_state.is_shown(),
10986            "No signature help should be shown when completions menu is open"
10987        );
10988    });
10989
10990    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10991        editor.context_menu_next(&Default::default(), window, cx);
10992        editor
10993            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10994            .unwrap()
10995    });
10996    cx.assert_editor_state(indoc! {"
10997        one.second_completionˇ
10998        two
10999        three
11000    "});
11001
11002    handle_resolve_completion_request(
11003        &mut cx,
11004        Some(vec![
11005            (
11006                //This overlaps with the primary completion edit which is
11007                //misbehavior from the LSP spec, test that we filter it out
11008                indoc! {"
11009                    one.second_ˇcompletion
11010                    two
11011                    threeˇ
11012                "},
11013                "overlapping additional edit",
11014            ),
11015            (
11016                indoc! {"
11017                    one.second_completion
11018                    two
11019                    threeˇ
11020                "},
11021                "\nadditional edit",
11022            ),
11023        ]),
11024    )
11025    .await;
11026    apply_additional_edits.await.unwrap();
11027    cx.assert_editor_state(indoc! {"
11028        one.second_completionˇ
11029        two
11030        three
11031        additional edit
11032    "});
11033
11034    cx.set_state(indoc! {"
11035        one.second_completion
11036        twoˇ
11037        threeˇ
11038        additional edit
11039    "});
11040    cx.simulate_keystroke(" ");
11041    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11042    cx.simulate_keystroke("s");
11043    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11044
11045    cx.assert_editor_state(indoc! {"
11046        one.second_completion
11047        two sˇ
11048        three sˇ
11049        additional edit
11050    "});
11051    handle_completion_request(
11052        &mut cx,
11053        indoc! {"
11054            one.second_completion
11055            two s
11056            three <s|>
11057            additional edit
11058        "},
11059        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11060        counter.clone(),
11061    )
11062    .await;
11063    cx.condition(|editor, _| editor.context_menu_visible())
11064        .await;
11065    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
11066
11067    cx.simulate_keystroke("i");
11068
11069    handle_completion_request(
11070        &mut cx,
11071        indoc! {"
11072            one.second_completion
11073            two si
11074            three <si|>
11075            additional edit
11076        "},
11077        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
11078        counter.clone(),
11079    )
11080    .await;
11081    cx.condition(|editor, _| editor.context_menu_visible())
11082        .await;
11083    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
11084
11085    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11086        editor
11087            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11088            .unwrap()
11089    });
11090    cx.assert_editor_state(indoc! {"
11091        one.second_completion
11092        two sixth_completionˇ
11093        three sixth_completionˇ
11094        additional edit
11095    "});
11096
11097    apply_additional_edits.await.unwrap();
11098
11099    update_test_language_settings(&mut cx, |settings| {
11100        settings.defaults.show_completions_on_input = Some(false);
11101    });
11102    cx.set_state("editorˇ");
11103    cx.simulate_keystroke(".");
11104    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11105    cx.simulate_keystrokes("c l o");
11106    cx.assert_editor_state("editor.cloˇ");
11107    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11108    cx.update_editor(|editor, window, cx| {
11109        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11110    });
11111    handle_completion_request(
11112        &mut cx,
11113        "editor.<clo|>",
11114        vec!["close", "clobber"],
11115        counter.clone(),
11116    )
11117    .await;
11118    cx.condition(|editor, _| editor.context_menu_visible())
11119        .await;
11120    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11121
11122    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11123        editor
11124            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11125            .unwrap()
11126    });
11127    cx.assert_editor_state("editor.closeˇ");
11128    handle_resolve_completion_request(&mut cx, None).await;
11129    apply_additional_edits.await.unwrap();
11130}
11131
11132#[gpui::test]
11133async fn test_word_completion(cx: &mut TestAppContext) {
11134    let lsp_fetch_timeout_ms = 10;
11135    init_test(cx, |language_settings| {
11136        language_settings.defaults.completions = Some(CompletionSettings {
11137            words: WordsCompletionMode::Fallback,
11138            lsp: true,
11139            lsp_fetch_timeout_ms: 10,
11140            lsp_insert_mode: LspInsertMode::Insert,
11141        });
11142    });
11143
11144    let mut cx = EditorLspTestContext::new_rust(
11145        lsp::ServerCapabilities {
11146            completion_provider: Some(lsp::CompletionOptions {
11147                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11148                ..lsp::CompletionOptions::default()
11149            }),
11150            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11151            ..lsp::ServerCapabilities::default()
11152        },
11153        cx,
11154    )
11155    .await;
11156
11157    let throttle_completions = Arc::new(AtomicBool::new(false));
11158
11159    let lsp_throttle_completions = throttle_completions.clone();
11160    let _completion_requests_handler =
11161        cx.lsp
11162            .server
11163            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11164                let lsp_throttle_completions = lsp_throttle_completions.clone();
11165                let cx = cx.clone();
11166                async move {
11167                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11168                        cx.background_executor()
11169                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11170                            .await;
11171                    }
11172                    Ok(Some(lsp::CompletionResponse::Array(vec![
11173                        lsp::CompletionItem {
11174                            label: "first".into(),
11175                            ..lsp::CompletionItem::default()
11176                        },
11177                        lsp::CompletionItem {
11178                            label: "last".into(),
11179                            ..lsp::CompletionItem::default()
11180                        },
11181                    ])))
11182                }
11183            });
11184
11185    cx.set_state(indoc! {"
11186        oneˇ
11187        two
11188        three
11189    "});
11190    cx.simulate_keystroke(".");
11191    cx.executor().run_until_parked();
11192    cx.condition(|editor, _| editor.context_menu_visible())
11193        .await;
11194    cx.update_editor(|editor, window, cx| {
11195        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11196        {
11197            assert_eq!(
11198                completion_menu_entries(&menu),
11199                &["first", "last"],
11200                "When LSP server is fast to reply, no fallback word completions are used"
11201            );
11202        } else {
11203            panic!("expected completion menu to be open");
11204        }
11205        editor.cancel(&Cancel, window, cx);
11206    });
11207    cx.executor().run_until_parked();
11208    cx.condition(|editor, _| !editor.context_menu_visible())
11209        .await;
11210
11211    throttle_completions.store(true, atomic::Ordering::Release);
11212    cx.simulate_keystroke(".");
11213    cx.executor()
11214        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11215    cx.executor().run_until_parked();
11216    cx.condition(|editor, _| editor.context_menu_visible())
11217        .await;
11218    cx.update_editor(|editor, _, _| {
11219        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11220        {
11221            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11222                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11223        } else {
11224            panic!("expected completion menu to be open");
11225        }
11226    });
11227}
11228
11229#[gpui::test]
11230async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11231    init_test(cx, |language_settings| {
11232        language_settings.defaults.completions = Some(CompletionSettings {
11233            words: WordsCompletionMode::Enabled,
11234            lsp: true,
11235            lsp_fetch_timeout_ms: 0,
11236            lsp_insert_mode: LspInsertMode::Insert,
11237        });
11238    });
11239
11240    let mut cx = EditorLspTestContext::new_rust(
11241        lsp::ServerCapabilities {
11242            completion_provider: Some(lsp::CompletionOptions {
11243                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11244                ..lsp::CompletionOptions::default()
11245            }),
11246            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11247            ..lsp::ServerCapabilities::default()
11248        },
11249        cx,
11250    )
11251    .await;
11252
11253    let _completion_requests_handler =
11254        cx.lsp
11255            .server
11256            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11257                Ok(Some(lsp::CompletionResponse::Array(vec![
11258                    lsp::CompletionItem {
11259                        label: "first".into(),
11260                        ..lsp::CompletionItem::default()
11261                    },
11262                    lsp::CompletionItem {
11263                        label: "last".into(),
11264                        ..lsp::CompletionItem::default()
11265                    },
11266                ])))
11267            });
11268
11269    cx.set_state(indoc! {"ˇ
11270        first
11271        last
11272        second
11273    "});
11274    cx.simulate_keystroke(".");
11275    cx.executor().run_until_parked();
11276    cx.condition(|editor, _| editor.context_menu_visible())
11277        .await;
11278    cx.update_editor(|editor, _, _| {
11279        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11280        {
11281            assert_eq!(
11282                completion_menu_entries(&menu),
11283                &["first", "last", "second"],
11284                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11285            );
11286        } else {
11287            panic!("expected completion menu to be open");
11288        }
11289    });
11290}
11291
11292#[gpui::test]
11293async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11294    init_test(cx, |language_settings| {
11295        language_settings.defaults.completions = Some(CompletionSettings {
11296            words: WordsCompletionMode::Disabled,
11297            lsp: true,
11298            lsp_fetch_timeout_ms: 0,
11299            lsp_insert_mode: LspInsertMode::Insert,
11300        });
11301    });
11302
11303    let mut cx = EditorLspTestContext::new_rust(
11304        lsp::ServerCapabilities {
11305            completion_provider: Some(lsp::CompletionOptions {
11306                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11307                ..lsp::CompletionOptions::default()
11308            }),
11309            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11310            ..lsp::ServerCapabilities::default()
11311        },
11312        cx,
11313    )
11314    .await;
11315
11316    let _completion_requests_handler =
11317        cx.lsp
11318            .server
11319            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11320                panic!("LSP completions should not be queried when dealing with word completions")
11321            });
11322
11323    cx.set_state(indoc! {"ˇ
11324        first
11325        last
11326        second
11327    "});
11328    cx.update_editor(|editor, window, cx| {
11329        editor.show_word_completions(&ShowWordCompletions, window, cx);
11330    });
11331    cx.executor().run_until_parked();
11332    cx.condition(|editor, _| editor.context_menu_visible())
11333        .await;
11334    cx.update_editor(|editor, _, _| {
11335        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11336        {
11337            assert_eq!(
11338                completion_menu_entries(&menu),
11339                &["first", "last", "second"],
11340                "`ShowWordCompletions` action should show word completions"
11341            );
11342        } else {
11343            panic!("expected completion menu to be open");
11344        }
11345    });
11346
11347    cx.simulate_keystroke("l");
11348    cx.executor().run_until_parked();
11349    cx.condition(|editor, _| editor.context_menu_visible())
11350        .await;
11351    cx.update_editor(|editor, _, _| {
11352        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11353        {
11354            assert_eq!(
11355                completion_menu_entries(&menu),
11356                &["last"],
11357                "After showing word completions, further editing should filter them and not query the LSP"
11358            );
11359        } else {
11360            panic!("expected completion menu to be open");
11361        }
11362    });
11363}
11364
11365#[gpui::test]
11366async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11367    init_test(cx, |language_settings| {
11368        language_settings.defaults.completions = Some(CompletionSettings {
11369            words: WordsCompletionMode::Fallback,
11370            lsp: false,
11371            lsp_fetch_timeout_ms: 0,
11372            lsp_insert_mode: LspInsertMode::Insert,
11373        });
11374    });
11375
11376    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11377
11378    cx.set_state(indoc! {"ˇ
11379        0_usize
11380        let
11381        33
11382        4.5f32
11383    "});
11384    cx.update_editor(|editor, window, cx| {
11385        editor.show_completions(&ShowCompletions::default(), window, cx);
11386    });
11387    cx.executor().run_until_parked();
11388    cx.condition(|editor, _| editor.context_menu_visible())
11389        .await;
11390    cx.update_editor(|editor, window, cx| {
11391        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11392        {
11393            assert_eq!(
11394                completion_menu_entries(&menu),
11395                &["let"],
11396                "With no digits in the completion query, no digits should be in the word completions"
11397            );
11398        } else {
11399            panic!("expected completion menu to be open");
11400        }
11401        editor.cancel(&Cancel, window, cx);
11402    });
11403
11404    cx.set_state(indoc! {"11405        0_usize
11406        let
11407        3
11408        33.35f32
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, _, _| {
11417        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11418        {
11419            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11420                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11421        } else {
11422            panic!("expected completion menu to be open");
11423        }
11424    });
11425}
11426
11427fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11428    let position = || lsp::Position {
11429        line: params.text_document_position.position.line,
11430        character: params.text_document_position.position.character,
11431    };
11432    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11433        range: lsp::Range {
11434            start: position(),
11435            end: position(),
11436        },
11437        new_text: text.to_string(),
11438    }))
11439}
11440
11441#[gpui::test]
11442async fn test_multiline_completion(cx: &mut TestAppContext) {
11443    init_test(cx, |_| {});
11444
11445    let fs = FakeFs::new(cx.executor());
11446    fs.insert_tree(
11447        path!("/a"),
11448        json!({
11449            "main.ts": "a",
11450        }),
11451    )
11452    .await;
11453
11454    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11455    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11456    let typescript_language = Arc::new(Language::new(
11457        LanguageConfig {
11458            name: "TypeScript".into(),
11459            matcher: LanguageMatcher {
11460                path_suffixes: vec!["ts".to_string()],
11461                ..LanguageMatcher::default()
11462            },
11463            line_comments: vec!["// ".into()],
11464            ..LanguageConfig::default()
11465        },
11466        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11467    ));
11468    language_registry.add(typescript_language.clone());
11469    let mut fake_servers = language_registry.register_fake_lsp(
11470        "TypeScript",
11471        FakeLspAdapter {
11472            capabilities: lsp::ServerCapabilities {
11473                completion_provider: Some(lsp::CompletionOptions {
11474                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11475                    ..lsp::CompletionOptions::default()
11476                }),
11477                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11478                ..lsp::ServerCapabilities::default()
11479            },
11480            // Emulate vtsls label generation
11481            label_for_completion: Some(Box::new(|item, _| {
11482                let text = if let Some(description) = item
11483                    .label_details
11484                    .as_ref()
11485                    .and_then(|label_details| label_details.description.as_ref())
11486                {
11487                    format!("{} {}", item.label, description)
11488                } else if let Some(detail) = &item.detail {
11489                    format!("{} {}", item.label, detail)
11490                } else {
11491                    item.label.clone()
11492                };
11493                let len = text.len();
11494                Some(language::CodeLabel {
11495                    text,
11496                    runs: Vec::new(),
11497                    filter_range: 0..len,
11498                })
11499            })),
11500            ..FakeLspAdapter::default()
11501        },
11502    );
11503    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11504    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11505    let worktree_id = workspace
11506        .update(cx, |workspace, _window, cx| {
11507            workspace.project().update(cx, |project, cx| {
11508                project.worktrees(cx).next().unwrap().read(cx).id()
11509            })
11510        })
11511        .unwrap();
11512    let _buffer = project
11513        .update(cx, |project, cx| {
11514            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11515        })
11516        .await
11517        .unwrap();
11518    let editor = workspace
11519        .update(cx, |workspace, window, cx| {
11520            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11521        })
11522        .unwrap()
11523        .await
11524        .unwrap()
11525        .downcast::<Editor>()
11526        .unwrap();
11527    let fake_server = fake_servers.next().await.unwrap();
11528
11529    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11530    let multiline_label_2 = "a\nb\nc\n";
11531    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11532    let multiline_description = "d\ne\nf\n";
11533    let multiline_detail_2 = "g\nh\ni\n";
11534
11535    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11536        move |params, _| async move {
11537            Ok(Some(lsp::CompletionResponse::Array(vec![
11538                lsp::CompletionItem {
11539                    label: multiline_label.to_string(),
11540                    text_edit: gen_text_edit(&params, "new_text_1"),
11541                    ..lsp::CompletionItem::default()
11542                },
11543                lsp::CompletionItem {
11544                    label: "single line label 1".to_string(),
11545                    detail: Some(multiline_detail.to_string()),
11546                    text_edit: gen_text_edit(&params, "new_text_2"),
11547                    ..lsp::CompletionItem::default()
11548                },
11549                lsp::CompletionItem {
11550                    label: "single line label 2".to_string(),
11551                    label_details: Some(lsp::CompletionItemLabelDetails {
11552                        description: Some(multiline_description.to_string()),
11553                        detail: None,
11554                    }),
11555                    text_edit: gen_text_edit(&params, "new_text_2"),
11556                    ..lsp::CompletionItem::default()
11557                },
11558                lsp::CompletionItem {
11559                    label: multiline_label_2.to_string(),
11560                    detail: Some(multiline_detail_2.to_string()),
11561                    text_edit: gen_text_edit(&params, "new_text_3"),
11562                    ..lsp::CompletionItem::default()
11563                },
11564                lsp::CompletionItem {
11565                    label: "Label with many     spaces and \t but without newlines".to_string(),
11566                    detail: Some(
11567                        "Details with many     spaces and \t but without newlines".to_string(),
11568                    ),
11569                    text_edit: gen_text_edit(&params, "new_text_4"),
11570                    ..lsp::CompletionItem::default()
11571                },
11572            ])))
11573        },
11574    );
11575
11576    editor.update_in(cx, |editor, window, cx| {
11577        cx.focus_self(window);
11578        editor.move_to_end(&MoveToEnd, window, cx);
11579        editor.handle_input(".", window, cx);
11580    });
11581    cx.run_until_parked();
11582    completion_handle.next().await.unwrap();
11583
11584    editor.update(cx, |editor, _| {
11585        assert!(editor.context_menu_visible());
11586        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11587        {
11588            let completion_labels = menu
11589                .completions
11590                .borrow()
11591                .iter()
11592                .map(|c| c.label.text.clone())
11593                .collect::<Vec<_>>();
11594            assert_eq!(
11595                completion_labels,
11596                &[
11597                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11598                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11599                    "single line label 2 d e f ",
11600                    "a b c g h i ",
11601                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11602                ],
11603                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11604            );
11605
11606            for completion in menu
11607                .completions
11608                .borrow()
11609                .iter() {
11610                    assert_eq!(
11611                        completion.label.filter_range,
11612                        0..completion.label.text.len(),
11613                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11614                    );
11615                }
11616        } else {
11617            panic!("expected completion menu to be open");
11618        }
11619    });
11620}
11621
11622#[gpui::test]
11623async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11624    init_test(cx, |_| {});
11625    let mut cx = EditorLspTestContext::new_rust(
11626        lsp::ServerCapabilities {
11627            completion_provider: Some(lsp::CompletionOptions {
11628                trigger_characters: Some(vec![".".to_string()]),
11629                ..Default::default()
11630            }),
11631            ..Default::default()
11632        },
11633        cx,
11634    )
11635    .await;
11636    cx.lsp
11637        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11638            Ok(Some(lsp::CompletionResponse::Array(vec![
11639                lsp::CompletionItem {
11640                    label: "first".into(),
11641                    ..Default::default()
11642                },
11643                lsp::CompletionItem {
11644                    label: "last".into(),
11645                    ..Default::default()
11646                },
11647            ])))
11648        });
11649    cx.set_state("variableˇ");
11650    cx.simulate_keystroke(".");
11651    cx.executor().run_until_parked();
11652
11653    cx.update_editor(|editor, _, _| {
11654        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11655        {
11656            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11657        } else {
11658            panic!("expected completion menu to be open");
11659        }
11660    });
11661
11662    cx.update_editor(|editor, window, cx| {
11663        editor.move_page_down(&MovePageDown::default(), window, cx);
11664        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11665        {
11666            assert!(
11667                menu.selected_item == 1,
11668                "expected PageDown to select the last item from the context menu"
11669            );
11670        } else {
11671            panic!("expected completion menu to stay open after PageDown");
11672        }
11673    });
11674
11675    cx.update_editor(|editor, window, cx| {
11676        editor.move_page_up(&MovePageUp::default(), window, cx);
11677        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11678        {
11679            assert!(
11680                menu.selected_item == 0,
11681                "expected PageUp to select the first item from the context menu"
11682            );
11683        } else {
11684            panic!("expected completion menu to stay open after PageUp");
11685        }
11686    });
11687}
11688
11689#[gpui::test]
11690async fn test_as_is_completions(cx: &mut TestAppContext) {
11691    init_test(cx, |_| {});
11692    let mut cx = EditorLspTestContext::new_rust(
11693        lsp::ServerCapabilities {
11694            completion_provider: Some(lsp::CompletionOptions {
11695                ..Default::default()
11696            }),
11697            ..Default::default()
11698        },
11699        cx,
11700    )
11701    .await;
11702    cx.lsp
11703        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11704            Ok(Some(lsp::CompletionResponse::Array(vec![
11705                lsp::CompletionItem {
11706                    label: "unsafe".into(),
11707                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11708                        range: lsp::Range {
11709                            start: lsp::Position {
11710                                line: 1,
11711                                character: 2,
11712                            },
11713                            end: lsp::Position {
11714                                line: 1,
11715                                character: 3,
11716                            },
11717                        },
11718                        new_text: "unsafe".to_string(),
11719                    })),
11720                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11721                    ..Default::default()
11722                },
11723            ])))
11724        });
11725    cx.set_state("fn a() {}\n");
11726    cx.executor().run_until_parked();
11727    cx.update_editor(|editor, window, cx| {
11728        editor.show_completions(
11729            &ShowCompletions {
11730                trigger: Some("\n".into()),
11731            },
11732            window,
11733            cx,
11734        );
11735    });
11736    cx.executor().run_until_parked();
11737
11738    cx.update_editor(|editor, window, cx| {
11739        editor.confirm_completion(&Default::default(), window, cx)
11740    });
11741    cx.executor().run_until_parked();
11742    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11743}
11744
11745#[gpui::test]
11746async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11747    init_test(cx, |_| {});
11748
11749    let mut cx = EditorLspTestContext::new_rust(
11750        lsp::ServerCapabilities {
11751            completion_provider: Some(lsp::CompletionOptions {
11752                trigger_characters: Some(vec![".".to_string()]),
11753                resolve_provider: Some(true),
11754                ..Default::default()
11755            }),
11756            ..Default::default()
11757        },
11758        cx,
11759    )
11760    .await;
11761
11762    cx.set_state("fn main() { let a = 2ˇ; }");
11763    cx.simulate_keystroke(".");
11764    let completion_item = lsp::CompletionItem {
11765        label: "Some".into(),
11766        kind: Some(lsp::CompletionItemKind::SNIPPET),
11767        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11768        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11769            kind: lsp::MarkupKind::Markdown,
11770            value: "```rust\nSome(2)\n```".to_string(),
11771        })),
11772        deprecated: Some(false),
11773        sort_text: Some("Some".to_string()),
11774        filter_text: Some("Some".to_string()),
11775        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11776        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11777            range: lsp::Range {
11778                start: lsp::Position {
11779                    line: 0,
11780                    character: 22,
11781                },
11782                end: lsp::Position {
11783                    line: 0,
11784                    character: 22,
11785                },
11786            },
11787            new_text: "Some(2)".to_string(),
11788        })),
11789        additional_text_edits: Some(vec![lsp::TextEdit {
11790            range: lsp::Range {
11791                start: lsp::Position {
11792                    line: 0,
11793                    character: 20,
11794                },
11795                end: lsp::Position {
11796                    line: 0,
11797                    character: 22,
11798                },
11799            },
11800            new_text: "".to_string(),
11801        }]),
11802        ..Default::default()
11803    };
11804
11805    let closure_completion_item = completion_item.clone();
11806    let counter = Arc::new(AtomicUsize::new(0));
11807    let counter_clone = counter.clone();
11808    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11809        let task_completion_item = closure_completion_item.clone();
11810        counter_clone.fetch_add(1, atomic::Ordering::Release);
11811        async move {
11812            Ok(Some(lsp::CompletionResponse::Array(vec![
11813                task_completion_item,
11814            ])))
11815        }
11816    });
11817
11818    cx.condition(|editor, _| editor.context_menu_visible())
11819        .await;
11820    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11821    assert!(request.next().await.is_some());
11822    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11823
11824    cx.simulate_keystrokes("S o m");
11825    cx.condition(|editor, _| editor.context_menu_visible())
11826        .await;
11827    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11828    assert!(request.next().await.is_some());
11829    assert!(request.next().await.is_some());
11830    assert!(request.next().await.is_some());
11831    request.close();
11832    assert!(request.next().await.is_none());
11833    assert_eq!(
11834        counter.load(atomic::Ordering::Acquire),
11835        4,
11836        "With the completions menu open, only one LSP request should happen per input"
11837    );
11838}
11839
11840#[gpui::test]
11841async fn test_toggle_comment(cx: &mut TestAppContext) {
11842    init_test(cx, |_| {});
11843    let mut cx = EditorTestContext::new(cx).await;
11844    let language = Arc::new(Language::new(
11845        LanguageConfig {
11846            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11847            ..Default::default()
11848        },
11849        Some(tree_sitter_rust::LANGUAGE.into()),
11850    ));
11851    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11852
11853    // If multiple selections intersect a line, the line is only toggled once.
11854    cx.set_state(indoc! {"
11855        fn a() {
11856            «//b();
11857            ˇ»// «c();
11858            //ˇ»  d();
11859        }
11860    "});
11861
11862    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11863
11864    cx.assert_editor_state(indoc! {"
11865        fn a() {
11866            «b();
11867            c();
11868            ˇ» d();
11869        }
11870    "});
11871
11872    // The comment prefix is inserted at the same column for every line in a
11873    // selection.
11874    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11875
11876    cx.assert_editor_state(indoc! {"
11877        fn a() {
11878            // «b();
11879            // c();
11880            ˇ»//  d();
11881        }
11882    "});
11883
11884    // If a selection ends at the beginning of a line, that line is not toggled.
11885    cx.set_selections_state(indoc! {"
11886        fn a() {
11887            // b();
11888            «// c();
11889        ˇ»    //  d();
11890        }
11891    "});
11892
11893    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11894
11895    cx.assert_editor_state(indoc! {"
11896        fn a() {
11897            // b();
11898            «c();
11899        ˇ»    //  d();
11900        }
11901    "});
11902
11903    // If a selection span a single line and is empty, the line is toggled.
11904    cx.set_state(indoc! {"
11905        fn a() {
11906            a();
11907            b();
11908        ˇ
11909        }
11910    "});
11911
11912    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11913
11914    cx.assert_editor_state(indoc! {"
11915        fn a() {
11916            a();
11917            b();
11918        //•ˇ
11919        }
11920    "});
11921
11922    // If a selection span multiple lines, empty lines are not toggled.
11923    cx.set_state(indoc! {"
11924        fn a() {
11925            «a();
11926
11927            c();ˇ»
11928        }
11929    "});
11930
11931    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11932
11933    cx.assert_editor_state(indoc! {"
11934        fn a() {
11935            // «a();
11936
11937            // c();ˇ»
11938        }
11939    "});
11940
11941    // If a selection includes multiple comment prefixes, all lines are uncommented.
11942    cx.set_state(indoc! {"
11943        fn a() {
11944            «// a();
11945            /// b();
11946            //! c();ˇ»
11947        }
11948    "});
11949
11950    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11951
11952    cx.assert_editor_state(indoc! {"
11953        fn a() {
11954            «a();
11955            b();
11956            c();ˇ»
11957        }
11958    "});
11959}
11960
11961#[gpui::test]
11962async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11963    init_test(cx, |_| {});
11964    let mut cx = EditorTestContext::new(cx).await;
11965    let language = Arc::new(Language::new(
11966        LanguageConfig {
11967            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11968            ..Default::default()
11969        },
11970        Some(tree_sitter_rust::LANGUAGE.into()),
11971    ));
11972    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11973
11974    let toggle_comments = &ToggleComments {
11975        advance_downwards: false,
11976        ignore_indent: true,
11977    };
11978
11979    // If multiple selections intersect a line, the line is only toggled once.
11980    cx.set_state(indoc! {"
11981        fn a() {
11982        //    «b();
11983        //    c();
11984        //    ˇ» d();
11985        }
11986    "});
11987
11988    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11989
11990    cx.assert_editor_state(indoc! {"
11991        fn a() {
11992            «b();
11993            c();
11994            ˇ» d();
11995        }
11996    "});
11997
11998    // The comment prefix is inserted at the beginning of each line
11999    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12000
12001    cx.assert_editor_state(indoc! {"
12002        fn a() {
12003        //    «b();
12004        //    c();
12005        //    ˇ» d();
12006        }
12007    "});
12008
12009    // If a selection ends at the beginning of a line, that line is not toggled.
12010    cx.set_selections_state(indoc! {"
12011        fn a() {
12012        //    b();
12013        //    «c();
12014        ˇ»//     d();
12015        }
12016    "});
12017
12018    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12019
12020    cx.assert_editor_state(indoc! {"
12021        fn a() {
12022        //    b();
12023            «c();
12024        ˇ»//     d();
12025        }
12026    "});
12027
12028    // If a selection span a single line and is empty, the line is toggled.
12029    cx.set_state(indoc! {"
12030        fn a() {
12031            a();
12032            b();
12033        ˇ
12034        }
12035    "});
12036
12037    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12038
12039    cx.assert_editor_state(indoc! {"
12040        fn a() {
12041            a();
12042            b();
12043        //ˇ
12044        }
12045    "});
12046
12047    // If a selection span multiple lines, empty lines are not toggled.
12048    cx.set_state(indoc! {"
12049        fn a() {
12050            «a();
12051
12052            c();ˇ»
12053        }
12054    "});
12055
12056    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12057
12058    cx.assert_editor_state(indoc! {"
12059        fn a() {
12060        //    «a();
12061
12062        //    c();ˇ»
12063        }
12064    "});
12065
12066    // If a selection includes multiple comment prefixes, all lines are uncommented.
12067    cx.set_state(indoc! {"
12068        fn a() {
12069        //    «a();
12070        ///    b();
12071        //!    c();ˇ»
12072        }
12073    "});
12074
12075    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
12076
12077    cx.assert_editor_state(indoc! {"
12078        fn a() {
12079            «a();
12080            b();
12081            c();ˇ»
12082        }
12083    "});
12084}
12085
12086#[gpui::test]
12087async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
12088    init_test(cx, |_| {});
12089
12090    let language = Arc::new(Language::new(
12091        LanguageConfig {
12092            line_comments: vec!["// ".into()],
12093            ..Default::default()
12094        },
12095        Some(tree_sitter_rust::LANGUAGE.into()),
12096    ));
12097
12098    let mut cx = EditorTestContext::new(cx).await;
12099
12100    cx.language_registry().add(language.clone());
12101    cx.update_buffer(|buffer, cx| {
12102        buffer.set_language(Some(language), cx);
12103    });
12104
12105    let toggle_comments = &ToggleComments {
12106        advance_downwards: true,
12107        ignore_indent: false,
12108    };
12109
12110    // Single cursor on one line -> advance
12111    // Cursor moves horizontally 3 characters as well on non-blank line
12112    cx.set_state(indoc!(
12113        "fn a() {
12114             ˇdog();
12115             cat();
12116        }"
12117    ));
12118    cx.update_editor(|editor, window, cx| {
12119        editor.toggle_comments(toggle_comments, window, cx);
12120    });
12121    cx.assert_editor_state(indoc!(
12122        "fn a() {
12123             // dog();
12124             catˇ();
12125        }"
12126    ));
12127
12128    // Single selection on one line -> don't advance
12129    cx.set_state(indoc!(
12130        "fn a() {
12131             «dog()ˇ»;
12132             cat();
12133        }"
12134    ));
12135    cx.update_editor(|editor, window, cx| {
12136        editor.toggle_comments(toggle_comments, window, cx);
12137    });
12138    cx.assert_editor_state(indoc!(
12139        "fn a() {
12140             // «dog()ˇ»;
12141             cat();
12142        }"
12143    ));
12144
12145    // Multiple cursors on one line -> advance
12146    cx.set_state(indoc!(
12147        "fn a() {
12148             ˇdˇog();
12149             cat();
12150        }"
12151    ));
12152    cx.update_editor(|editor, window, cx| {
12153        editor.toggle_comments(toggle_comments, window, cx);
12154    });
12155    cx.assert_editor_state(indoc!(
12156        "fn a() {
12157             // dog();
12158             catˇ(ˇ);
12159        }"
12160    ));
12161
12162    // Multiple cursors on one line, with selection -> don't advance
12163    cx.set_state(indoc!(
12164        "fn a() {
12165             ˇdˇog«()ˇ»;
12166             cat();
12167        }"
12168    ));
12169    cx.update_editor(|editor, window, cx| {
12170        editor.toggle_comments(toggle_comments, window, cx);
12171    });
12172    cx.assert_editor_state(indoc!(
12173        "fn a() {
12174             // ˇdˇog«()ˇ»;
12175             cat();
12176        }"
12177    ));
12178
12179    // Single cursor on one line -> advance
12180    // Cursor moves to column 0 on blank line
12181    cx.set_state(indoc!(
12182        "fn a() {
12183             ˇdog();
12184
12185             cat();
12186        }"
12187    ));
12188    cx.update_editor(|editor, window, cx| {
12189        editor.toggle_comments(toggle_comments, window, cx);
12190    });
12191    cx.assert_editor_state(indoc!(
12192        "fn a() {
12193             // dog();
12194        ˇ
12195             cat();
12196        }"
12197    ));
12198
12199    // Single cursor on one line -> advance
12200    // Cursor starts and ends at column 0
12201    cx.set_state(indoc!(
12202        "fn a() {
12203         ˇ    dog();
12204             cat();
12205        }"
12206    ));
12207    cx.update_editor(|editor, window, cx| {
12208        editor.toggle_comments(toggle_comments, window, cx);
12209    });
12210    cx.assert_editor_state(indoc!(
12211        "fn a() {
12212             // dog();
12213         ˇ    cat();
12214        }"
12215    ));
12216}
12217
12218#[gpui::test]
12219async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12220    init_test(cx, |_| {});
12221
12222    let mut cx = EditorTestContext::new(cx).await;
12223
12224    let html_language = Arc::new(
12225        Language::new(
12226            LanguageConfig {
12227                name: "HTML".into(),
12228                block_comment: Some(("<!-- ".into(), " -->".into())),
12229                ..Default::default()
12230            },
12231            Some(tree_sitter_html::LANGUAGE.into()),
12232        )
12233        .with_injection_query(
12234            r#"
12235            (script_element
12236                (raw_text) @injection.content
12237                (#set! injection.language "javascript"))
12238            "#,
12239        )
12240        .unwrap(),
12241    );
12242
12243    let javascript_language = Arc::new(Language::new(
12244        LanguageConfig {
12245            name: "JavaScript".into(),
12246            line_comments: vec!["// ".into()],
12247            ..Default::default()
12248        },
12249        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12250    ));
12251
12252    cx.language_registry().add(html_language.clone());
12253    cx.language_registry().add(javascript_language.clone());
12254    cx.update_buffer(|buffer, cx| {
12255        buffer.set_language(Some(html_language), cx);
12256    });
12257
12258    // Toggle comments for empty selections
12259    cx.set_state(
12260        &r#"
12261            <p>A</p>ˇ
12262            <p>B</p>ˇ
12263            <p>C</p>ˇ
12264        "#
12265        .unindent(),
12266    );
12267    cx.update_editor(|editor, window, cx| {
12268        editor.toggle_comments(&ToggleComments::default(), window, cx)
12269    });
12270    cx.assert_editor_state(
12271        &r#"
12272            <!-- <p>A</p>ˇ -->
12273            <!-- <p>B</p>ˇ -->
12274            <!-- <p>C</p>ˇ -->
12275        "#
12276        .unindent(),
12277    );
12278    cx.update_editor(|editor, window, cx| {
12279        editor.toggle_comments(&ToggleComments::default(), window, cx)
12280    });
12281    cx.assert_editor_state(
12282        &r#"
12283            <p>A</p>ˇ
12284            <p>B</p>ˇ
12285            <p>C</p>ˇ
12286        "#
12287        .unindent(),
12288    );
12289
12290    // Toggle comments for mixture of empty and non-empty selections, where
12291    // multiple selections occupy a given line.
12292    cx.set_state(
12293        &r#"
12294            <p>A«</p>
12295            <p>ˇ»B</p>ˇ
12296            <p>C«</p>
12297            <p>ˇ»D</p>ˇ
12298        "#
12299        .unindent(),
12300    );
12301
12302    cx.update_editor(|editor, window, cx| {
12303        editor.toggle_comments(&ToggleComments::default(), window, cx)
12304    });
12305    cx.assert_editor_state(
12306        &r#"
12307            <!-- <p>A«</p>
12308            <p>ˇ»B</p>ˇ -->
12309            <!-- <p>C«</p>
12310            <p>ˇ»D</p>ˇ -->
12311        "#
12312        .unindent(),
12313    );
12314    cx.update_editor(|editor, window, cx| {
12315        editor.toggle_comments(&ToggleComments::default(), window, cx)
12316    });
12317    cx.assert_editor_state(
12318        &r#"
12319            <p>A«</p>
12320            <p>ˇ»B</p>ˇ
12321            <p>C«</p>
12322            <p>ˇ»D</p>ˇ
12323        "#
12324        .unindent(),
12325    );
12326
12327    // Toggle comments when different languages are active for different
12328    // selections.
12329    cx.set_state(
12330        &r#"
12331            ˇ<script>
12332                ˇvar x = new Y();
12333            ˇ</script>
12334        "#
12335        .unindent(),
12336    );
12337    cx.executor().run_until_parked();
12338    cx.update_editor(|editor, window, cx| {
12339        editor.toggle_comments(&ToggleComments::default(), window, cx)
12340    });
12341    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12342    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12343    cx.assert_editor_state(
12344        &r#"
12345            <!-- ˇ<script> -->
12346                // ˇvar x = new Y();
12347            <!-- ˇ</script> -->
12348        "#
12349        .unindent(),
12350    );
12351}
12352
12353#[gpui::test]
12354fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12355    init_test(cx, |_| {});
12356
12357    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12358    let multibuffer = cx.new(|cx| {
12359        let mut multibuffer = MultiBuffer::new(ReadWrite);
12360        multibuffer.push_excerpts(
12361            buffer.clone(),
12362            [
12363                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12364                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12365            ],
12366            cx,
12367        );
12368        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12369        multibuffer
12370    });
12371
12372    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12373    editor.update_in(cx, |editor, window, cx| {
12374        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12375        editor.change_selections(None, window, cx, |s| {
12376            s.select_ranges([
12377                Point::new(0, 0)..Point::new(0, 0),
12378                Point::new(1, 0)..Point::new(1, 0),
12379            ])
12380        });
12381
12382        editor.handle_input("X", window, cx);
12383        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12384        assert_eq!(
12385            editor.selections.ranges(cx),
12386            [
12387                Point::new(0, 1)..Point::new(0, 1),
12388                Point::new(1, 1)..Point::new(1, 1),
12389            ]
12390        );
12391
12392        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12393        editor.change_selections(None, window, cx, |s| {
12394            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12395        });
12396        editor.backspace(&Default::default(), window, cx);
12397        assert_eq!(editor.text(cx), "Xa\nbbb");
12398        assert_eq!(
12399            editor.selections.ranges(cx),
12400            [Point::new(1, 0)..Point::new(1, 0)]
12401        );
12402
12403        editor.change_selections(None, window, cx, |s| {
12404            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12405        });
12406        editor.backspace(&Default::default(), window, cx);
12407        assert_eq!(editor.text(cx), "X\nbb");
12408        assert_eq!(
12409            editor.selections.ranges(cx),
12410            [Point::new(0, 1)..Point::new(0, 1)]
12411        );
12412    });
12413}
12414
12415#[gpui::test]
12416fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12417    init_test(cx, |_| {});
12418
12419    let markers = vec![('[', ']').into(), ('(', ')').into()];
12420    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12421        indoc! {"
12422            [aaaa
12423            (bbbb]
12424            cccc)",
12425        },
12426        markers.clone(),
12427    );
12428    let excerpt_ranges = markers.into_iter().map(|marker| {
12429        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12430        ExcerptRange::new(context.clone())
12431    });
12432    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12433    let multibuffer = cx.new(|cx| {
12434        let mut multibuffer = MultiBuffer::new(ReadWrite);
12435        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12436        multibuffer
12437    });
12438
12439    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12440    editor.update_in(cx, |editor, window, cx| {
12441        let (expected_text, selection_ranges) = marked_text_ranges(
12442            indoc! {"
12443                aaaa
12444                bˇbbb
12445                bˇbbˇb
12446                cccc"
12447            },
12448            true,
12449        );
12450        assert_eq!(editor.text(cx), expected_text);
12451        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12452
12453        editor.handle_input("X", window, cx);
12454
12455        let (expected_text, expected_selections) = marked_text_ranges(
12456            indoc! {"
12457                aaaa
12458                bXˇbbXb
12459                bXˇbbXˇb
12460                cccc"
12461            },
12462            false,
12463        );
12464        assert_eq!(editor.text(cx), expected_text);
12465        assert_eq!(editor.selections.ranges(cx), expected_selections);
12466
12467        editor.newline(&Newline, window, cx);
12468        let (expected_text, expected_selections) = marked_text_ranges(
12469            indoc! {"
12470                aaaa
12471                bX
12472                ˇbbX
12473                b
12474                bX
12475                ˇbbX
12476                ˇb
12477                cccc"
12478            },
12479            false,
12480        );
12481        assert_eq!(editor.text(cx), expected_text);
12482        assert_eq!(editor.selections.ranges(cx), expected_selections);
12483    });
12484}
12485
12486#[gpui::test]
12487fn test_refresh_selections(cx: &mut TestAppContext) {
12488    init_test(cx, |_| {});
12489
12490    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12491    let mut excerpt1_id = None;
12492    let multibuffer = cx.new(|cx| {
12493        let mut multibuffer = MultiBuffer::new(ReadWrite);
12494        excerpt1_id = multibuffer
12495            .push_excerpts(
12496                buffer.clone(),
12497                [
12498                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12499                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12500                ],
12501                cx,
12502            )
12503            .into_iter()
12504            .next();
12505        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12506        multibuffer
12507    });
12508
12509    let editor = cx.add_window(|window, cx| {
12510        let mut editor = build_editor(multibuffer.clone(), window, cx);
12511        let snapshot = editor.snapshot(window, cx);
12512        editor.change_selections(None, window, cx, |s| {
12513            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12514        });
12515        editor.begin_selection(
12516            Point::new(2, 1).to_display_point(&snapshot),
12517            true,
12518            1,
12519            window,
12520            cx,
12521        );
12522        assert_eq!(
12523            editor.selections.ranges(cx),
12524            [
12525                Point::new(1, 3)..Point::new(1, 3),
12526                Point::new(2, 1)..Point::new(2, 1),
12527            ]
12528        );
12529        editor
12530    });
12531
12532    // Refreshing selections is a no-op when excerpts haven't changed.
12533    _ = editor.update(cx, |editor, window, cx| {
12534        editor.change_selections(None, window, cx, |s| s.refresh());
12535        assert_eq!(
12536            editor.selections.ranges(cx),
12537            [
12538                Point::new(1, 3)..Point::new(1, 3),
12539                Point::new(2, 1)..Point::new(2, 1),
12540            ]
12541        );
12542    });
12543
12544    multibuffer.update(cx, |multibuffer, cx| {
12545        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12546    });
12547    _ = editor.update(cx, |editor, window, cx| {
12548        // Removing an excerpt causes the first selection to become degenerate.
12549        assert_eq!(
12550            editor.selections.ranges(cx),
12551            [
12552                Point::new(0, 0)..Point::new(0, 0),
12553                Point::new(0, 1)..Point::new(0, 1)
12554            ]
12555        );
12556
12557        // Refreshing selections will relocate the first selection to the original buffer
12558        // location.
12559        editor.change_selections(None, window, cx, |s| s.refresh());
12560        assert_eq!(
12561            editor.selections.ranges(cx),
12562            [
12563                Point::new(0, 1)..Point::new(0, 1),
12564                Point::new(0, 3)..Point::new(0, 3)
12565            ]
12566        );
12567        assert!(editor.selections.pending_anchor().is_some());
12568    });
12569}
12570
12571#[gpui::test]
12572fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12573    init_test(cx, |_| {});
12574
12575    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12576    let mut excerpt1_id = None;
12577    let multibuffer = cx.new(|cx| {
12578        let mut multibuffer = MultiBuffer::new(ReadWrite);
12579        excerpt1_id = multibuffer
12580            .push_excerpts(
12581                buffer.clone(),
12582                [
12583                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12584                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12585                ],
12586                cx,
12587            )
12588            .into_iter()
12589            .next();
12590        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12591        multibuffer
12592    });
12593
12594    let editor = cx.add_window(|window, cx| {
12595        let mut editor = build_editor(multibuffer.clone(), window, cx);
12596        let snapshot = editor.snapshot(window, cx);
12597        editor.begin_selection(
12598            Point::new(1, 3).to_display_point(&snapshot),
12599            false,
12600            1,
12601            window,
12602            cx,
12603        );
12604        assert_eq!(
12605            editor.selections.ranges(cx),
12606            [Point::new(1, 3)..Point::new(1, 3)]
12607        );
12608        editor
12609    });
12610
12611    multibuffer.update(cx, |multibuffer, cx| {
12612        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12613    });
12614    _ = editor.update(cx, |editor, window, cx| {
12615        assert_eq!(
12616            editor.selections.ranges(cx),
12617            [Point::new(0, 0)..Point::new(0, 0)]
12618        );
12619
12620        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12621        editor.change_selections(None, window, cx, |s| s.refresh());
12622        assert_eq!(
12623            editor.selections.ranges(cx),
12624            [Point::new(0, 3)..Point::new(0, 3)]
12625        );
12626        assert!(editor.selections.pending_anchor().is_some());
12627    });
12628}
12629
12630#[gpui::test]
12631async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12632    init_test(cx, |_| {});
12633
12634    let language = Arc::new(
12635        Language::new(
12636            LanguageConfig {
12637                brackets: BracketPairConfig {
12638                    pairs: vec![
12639                        BracketPair {
12640                            start: "{".to_string(),
12641                            end: "}".to_string(),
12642                            close: true,
12643                            surround: true,
12644                            newline: true,
12645                        },
12646                        BracketPair {
12647                            start: "/* ".to_string(),
12648                            end: " */".to_string(),
12649                            close: true,
12650                            surround: true,
12651                            newline: true,
12652                        },
12653                    ],
12654                    ..Default::default()
12655                },
12656                ..Default::default()
12657            },
12658            Some(tree_sitter_rust::LANGUAGE.into()),
12659        )
12660        .with_indents_query("")
12661        .unwrap(),
12662    );
12663
12664    let text = concat!(
12665        "{   }\n",     //
12666        "  x\n",       //
12667        "  /*   */\n", //
12668        "x\n",         //
12669        "{{} }\n",     //
12670    );
12671
12672    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12673    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12674    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12675    editor
12676        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12677        .await;
12678
12679    editor.update_in(cx, |editor, window, cx| {
12680        editor.change_selections(None, window, cx, |s| {
12681            s.select_display_ranges([
12682                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12683                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12684                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12685            ])
12686        });
12687        editor.newline(&Newline, window, cx);
12688
12689        assert_eq!(
12690            editor.buffer().read(cx).read(cx).text(),
12691            concat!(
12692                "{ \n",    // Suppress rustfmt
12693                "\n",      //
12694                "}\n",     //
12695                "  x\n",   //
12696                "  /* \n", //
12697                "  \n",    //
12698                "  */\n",  //
12699                "x\n",     //
12700                "{{} \n",  //
12701                "}\n",     //
12702            )
12703        );
12704    });
12705}
12706
12707#[gpui::test]
12708fn test_highlighted_ranges(cx: &mut TestAppContext) {
12709    init_test(cx, |_| {});
12710
12711    let editor = cx.add_window(|window, cx| {
12712        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12713        build_editor(buffer.clone(), window, cx)
12714    });
12715
12716    _ = editor.update(cx, |editor, window, cx| {
12717        struct Type1;
12718        struct Type2;
12719
12720        let buffer = editor.buffer.read(cx).snapshot(cx);
12721
12722        let anchor_range =
12723            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12724
12725        editor.highlight_background::<Type1>(
12726            &[
12727                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12728                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12729                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12730                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12731            ],
12732            |_| Hsla::red(),
12733            cx,
12734        );
12735        editor.highlight_background::<Type2>(
12736            &[
12737                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12738                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12739                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12740                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12741            ],
12742            |_| Hsla::green(),
12743            cx,
12744        );
12745
12746        let snapshot = editor.snapshot(window, cx);
12747        let mut highlighted_ranges = editor.background_highlights_in_range(
12748            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12749            &snapshot,
12750            cx.theme().colors(),
12751        );
12752        // Enforce a consistent ordering based on color without relying on the ordering of the
12753        // highlight's `TypeId` which is non-executor.
12754        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12755        assert_eq!(
12756            highlighted_ranges,
12757            &[
12758                (
12759                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12760                    Hsla::red(),
12761                ),
12762                (
12763                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12764                    Hsla::red(),
12765                ),
12766                (
12767                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12768                    Hsla::green(),
12769                ),
12770                (
12771                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12772                    Hsla::green(),
12773                ),
12774            ]
12775        );
12776        assert_eq!(
12777            editor.background_highlights_in_range(
12778                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12779                &snapshot,
12780                cx.theme().colors(),
12781            ),
12782            &[(
12783                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12784                Hsla::red(),
12785            )]
12786        );
12787    });
12788}
12789
12790#[gpui::test]
12791async fn test_following(cx: &mut TestAppContext) {
12792    init_test(cx, |_| {});
12793
12794    let fs = FakeFs::new(cx.executor());
12795    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12796
12797    let buffer = project.update(cx, |project, cx| {
12798        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12799        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12800    });
12801    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12802    let follower = cx.update(|cx| {
12803        cx.open_window(
12804            WindowOptions {
12805                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12806                    gpui::Point::new(px(0.), px(0.)),
12807                    gpui::Point::new(px(10.), px(80.)),
12808                ))),
12809                ..Default::default()
12810            },
12811            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12812        )
12813        .unwrap()
12814    });
12815
12816    let is_still_following = Rc::new(RefCell::new(true));
12817    let follower_edit_event_count = Rc::new(RefCell::new(0));
12818    let pending_update = Rc::new(RefCell::new(None));
12819    let leader_entity = leader.root(cx).unwrap();
12820    let follower_entity = follower.root(cx).unwrap();
12821    _ = follower.update(cx, {
12822        let update = pending_update.clone();
12823        let is_still_following = is_still_following.clone();
12824        let follower_edit_event_count = follower_edit_event_count.clone();
12825        |_, window, cx| {
12826            cx.subscribe_in(
12827                &leader_entity,
12828                window,
12829                move |_, leader, event, window, cx| {
12830                    leader.read(cx).add_event_to_update_proto(
12831                        event,
12832                        &mut update.borrow_mut(),
12833                        window,
12834                        cx,
12835                    );
12836                },
12837            )
12838            .detach();
12839
12840            cx.subscribe_in(
12841                &follower_entity,
12842                window,
12843                move |_, _, event: &EditorEvent, _window, _cx| {
12844                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12845                        *is_still_following.borrow_mut() = false;
12846                    }
12847
12848                    if let EditorEvent::BufferEdited = event {
12849                        *follower_edit_event_count.borrow_mut() += 1;
12850                    }
12851                },
12852            )
12853            .detach();
12854        }
12855    });
12856
12857    // Update the selections only
12858    _ = leader.update(cx, |leader, window, cx| {
12859        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12860    });
12861    follower
12862        .update(cx, |follower, window, cx| {
12863            follower.apply_update_proto(
12864                &project,
12865                pending_update.borrow_mut().take().unwrap(),
12866                window,
12867                cx,
12868            )
12869        })
12870        .unwrap()
12871        .await
12872        .unwrap();
12873    _ = follower.update(cx, |follower, _, cx| {
12874        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12875    });
12876    assert!(*is_still_following.borrow());
12877    assert_eq!(*follower_edit_event_count.borrow(), 0);
12878
12879    // Update the scroll position only
12880    _ = leader.update(cx, |leader, window, cx| {
12881        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12882    });
12883    follower
12884        .update(cx, |follower, window, cx| {
12885            follower.apply_update_proto(
12886                &project,
12887                pending_update.borrow_mut().take().unwrap(),
12888                window,
12889                cx,
12890            )
12891        })
12892        .unwrap()
12893        .await
12894        .unwrap();
12895    assert_eq!(
12896        follower
12897            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12898            .unwrap(),
12899        gpui::Point::new(1.5, 3.5)
12900    );
12901    assert!(*is_still_following.borrow());
12902    assert_eq!(*follower_edit_event_count.borrow(), 0);
12903
12904    // Update the selections and scroll position. The follower's scroll position is updated
12905    // via autoscroll, not via the leader's exact scroll position.
12906    _ = leader.update(cx, |leader, window, cx| {
12907        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12908        leader.request_autoscroll(Autoscroll::newest(), cx);
12909        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12910    });
12911    follower
12912        .update(cx, |follower, window, cx| {
12913            follower.apply_update_proto(
12914                &project,
12915                pending_update.borrow_mut().take().unwrap(),
12916                window,
12917                cx,
12918            )
12919        })
12920        .unwrap()
12921        .await
12922        .unwrap();
12923    _ = follower.update(cx, |follower, _, cx| {
12924        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12925        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12926    });
12927    assert!(*is_still_following.borrow());
12928
12929    // Creating a pending selection that precedes another selection
12930    _ = leader.update(cx, |leader, window, cx| {
12931        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12932        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12933    });
12934    follower
12935        .update(cx, |follower, window, cx| {
12936            follower.apply_update_proto(
12937                &project,
12938                pending_update.borrow_mut().take().unwrap(),
12939                window,
12940                cx,
12941            )
12942        })
12943        .unwrap()
12944        .await
12945        .unwrap();
12946    _ = follower.update(cx, |follower, _, cx| {
12947        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12948    });
12949    assert!(*is_still_following.borrow());
12950
12951    // Extend the pending selection so that it surrounds another selection
12952    _ = leader.update(cx, |leader, window, cx| {
12953        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12954    });
12955    follower
12956        .update(cx, |follower, window, cx| {
12957            follower.apply_update_proto(
12958                &project,
12959                pending_update.borrow_mut().take().unwrap(),
12960                window,
12961                cx,
12962            )
12963        })
12964        .unwrap()
12965        .await
12966        .unwrap();
12967    _ = follower.update(cx, |follower, _, cx| {
12968        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12969    });
12970
12971    // Scrolling locally breaks the follow
12972    _ = follower.update(cx, |follower, window, cx| {
12973        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12974        follower.set_scroll_anchor(
12975            ScrollAnchor {
12976                anchor: top_anchor,
12977                offset: gpui::Point::new(0.0, 0.5),
12978            },
12979            window,
12980            cx,
12981        );
12982    });
12983    assert!(!(*is_still_following.borrow()));
12984}
12985
12986#[gpui::test]
12987async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12988    init_test(cx, |_| {});
12989
12990    let fs = FakeFs::new(cx.executor());
12991    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12992    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12993    let pane = workspace
12994        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12995        .unwrap();
12996
12997    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12998
12999    let leader = pane.update_in(cx, |_, window, cx| {
13000        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
13001        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
13002    });
13003
13004    // Start following the editor when it has no excerpts.
13005    let mut state_message =
13006        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13007    let workspace_entity = workspace.root(cx).unwrap();
13008    let follower_1 = cx
13009        .update_window(*workspace.deref(), |_, window, cx| {
13010            Editor::from_state_proto(
13011                workspace_entity,
13012                ViewId {
13013                    creator: CollaboratorId::PeerId(PeerId::default()),
13014                    id: 0,
13015                },
13016                &mut state_message,
13017                window,
13018                cx,
13019            )
13020        })
13021        .unwrap()
13022        .unwrap()
13023        .await
13024        .unwrap();
13025
13026    let update_message = Rc::new(RefCell::new(None));
13027    follower_1.update_in(cx, {
13028        let update = update_message.clone();
13029        |_, window, cx| {
13030            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
13031                leader.read(cx).add_event_to_update_proto(
13032                    event,
13033                    &mut update.borrow_mut(),
13034                    window,
13035                    cx,
13036                );
13037            })
13038            .detach();
13039        }
13040    });
13041
13042    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
13043        (
13044            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
13045            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
13046        )
13047    });
13048
13049    // Insert some excerpts.
13050    leader.update(cx, |leader, cx| {
13051        leader.buffer.update(cx, |multibuffer, cx| {
13052            multibuffer.set_excerpts_for_path(
13053                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
13054                buffer_1.clone(),
13055                vec![
13056                    Point::row_range(0..3),
13057                    Point::row_range(1..6),
13058                    Point::row_range(12..15),
13059                ],
13060                0,
13061                cx,
13062            );
13063            multibuffer.set_excerpts_for_path(
13064                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
13065                buffer_2.clone(),
13066                vec![Point::row_range(0..6), Point::row_range(8..12)],
13067                0,
13068                cx,
13069            );
13070        });
13071    });
13072
13073    // Apply the update of adding the excerpts.
13074    follower_1
13075        .update_in(cx, |follower, window, cx| {
13076            follower.apply_update_proto(
13077                &project,
13078                update_message.borrow().clone().unwrap(),
13079                window,
13080                cx,
13081            )
13082        })
13083        .await
13084        .unwrap();
13085    assert_eq!(
13086        follower_1.update(cx, |editor, cx| editor.text(cx)),
13087        leader.update(cx, |editor, cx| editor.text(cx))
13088    );
13089    update_message.borrow_mut().take();
13090
13091    // Start following separately after it already has excerpts.
13092    let mut state_message =
13093        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13094    let workspace_entity = workspace.root(cx).unwrap();
13095    let follower_2 = cx
13096        .update_window(*workspace.deref(), |_, window, cx| {
13097            Editor::from_state_proto(
13098                workspace_entity,
13099                ViewId {
13100                    creator: CollaboratorId::PeerId(PeerId::default()),
13101                    id: 0,
13102                },
13103                &mut state_message,
13104                window,
13105                cx,
13106            )
13107        })
13108        .unwrap()
13109        .unwrap()
13110        .await
13111        .unwrap();
13112    assert_eq!(
13113        follower_2.update(cx, |editor, cx| editor.text(cx)),
13114        leader.update(cx, |editor, cx| editor.text(cx))
13115    );
13116
13117    // Remove some excerpts.
13118    leader.update(cx, |leader, cx| {
13119        leader.buffer.update(cx, |multibuffer, cx| {
13120            let excerpt_ids = multibuffer.excerpt_ids();
13121            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13122            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13123        });
13124    });
13125
13126    // Apply the update of removing the excerpts.
13127    follower_1
13128        .update_in(cx, |follower, window, cx| {
13129            follower.apply_update_proto(
13130                &project,
13131                update_message.borrow().clone().unwrap(),
13132                window,
13133                cx,
13134            )
13135        })
13136        .await
13137        .unwrap();
13138    follower_2
13139        .update_in(cx, |follower, window, cx| {
13140            follower.apply_update_proto(
13141                &project,
13142                update_message.borrow().clone().unwrap(),
13143                window,
13144                cx,
13145            )
13146        })
13147        .await
13148        .unwrap();
13149    update_message.borrow_mut().take();
13150    assert_eq!(
13151        follower_1.update(cx, |editor, cx| editor.text(cx)),
13152        leader.update(cx, |editor, cx| editor.text(cx))
13153    );
13154}
13155
13156#[gpui::test]
13157async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13158    init_test(cx, |_| {});
13159
13160    let mut cx = EditorTestContext::new(cx).await;
13161    let lsp_store =
13162        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13163
13164    cx.set_state(indoc! {"
13165        ˇfn func(abc def: i32) -> u32 {
13166        }
13167    "});
13168
13169    cx.update(|_, cx| {
13170        lsp_store.update(cx, |lsp_store, cx| {
13171            lsp_store
13172                .update_diagnostics(
13173                    LanguageServerId(0),
13174                    lsp::PublishDiagnosticsParams {
13175                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13176                        version: None,
13177                        diagnostics: vec![
13178                            lsp::Diagnostic {
13179                                range: lsp::Range::new(
13180                                    lsp::Position::new(0, 11),
13181                                    lsp::Position::new(0, 12),
13182                                ),
13183                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13184                                ..Default::default()
13185                            },
13186                            lsp::Diagnostic {
13187                                range: lsp::Range::new(
13188                                    lsp::Position::new(0, 12),
13189                                    lsp::Position::new(0, 15),
13190                                ),
13191                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13192                                ..Default::default()
13193                            },
13194                            lsp::Diagnostic {
13195                                range: lsp::Range::new(
13196                                    lsp::Position::new(0, 25),
13197                                    lsp::Position::new(0, 28),
13198                                ),
13199                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13200                                ..Default::default()
13201                            },
13202                        ],
13203                    },
13204                    &[],
13205                    cx,
13206                )
13207                .unwrap()
13208        });
13209    });
13210
13211    executor.run_until_parked();
13212
13213    cx.update_editor(|editor, window, cx| {
13214        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13215    });
13216
13217    cx.assert_editor_state(indoc! {"
13218        fn func(abc def: i32) -> ˇu32 {
13219        }
13220    "});
13221
13222    cx.update_editor(|editor, window, cx| {
13223        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13224    });
13225
13226    cx.assert_editor_state(indoc! {"
13227        fn func(abc ˇdef: i32) -> u32 {
13228        }
13229    "});
13230
13231    cx.update_editor(|editor, window, cx| {
13232        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13233    });
13234
13235    cx.assert_editor_state(indoc! {"
13236        fn func(abcˇ def: i32) -> u32 {
13237        }
13238    "});
13239
13240    cx.update_editor(|editor, window, cx| {
13241        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13242    });
13243
13244    cx.assert_editor_state(indoc! {"
13245        fn func(abc def: i32) -> ˇu32 {
13246        }
13247    "});
13248}
13249
13250#[gpui::test]
13251async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13252    init_test(cx, |_| {});
13253
13254    let mut cx = EditorTestContext::new(cx).await;
13255
13256    let diff_base = r#"
13257        use some::mod;
13258
13259        const A: u32 = 42;
13260
13261        fn main() {
13262            println!("hello");
13263
13264            println!("world");
13265        }
13266        "#
13267    .unindent();
13268
13269    // Edits are modified, removed, modified, added
13270    cx.set_state(
13271        &r#"
13272        use some::modified;
13273
13274        ˇ
13275        fn main() {
13276            println!("hello there");
13277
13278            println!("around the");
13279            println!("world");
13280        }
13281        "#
13282        .unindent(),
13283    );
13284
13285    cx.set_head_text(&diff_base);
13286    executor.run_until_parked();
13287
13288    cx.update_editor(|editor, window, cx| {
13289        //Wrap around the bottom of the buffer
13290        for _ in 0..3 {
13291            editor.go_to_next_hunk(&GoToHunk, window, cx);
13292        }
13293    });
13294
13295    cx.assert_editor_state(
13296        &r#"
13297        ˇuse some::modified;
13298
13299
13300        fn main() {
13301            println!("hello there");
13302
13303            println!("around the");
13304            println!("world");
13305        }
13306        "#
13307        .unindent(),
13308    );
13309
13310    cx.update_editor(|editor, window, cx| {
13311        //Wrap around the top of the buffer
13312        for _ in 0..2 {
13313            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13314        }
13315    });
13316
13317    cx.assert_editor_state(
13318        &r#"
13319        use some::modified;
13320
13321
13322        fn main() {
13323        ˇ    println!("hello there");
13324
13325            println!("around the");
13326            println!("world");
13327        }
13328        "#
13329        .unindent(),
13330    );
13331
13332    cx.update_editor(|editor, window, cx| {
13333        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13334    });
13335
13336    cx.assert_editor_state(
13337        &r#"
13338        use some::modified;
13339
13340        ˇ
13341        fn main() {
13342            println!("hello there");
13343
13344            println!("around the");
13345            println!("world");
13346        }
13347        "#
13348        .unindent(),
13349    );
13350
13351    cx.update_editor(|editor, window, cx| {
13352        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13353    });
13354
13355    cx.assert_editor_state(
13356        &r#"
13357        ˇuse some::modified;
13358
13359
13360        fn main() {
13361            println!("hello there");
13362
13363            println!("around the");
13364            println!("world");
13365        }
13366        "#
13367        .unindent(),
13368    );
13369
13370    cx.update_editor(|editor, window, cx| {
13371        for _ in 0..2 {
13372            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13373        }
13374    });
13375
13376    cx.assert_editor_state(
13377        &r#"
13378        use some::modified;
13379
13380
13381        fn main() {
13382        ˇ    println!("hello there");
13383
13384            println!("around the");
13385            println!("world");
13386        }
13387        "#
13388        .unindent(),
13389    );
13390
13391    cx.update_editor(|editor, window, cx| {
13392        editor.fold(&Fold, window, cx);
13393    });
13394
13395    cx.update_editor(|editor, window, cx| {
13396        editor.go_to_next_hunk(&GoToHunk, window, cx);
13397    });
13398
13399    cx.assert_editor_state(
13400        &r#"
13401        ˇuse some::modified;
13402
13403
13404        fn main() {
13405            println!("hello there");
13406
13407            println!("around the");
13408            println!("world");
13409        }
13410        "#
13411        .unindent(),
13412    );
13413}
13414
13415#[test]
13416fn test_split_words() {
13417    fn split(text: &str) -> Vec<&str> {
13418        split_words(text).collect()
13419    }
13420
13421    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13422    assert_eq!(split("hello_world"), &["hello_", "world"]);
13423    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13424    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13425    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13426    assert_eq!(split("helloworld"), &["helloworld"]);
13427
13428    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13429}
13430
13431#[gpui::test]
13432async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13433    init_test(cx, |_| {});
13434
13435    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13436    let mut assert = |before, after| {
13437        let _state_context = cx.set_state(before);
13438        cx.run_until_parked();
13439        cx.update_editor(|editor, window, cx| {
13440            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13441        });
13442        cx.run_until_parked();
13443        cx.assert_editor_state(after);
13444    };
13445
13446    // Outside bracket jumps to outside of matching bracket
13447    assert("console.logˇ(var);", "console.log(var)ˇ;");
13448    assert("console.log(var)ˇ;", "console.logˇ(var);");
13449
13450    // Inside bracket jumps to inside of matching bracket
13451    assert("console.log(ˇvar);", "console.log(varˇ);");
13452    assert("console.log(varˇ);", "console.log(ˇvar);");
13453
13454    // When outside a bracket and inside, favor jumping to the inside bracket
13455    assert(
13456        "console.log('foo', [1, 2, 3]ˇ);",
13457        "console.log(ˇ'foo', [1, 2, 3]);",
13458    );
13459    assert(
13460        "console.log(ˇ'foo', [1, 2, 3]);",
13461        "console.log('foo', [1, 2, 3]ˇ);",
13462    );
13463
13464    // Bias forward if two options are equally likely
13465    assert(
13466        "let result = curried_fun()ˇ();",
13467        "let result = curried_fun()()ˇ;",
13468    );
13469
13470    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13471    assert(
13472        indoc! {"
13473            function test() {
13474                console.log('test')ˇ
13475            }"},
13476        indoc! {"
13477            function test() {
13478                console.logˇ('test')
13479            }"},
13480    );
13481}
13482
13483#[gpui::test]
13484async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13485    init_test(cx, |_| {});
13486
13487    let fs = FakeFs::new(cx.executor());
13488    fs.insert_tree(
13489        path!("/a"),
13490        json!({
13491            "main.rs": "fn main() { let a = 5; }",
13492            "other.rs": "// Test file",
13493        }),
13494    )
13495    .await;
13496    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13497
13498    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13499    language_registry.add(Arc::new(Language::new(
13500        LanguageConfig {
13501            name: "Rust".into(),
13502            matcher: LanguageMatcher {
13503                path_suffixes: vec!["rs".to_string()],
13504                ..Default::default()
13505            },
13506            brackets: BracketPairConfig {
13507                pairs: vec![BracketPair {
13508                    start: "{".to_string(),
13509                    end: "}".to_string(),
13510                    close: true,
13511                    surround: true,
13512                    newline: true,
13513                }],
13514                disabled_scopes_by_bracket_ix: Vec::new(),
13515            },
13516            ..Default::default()
13517        },
13518        Some(tree_sitter_rust::LANGUAGE.into()),
13519    )));
13520    let mut fake_servers = language_registry.register_fake_lsp(
13521        "Rust",
13522        FakeLspAdapter {
13523            capabilities: lsp::ServerCapabilities {
13524                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13525                    first_trigger_character: "{".to_string(),
13526                    more_trigger_character: None,
13527                }),
13528                ..Default::default()
13529            },
13530            ..Default::default()
13531        },
13532    );
13533
13534    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13535
13536    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13537
13538    let worktree_id = workspace
13539        .update(cx, |workspace, _, cx| {
13540            workspace.project().update(cx, |project, cx| {
13541                project.worktrees(cx).next().unwrap().read(cx).id()
13542            })
13543        })
13544        .unwrap();
13545
13546    let buffer = project
13547        .update(cx, |project, cx| {
13548            project.open_local_buffer(path!("/a/main.rs"), cx)
13549        })
13550        .await
13551        .unwrap();
13552    let editor_handle = workspace
13553        .update(cx, |workspace, window, cx| {
13554            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13555        })
13556        .unwrap()
13557        .await
13558        .unwrap()
13559        .downcast::<Editor>()
13560        .unwrap();
13561
13562    cx.executor().start_waiting();
13563    let fake_server = fake_servers.next().await.unwrap();
13564
13565    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13566        |params, _| async move {
13567            assert_eq!(
13568                params.text_document_position.text_document.uri,
13569                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13570            );
13571            assert_eq!(
13572                params.text_document_position.position,
13573                lsp::Position::new(0, 21),
13574            );
13575
13576            Ok(Some(vec![lsp::TextEdit {
13577                new_text: "]".to_string(),
13578                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13579            }]))
13580        },
13581    );
13582
13583    editor_handle.update_in(cx, |editor, window, cx| {
13584        window.focus(&editor.focus_handle(cx));
13585        editor.change_selections(None, window, cx, |s| {
13586            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13587        });
13588        editor.handle_input("{", window, cx);
13589    });
13590
13591    cx.executor().run_until_parked();
13592
13593    buffer.update(cx, |buffer, _| {
13594        assert_eq!(
13595            buffer.text(),
13596            "fn main() { let a = {5}; }",
13597            "No extra braces from on type formatting should appear in the buffer"
13598        )
13599    });
13600}
13601
13602#[gpui::test]
13603async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13604    init_test(cx, |_| {});
13605
13606    let fs = FakeFs::new(cx.executor());
13607    fs.insert_tree(
13608        path!("/a"),
13609        json!({
13610            "main.rs": "fn main() { let a = 5; }",
13611            "other.rs": "// Test file",
13612        }),
13613    )
13614    .await;
13615
13616    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13617
13618    let server_restarts = Arc::new(AtomicUsize::new(0));
13619    let closure_restarts = Arc::clone(&server_restarts);
13620    let language_server_name = "test language server";
13621    let language_name: LanguageName = "Rust".into();
13622
13623    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13624    language_registry.add(Arc::new(Language::new(
13625        LanguageConfig {
13626            name: language_name.clone(),
13627            matcher: LanguageMatcher {
13628                path_suffixes: vec!["rs".to_string()],
13629                ..Default::default()
13630            },
13631            ..Default::default()
13632        },
13633        Some(tree_sitter_rust::LANGUAGE.into()),
13634    )));
13635    let mut fake_servers = language_registry.register_fake_lsp(
13636        "Rust",
13637        FakeLspAdapter {
13638            name: language_server_name,
13639            initialization_options: Some(json!({
13640                "testOptionValue": true
13641            })),
13642            initializer: Some(Box::new(move |fake_server| {
13643                let task_restarts = Arc::clone(&closure_restarts);
13644                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13645                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13646                    futures::future::ready(Ok(()))
13647                });
13648            })),
13649            ..Default::default()
13650        },
13651    );
13652
13653    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13654    let _buffer = project
13655        .update(cx, |project, cx| {
13656            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13657        })
13658        .await
13659        .unwrap();
13660    let _fake_server = fake_servers.next().await.unwrap();
13661    update_test_language_settings(cx, |language_settings| {
13662        language_settings.languages.insert(
13663            language_name.clone(),
13664            LanguageSettingsContent {
13665                tab_size: NonZeroU32::new(8),
13666                ..Default::default()
13667            },
13668        );
13669    });
13670    cx.executor().run_until_parked();
13671    assert_eq!(
13672        server_restarts.load(atomic::Ordering::Acquire),
13673        0,
13674        "Should not restart LSP server on an unrelated change"
13675    );
13676
13677    update_test_project_settings(cx, |project_settings| {
13678        project_settings.lsp.insert(
13679            "Some other server name".into(),
13680            LspSettings {
13681                binary: None,
13682                settings: None,
13683                initialization_options: Some(json!({
13684                    "some other init value": false
13685                })),
13686                enable_lsp_tasks: false,
13687            },
13688        );
13689    });
13690    cx.executor().run_until_parked();
13691    assert_eq!(
13692        server_restarts.load(atomic::Ordering::Acquire),
13693        0,
13694        "Should not restart LSP server on an unrelated LSP settings change"
13695    );
13696
13697    update_test_project_settings(cx, |project_settings| {
13698        project_settings.lsp.insert(
13699            language_server_name.into(),
13700            LspSettings {
13701                binary: None,
13702                settings: None,
13703                initialization_options: Some(json!({
13704                    "anotherInitValue": false
13705                })),
13706                enable_lsp_tasks: false,
13707            },
13708        );
13709    });
13710    cx.executor().run_until_parked();
13711    assert_eq!(
13712        server_restarts.load(atomic::Ordering::Acquire),
13713        1,
13714        "Should restart LSP server on a related LSP settings change"
13715    );
13716
13717    update_test_project_settings(cx, |project_settings| {
13718        project_settings.lsp.insert(
13719            language_server_name.into(),
13720            LspSettings {
13721                binary: None,
13722                settings: None,
13723                initialization_options: Some(json!({
13724                    "anotherInitValue": false
13725                })),
13726                enable_lsp_tasks: false,
13727            },
13728        );
13729    });
13730    cx.executor().run_until_parked();
13731    assert_eq!(
13732        server_restarts.load(atomic::Ordering::Acquire),
13733        1,
13734        "Should not restart LSP server on a related LSP settings change that is the same"
13735    );
13736
13737    update_test_project_settings(cx, |project_settings| {
13738        project_settings.lsp.insert(
13739            language_server_name.into(),
13740            LspSettings {
13741                binary: None,
13742                settings: None,
13743                initialization_options: None,
13744                enable_lsp_tasks: false,
13745            },
13746        );
13747    });
13748    cx.executor().run_until_parked();
13749    assert_eq!(
13750        server_restarts.load(atomic::Ordering::Acquire),
13751        2,
13752        "Should restart LSP server on another related LSP settings change"
13753    );
13754}
13755
13756#[gpui::test]
13757async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13758    init_test(cx, |_| {});
13759
13760    let mut cx = EditorLspTestContext::new_rust(
13761        lsp::ServerCapabilities {
13762            completion_provider: Some(lsp::CompletionOptions {
13763                trigger_characters: Some(vec![".".to_string()]),
13764                resolve_provider: Some(true),
13765                ..Default::default()
13766            }),
13767            ..Default::default()
13768        },
13769        cx,
13770    )
13771    .await;
13772
13773    cx.set_state("fn main() { let a = 2ˇ; }");
13774    cx.simulate_keystroke(".");
13775    let completion_item = lsp::CompletionItem {
13776        label: "some".into(),
13777        kind: Some(lsp::CompletionItemKind::SNIPPET),
13778        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13779        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13780            kind: lsp::MarkupKind::Markdown,
13781            value: "```rust\nSome(2)\n```".to_string(),
13782        })),
13783        deprecated: Some(false),
13784        sort_text: Some("fffffff2".to_string()),
13785        filter_text: Some("some".to_string()),
13786        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13787        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13788            range: lsp::Range {
13789                start: lsp::Position {
13790                    line: 0,
13791                    character: 22,
13792                },
13793                end: lsp::Position {
13794                    line: 0,
13795                    character: 22,
13796                },
13797            },
13798            new_text: "Some(2)".to_string(),
13799        })),
13800        additional_text_edits: Some(vec![lsp::TextEdit {
13801            range: lsp::Range {
13802                start: lsp::Position {
13803                    line: 0,
13804                    character: 20,
13805                },
13806                end: lsp::Position {
13807                    line: 0,
13808                    character: 22,
13809                },
13810            },
13811            new_text: "".to_string(),
13812        }]),
13813        ..Default::default()
13814    };
13815
13816    let closure_completion_item = completion_item.clone();
13817    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13818        let task_completion_item = closure_completion_item.clone();
13819        async move {
13820            Ok(Some(lsp::CompletionResponse::Array(vec![
13821                task_completion_item,
13822            ])))
13823        }
13824    });
13825
13826    request.next().await;
13827
13828    cx.condition(|editor, _| editor.context_menu_visible())
13829        .await;
13830    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13831        editor
13832            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13833            .unwrap()
13834    });
13835    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13836
13837    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13838        let task_completion_item = completion_item.clone();
13839        async move { Ok(task_completion_item) }
13840    })
13841    .next()
13842    .await
13843    .unwrap();
13844    apply_additional_edits.await.unwrap();
13845    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13846}
13847
13848#[gpui::test]
13849async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13850    init_test(cx, |_| {});
13851
13852    let mut cx = EditorLspTestContext::new_rust(
13853        lsp::ServerCapabilities {
13854            completion_provider: Some(lsp::CompletionOptions {
13855                trigger_characters: Some(vec![".".to_string()]),
13856                resolve_provider: Some(true),
13857                ..Default::default()
13858            }),
13859            ..Default::default()
13860        },
13861        cx,
13862    )
13863    .await;
13864
13865    cx.set_state("fn main() { let a = 2ˇ; }");
13866    cx.simulate_keystroke(".");
13867
13868    let item1 = lsp::CompletionItem {
13869        label: "method id()".to_string(),
13870        filter_text: Some("id".to_string()),
13871        detail: None,
13872        documentation: None,
13873        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13874            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13875            new_text: ".id".to_string(),
13876        })),
13877        ..lsp::CompletionItem::default()
13878    };
13879
13880    let item2 = lsp::CompletionItem {
13881        label: "other".to_string(),
13882        filter_text: Some("other".to_string()),
13883        detail: None,
13884        documentation: None,
13885        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13886            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13887            new_text: ".other".to_string(),
13888        })),
13889        ..lsp::CompletionItem::default()
13890    };
13891
13892    let item1 = item1.clone();
13893    cx.set_request_handler::<lsp::request::Completion, _, _>({
13894        let item1 = item1.clone();
13895        move |_, _, _| {
13896            let item1 = item1.clone();
13897            let item2 = item2.clone();
13898            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13899        }
13900    })
13901    .next()
13902    .await;
13903
13904    cx.condition(|editor, _| editor.context_menu_visible())
13905        .await;
13906    cx.update_editor(|editor, _, _| {
13907        let context_menu = editor.context_menu.borrow_mut();
13908        let context_menu = context_menu
13909            .as_ref()
13910            .expect("Should have the context menu deployed");
13911        match context_menu {
13912            CodeContextMenu::Completions(completions_menu) => {
13913                let completions = completions_menu.completions.borrow_mut();
13914                assert_eq!(
13915                    completions
13916                        .iter()
13917                        .map(|completion| &completion.label.text)
13918                        .collect::<Vec<_>>(),
13919                    vec!["method id()", "other"]
13920                )
13921            }
13922            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13923        }
13924    });
13925
13926    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13927        let item1 = item1.clone();
13928        move |_, item_to_resolve, _| {
13929            let item1 = item1.clone();
13930            async move {
13931                if item1 == item_to_resolve {
13932                    Ok(lsp::CompletionItem {
13933                        label: "method id()".to_string(),
13934                        filter_text: Some("id".to_string()),
13935                        detail: Some("Now resolved!".to_string()),
13936                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13937                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13938                            range: lsp::Range::new(
13939                                lsp::Position::new(0, 22),
13940                                lsp::Position::new(0, 22),
13941                            ),
13942                            new_text: ".id".to_string(),
13943                        })),
13944                        ..lsp::CompletionItem::default()
13945                    })
13946                } else {
13947                    Ok(item_to_resolve)
13948                }
13949            }
13950        }
13951    })
13952    .next()
13953    .await
13954    .unwrap();
13955    cx.run_until_parked();
13956
13957    cx.update_editor(|editor, window, cx| {
13958        editor.context_menu_next(&Default::default(), window, cx);
13959    });
13960
13961    cx.update_editor(|editor, _, _| {
13962        let context_menu = editor.context_menu.borrow_mut();
13963        let context_menu = context_menu
13964            .as_ref()
13965            .expect("Should have the context menu deployed");
13966        match context_menu {
13967            CodeContextMenu::Completions(completions_menu) => {
13968                let completions = completions_menu.completions.borrow_mut();
13969                assert_eq!(
13970                    completions
13971                        .iter()
13972                        .map(|completion| &completion.label.text)
13973                        .collect::<Vec<_>>(),
13974                    vec!["method id() Now resolved!", "other"],
13975                    "Should update first completion label, but not second as the filter text did not match."
13976                );
13977            }
13978            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13979        }
13980    });
13981}
13982
13983#[gpui::test]
13984async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
13985    init_test(cx, |_| {});
13986    let mut cx = EditorLspTestContext::new_rust(
13987        lsp::ServerCapabilities {
13988            hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
13989            code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
13990            completion_provider: Some(lsp::CompletionOptions {
13991                resolve_provider: Some(true),
13992                ..Default::default()
13993            }),
13994            ..Default::default()
13995        },
13996        cx,
13997    )
13998    .await;
13999    cx.set_state(indoc! {"
14000        struct TestStruct {
14001            field: i32
14002        }
14003
14004        fn mainˇ() {
14005            let unused_var = 42;
14006            let test_struct = TestStruct { field: 42 };
14007        }
14008    "});
14009    let symbol_range = cx.lsp_range(indoc! {"
14010        struct TestStruct {
14011            field: i32
14012        }
14013
14014        «fn main»() {
14015            let unused_var = 42;
14016            let test_struct = TestStruct { field: 42 };
14017        }
14018    "});
14019    let mut hover_requests =
14020        cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
14021            Ok(Some(lsp::Hover {
14022                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
14023                    kind: lsp::MarkupKind::Markdown,
14024                    value: "Function documentation".to_string(),
14025                }),
14026                range: Some(symbol_range),
14027            }))
14028        });
14029
14030    // Case 1: Test that code action menu hide hover popover
14031    cx.dispatch_action(Hover);
14032    hover_requests.next().await;
14033    cx.condition(|editor, _| editor.hover_state.visible()).await;
14034    let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
14035        move |_, _, _| async move {
14036            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
14037                lsp::CodeAction {
14038                    title: "Remove unused variable".to_string(),
14039                    kind: Some(CodeActionKind::QUICKFIX),
14040                    edit: Some(lsp::WorkspaceEdit {
14041                        changes: Some(
14042                            [(
14043                                lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
14044                                vec![lsp::TextEdit {
14045                                    range: lsp::Range::new(
14046                                        lsp::Position::new(5, 4),
14047                                        lsp::Position::new(5, 27),
14048                                    ),
14049                                    new_text: "".to_string(),
14050                                }],
14051                            )]
14052                            .into_iter()
14053                            .collect(),
14054                        ),
14055                        ..Default::default()
14056                    }),
14057                    ..Default::default()
14058                },
14059            )]))
14060        },
14061    );
14062    cx.update_editor(|editor, window, cx| {
14063        editor.toggle_code_actions(
14064            &ToggleCodeActions {
14065                deployed_from_indicator: None,
14066                quick_launch: false,
14067            },
14068            window,
14069            cx,
14070        );
14071    });
14072    code_action_requests.next().await;
14073    cx.run_until_parked();
14074    cx.condition(|editor, _| editor.context_menu_visible())
14075        .await;
14076    cx.update_editor(|editor, _, _| {
14077        assert!(
14078            !editor.hover_state.visible(),
14079            "Hover popover should be hidden when code action menu is shown"
14080        );
14081        // Hide code actions
14082        editor.context_menu.take();
14083    });
14084
14085    // Case 2: Test that code completions hide hover popover
14086    cx.dispatch_action(Hover);
14087    hover_requests.next().await;
14088    cx.condition(|editor, _| editor.hover_state.visible()).await;
14089    let counter = Arc::new(AtomicUsize::new(0));
14090    let mut completion_requests =
14091        cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14092            let counter = counter.clone();
14093            async move {
14094                counter.fetch_add(1, atomic::Ordering::Release);
14095                Ok(Some(lsp::CompletionResponse::Array(vec![
14096                    lsp::CompletionItem {
14097                        label: "main".into(),
14098                        kind: Some(lsp::CompletionItemKind::FUNCTION),
14099                        detail: Some("() -> ()".to_string()),
14100                        ..Default::default()
14101                    },
14102                    lsp::CompletionItem {
14103                        label: "TestStruct".into(),
14104                        kind: Some(lsp::CompletionItemKind::STRUCT),
14105                        detail: Some("struct TestStruct".to_string()),
14106                        ..Default::default()
14107                    },
14108                ])))
14109            }
14110        });
14111    cx.update_editor(|editor, window, cx| {
14112        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
14113    });
14114    completion_requests.next().await;
14115    cx.condition(|editor, _| editor.context_menu_visible())
14116        .await;
14117    cx.update_editor(|editor, _, _| {
14118        assert!(
14119            !editor.hover_state.visible(),
14120            "Hover popover should be hidden when completion menu is shown"
14121        );
14122    });
14123}
14124
14125#[gpui::test]
14126async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
14127    init_test(cx, |_| {});
14128
14129    let mut cx = EditorLspTestContext::new_rust(
14130        lsp::ServerCapabilities {
14131            completion_provider: Some(lsp::CompletionOptions {
14132                trigger_characters: Some(vec![".".to_string()]),
14133                resolve_provider: Some(true),
14134                ..Default::default()
14135            }),
14136            ..Default::default()
14137        },
14138        cx,
14139    )
14140    .await;
14141
14142    cx.set_state("fn main() { let a = 2ˇ; }");
14143    cx.simulate_keystroke(".");
14144
14145    let unresolved_item_1 = lsp::CompletionItem {
14146        label: "id".to_string(),
14147        filter_text: Some("id".to_string()),
14148        detail: None,
14149        documentation: None,
14150        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14151            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14152            new_text: ".id".to_string(),
14153        })),
14154        ..lsp::CompletionItem::default()
14155    };
14156    let resolved_item_1 = lsp::CompletionItem {
14157        additional_text_edits: Some(vec![lsp::TextEdit {
14158            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14159            new_text: "!!".to_string(),
14160        }]),
14161        ..unresolved_item_1.clone()
14162    };
14163    let unresolved_item_2 = lsp::CompletionItem {
14164        label: "other".to_string(),
14165        filter_text: Some("other".to_string()),
14166        detail: None,
14167        documentation: None,
14168        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
14169            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
14170            new_text: ".other".to_string(),
14171        })),
14172        ..lsp::CompletionItem::default()
14173    };
14174    let resolved_item_2 = lsp::CompletionItem {
14175        additional_text_edits: Some(vec![lsp::TextEdit {
14176            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
14177            new_text: "??".to_string(),
14178        }]),
14179        ..unresolved_item_2.clone()
14180    };
14181
14182    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
14183    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
14184    cx.lsp
14185        .server
14186        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14187            let unresolved_item_1 = unresolved_item_1.clone();
14188            let resolved_item_1 = resolved_item_1.clone();
14189            let unresolved_item_2 = unresolved_item_2.clone();
14190            let resolved_item_2 = resolved_item_2.clone();
14191            let resolve_requests_1 = resolve_requests_1.clone();
14192            let resolve_requests_2 = resolve_requests_2.clone();
14193            move |unresolved_request, _| {
14194                let unresolved_item_1 = unresolved_item_1.clone();
14195                let resolved_item_1 = resolved_item_1.clone();
14196                let unresolved_item_2 = unresolved_item_2.clone();
14197                let resolved_item_2 = resolved_item_2.clone();
14198                let resolve_requests_1 = resolve_requests_1.clone();
14199                let resolve_requests_2 = resolve_requests_2.clone();
14200                async move {
14201                    if unresolved_request == unresolved_item_1 {
14202                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
14203                        Ok(resolved_item_1.clone())
14204                    } else if unresolved_request == unresolved_item_2 {
14205                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
14206                        Ok(resolved_item_2.clone())
14207                    } else {
14208                        panic!("Unexpected completion item {unresolved_request:?}")
14209                    }
14210                }
14211            }
14212        })
14213        .detach();
14214
14215    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14216        let unresolved_item_1 = unresolved_item_1.clone();
14217        let unresolved_item_2 = unresolved_item_2.clone();
14218        async move {
14219            Ok(Some(lsp::CompletionResponse::Array(vec![
14220                unresolved_item_1,
14221                unresolved_item_2,
14222            ])))
14223        }
14224    })
14225    .next()
14226    .await;
14227
14228    cx.condition(|editor, _| editor.context_menu_visible())
14229        .await;
14230    cx.update_editor(|editor, _, _| {
14231        let context_menu = editor.context_menu.borrow_mut();
14232        let context_menu = context_menu
14233            .as_ref()
14234            .expect("Should have the context menu deployed");
14235        match context_menu {
14236            CodeContextMenu::Completions(completions_menu) => {
14237                let completions = completions_menu.completions.borrow_mut();
14238                assert_eq!(
14239                    completions
14240                        .iter()
14241                        .map(|completion| &completion.label.text)
14242                        .collect::<Vec<_>>(),
14243                    vec!["id", "other"]
14244                )
14245            }
14246            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14247        }
14248    });
14249    cx.run_until_parked();
14250
14251    cx.update_editor(|editor, window, cx| {
14252        editor.context_menu_next(&ContextMenuNext, window, cx);
14253    });
14254    cx.run_until_parked();
14255    cx.update_editor(|editor, window, cx| {
14256        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14257    });
14258    cx.run_until_parked();
14259    cx.update_editor(|editor, window, cx| {
14260        editor.context_menu_next(&ContextMenuNext, window, cx);
14261    });
14262    cx.run_until_parked();
14263    cx.update_editor(|editor, window, cx| {
14264        editor
14265            .compose_completion(&ComposeCompletion::default(), window, cx)
14266            .expect("No task returned")
14267    })
14268    .await
14269    .expect("Completion failed");
14270    cx.run_until_parked();
14271
14272    cx.update_editor(|editor, _, cx| {
14273        assert_eq!(
14274            resolve_requests_1.load(atomic::Ordering::Acquire),
14275            1,
14276            "Should always resolve once despite multiple selections"
14277        );
14278        assert_eq!(
14279            resolve_requests_2.load(atomic::Ordering::Acquire),
14280            1,
14281            "Should always resolve once after multiple selections and applying the completion"
14282        );
14283        assert_eq!(
14284            editor.text(cx),
14285            "fn main() { let a = ??.other; }",
14286            "Should use resolved data when applying the completion"
14287        );
14288    });
14289}
14290
14291#[gpui::test]
14292async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14293    init_test(cx, |_| {});
14294
14295    let item_0 = lsp::CompletionItem {
14296        label: "abs".into(),
14297        insert_text: Some("abs".into()),
14298        data: Some(json!({ "very": "special"})),
14299        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14300        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14301            lsp::InsertReplaceEdit {
14302                new_text: "abs".to_string(),
14303                insert: lsp::Range::default(),
14304                replace: lsp::Range::default(),
14305            },
14306        )),
14307        ..lsp::CompletionItem::default()
14308    };
14309    let items = iter::once(item_0.clone())
14310        .chain((11..51).map(|i| lsp::CompletionItem {
14311            label: format!("item_{}", i),
14312            insert_text: Some(format!("item_{}", i)),
14313            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14314            ..lsp::CompletionItem::default()
14315        }))
14316        .collect::<Vec<_>>();
14317
14318    let default_commit_characters = vec!["?".to_string()];
14319    let default_data = json!({ "default": "data"});
14320    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14321    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14322    let default_edit_range = lsp::Range {
14323        start: lsp::Position {
14324            line: 0,
14325            character: 5,
14326        },
14327        end: lsp::Position {
14328            line: 0,
14329            character: 5,
14330        },
14331    };
14332
14333    let mut cx = EditorLspTestContext::new_rust(
14334        lsp::ServerCapabilities {
14335            completion_provider: Some(lsp::CompletionOptions {
14336                trigger_characters: Some(vec![".".to_string()]),
14337                resolve_provider: Some(true),
14338                ..Default::default()
14339            }),
14340            ..Default::default()
14341        },
14342        cx,
14343    )
14344    .await;
14345
14346    cx.set_state("fn main() { let a = 2ˇ; }");
14347    cx.simulate_keystroke(".");
14348
14349    let completion_data = default_data.clone();
14350    let completion_characters = default_commit_characters.clone();
14351    let completion_items = items.clone();
14352    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14353        let default_data = completion_data.clone();
14354        let default_commit_characters = completion_characters.clone();
14355        let items = completion_items.clone();
14356        async move {
14357            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14358                items,
14359                item_defaults: Some(lsp::CompletionListItemDefaults {
14360                    data: Some(default_data.clone()),
14361                    commit_characters: Some(default_commit_characters.clone()),
14362                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14363                        default_edit_range,
14364                    )),
14365                    insert_text_format: Some(default_insert_text_format),
14366                    insert_text_mode: Some(default_insert_text_mode),
14367                }),
14368                ..lsp::CompletionList::default()
14369            })))
14370        }
14371    })
14372    .next()
14373    .await;
14374
14375    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14376    cx.lsp
14377        .server
14378        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14379            let closure_resolved_items = resolved_items.clone();
14380            move |item_to_resolve, _| {
14381                let closure_resolved_items = closure_resolved_items.clone();
14382                async move {
14383                    closure_resolved_items.lock().push(item_to_resolve.clone());
14384                    Ok(item_to_resolve)
14385                }
14386            }
14387        })
14388        .detach();
14389
14390    cx.condition(|editor, _| editor.context_menu_visible())
14391        .await;
14392    cx.run_until_parked();
14393    cx.update_editor(|editor, _, _| {
14394        let menu = editor.context_menu.borrow_mut();
14395        match menu.as_ref().expect("should have the completions menu") {
14396            CodeContextMenu::Completions(completions_menu) => {
14397                assert_eq!(
14398                    completions_menu
14399                        .entries
14400                        .borrow()
14401                        .iter()
14402                        .map(|mat| mat.string.clone())
14403                        .collect::<Vec<String>>(),
14404                    items
14405                        .iter()
14406                        .map(|completion| completion.label.clone())
14407                        .collect::<Vec<String>>()
14408                );
14409            }
14410            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14411        }
14412    });
14413    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14414    // with 4 from the end.
14415    assert_eq!(
14416        *resolved_items.lock(),
14417        [&items[0..16], &items[items.len() - 4..items.len()]]
14418            .concat()
14419            .iter()
14420            .cloned()
14421            .map(|mut item| {
14422                if item.data.is_none() {
14423                    item.data = Some(default_data.clone());
14424                }
14425                item
14426            })
14427            .collect::<Vec<lsp::CompletionItem>>(),
14428        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14429    );
14430    resolved_items.lock().clear();
14431
14432    cx.update_editor(|editor, window, cx| {
14433        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14434    });
14435    cx.run_until_parked();
14436    // Completions that have already been resolved are skipped.
14437    assert_eq!(
14438        *resolved_items.lock(),
14439        items[items.len() - 16..items.len() - 4]
14440            .iter()
14441            .cloned()
14442            .map(|mut item| {
14443                if item.data.is_none() {
14444                    item.data = Some(default_data.clone());
14445                }
14446                item
14447            })
14448            .collect::<Vec<lsp::CompletionItem>>()
14449    );
14450    resolved_items.lock().clear();
14451}
14452
14453#[gpui::test]
14454async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14455    init_test(cx, |_| {});
14456
14457    let mut cx = EditorLspTestContext::new(
14458        Language::new(
14459            LanguageConfig {
14460                matcher: LanguageMatcher {
14461                    path_suffixes: vec!["jsx".into()],
14462                    ..Default::default()
14463                },
14464                overrides: [(
14465                    "element".into(),
14466                    LanguageConfigOverride {
14467                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14468                        ..Default::default()
14469                    },
14470                )]
14471                .into_iter()
14472                .collect(),
14473                ..Default::default()
14474            },
14475            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14476        )
14477        .with_override_query("(jsx_self_closing_element) @element")
14478        .unwrap(),
14479        lsp::ServerCapabilities {
14480            completion_provider: Some(lsp::CompletionOptions {
14481                trigger_characters: Some(vec![":".to_string()]),
14482                ..Default::default()
14483            }),
14484            ..Default::default()
14485        },
14486        cx,
14487    )
14488    .await;
14489
14490    cx.lsp
14491        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14492            Ok(Some(lsp::CompletionResponse::Array(vec![
14493                lsp::CompletionItem {
14494                    label: "bg-blue".into(),
14495                    ..Default::default()
14496                },
14497                lsp::CompletionItem {
14498                    label: "bg-red".into(),
14499                    ..Default::default()
14500                },
14501                lsp::CompletionItem {
14502                    label: "bg-yellow".into(),
14503                    ..Default::default()
14504                },
14505            ])))
14506        });
14507
14508    cx.set_state(r#"<p class="bgˇ" />"#);
14509
14510    // Trigger completion when typing a dash, because the dash is an extra
14511    // word character in the 'element' scope, which contains the cursor.
14512    cx.simulate_keystroke("-");
14513    cx.executor().run_until_parked();
14514    cx.update_editor(|editor, _, _| {
14515        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14516        {
14517            assert_eq!(
14518                completion_menu_entries(&menu),
14519                &["bg-red", "bg-blue", "bg-yellow"]
14520            );
14521        } else {
14522            panic!("expected completion menu to be open");
14523        }
14524    });
14525
14526    cx.simulate_keystroke("l");
14527    cx.executor().run_until_parked();
14528    cx.update_editor(|editor, _, _| {
14529        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14530        {
14531            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14532        } else {
14533            panic!("expected completion menu to be open");
14534        }
14535    });
14536
14537    // When filtering completions, consider the character after the '-' to
14538    // be the start of a subword.
14539    cx.set_state(r#"<p class="yelˇ" />"#);
14540    cx.simulate_keystroke("l");
14541    cx.executor().run_until_parked();
14542    cx.update_editor(|editor, _, _| {
14543        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14544        {
14545            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14546        } else {
14547            panic!("expected completion menu to be open");
14548        }
14549    });
14550}
14551
14552fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14553    let entries = menu.entries.borrow();
14554    entries.iter().map(|mat| mat.string.clone()).collect()
14555}
14556
14557#[gpui::test]
14558async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14559    init_test(cx, |settings| {
14560        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14561            FormatterList(vec![Formatter::Prettier].into()),
14562        ))
14563    });
14564
14565    let fs = FakeFs::new(cx.executor());
14566    fs.insert_file(path!("/file.ts"), Default::default()).await;
14567
14568    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14569    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14570
14571    language_registry.add(Arc::new(Language::new(
14572        LanguageConfig {
14573            name: "TypeScript".into(),
14574            matcher: LanguageMatcher {
14575                path_suffixes: vec!["ts".to_string()],
14576                ..Default::default()
14577            },
14578            ..Default::default()
14579        },
14580        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14581    )));
14582    update_test_language_settings(cx, |settings| {
14583        settings.defaults.prettier = Some(PrettierSettings {
14584            allowed: true,
14585            ..PrettierSettings::default()
14586        });
14587    });
14588
14589    let test_plugin = "test_plugin";
14590    let _ = language_registry.register_fake_lsp(
14591        "TypeScript",
14592        FakeLspAdapter {
14593            prettier_plugins: vec![test_plugin],
14594            ..Default::default()
14595        },
14596    );
14597
14598    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14599    let buffer = project
14600        .update(cx, |project, cx| {
14601            project.open_local_buffer(path!("/file.ts"), cx)
14602        })
14603        .await
14604        .unwrap();
14605
14606    let buffer_text = "one\ntwo\nthree\n";
14607    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14608    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14609    editor.update_in(cx, |editor, window, cx| {
14610        editor.set_text(buffer_text, window, cx)
14611    });
14612
14613    editor
14614        .update_in(cx, |editor, window, cx| {
14615            editor.perform_format(
14616                project.clone(),
14617                FormatTrigger::Manual,
14618                FormatTarget::Buffers,
14619                window,
14620                cx,
14621            )
14622        })
14623        .unwrap()
14624        .await;
14625    assert_eq!(
14626        editor.update(cx, |editor, cx| editor.text(cx)),
14627        buffer_text.to_string() + prettier_format_suffix,
14628        "Test prettier formatting was not applied to the original buffer text",
14629    );
14630
14631    update_test_language_settings(cx, |settings| {
14632        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14633    });
14634    let format = editor.update_in(cx, |editor, window, cx| {
14635        editor.perform_format(
14636            project.clone(),
14637            FormatTrigger::Manual,
14638            FormatTarget::Buffers,
14639            window,
14640            cx,
14641        )
14642    });
14643    format.await.unwrap();
14644    assert_eq!(
14645        editor.update(cx, |editor, cx| editor.text(cx)),
14646        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14647        "Autoformatting (via test prettier) was not applied to the original buffer text",
14648    );
14649}
14650
14651#[gpui::test]
14652async fn test_addition_reverts(cx: &mut TestAppContext) {
14653    init_test(cx, |_| {});
14654    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14655    let base_text = indoc! {r#"
14656        struct Row;
14657        struct Row1;
14658        struct Row2;
14659
14660        struct Row4;
14661        struct Row5;
14662        struct Row6;
14663
14664        struct Row8;
14665        struct Row9;
14666        struct Row10;"#};
14667
14668    // When addition hunks are not adjacent to carets, no hunk revert is performed
14669    assert_hunk_revert(
14670        indoc! {r#"struct Row;
14671                   struct Row1;
14672                   struct Row1.1;
14673                   struct Row1.2;
14674                   struct Row2;ˇ
14675
14676                   struct Row4;
14677                   struct Row5;
14678                   struct Row6;
14679
14680                   struct Row8;
14681                   ˇstruct Row9;
14682                   struct Row9.1;
14683                   struct Row9.2;
14684                   struct Row9.3;
14685                   struct Row10;"#},
14686        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14687        indoc! {r#"struct Row;
14688                   struct Row1;
14689                   struct Row1.1;
14690                   struct Row1.2;
14691                   struct Row2;ˇ
14692
14693                   struct Row4;
14694                   struct Row5;
14695                   struct Row6;
14696
14697                   struct Row8;
14698                   ˇstruct Row9;
14699                   struct Row9.1;
14700                   struct Row9.2;
14701                   struct Row9.3;
14702                   struct Row10;"#},
14703        base_text,
14704        &mut cx,
14705    );
14706    // Same for selections
14707    assert_hunk_revert(
14708        indoc! {r#"struct Row;
14709                   struct Row1;
14710                   struct Row2;
14711                   struct Row2.1;
14712                   struct Row2.2;
14713                   «ˇ
14714                   struct Row4;
14715                   struct» Row5;
14716                   «struct Row6;
14717                   ˇ»
14718                   struct Row9.1;
14719                   struct Row9.2;
14720                   struct Row9.3;
14721                   struct Row8;
14722                   struct Row9;
14723                   struct Row10;"#},
14724        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14725        indoc! {r#"struct Row;
14726                   struct Row1;
14727                   struct Row2;
14728                   struct Row2.1;
14729                   struct Row2.2;
14730                   «ˇ
14731                   struct Row4;
14732                   struct» Row5;
14733                   «struct Row6;
14734                   ˇ»
14735                   struct Row9.1;
14736                   struct Row9.2;
14737                   struct Row9.3;
14738                   struct Row8;
14739                   struct Row9;
14740                   struct Row10;"#},
14741        base_text,
14742        &mut cx,
14743    );
14744
14745    // When carets and selections intersect the addition hunks, those are reverted.
14746    // Adjacent carets got merged.
14747    assert_hunk_revert(
14748        indoc! {r#"struct Row;
14749                   ˇ// something on the top
14750                   struct Row1;
14751                   struct Row2;
14752                   struct Roˇw3.1;
14753                   struct Row2.2;
14754                   struct Row2.3;ˇ
14755
14756                   struct Row4;
14757                   struct ˇRow5.1;
14758                   struct Row5.2;
14759                   struct «Rowˇ»5.3;
14760                   struct Row5;
14761                   struct Row6;
14762                   ˇ
14763                   struct Row9.1;
14764                   struct «Rowˇ»9.2;
14765                   struct «ˇRow»9.3;
14766                   struct Row8;
14767                   struct Row9;
14768                   «ˇ// something on bottom»
14769                   struct Row10;"#},
14770        vec![
14771            DiffHunkStatusKind::Added,
14772            DiffHunkStatusKind::Added,
14773            DiffHunkStatusKind::Added,
14774            DiffHunkStatusKind::Added,
14775            DiffHunkStatusKind::Added,
14776        ],
14777        indoc! {r#"struct Row;
14778                   ˇstruct Row1;
14779                   struct Row2;
14780                   ˇ
14781                   struct Row4;
14782                   ˇstruct Row5;
14783                   struct Row6;
14784                   ˇ
14785                   ˇstruct Row8;
14786                   struct Row9;
14787                   ˇstruct Row10;"#},
14788        base_text,
14789        &mut cx,
14790    );
14791}
14792
14793#[gpui::test]
14794async fn test_modification_reverts(cx: &mut TestAppContext) {
14795    init_test(cx, |_| {});
14796    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14797    let base_text = indoc! {r#"
14798        struct Row;
14799        struct Row1;
14800        struct Row2;
14801
14802        struct Row4;
14803        struct Row5;
14804        struct Row6;
14805
14806        struct Row8;
14807        struct Row9;
14808        struct Row10;"#};
14809
14810    // Modification hunks behave the same as the addition ones.
14811    assert_hunk_revert(
14812        indoc! {r#"struct Row;
14813                   struct Row1;
14814                   struct Row33;
14815                   ˇ
14816                   struct Row4;
14817                   struct Row5;
14818                   struct Row6;
14819                   ˇ
14820                   struct Row99;
14821                   struct Row9;
14822                   struct Row10;"#},
14823        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14824        indoc! {r#"struct Row;
14825                   struct Row1;
14826                   struct Row33;
14827                   ˇ
14828                   struct Row4;
14829                   struct Row5;
14830                   struct Row6;
14831                   ˇ
14832                   struct Row99;
14833                   struct Row9;
14834                   struct Row10;"#},
14835        base_text,
14836        &mut cx,
14837    );
14838    assert_hunk_revert(
14839        indoc! {r#"struct Row;
14840                   struct Row1;
14841                   struct Row33;
14842                   «ˇ
14843                   struct Row4;
14844                   struct» Row5;
14845                   «struct Row6;
14846                   ˇ»
14847                   struct Row99;
14848                   struct Row9;
14849                   struct Row10;"#},
14850        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14851        indoc! {r#"struct Row;
14852                   struct Row1;
14853                   struct Row33;
14854                   «ˇ
14855                   struct Row4;
14856                   struct» Row5;
14857                   «struct Row6;
14858                   ˇ»
14859                   struct Row99;
14860                   struct Row9;
14861                   struct Row10;"#},
14862        base_text,
14863        &mut cx,
14864    );
14865
14866    assert_hunk_revert(
14867        indoc! {r#"ˇstruct Row1.1;
14868                   struct Row1;
14869                   «ˇstr»uct Row22;
14870
14871                   struct ˇRow44;
14872                   struct Row5;
14873                   struct «Rˇ»ow66;ˇ
14874
14875                   «struˇ»ct Row88;
14876                   struct Row9;
14877                   struct Row1011;ˇ"#},
14878        vec![
14879            DiffHunkStatusKind::Modified,
14880            DiffHunkStatusKind::Modified,
14881            DiffHunkStatusKind::Modified,
14882            DiffHunkStatusKind::Modified,
14883            DiffHunkStatusKind::Modified,
14884            DiffHunkStatusKind::Modified,
14885        ],
14886        indoc! {r#"struct Row;
14887                   ˇstruct Row1;
14888                   struct Row2;
14889                   ˇ
14890                   struct Row4;
14891                   ˇstruct Row5;
14892                   struct Row6;
14893                   ˇ
14894                   struct Row8;
14895                   ˇstruct Row9;
14896                   struct Row10;ˇ"#},
14897        base_text,
14898        &mut cx,
14899    );
14900}
14901
14902#[gpui::test]
14903async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14904    init_test(cx, |_| {});
14905    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14906    let base_text = indoc! {r#"
14907        one
14908
14909        two
14910        three
14911        "#};
14912
14913    cx.set_head_text(base_text);
14914    cx.set_state("\nˇ\n");
14915    cx.executor().run_until_parked();
14916    cx.update_editor(|editor, _window, cx| {
14917        editor.expand_selected_diff_hunks(cx);
14918    });
14919    cx.executor().run_until_parked();
14920    cx.update_editor(|editor, window, cx| {
14921        editor.backspace(&Default::default(), window, cx);
14922    });
14923    cx.run_until_parked();
14924    cx.assert_state_with_diff(
14925        indoc! {r#"
14926
14927        - two
14928        - threeˇ
14929        +
14930        "#}
14931        .to_string(),
14932    );
14933}
14934
14935#[gpui::test]
14936async fn test_deletion_reverts(cx: &mut TestAppContext) {
14937    init_test(cx, |_| {});
14938    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14939    let base_text = indoc! {r#"struct Row;
14940struct Row1;
14941struct Row2;
14942
14943struct Row4;
14944struct Row5;
14945struct Row6;
14946
14947struct Row8;
14948struct Row9;
14949struct Row10;"#};
14950
14951    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14952    assert_hunk_revert(
14953        indoc! {r#"struct Row;
14954                   struct Row2;
14955
14956                   ˇstruct Row4;
14957                   struct Row5;
14958                   struct Row6;
14959                   ˇ
14960                   struct Row8;
14961                   struct Row10;"#},
14962        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14963        indoc! {r#"struct Row;
14964                   struct Row2;
14965
14966                   ˇstruct Row4;
14967                   struct Row5;
14968                   struct Row6;
14969                   ˇ
14970                   struct Row8;
14971                   struct Row10;"#},
14972        base_text,
14973        &mut cx,
14974    );
14975    assert_hunk_revert(
14976        indoc! {r#"struct Row;
14977                   struct Row2;
14978
14979                   «ˇstruct Row4;
14980                   struct» Row5;
14981                   «struct Row6;
14982                   ˇ»
14983                   struct Row8;
14984                   struct Row10;"#},
14985        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14986        indoc! {r#"struct Row;
14987                   struct Row2;
14988
14989                   «ˇstruct Row4;
14990                   struct» Row5;
14991                   «struct Row6;
14992                   ˇ»
14993                   struct Row8;
14994                   struct Row10;"#},
14995        base_text,
14996        &mut cx,
14997    );
14998
14999    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
15000    assert_hunk_revert(
15001        indoc! {r#"struct Row;
15002                   ˇstruct Row2;
15003
15004                   struct Row4;
15005                   struct Row5;
15006                   struct Row6;
15007
15008                   struct Row8;ˇ
15009                   struct Row10;"#},
15010        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
15011        indoc! {r#"struct Row;
15012                   struct Row1;
15013                   ˇstruct Row2;
15014
15015                   struct Row4;
15016                   struct Row5;
15017                   struct Row6;
15018
15019                   struct Row8;ˇ
15020                   struct Row9;
15021                   struct Row10;"#},
15022        base_text,
15023        &mut cx,
15024    );
15025    assert_hunk_revert(
15026        indoc! {r#"struct Row;
15027                   struct Row2«ˇ;
15028                   struct Row4;
15029                   struct» Row5;
15030                   «struct Row6;
15031
15032                   struct Row8;ˇ»
15033                   struct Row10;"#},
15034        vec![
15035            DiffHunkStatusKind::Deleted,
15036            DiffHunkStatusKind::Deleted,
15037            DiffHunkStatusKind::Deleted,
15038        ],
15039        indoc! {r#"struct Row;
15040                   struct Row1;
15041                   struct Row2«ˇ;
15042
15043                   struct Row4;
15044                   struct» Row5;
15045                   «struct Row6;
15046
15047                   struct Row8;ˇ»
15048                   struct Row9;
15049                   struct Row10;"#},
15050        base_text,
15051        &mut cx,
15052    );
15053}
15054
15055#[gpui::test]
15056async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
15057    init_test(cx, |_| {});
15058
15059    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
15060    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
15061    let base_text_3 =
15062        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
15063
15064    let text_1 = edit_first_char_of_every_line(base_text_1);
15065    let text_2 = edit_first_char_of_every_line(base_text_2);
15066    let text_3 = edit_first_char_of_every_line(base_text_3);
15067
15068    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
15069    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
15070    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
15071
15072    let multibuffer = cx.new(|cx| {
15073        let mut multibuffer = MultiBuffer::new(ReadWrite);
15074        multibuffer.push_excerpts(
15075            buffer_1.clone(),
15076            [
15077                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15078                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15079                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15080            ],
15081            cx,
15082        );
15083        multibuffer.push_excerpts(
15084            buffer_2.clone(),
15085            [
15086                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15087                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15088                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15089            ],
15090            cx,
15091        );
15092        multibuffer.push_excerpts(
15093            buffer_3.clone(),
15094            [
15095                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15096                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15097                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15098            ],
15099            cx,
15100        );
15101        multibuffer
15102    });
15103
15104    let fs = FakeFs::new(cx.executor());
15105    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
15106    let (editor, cx) = cx
15107        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
15108    editor.update_in(cx, |editor, _window, cx| {
15109        for (buffer, diff_base) in [
15110            (buffer_1.clone(), base_text_1),
15111            (buffer_2.clone(), base_text_2),
15112            (buffer_3.clone(), base_text_3),
15113        ] {
15114            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15115            editor
15116                .buffer
15117                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15118        }
15119    });
15120    cx.executor().run_until_parked();
15121
15122    editor.update_in(cx, |editor, window, cx| {
15123        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}");
15124        editor.select_all(&SelectAll, window, cx);
15125        editor.git_restore(&Default::default(), window, cx);
15126    });
15127    cx.executor().run_until_parked();
15128
15129    // When all ranges are selected, all buffer hunks are reverted.
15130    editor.update(cx, |editor, cx| {
15131        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");
15132    });
15133    buffer_1.update(cx, |buffer, _| {
15134        assert_eq!(buffer.text(), base_text_1);
15135    });
15136    buffer_2.update(cx, |buffer, _| {
15137        assert_eq!(buffer.text(), base_text_2);
15138    });
15139    buffer_3.update(cx, |buffer, _| {
15140        assert_eq!(buffer.text(), base_text_3);
15141    });
15142
15143    editor.update_in(cx, |editor, window, cx| {
15144        editor.undo(&Default::default(), window, cx);
15145    });
15146
15147    editor.update_in(cx, |editor, window, cx| {
15148        editor.change_selections(None, window, cx, |s| {
15149            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
15150        });
15151        editor.git_restore(&Default::default(), window, cx);
15152    });
15153
15154    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
15155    // but not affect buffer_2 and its related excerpts.
15156    editor.update(cx, |editor, cx| {
15157        assert_eq!(
15158            editor.text(cx),
15159            "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}"
15160        );
15161    });
15162    buffer_1.update(cx, |buffer, _| {
15163        assert_eq!(buffer.text(), base_text_1);
15164    });
15165    buffer_2.update(cx, |buffer, _| {
15166        assert_eq!(
15167            buffer.text(),
15168            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
15169        );
15170    });
15171    buffer_3.update(cx, |buffer, _| {
15172        assert_eq!(
15173            buffer.text(),
15174            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
15175        );
15176    });
15177
15178    fn edit_first_char_of_every_line(text: &str) -> String {
15179        text.split('\n')
15180            .map(|line| format!("X{}", &line[1..]))
15181            .collect::<Vec<_>>()
15182            .join("\n")
15183    }
15184}
15185
15186#[gpui::test]
15187async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
15188    init_test(cx, |_| {});
15189
15190    let cols = 4;
15191    let rows = 10;
15192    let sample_text_1 = sample_text(rows, cols, 'a');
15193    assert_eq!(
15194        sample_text_1,
15195        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
15196    );
15197    let sample_text_2 = sample_text(rows, cols, 'l');
15198    assert_eq!(
15199        sample_text_2,
15200        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
15201    );
15202    let sample_text_3 = sample_text(rows, cols, 'v');
15203    assert_eq!(
15204        sample_text_3,
15205        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
15206    );
15207
15208    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
15209    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
15210    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
15211
15212    let multi_buffer = cx.new(|cx| {
15213        let mut multibuffer = MultiBuffer::new(ReadWrite);
15214        multibuffer.push_excerpts(
15215            buffer_1.clone(),
15216            [
15217                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15218                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15219                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15220            ],
15221            cx,
15222        );
15223        multibuffer.push_excerpts(
15224            buffer_2.clone(),
15225            [
15226                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15227                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15228                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15229            ],
15230            cx,
15231        );
15232        multibuffer.push_excerpts(
15233            buffer_3.clone(),
15234            [
15235                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15236                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15237                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15238            ],
15239            cx,
15240        );
15241        multibuffer
15242    });
15243
15244    let fs = FakeFs::new(cx.executor());
15245    fs.insert_tree(
15246        "/a",
15247        json!({
15248            "main.rs": sample_text_1,
15249            "other.rs": sample_text_2,
15250            "lib.rs": sample_text_3,
15251        }),
15252    )
15253    .await;
15254    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15255    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15256    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15257    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15258        Editor::new(
15259            EditorMode::full(),
15260            multi_buffer,
15261            Some(project.clone()),
15262            window,
15263            cx,
15264        )
15265    });
15266    let multibuffer_item_id = workspace
15267        .update(cx, |workspace, window, cx| {
15268            assert!(
15269                workspace.active_item(cx).is_none(),
15270                "active item should be None before the first item is added"
15271            );
15272            workspace.add_item_to_active_pane(
15273                Box::new(multi_buffer_editor.clone()),
15274                None,
15275                true,
15276                window,
15277                cx,
15278            );
15279            let active_item = workspace
15280                .active_item(cx)
15281                .expect("should have an active item after adding the multi buffer");
15282            assert!(
15283                !active_item.is_singleton(cx),
15284                "A multi buffer was expected to active after adding"
15285            );
15286            active_item.item_id()
15287        })
15288        .unwrap();
15289    cx.executor().run_until_parked();
15290
15291    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15292        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15293            s.select_ranges(Some(1..2))
15294        });
15295        editor.open_excerpts(&OpenExcerpts, window, cx);
15296    });
15297    cx.executor().run_until_parked();
15298    let first_item_id = workspace
15299        .update(cx, |workspace, window, cx| {
15300            let active_item = workspace
15301                .active_item(cx)
15302                .expect("should have an active item after navigating into the 1st buffer");
15303            let first_item_id = active_item.item_id();
15304            assert_ne!(
15305                first_item_id, multibuffer_item_id,
15306                "Should navigate into the 1st buffer and activate it"
15307            );
15308            assert!(
15309                active_item.is_singleton(cx),
15310                "New active item should be a singleton buffer"
15311            );
15312            assert_eq!(
15313                active_item
15314                    .act_as::<Editor>(cx)
15315                    .expect("should have navigated into an editor for the 1st buffer")
15316                    .read(cx)
15317                    .text(cx),
15318                sample_text_1
15319            );
15320
15321            workspace
15322                .go_back(workspace.active_pane().downgrade(), window, cx)
15323                .detach_and_log_err(cx);
15324
15325            first_item_id
15326        })
15327        .unwrap();
15328    cx.executor().run_until_parked();
15329    workspace
15330        .update(cx, |workspace, _, cx| {
15331            let active_item = workspace
15332                .active_item(cx)
15333                .expect("should have an active item after navigating back");
15334            assert_eq!(
15335                active_item.item_id(),
15336                multibuffer_item_id,
15337                "Should navigate back to the multi buffer"
15338            );
15339            assert!(!active_item.is_singleton(cx));
15340        })
15341        .unwrap();
15342
15343    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15344        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15345            s.select_ranges(Some(39..40))
15346        });
15347        editor.open_excerpts(&OpenExcerpts, window, cx);
15348    });
15349    cx.executor().run_until_parked();
15350    let second_item_id = workspace
15351        .update(cx, |workspace, window, cx| {
15352            let active_item = workspace
15353                .active_item(cx)
15354                .expect("should have an active item after navigating into the 2nd buffer");
15355            let second_item_id = active_item.item_id();
15356            assert_ne!(
15357                second_item_id, multibuffer_item_id,
15358                "Should navigate away from the multibuffer"
15359            );
15360            assert_ne!(
15361                second_item_id, first_item_id,
15362                "Should navigate into the 2nd buffer and activate it"
15363            );
15364            assert!(
15365                active_item.is_singleton(cx),
15366                "New active item should be a singleton buffer"
15367            );
15368            assert_eq!(
15369                active_item
15370                    .act_as::<Editor>(cx)
15371                    .expect("should have navigated into an editor")
15372                    .read(cx)
15373                    .text(cx),
15374                sample_text_2
15375            );
15376
15377            workspace
15378                .go_back(workspace.active_pane().downgrade(), window, cx)
15379                .detach_and_log_err(cx);
15380
15381            second_item_id
15382        })
15383        .unwrap();
15384    cx.executor().run_until_parked();
15385    workspace
15386        .update(cx, |workspace, _, cx| {
15387            let active_item = workspace
15388                .active_item(cx)
15389                .expect("should have an active item after navigating back from the 2nd buffer");
15390            assert_eq!(
15391                active_item.item_id(),
15392                multibuffer_item_id,
15393                "Should navigate back from the 2nd buffer to the multi buffer"
15394            );
15395            assert!(!active_item.is_singleton(cx));
15396        })
15397        .unwrap();
15398
15399    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15400        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15401            s.select_ranges(Some(70..70))
15402        });
15403        editor.open_excerpts(&OpenExcerpts, window, cx);
15404    });
15405    cx.executor().run_until_parked();
15406    workspace
15407        .update(cx, |workspace, window, cx| {
15408            let active_item = workspace
15409                .active_item(cx)
15410                .expect("should have an active item after navigating into the 3rd buffer");
15411            let third_item_id = active_item.item_id();
15412            assert_ne!(
15413                third_item_id, multibuffer_item_id,
15414                "Should navigate into the 3rd buffer and activate it"
15415            );
15416            assert_ne!(third_item_id, first_item_id);
15417            assert_ne!(third_item_id, second_item_id);
15418            assert!(
15419                active_item.is_singleton(cx),
15420                "New active item should be a singleton buffer"
15421            );
15422            assert_eq!(
15423                active_item
15424                    .act_as::<Editor>(cx)
15425                    .expect("should have navigated into an editor")
15426                    .read(cx)
15427                    .text(cx),
15428                sample_text_3
15429            );
15430
15431            workspace
15432                .go_back(workspace.active_pane().downgrade(), window, cx)
15433                .detach_and_log_err(cx);
15434        })
15435        .unwrap();
15436    cx.executor().run_until_parked();
15437    workspace
15438        .update(cx, |workspace, _, cx| {
15439            let active_item = workspace
15440                .active_item(cx)
15441                .expect("should have an active item after navigating back from the 3rd buffer");
15442            assert_eq!(
15443                active_item.item_id(),
15444                multibuffer_item_id,
15445                "Should navigate back from the 3rd buffer to the multi buffer"
15446            );
15447            assert!(!active_item.is_singleton(cx));
15448        })
15449        .unwrap();
15450}
15451
15452#[gpui::test]
15453async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15454    init_test(cx, |_| {});
15455
15456    let mut cx = EditorTestContext::new(cx).await;
15457
15458    let diff_base = r#"
15459        use some::mod;
15460
15461        const A: u32 = 42;
15462
15463        fn main() {
15464            println!("hello");
15465
15466            println!("world");
15467        }
15468        "#
15469    .unindent();
15470
15471    cx.set_state(
15472        &r#"
15473        use some::modified;
15474
15475        ˇ
15476        fn main() {
15477            println!("hello there");
15478
15479            println!("around the");
15480            println!("world");
15481        }
15482        "#
15483        .unindent(),
15484    );
15485
15486    cx.set_head_text(&diff_base);
15487    executor.run_until_parked();
15488
15489    cx.update_editor(|editor, window, cx| {
15490        editor.go_to_next_hunk(&GoToHunk, window, cx);
15491        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15492    });
15493    executor.run_until_parked();
15494    cx.assert_state_with_diff(
15495        r#"
15496          use some::modified;
15497
15498
15499          fn main() {
15500        -     println!("hello");
15501        + ˇ    println!("hello there");
15502
15503              println!("around the");
15504              println!("world");
15505          }
15506        "#
15507        .unindent(),
15508    );
15509
15510    cx.update_editor(|editor, window, cx| {
15511        for _ in 0..2 {
15512            editor.go_to_next_hunk(&GoToHunk, window, cx);
15513            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15514        }
15515    });
15516    executor.run_until_parked();
15517    cx.assert_state_with_diff(
15518        r#"
15519        - use some::mod;
15520        + ˇuse some::modified;
15521
15522
15523          fn main() {
15524        -     println!("hello");
15525        +     println!("hello there");
15526
15527        +     println!("around the");
15528              println!("world");
15529          }
15530        "#
15531        .unindent(),
15532    );
15533
15534    cx.update_editor(|editor, window, cx| {
15535        editor.go_to_next_hunk(&GoToHunk, window, cx);
15536        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15537    });
15538    executor.run_until_parked();
15539    cx.assert_state_with_diff(
15540        r#"
15541        - use some::mod;
15542        + use some::modified;
15543
15544        - const A: u32 = 42;
15545          ˇ
15546          fn main() {
15547        -     println!("hello");
15548        +     println!("hello there");
15549
15550        +     println!("around the");
15551              println!("world");
15552          }
15553        "#
15554        .unindent(),
15555    );
15556
15557    cx.update_editor(|editor, window, cx| {
15558        editor.cancel(&Cancel, window, cx);
15559    });
15560
15561    cx.assert_state_with_diff(
15562        r#"
15563          use some::modified;
15564
15565          ˇ
15566          fn main() {
15567              println!("hello there");
15568
15569              println!("around the");
15570              println!("world");
15571          }
15572        "#
15573        .unindent(),
15574    );
15575}
15576
15577#[gpui::test]
15578async fn test_diff_base_change_with_expanded_diff_hunks(
15579    executor: BackgroundExecutor,
15580    cx: &mut TestAppContext,
15581) {
15582    init_test(cx, |_| {});
15583
15584    let mut cx = EditorTestContext::new(cx).await;
15585
15586    let diff_base = r#"
15587        use some::mod1;
15588        use some::mod2;
15589
15590        const A: u32 = 42;
15591        const B: u32 = 42;
15592        const C: u32 = 42;
15593
15594        fn main() {
15595            println!("hello");
15596
15597            println!("world");
15598        }
15599        "#
15600    .unindent();
15601
15602    cx.set_state(
15603        &r#"
15604        use some::mod2;
15605
15606        const A: u32 = 42;
15607        const C: u32 = 42;
15608
15609        fn main(ˇ) {
15610            //println!("hello");
15611
15612            println!("world");
15613            //
15614            //
15615        }
15616        "#
15617        .unindent(),
15618    );
15619
15620    cx.set_head_text(&diff_base);
15621    executor.run_until_parked();
15622
15623    cx.update_editor(|editor, window, cx| {
15624        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15625    });
15626    executor.run_until_parked();
15627    cx.assert_state_with_diff(
15628        r#"
15629        - use some::mod1;
15630          use some::mod2;
15631
15632          const A: u32 = 42;
15633        - const B: u32 = 42;
15634          const C: u32 = 42;
15635
15636          fn main(ˇ) {
15637        -     println!("hello");
15638        +     //println!("hello");
15639
15640              println!("world");
15641        +     //
15642        +     //
15643          }
15644        "#
15645        .unindent(),
15646    );
15647
15648    cx.set_head_text("new diff base!");
15649    executor.run_until_parked();
15650    cx.assert_state_with_diff(
15651        r#"
15652        - new diff base!
15653        + use some::mod2;
15654        +
15655        + const A: u32 = 42;
15656        + const C: u32 = 42;
15657        +
15658        + fn main(ˇ) {
15659        +     //println!("hello");
15660        +
15661        +     println!("world");
15662        +     //
15663        +     //
15664        + }
15665        "#
15666        .unindent(),
15667    );
15668}
15669
15670#[gpui::test]
15671async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15672    init_test(cx, |_| {});
15673
15674    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15675    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15676    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15677    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15678    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15679    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15680
15681    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15682    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15683    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15684
15685    let multi_buffer = cx.new(|cx| {
15686        let mut multibuffer = MultiBuffer::new(ReadWrite);
15687        multibuffer.push_excerpts(
15688            buffer_1.clone(),
15689            [
15690                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15691                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15692                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15693            ],
15694            cx,
15695        );
15696        multibuffer.push_excerpts(
15697            buffer_2.clone(),
15698            [
15699                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15700                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15701                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15702            ],
15703            cx,
15704        );
15705        multibuffer.push_excerpts(
15706            buffer_3.clone(),
15707            [
15708                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15709                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15710                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15711            ],
15712            cx,
15713        );
15714        multibuffer
15715    });
15716
15717    let editor =
15718        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15719    editor
15720        .update(cx, |editor, _window, cx| {
15721            for (buffer, diff_base) in [
15722                (buffer_1.clone(), file_1_old),
15723                (buffer_2.clone(), file_2_old),
15724                (buffer_3.clone(), file_3_old),
15725            ] {
15726                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15727                editor
15728                    .buffer
15729                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15730            }
15731        })
15732        .unwrap();
15733
15734    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15735    cx.run_until_parked();
15736
15737    cx.assert_editor_state(
15738        &"
15739            ˇaaa
15740            ccc
15741            ddd
15742
15743            ggg
15744            hhh
15745
15746
15747            lll
15748            mmm
15749            NNN
15750
15751            qqq
15752            rrr
15753
15754            uuu
15755            111
15756            222
15757            333
15758
15759            666
15760            777
15761
15762            000
15763            !!!"
15764        .unindent(),
15765    );
15766
15767    cx.update_editor(|editor, window, cx| {
15768        editor.select_all(&SelectAll, window, cx);
15769        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15770    });
15771    cx.executor().run_until_parked();
15772
15773    cx.assert_state_with_diff(
15774        "
15775            «aaa
15776          - bbb
15777            ccc
15778            ddd
15779
15780            ggg
15781            hhh
15782
15783
15784            lll
15785            mmm
15786          - nnn
15787          + NNN
15788
15789            qqq
15790            rrr
15791
15792            uuu
15793            111
15794            222
15795            333
15796
15797          + 666
15798            777
15799
15800            000
15801            !!!ˇ»"
15802            .unindent(),
15803    );
15804}
15805
15806#[gpui::test]
15807async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15808    init_test(cx, |_| {});
15809
15810    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15811    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15812
15813    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15814    let multi_buffer = cx.new(|cx| {
15815        let mut multibuffer = MultiBuffer::new(ReadWrite);
15816        multibuffer.push_excerpts(
15817            buffer.clone(),
15818            [
15819                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15820                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15821                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15822            ],
15823            cx,
15824        );
15825        multibuffer
15826    });
15827
15828    let editor =
15829        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15830    editor
15831        .update(cx, |editor, _window, cx| {
15832            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15833            editor
15834                .buffer
15835                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15836        })
15837        .unwrap();
15838
15839    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15840    cx.run_until_parked();
15841
15842    cx.update_editor(|editor, window, cx| {
15843        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15844    });
15845    cx.executor().run_until_parked();
15846
15847    // When the start of a hunk coincides with the start of its excerpt,
15848    // the hunk is expanded. When the start of a a hunk is earlier than
15849    // the start of its excerpt, the hunk is not expanded.
15850    cx.assert_state_with_diff(
15851        "
15852            ˇaaa
15853          - bbb
15854          + BBB
15855
15856          - ddd
15857          - eee
15858          + DDD
15859          + EEE
15860            fff
15861
15862            iii
15863        "
15864        .unindent(),
15865    );
15866}
15867
15868#[gpui::test]
15869async fn test_edits_around_expanded_insertion_hunks(
15870    executor: BackgroundExecutor,
15871    cx: &mut TestAppContext,
15872) {
15873    init_test(cx, |_| {});
15874
15875    let mut cx = EditorTestContext::new(cx).await;
15876
15877    let diff_base = r#"
15878        use some::mod1;
15879        use some::mod2;
15880
15881        const A: u32 = 42;
15882
15883        fn main() {
15884            println!("hello");
15885
15886            println!("world");
15887        }
15888        "#
15889    .unindent();
15890    executor.run_until_parked();
15891    cx.set_state(
15892        &r#"
15893        use some::mod1;
15894        use some::mod2;
15895
15896        const A: u32 = 42;
15897        const B: u32 = 42;
15898        const C: u32 = 42;
15899        ˇ
15900
15901        fn main() {
15902            println!("hello");
15903
15904            println!("world");
15905        }
15906        "#
15907        .unindent(),
15908    );
15909
15910    cx.set_head_text(&diff_base);
15911    executor.run_until_parked();
15912
15913    cx.update_editor(|editor, window, cx| {
15914        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15915    });
15916    executor.run_until_parked();
15917
15918    cx.assert_state_with_diff(
15919        r#"
15920        use some::mod1;
15921        use some::mod2;
15922
15923        const A: u32 = 42;
15924      + const B: u32 = 42;
15925      + const C: u32 = 42;
15926      + ˇ
15927
15928        fn main() {
15929            println!("hello");
15930
15931            println!("world");
15932        }
15933      "#
15934        .unindent(),
15935    );
15936
15937    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15938    executor.run_until_parked();
15939
15940    cx.assert_state_with_diff(
15941        r#"
15942        use some::mod1;
15943        use some::mod2;
15944
15945        const A: u32 = 42;
15946      + const B: u32 = 42;
15947      + const C: u32 = 42;
15948      + const D: u32 = 42;
15949      + ˇ
15950
15951        fn main() {
15952            println!("hello");
15953
15954            println!("world");
15955        }
15956      "#
15957        .unindent(),
15958    );
15959
15960    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15961    executor.run_until_parked();
15962
15963    cx.assert_state_with_diff(
15964        r#"
15965        use some::mod1;
15966        use some::mod2;
15967
15968        const A: u32 = 42;
15969      + const B: u32 = 42;
15970      + const C: u32 = 42;
15971      + const D: u32 = 42;
15972      + const E: u32 = 42;
15973      + ˇ
15974
15975        fn main() {
15976            println!("hello");
15977
15978            println!("world");
15979        }
15980      "#
15981        .unindent(),
15982    );
15983
15984    cx.update_editor(|editor, window, cx| {
15985        editor.delete_line(&DeleteLine, window, cx);
15986    });
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        fn main() {
16001            println!("hello");
16002
16003            println!("world");
16004        }
16005      "#
16006        .unindent(),
16007    );
16008
16009    cx.update_editor(|editor, window, cx| {
16010        editor.move_up(&MoveUp, window, cx);
16011        editor.delete_line(&DeleteLine, window, cx);
16012        editor.move_up(&MoveUp, window, cx);
16013        editor.delete_line(&DeleteLine, window, cx);
16014        editor.move_up(&MoveUp, window, cx);
16015        editor.delete_line(&DeleteLine, window, cx);
16016    });
16017    executor.run_until_parked();
16018    cx.assert_state_with_diff(
16019        r#"
16020        use some::mod1;
16021        use some::mod2;
16022
16023        const A: u32 = 42;
16024      + const B: 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.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
16037        editor.delete_line(&DeleteLine, window, cx);
16038    });
16039    executor.run_until_parked();
16040    cx.assert_state_with_diff(
16041        r#"
16042        ˇ
16043        fn main() {
16044            println!("hello");
16045
16046            println!("world");
16047        }
16048      "#
16049        .unindent(),
16050    );
16051}
16052
16053#[gpui::test]
16054async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
16055    init_test(cx, |_| {});
16056
16057    let mut cx = EditorTestContext::new(cx).await;
16058    cx.set_head_text(indoc! { "
16059        one
16060        two
16061        three
16062        four
16063        five
16064        "
16065    });
16066    cx.set_state(indoc! { "
16067        one
16068        ˇthree
16069        five
16070    "});
16071    cx.run_until_parked();
16072    cx.update_editor(|editor, window, cx| {
16073        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16074    });
16075    cx.assert_state_with_diff(
16076        indoc! { "
16077        one
16078      - two
16079        ˇthree
16080      - four
16081        five
16082    "}
16083        .to_string(),
16084    );
16085    cx.update_editor(|editor, window, cx| {
16086        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16087    });
16088
16089    cx.assert_state_with_diff(
16090        indoc! { "
16091        one
16092        ˇthree
16093        five
16094    "}
16095        .to_string(),
16096    );
16097
16098    cx.set_state(indoc! { "
16099        one
16100        ˇTWO
16101        three
16102        four
16103        five
16104    "});
16105    cx.run_until_parked();
16106    cx.update_editor(|editor, window, cx| {
16107        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16108    });
16109
16110    cx.assert_state_with_diff(
16111        indoc! { "
16112            one
16113          - two
16114          + ˇTWO
16115            three
16116            four
16117            five
16118        "}
16119        .to_string(),
16120    );
16121    cx.update_editor(|editor, window, cx| {
16122        editor.move_up(&Default::default(), window, cx);
16123        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
16124    });
16125    cx.assert_state_with_diff(
16126        indoc! { "
16127            one
16128            ˇTWO
16129            three
16130            four
16131            five
16132        "}
16133        .to_string(),
16134    );
16135}
16136
16137#[gpui::test]
16138async fn test_edits_around_expanded_deletion_hunks(
16139    executor: BackgroundExecutor,
16140    cx: &mut TestAppContext,
16141) {
16142    init_test(cx, |_| {});
16143
16144    let mut cx = EditorTestContext::new(cx).await;
16145
16146    let diff_base = r#"
16147        use some::mod1;
16148        use some::mod2;
16149
16150        const A: u32 = 42;
16151        const B: u32 = 42;
16152        const C: u32 = 42;
16153
16154
16155        fn main() {
16156            println!("hello");
16157
16158            println!("world");
16159        }
16160    "#
16161    .unindent();
16162    executor.run_until_parked();
16163    cx.set_state(
16164        &r#"
16165        use some::mod1;
16166        use some::mod2;
16167
16168        ˇconst B: u32 = 42;
16169        const C: u32 = 42;
16170
16171
16172        fn main() {
16173            println!("hello");
16174
16175            println!("world");
16176        }
16177        "#
16178        .unindent(),
16179    );
16180
16181    cx.set_head_text(&diff_base);
16182    executor.run_until_parked();
16183
16184    cx.update_editor(|editor, window, cx| {
16185        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16186    });
16187    executor.run_until_parked();
16188
16189    cx.assert_state_with_diff(
16190        r#"
16191        use some::mod1;
16192        use some::mod2;
16193
16194      - const A: u32 = 42;
16195        ˇconst B: u32 = 42;
16196        const C: u32 = 42;
16197
16198
16199        fn main() {
16200            println!("hello");
16201
16202            println!("world");
16203        }
16204      "#
16205        .unindent(),
16206    );
16207
16208    cx.update_editor(|editor, window, cx| {
16209        editor.delete_line(&DeleteLine, window, cx);
16210    });
16211    executor.run_until_parked();
16212    cx.assert_state_with_diff(
16213        r#"
16214        use some::mod1;
16215        use some::mod2;
16216
16217      - const A: u32 = 42;
16218      - const B: u32 = 42;
16219        ˇconst C: u32 = 42;
16220
16221
16222        fn main() {
16223            println!("hello");
16224
16225            println!("world");
16226        }
16227      "#
16228        .unindent(),
16229    );
16230
16231    cx.update_editor(|editor, window, cx| {
16232        editor.delete_line(&DeleteLine, window, cx);
16233    });
16234    executor.run_until_parked();
16235    cx.assert_state_with_diff(
16236        r#"
16237        use some::mod1;
16238        use some::mod2;
16239
16240      - const A: u32 = 42;
16241      - const B: u32 = 42;
16242      - const C: u32 = 42;
16243        ˇ
16244
16245        fn main() {
16246            println!("hello");
16247
16248            println!("world");
16249        }
16250      "#
16251        .unindent(),
16252    );
16253
16254    cx.update_editor(|editor, window, cx| {
16255        editor.handle_input("replacement", window, cx);
16256    });
16257    executor.run_until_parked();
16258    cx.assert_state_with_diff(
16259        r#"
16260        use some::mod1;
16261        use some::mod2;
16262
16263      - const A: u32 = 42;
16264      - const B: u32 = 42;
16265      - const C: u32 = 42;
16266      -
16267      + replacementˇ
16268
16269        fn main() {
16270            println!("hello");
16271
16272            println!("world");
16273        }
16274      "#
16275        .unindent(),
16276    );
16277}
16278
16279#[gpui::test]
16280async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16281    init_test(cx, |_| {});
16282
16283    let mut cx = EditorTestContext::new(cx).await;
16284
16285    let base_text = r#"
16286        one
16287        two
16288        three
16289        four
16290        five
16291    "#
16292    .unindent();
16293    executor.run_until_parked();
16294    cx.set_state(
16295        &r#"
16296        one
16297        two
16298        fˇour
16299        five
16300        "#
16301        .unindent(),
16302    );
16303
16304    cx.set_head_text(&base_text);
16305    executor.run_until_parked();
16306
16307    cx.update_editor(|editor, window, cx| {
16308        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16309    });
16310    executor.run_until_parked();
16311
16312    cx.assert_state_with_diff(
16313        r#"
16314          one
16315          two
16316        - three
16317          fˇour
16318          five
16319        "#
16320        .unindent(),
16321    );
16322
16323    cx.update_editor(|editor, window, cx| {
16324        editor.backspace(&Backspace, window, cx);
16325        editor.backspace(&Backspace, window, cx);
16326    });
16327    executor.run_until_parked();
16328    cx.assert_state_with_diff(
16329        r#"
16330          one
16331          two
16332        - threeˇ
16333        - four
16334        + our
16335          five
16336        "#
16337        .unindent(),
16338    );
16339}
16340
16341#[gpui::test]
16342async fn test_edit_after_expanded_modification_hunk(
16343    executor: BackgroundExecutor,
16344    cx: &mut TestAppContext,
16345) {
16346    init_test(cx, |_| {});
16347
16348    let mut cx = EditorTestContext::new(cx).await;
16349
16350    let diff_base = r#"
16351        use some::mod1;
16352        use some::mod2;
16353
16354        const A: u32 = 42;
16355        const B: u32 = 42;
16356        const C: u32 = 42;
16357        const D: u32 = 42;
16358
16359
16360        fn main() {
16361            println!("hello");
16362
16363            println!("world");
16364        }"#
16365    .unindent();
16366
16367    cx.set_state(
16368        &r#"
16369        use some::mod1;
16370        use some::mod2;
16371
16372        const A: u32 = 42;
16373        const B: u32 = 42;
16374        const C: u32 = 43ˇ
16375        const D: u32 = 42;
16376
16377
16378        fn main() {
16379            println!("hello");
16380
16381            println!("world");
16382        }"#
16383        .unindent(),
16384    );
16385
16386    cx.set_head_text(&diff_base);
16387    executor.run_until_parked();
16388    cx.update_editor(|editor, window, cx| {
16389        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16390    });
16391    executor.run_until_parked();
16392
16393    cx.assert_state_with_diff(
16394        r#"
16395        use some::mod1;
16396        use some::mod2;
16397
16398        const A: u32 = 42;
16399        const B: u32 = 42;
16400      - const C: u32 = 42;
16401      + const C: u32 = 43ˇ
16402        const D: u32 = 42;
16403
16404
16405        fn main() {
16406            println!("hello");
16407
16408            println!("world");
16409        }"#
16410        .unindent(),
16411    );
16412
16413    cx.update_editor(|editor, window, cx| {
16414        editor.handle_input("\nnew_line\n", window, cx);
16415    });
16416    executor.run_until_parked();
16417
16418    cx.assert_state_with_diff(
16419        r#"
16420        use some::mod1;
16421        use some::mod2;
16422
16423        const A: u32 = 42;
16424        const B: u32 = 42;
16425      - const C: u32 = 42;
16426      + const C: u32 = 43
16427      + new_line
16428      + ˇ
16429        const D: u32 = 42;
16430
16431
16432        fn main() {
16433            println!("hello");
16434
16435            println!("world");
16436        }"#
16437        .unindent(),
16438    );
16439}
16440
16441#[gpui::test]
16442async fn test_stage_and_unstage_added_file_hunk(
16443    executor: BackgroundExecutor,
16444    cx: &mut TestAppContext,
16445) {
16446    init_test(cx, |_| {});
16447
16448    let mut cx = EditorTestContext::new(cx).await;
16449    cx.update_editor(|editor, _, cx| {
16450        editor.set_expand_all_diff_hunks(cx);
16451    });
16452
16453    let working_copy = r#"
16454            ˇfn main() {
16455                println!("hello, world!");
16456            }
16457        "#
16458    .unindent();
16459
16460    cx.set_state(&working_copy);
16461    executor.run_until_parked();
16462
16463    cx.assert_state_with_diff(
16464        r#"
16465            + ˇfn main() {
16466            +     println!("hello, world!");
16467            + }
16468        "#
16469        .unindent(),
16470    );
16471    cx.assert_index_text(None);
16472
16473    cx.update_editor(|editor, window, cx| {
16474        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16475    });
16476    executor.run_until_parked();
16477    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16478    cx.assert_state_with_diff(
16479        r#"
16480            + ˇfn main() {
16481            +     println!("hello, world!");
16482            + }
16483        "#
16484        .unindent(),
16485    );
16486
16487    cx.update_editor(|editor, window, cx| {
16488        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16489    });
16490    executor.run_until_parked();
16491    cx.assert_index_text(None);
16492}
16493
16494async fn setup_indent_guides_editor(
16495    text: &str,
16496    cx: &mut TestAppContext,
16497) -> (BufferId, EditorTestContext) {
16498    init_test(cx, |_| {});
16499
16500    let mut cx = EditorTestContext::new(cx).await;
16501
16502    let buffer_id = cx.update_editor(|editor, window, cx| {
16503        editor.set_text(text, window, cx);
16504        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16505
16506        buffer_ids[0]
16507    });
16508
16509    (buffer_id, cx)
16510}
16511
16512fn assert_indent_guides(
16513    range: Range<u32>,
16514    expected: Vec<IndentGuide>,
16515    active_indices: Option<Vec<usize>>,
16516    cx: &mut EditorTestContext,
16517) {
16518    let indent_guides = cx.update_editor(|editor, window, cx| {
16519        let snapshot = editor.snapshot(window, cx).display_snapshot;
16520        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16521            editor,
16522            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16523            true,
16524            &snapshot,
16525            cx,
16526        );
16527
16528        indent_guides.sort_by(|a, b| {
16529            a.depth.cmp(&b.depth).then(
16530                a.start_row
16531                    .cmp(&b.start_row)
16532                    .then(a.end_row.cmp(&b.end_row)),
16533            )
16534        });
16535        indent_guides
16536    });
16537
16538    if let Some(expected) = active_indices {
16539        let active_indices = cx.update_editor(|editor, window, cx| {
16540            let snapshot = editor.snapshot(window, cx).display_snapshot;
16541            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16542        });
16543
16544        assert_eq!(
16545            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16546            expected,
16547            "Active indent guide indices do not match"
16548        );
16549    }
16550
16551    assert_eq!(indent_guides, expected, "Indent guides do not match");
16552}
16553
16554fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16555    IndentGuide {
16556        buffer_id,
16557        start_row: MultiBufferRow(start_row),
16558        end_row: MultiBufferRow(end_row),
16559        depth,
16560        tab_size: 4,
16561        settings: IndentGuideSettings {
16562            enabled: true,
16563            line_width: 1,
16564            active_line_width: 1,
16565            ..Default::default()
16566        },
16567    }
16568}
16569
16570#[gpui::test]
16571async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16572    let (buffer_id, mut cx) = setup_indent_guides_editor(
16573        &"
16574    fn main() {
16575        let a = 1;
16576    }"
16577        .unindent(),
16578        cx,
16579    )
16580    .await;
16581
16582    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16583}
16584
16585#[gpui::test]
16586async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16587    let (buffer_id, mut cx) = setup_indent_guides_editor(
16588        &"
16589    fn main() {
16590        let a = 1;
16591        let b = 2;
16592    }"
16593        .unindent(),
16594        cx,
16595    )
16596    .await;
16597
16598    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16599}
16600
16601#[gpui::test]
16602async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16603    let (buffer_id, mut cx) = setup_indent_guides_editor(
16604        &"
16605    fn main() {
16606        let a = 1;
16607        if a == 3 {
16608            let b = 2;
16609        } else {
16610            let c = 3;
16611        }
16612    }"
16613        .unindent(),
16614        cx,
16615    )
16616    .await;
16617
16618    assert_indent_guides(
16619        0..8,
16620        vec![
16621            indent_guide(buffer_id, 1, 6, 0),
16622            indent_guide(buffer_id, 3, 3, 1),
16623            indent_guide(buffer_id, 5, 5, 1),
16624        ],
16625        None,
16626        &mut cx,
16627    );
16628}
16629
16630#[gpui::test]
16631async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16632    let (buffer_id, mut cx) = setup_indent_guides_editor(
16633        &"
16634    fn main() {
16635        let a = 1;
16636            let b = 2;
16637        let c = 3;
16638    }"
16639        .unindent(),
16640        cx,
16641    )
16642    .await;
16643
16644    assert_indent_guides(
16645        0..5,
16646        vec![
16647            indent_guide(buffer_id, 1, 3, 0),
16648            indent_guide(buffer_id, 2, 2, 1),
16649        ],
16650        None,
16651        &mut cx,
16652    );
16653}
16654
16655#[gpui::test]
16656async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16657    let (buffer_id, mut cx) = setup_indent_guides_editor(
16658        &"
16659        fn main() {
16660            let a = 1;
16661
16662            let c = 3;
16663        }"
16664        .unindent(),
16665        cx,
16666    )
16667    .await;
16668
16669    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16670}
16671
16672#[gpui::test]
16673async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16674    let (buffer_id, mut cx) = setup_indent_guides_editor(
16675        &"
16676        fn main() {
16677            let a = 1;
16678
16679            let c = 3;
16680
16681            if a == 3 {
16682                let b = 2;
16683            } else {
16684                let c = 3;
16685            }
16686        }"
16687        .unindent(),
16688        cx,
16689    )
16690    .await;
16691
16692    assert_indent_guides(
16693        0..11,
16694        vec![
16695            indent_guide(buffer_id, 1, 9, 0),
16696            indent_guide(buffer_id, 6, 6, 1),
16697            indent_guide(buffer_id, 8, 8, 1),
16698        ],
16699        None,
16700        &mut cx,
16701    );
16702}
16703
16704#[gpui::test]
16705async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16706    let (buffer_id, mut cx) = setup_indent_guides_editor(
16707        &"
16708        fn main() {
16709            let a = 1;
16710
16711            let c = 3;
16712
16713            if a == 3 {
16714                let b = 2;
16715            } else {
16716                let c = 3;
16717            }
16718        }"
16719        .unindent(),
16720        cx,
16721    )
16722    .await;
16723
16724    assert_indent_guides(
16725        1..11,
16726        vec![
16727            indent_guide(buffer_id, 1, 9, 0),
16728            indent_guide(buffer_id, 6, 6, 1),
16729            indent_guide(buffer_id, 8, 8, 1),
16730        ],
16731        None,
16732        &mut cx,
16733    );
16734}
16735
16736#[gpui::test]
16737async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16738    let (buffer_id, mut cx) = setup_indent_guides_editor(
16739        &"
16740        fn main() {
16741            let a = 1;
16742
16743            let c = 3;
16744
16745            if a == 3 {
16746                let b = 2;
16747            } else {
16748                let c = 3;
16749            }
16750        }"
16751        .unindent(),
16752        cx,
16753    )
16754    .await;
16755
16756    assert_indent_guides(
16757        1..10,
16758        vec![
16759            indent_guide(buffer_id, 1, 9, 0),
16760            indent_guide(buffer_id, 6, 6, 1),
16761            indent_guide(buffer_id, 8, 8, 1),
16762        ],
16763        None,
16764        &mut cx,
16765    );
16766}
16767
16768#[gpui::test]
16769async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16770    let (buffer_id, mut cx) = setup_indent_guides_editor(
16771        &"
16772        block1
16773            block2
16774                block3
16775                    block4
16776            block2
16777        block1
16778        block1"
16779            .unindent(),
16780        cx,
16781    )
16782    .await;
16783
16784    assert_indent_guides(
16785        1..10,
16786        vec![
16787            indent_guide(buffer_id, 1, 4, 0),
16788            indent_guide(buffer_id, 2, 3, 1),
16789            indent_guide(buffer_id, 3, 3, 2),
16790        ],
16791        None,
16792        &mut cx,
16793    );
16794}
16795
16796#[gpui::test]
16797async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16798    let (buffer_id, mut cx) = setup_indent_guides_editor(
16799        &"
16800        block1
16801            block2
16802                block3
16803
16804        block1
16805        block1"
16806            .unindent(),
16807        cx,
16808    )
16809    .await;
16810
16811    assert_indent_guides(
16812        0..6,
16813        vec![
16814            indent_guide(buffer_id, 1, 2, 0),
16815            indent_guide(buffer_id, 2, 2, 1),
16816        ],
16817        None,
16818        &mut cx,
16819    );
16820}
16821
16822#[gpui::test]
16823async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16824    let (buffer_id, mut cx) = setup_indent_guides_editor(
16825        &"
16826        block1
16827
16828
16829
16830            block2
16831        "
16832        .unindent(),
16833        cx,
16834    )
16835    .await;
16836
16837    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16838}
16839
16840#[gpui::test]
16841async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16842    let (buffer_id, mut cx) = setup_indent_guides_editor(
16843        &"
16844        def a:
16845        \tb = 3
16846        \tif True:
16847        \t\tc = 4
16848        \t\td = 5
16849        \tprint(b)
16850        "
16851        .unindent(),
16852        cx,
16853    )
16854    .await;
16855
16856    assert_indent_guides(
16857        0..6,
16858        vec![
16859            indent_guide(buffer_id, 1, 5, 0),
16860            indent_guide(buffer_id, 3, 4, 1),
16861        ],
16862        None,
16863        &mut cx,
16864    );
16865}
16866
16867#[gpui::test]
16868async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16869    let (buffer_id, mut cx) = setup_indent_guides_editor(
16870        &"
16871    fn main() {
16872        let a = 1;
16873    }"
16874        .unindent(),
16875        cx,
16876    )
16877    .await;
16878
16879    cx.update_editor(|editor, window, cx| {
16880        editor.change_selections(None, window, cx, |s| {
16881            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16882        });
16883    });
16884
16885    assert_indent_guides(
16886        0..3,
16887        vec![indent_guide(buffer_id, 1, 1, 0)],
16888        Some(vec![0]),
16889        &mut cx,
16890    );
16891}
16892
16893#[gpui::test]
16894async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16895    let (buffer_id, mut cx) = setup_indent_guides_editor(
16896        &"
16897    fn main() {
16898        if 1 == 2 {
16899            let a = 1;
16900        }
16901    }"
16902        .unindent(),
16903        cx,
16904    )
16905    .await;
16906
16907    cx.update_editor(|editor, window, cx| {
16908        editor.change_selections(None, window, cx, |s| {
16909            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16910        });
16911    });
16912
16913    assert_indent_guides(
16914        0..4,
16915        vec![
16916            indent_guide(buffer_id, 1, 3, 0),
16917            indent_guide(buffer_id, 2, 2, 1),
16918        ],
16919        Some(vec![1]),
16920        &mut cx,
16921    );
16922
16923    cx.update_editor(|editor, window, cx| {
16924        editor.change_selections(None, window, cx, |s| {
16925            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16926        });
16927    });
16928
16929    assert_indent_guides(
16930        0..4,
16931        vec![
16932            indent_guide(buffer_id, 1, 3, 0),
16933            indent_guide(buffer_id, 2, 2, 1),
16934        ],
16935        Some(vec![1]),
16936        &mut cx,
16937    );
16938
16939    cx.update_editor(|editor, window, cx| {
16940        editor.change_selections(None, window, cx, |s| {
16941            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16942        });
16943    });
16944
16945    assert_indent_guides(
16946        0..4,
16947        vec![
16948            indent_guide(buffer_id, 1, 3, 0),
16949            indent_guide(buffer_id, 2, 2, 1),
16950        ],
16951        Some(vec![0]),
16952        &mut cx,
16953    );
16954}
16955
16956#[gpui::test]
16957async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16958    let (buffer_id, mut cx) = setup_indent_guides_editor(
16959        &"
16960    fn main() {
16961        let a = 1;
16962
16963        let b = 2;
16964    }"
16965        .unindent(),
16966        cx,
16967    )
16968    .await;
16969
16970    cx.update_editor(|editor, window, cx| {
16971        editor.change_selections(None, window, cx, |s| {
16972            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16973        });
16974    });
16975
16976    assert_indent_guides(
16977        0..5,
16978        vec![indent_guide(buffer_id, 1, 3, 0)],
16979        Some(vec![0]),
16980        &mut cx,
16981    );
16982}
16983
16984#[gpui::test]
16985async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16986    let (buffer_id, mut cx) = setup_indent_guides_editor(
16987        &"
16988    def m:
16989        a = 1
16990        pass"
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(1, 0)..Point::new(1, 0)])
16999        });
17000    });
17001
17002    assert_indent_guides(
17003        0..3,
17004        vec![indent_guide(buffer_id, 1, 2, 0)],
17005        Some(vec![0]),
17006        &mut cx,
17007    );
17008}
17009
17010#[gpui::test]
17011async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
17012    init_test(cx, |_| {});
17013    let mut cx = EditorTestContext::new(cx).await;
17014    let text = indoc! {
17015        "
17016        impl A {
17017            fn b() {
17018                0;
17019                3;
17020                5;
17021                6;
17022                7;
17023            }
17024        }
17025        "
17026    };
17027    let base_text = indoc! {
17028        "
17029        impl A {
17030            fn b() {
17031                0;
17032                1;
17033                2;
17034                3;
17035                4;
17036            }
17037            fn c() {
17038                5;
17039                6;
17040                7;
17041            }
17042        }
17043        "
17044    };
17045
17046    cx.update_editor(|editor, window, cx| {
17047        editor.set_text(text, window, cx);
17048
17049        editor.buffer().update(cx, |multibuffer, cx| {
17050            let buffer = multibuffer.as_singleton().unwrap();
17051            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
17052
17053            multibuffer.set_all_diff_hunks_expanded(cx);
17054            multibuffer.add_diff(diff, cx);
17055
17056            buffer.read(cx).remote_id()
17057        })
17058    });
17059    cx.run_until_parked();
17060
17061    cx.assert_state_with_diff(
17062        indoc! { "
17063          impl A {
17064              fn b() {
17065                  0;
17066        -         1;
17067        -         2;
17068                  3;
17069        -         4;
17070        -     }
17071        -     fn c() {
17072                  5;
17073                  6;
17074                  7;
17075              }
17076          }
17077          ˇ"
17078        }
17079        .to_string(),
17080    );
17081
17082    let mut actual_guides = cx.update_editor(|editor, window, cx| {
17083        editor
17084            .snapshot(window, cx)
17085            .buffer_snapshot
17086            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
17087            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
17088            .collect::<Vec<_>>()
17089    });
17090    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
17091    assert_eq!(
17092        actual_guides,
17093        vec![
17094            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
17095            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
17096            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
17097        ]
17098    );
17099}
17100
17101#[gpui::test]
17102async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
17103    init_test(cx, |_| {});
17104    let mut cx = EditorTestContext::new(cx).await;
17105
17106    let diff_base = r#"
17107        a
17108        b
17109        c
17110        "#
17111    .unindent();
17112
17113    cx.set_state(
17114        &r#"
17115        ˇA
17116        b
17117        C
17118        "#
17119        .unindent(),
17120    );
17121    cx.set_head_text(&diff_base);
17122    cx.update_editor(|editor, window, cx| {
17123        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17124    });
17125    executor.run_until_parked();
17126
17127    let both_hunks_expanded = r#"
17128        - a
17129        + ˇA
17130          b
17131        - c
17132        + C
17133        "#
17134    .unindent();
17135
17136    cx.assert_state_with_diff(both_hunks_expanded.clone());
17137
17138    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17139        let snapshot = editor.snapshot(window, cx);
17140        let hunks = editor
17141            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17142            .collect::<Vec<_>>();
17143        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17144        let buffer_id = hunks[0].buffer_id;
17145        hunks
17146            .into_iter()
17147            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17148            .collect::<Vec<_>>()
17149    });
17150    assert_eq!(hunk_ranges.len(), 2);
17151
17152    cx.update_editor(|editor, _, cx| {
17153        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17154    });
17155    executor.run_until_parked();
17156
17157    let second_hunk_expanded = r#"
17158          ˇA
17159          b
17160        - c
17161        + C
17162        "#
17163    .unindent();
17164
17165    cx.assert_state_with_diff(second_hunk_expanded);
17166
17167    cx.update_editor(|editor, _, cx| {
17168        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17169    });
17170    executor.run_until_parked();
17171
17172    cx.assert_state_with_diff(both_hunks_expanded.clone());
17173
17174    cx.update_editor(|editor, _, cx| {
17175        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17176    });
17177    executor.run_until_parked();
17178
17179    let first_hunk_expanded = r#"
17180        - a
17181        + ˇA
17182          b
17183          C
17184        "#
17185    .unindent();
17186
17187    cx.assert_state_with_diff(first_hunk_expanded);
17188
17189    cx.update_editor(|editor, _, cx| {
17190        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17191    });
17192    executor.run_until_parked();
17193
17194    cx.assert_state_with_diff(both_hunks_expanded);
17195
17196    cx.set_state(
17197        &r#"
17198        ˇA
17199        b
17200        "#
17201        .unindent(),
17202    );
17203    cx.run_until_parked();
17204
17205    // TODO this cursor position seems bad
17206    cx.assert_state_with_diff(
17207        r#"
17208        - ˇa
17209        + A
17210          b
17211        "#
17212        .unindent(),
17213    );
17214
17215    cx.update_editor(|editor, window, cx| {
17216        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17217    });
17218
17219    cx.assert_state_with_diff(
17220        r#"
17221            - ˇa
17222            + A
17223              b
17224            - c
17225            "#
17226        .unindent(),
17227    );
17228
17229    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17230        let snapshot = editor.snapshot(window, cx);
17231        let hunks = editor
17232            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17233            .collect::<Vec<_>>();
17234        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17235        let buffer_id = hunks[0].buffer_id;
17236        hunks
17237            .into_iter()
17238            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17239            .collect::<Vec<_>>()
17240    });
17241    assert_eq!(hunk_ranges.len(), 2);
17242
17243    cx.update_editor(|editor, _, cx| {
17244        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17245    });
17246    executor.run_until_parked();
17247
17248    cx.assert_state_with_diff(
17249        r#"
17250        - ˇa
17251        + A
17252          b
17253        "#
17254        .unindent(),
17255    );
17256}
17257
17258#[gpui::test]
17259async fn test_toggle_deletion_hunk_at_start_of_file(
17260    executor: BackgroundExecutor,
17261    cx: &mut TestAppContext,
17262) {
17263    init_test(cx, |_| {});
17264    let mut cx = EditorTestContext::new(cx).await;
17265
17266    let diff_base = r#"
17267        a
17268        b
17269        c
17270        "#
17271    .unindent();
17272
17273    cx.set_state(
17274        &r#"
17275        ˇb
17276        c
17277        "#
17278        .unindent(),
17279    );
17280    cx.set_head_text(&diff_base);
17281    cx.update_editor(|editor, window, cx| {
17282        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17283    });
17284    executor.run_until_parked();
17285
17286    let hunk_expanded = r#"
17287        - a
17288          ˇb
17289          c
17290        "#
17291    .unindent();
17292
17293    cx.assert_state_with_diff(hunk_expanded.clone());
17294
17295    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17296        let snapshot = editor.snapshot(window, cx);
17297        let hunks = editor
17298            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17299            .collect::<Vec<_>>();
17300        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17301        let buffer_id = hunks[0].buffer_id;
17302        hunks
17303            .into_iter()
17304            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17305            .collect::<Vec<_>>()
17306    });
17307    assert_eq!(hunk_ranges.len(), 1);
17308
17309    cx.update_editor(|editor, _, cx| {
17310        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17311    });
17312    executor.run_until_parked();
17313
17314    let hunk_collapsed = r#"
17315          ˇb
17316          c
17317        "#
17318    .unindent();
17319
17320    cx.assert_state_with_diff(hunk_collapsed);
17321
17322    cx.update_editor(|editor, _, cx| {
17323        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17324    });
17325    executor.run_until_parked();
17326
17327    cx.assert_state_with_diff(hunk_expanded.clone());
17328}
17329
17330#[gpui::test]
17331async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17332    init_test(cx, |_| {});
17333
17334    let fs = FakeFs::new(cx.executor());
17335    fs.insert_tree(
17336        path!("/test"),
17337        json!({
17338            ".git": {},
17339            "file-1": "ONE\n",
17340            "file-2": "TWO\n",
17341            "file-3": "THREE\n",
17342        }),
17343    )
17344    .await;
17345
17346    fs.set_head_for_repo(
17347        path!("/test/.git").as_ref(),
17348        &[
17349            ("file-1".into(), "one\n".into()),
17350            ("file-2".into(), "two\n".into()),
17351            ("file-3".into(), "three\n".into()),
17352        ],
17353    );
17354
17355    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17356    let mut buffers = vec![];
17357    for i in 1..=3 {
17358        let buffer = project
17359            .update(cx, |project, cx| {
17360                let path = format!(path!("/test/file-{}"), i);
17361                project.open_local_buffer(path, cx)
17362            })
17363            .await
17364            .unwrap();
17365        buffers.push(buffer);
17366    }
17367
17368    let multibuffer = cx.new(|cx| {
17369        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17370        multibuffer.set_all_diff_hunks_expanded(cx);
17371        for buffer in &buffers {
17372            let snapshot = buffer.read(cx).snapshot();
17373            multibuffer.set_excerpts_for_path(
17374                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17375                buffer.clone(),
17376                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17377                DEFAULT_MULTIBUFFER_CONTEXT,
17378                cx,
17379            );
17380        }
17381        multibuffer
17382    });
17383
17384    let editor = cx.add_window(|window, cx| {
17385        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17386    });
17387    cx.run_until_parked();
17388
17389    let snapshot = editor
17390        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17391        .unwrap();
17392    let hunks = snapshot
17393        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17394        .map(|hunk| match hunk {
17395            DisplayDiffHunk::Unfolded {
17396                display_row_range, ..
17397            } => display_row_range,
17398            DisplayDiffHunk::Folded { .. } => unreachable!(),
17399        })
17400        .collect::<Vec<_>>();
17401    assert_eq!(
17402        hunks,
17403        [
17404            DisplayRow(2)..DisplayRow(4),
17405            DisplayRow(7)..DisplayRow(9),
17406            DisplayRow(12)..DisplayRow(14),
17407        ]
17408    );
17409}
17410
17411#[gpui::test]
17412async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17413    init_test(cx, |_| {});
17414
17415    let mut cx = EditorTestContext::new(cx).await;
17416    cx.set_head_text(indoc! { "
17417        one
17418        two
17419        three
17420        four
17421        five
17422        "
17423    });
17424    cx.set_index_text(indoc! { "
17425        one
17426        two
17427        three
17428        four
17429        five
17430        "
17431    });
17432    cx.set_state(indoc! {"
17433        one
17434        TWO
17435        ˇTHREE
17436        FOUR
17437        five
17438    "});
17439    cx.run_until_parked();
17440    cx.update_editor(|editor, window, cx| {
17441        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17442    });
17443    cx.run_until_parked();
17444    cx.assert_index_text(Some(indoc! {"
17445        one
17446        TWO
17447        THREE
17448        FOUR
17449        five
17450    "}));
17451    cx.set_state(indoc! { "
17452        one
17453        TWO
17454        ˇTHREE-HUNDRED
17455        FOUR
17456        five
17457    "});
17458    cx.run_until_parked();
17459    cx.update_editor(|editor, window, cx| {
17460        let snapshot = editor.snapshot(window, cx);
17461        let hunks = editor
17462            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17463            .collect::<Vec<_>>();
17464        assert_eq!(hunks.len(), 1);
17465        assert_eq!(
17466            hunks[0].status(),
17467            DiffHunkStatus {
17468                kind: DiffHunkStatusKind::Modified,
17469                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17470            }
17471        );
17472
17473        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17474    });
17475    cx.run_until_parked();
17476    cx.assert_index_text(Some(indoc! {"
17477        one
17478        TWO
17479        THREE-HUNDRED
17480        FOUR
17481        five
17482    "}));
17483}
17484
17485#[gpui::test]
17486fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17487    init_test(cx, |_| {});
17488
17489    let editor = cx.add_window(|window, cx| {
17490        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17491        build_editor(buffer, window, cx)
17492    });
17493
17494    let render_args = Arc::new(Mutex::new(None));
17495    let snapshot = editor
17496        .update(cx, |editor, window, cx| {
17497            let snapshot = editor.buffer().read(cx).snapshot(cx);
17498            let range =
17499                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17500
17501            struct RenderArgs {
17502                row: MultiBufferRow,
17503                folded: bool,
17504                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17505            }
17506
17507            let crease = Crease::inline(
17508                range,
17509                FoldPlaceholder::test(),
17510                {
17511                    let toggle_callback = render_args.clone();
17512                    move |row, folded, callback, _window, _cx| {
17513                        *toggle_callback.lock() = Some(RenderArgs {
17514                            row,
17515                            folded,
17516                            callback,
17517                        });
17518                        div()
17519                    }
17520                },
17521                |_row, _folded, _window, _cx| div(),
17522            );
17523
17524            editor.insert_creases(Some(crease), cx);
17525            let snapshot = editor.snapshot(window, cx);
17526            let _div = snapshot.render_crease_toggle(
17527                MultiBufferRow(1),
17528                false,
17529                cx.entity().clone(),
17530                window,
17531                cx,
17532            );
17533            snapshot
17534        })
17535        .unwrap();
17536
17537    let render_args = render_args.lock().take().unwrap();
17538    assert_eq!(render_args.row, MultiBufferRow(1));
17539    assert!(!render_args.folded);
17540    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17541
17542    cx.update_window(*editor, |_, window, cx| {
17543        (render_args.callback)(true, window, cx)
17544    })
17545    .unwrap();
17546    let snapshot = editor
17547        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17548        .unwrap();
17549    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17550
17551    cx.update_window(*editor, |_, window, cx| {
17552        (render_args.callback)(false, window, cx)
17553    })
17554    .unwrap();
17555    let snapshot = editor
17556        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17557        .unwrap();
17558    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17559}
17560
17561#[gpui::test]
17562async fn test_input_text(cx: &mut TestAppContext) {
17563    init_test(cx, |_| {});
17564    let mut cx = EditorTestContext::new(cx).await;
17565
17566    cx.set_state(
17567        &r#"ˇone
17568        two
17569
17570        three
17571        fourˇ
17572        five
17573
17574        siˇx"#
17575            .unindent(),
17576    );
17577
17578    cx.dispatch_action(HandleInput(String::new()));
17579    cx.assert_editor_state(
17580        &r#"ˇone
17581        two
17582
17583        three
17584        fourˇ
17585        five
17586
17587        siˇx"#
17588            .unindent(),
17589    );
17590
17591    cx.dispatch_action(HandleInput("AAAA".to_string()));
17592    cx.assert_editor_state(
17593        &r#"AAAAˇone
17594        two
17595
17596        three
17597        fourAAAAˇ
17598        five
17599
17600        siAAAAˇx"#
17601            .unindent(),
17602    );
17603}
17604
17605#[gpui::test]
17606async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17607    init_test(cx, |_| {});
17608
17609    let mut cx = EditorTestContext::new(cx).await;
17610    cx.set_state(
17611        r#"let foo = 1;
17612let foo = 2;
17613let foo = 3;
17614let fooˇ = 4;
17615let foo = 5;
17616let foo = 6;
17617let foo = 7;
17618let foo = 8;
17619let foo = 9;
17620let foo = 10;
17621let foo = 11;
17622let foo = 12;
17623let foo = 13;
17624let foo = 14;
17625let foo = 15;"#,
17626    );
17627
17628    cx.update_editor(|e, window, cx| {
17629        assert_eq!(
17630            e.next_scroll_position,
17631            NextScrollCursorCenterTopBottom::Center,
17632            "Default next scroll direction is center",
17633        );
17634
17635        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17636        assert_eq!(
17637            e.next_scroll_position,
17638            NextScrollCursorCenterTopBottom::Top,
17639            "After center, next scroll direction should be top",
17640        );
17641
17642        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17643        assert_eq!(
17644            e.next_scroll_position,
17645            NextScrollCursorCenterTopBottom::Bottom,
17646            "After top, next scroll direction should be bottom",
17647        );
17648
17649        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17650        assert_eq!(
17651            e.next_scroll_position,
17652            NextScrollCursorCenterTopBottom::Center,
17653            "After bottom, scrolling should start over",
17654        );
17655
17656        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17657        assert_eq!(
17658            e.next_scroll_position,
17659            NextScrollCursorCenterTopBottom::Top,
17660            "Scrolling continues if retriggered fast enough"
17661        );
17662    });
17663
17664    cx.executor()
17665        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17666    cx.executor().run_until_parked();
17667    cx.update_editor(|e, _, _| {
17668        assert_eq!(
17669            e.next_scroll_position,
17670            NextScrollCursorCenterTopBottom::Center,
17671            "If scrolling is not triggered fast enough, it should reset"
17672        );
17673    });
17674}
17675
17676#[gpui::test]
17677async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17678    init_test(cx, |_| {});
17679    let mut cx = EditorLspTestContext::new_rust(
17680        lsp::ServerCapabilities {
17681            definition_provider: Some(lsp::OneOf::Left(true)),
17682            references_provider: Some(lsp::OneOf::Left(true)),
17683            ..lsp::ServerCapabilities::default()
17684        },
17685        cx,
17686    )
17687    .await;
17688
17689    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17690        let go_to_definition = cx
17691            .lsp
17692            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17693                move |params, _| async move {
17694                    if empty_go_to_definition {
17695                        Ok(None)
17696                    } else {
17697                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17698                            uri: params.text_document_position_params.text_document.uri,
17699                            range: lsp::Range::new(
17700                                lsp::Position::new(4, 3),
17701                                lsp::Position::new(4, 6),
17702                            ),
17703                        })))
17704                    }
17705                },
17706            );
17707        let references = cx
17708            .lsp
17709            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17710                Ok(Some(vec![lsp::Location {
17711                    uri: params.text_document_position.text_document.uri,
17712                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17713                }]))
17714            });
17715        (go_to_definition, references)
17716    };
17717
17718    cx.set_state(
17719        &r#"fn one() {
17720            let mut a = ˇtwo();
17721        }
17722
17723        fn two() {}"#
17724            .unindent(),
17725    );
17726    set_up_lsp_handlers(false, &mut cx);
17727    let navigated = cx
17728        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17729        .await
17730        .expect("Failed to navigate to definition");
17731    assert_eq!(
17732        navigated,
17733        Navigated::Yes,
17734        "Should have navigated to definition from the GetDefinition response"
17735    );
17736    cx.assert_editor_state(
17737        &r#"fn one() {
17738            let mut a = two();
17739        }
17740
17741        fn «twoˇ»() {}"#
17742            .unindent(),
17743    );
17744
17745    let editors = cx.update_workspace(|workspace, _, cx| {
17746        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17747    });
17748    cx.update_editor(|_, _, test_editor_cx| {
17749        assert_eq!(
17750            editors.len(),
17751            1,
17752            "Initially, only one, test, editor should be open in the workspace"
17753        );
17754        assert_eq!(
17755            test_editor_cx.entity(),
17756            editors.last().expect("Asserted len is 1").clone()
17757        );
17758    });
17759
17760    set_up_lsp_handlers(true, &mut cx);
17761    let navigated = cx
17762        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17763        .await
17764        .expect("Failed to navigate to lookup references");
17765    assert_eq!(
17766        navigated,
17767        Navigated::Yes,
17768        "Should have navigated to references as a fallback after empty GoToDefinition response"
17769    );
17770    // We should not change the selections in the existing file,
17771    // if opening another milti buffer with the references
17772    cx.assert_editor_state(
17773        &r#"fn one() {
17774            let mut a = two();
17775        }
17776
17777        fn «twoˇ»() {}"#
17778            .unindent(),
17779    );
17780    let editors = cx.update_workspace(|workspace, _, cx| {
17781        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17782    });
17783    cx.update_editor(|_, _, test_editor_cx| {
17784        assert_eq!(
17785            editors.len(),
17786            2,
17787            "After falling back to references search, we open a new editor with the results"
17788        );
17789        let references_fallback_text = editors
17790            .into_iter()
17791            .find(|new_editor| *new_editor != test_editor_cx.entity())
17792            .expect("Should have one non-test editor now")
17793            .read(test_editor_cx)
17794            .text(test_editor_cx);
17795        assert_eq!(
17796            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17797            "Should use the range from the references response and not the GoToDefinition one"
17798        );
17799    });
17800}
17801
17802#[gpui::test]
17803async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17804    init_test(cx, |_| {});
17805    cx.update(|cx| {
17806        let mut editor_settings = EditorSettings::get_global(cx).clone();
17807        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17808        EditorSettings::override_global(editor_settings, cx);
17809    });
17810    let mut cx = EditorLspTestContext::new_rust(
17811        lsp::ServerCapabilities {
17812            definition_provider: Some(lsp::OneOf::Left(true)),
17813            references_provider: Some(lsp::OneOf::Left(true)),
17814            ..lsp::ServerCapabilities::default()
17815        },
17816        cx,
17817    )
17818    .await;
17819    let original_state = r#"fn one() {
17820        let mut a = ˇtwo();
17821    }
17822
17823    fn two() {}"#
17824        .unindent();
17825    cx.set_state(&original_state);
17826
17827    let mut go_to_definition = cx
17828        .lsp
17829        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17830            move |_, _| async move { Ok(None) },
17831        );
17832    let _references = cx
17833        .lsp
17834        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17835            panic!("Should not call for references with no go to definition fallback")
17836        });
17837
17838    let navigated = cx
17839        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17840        .await
17841        .expect("Failed to navigate to lookup references");
17842    go_to_definition
17843        .next()
17844        .await
17845        .expect("Should have called the go_to_definition handler");
17846
17847    assert_eq!(
17848        navigated,
17849        Navigated::No,
17850        "Should have navigated to references as a fallback after empty GoToDefinition response"
17851    );
17852    cx.assert_editor_state(&original_state);
17853    let editors = cx.update_workspace(|workspace, _, cx| {
17854        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17855    });
17856    cx.update_editor(|_, _, _| {
17857        assert_eq!(
17858            editors.len(),
17859            1,
17860            "After unsuccessful fallback, no other editor should have been opened"
17861        );
17862    });
17863}
17864
17865#[gpui::test]
17866async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17867    init_test(cx, |_| {});
17868
17869    let language = Arc::new(Language::new(
17870        LanguageConfig::default(),
17871        Some(tree_sitter_rust::LANGUAGE.into()),
17872    ));
17873
17874    let text = r#"
17875        #[cfg(test)]
17876        mod tests() {
17877            #[test]
17878            fn runnable_1() {
17879                let a = 1;
17880            }
17881
17882            #[test]
17883            fn runnable_2() {
17884                let a = 1;
17885                let b = 2;
17886            }
17887        }
17888    "#
17889    .unindent();
17890
17891    let fs = FakeFs::new(cx.executor());
17892    fs.insert_file("/file.rs", Default::default()).await;
17893
17894    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17895    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17896    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17897    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17898    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17899
17900    let editor = cx.new_window_entity(|window, cx| {
17901        Editor::new(
17902            EditorMode::full(),
17903            multi_buffer,
17904            Some(project.clone()),
17905            window,
17906            cx,
17907        )
17908    });
17909
17910    editor.update_in(cx, |editor, window, cx| {
17911        let snapshot = editor.buffer().read(cx).snapshot(cx);
17912        editor.tasks.insert(
17913            (buffer.read(cx).remote_id(), 3),
17914            RunnableTasks {
17915                templates: vec![],
17916                offset: snapshot.anchor_before(43),
17917                column: 0,
17918                extra_variables: HashMap::default(),
17919                context_range: BufferOffset(43)..BufferOffset(85),
17920            },
17921        );
17922        editor.tasks.insert(
17923            (buffer.read(cx).remote_id(), 8),
17924            RunnableTasks {
17925                templates: vec![],
17926                offset: snapshot.anchor_before(86),
17927                column: 0,
17928                extra_variables: HashMap::default(),
17929                context_range: BufferOffset(86)..BufferOffset(191),
17930            },
17931        );
17932
17933        // Test finding task when cursor is inside function body
17934        editor.change_selections(None, window, cx, |s| {
17935            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17936        });
17937        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17938        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17939
17940        // Test finding task when cursor is on function name
17941        editor.change_selections(None, window, cx, |s| {
17942            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17943        });
17944        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17945        assert_eq!(row, 8, "Should find task when cursor is on function name");
17946    });
17947}
17948
17949#[gpui::test]
17950async fn test_folding_buffers(cx: &mut TestAppContext) {
17951    init_test(cx, |_| {});
17952
17953    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17954    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17955    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17956
17957    let fs = FakeFs::new(cx.executor());
17958    fs.insert_tree(
17959        path!("/a"),
17960        json!({
17961            "first.rs": sample_text_1,
17962            "second.rs": sample_text_2,
17963            "third.rs": sample_text_3,
17964        }),
17965    )
17966    .await;
17967    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17968    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17969    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17970    let worktree = project.update(cx, |project, cx| {
17971        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17972        assert_eq!(worktrees.len(), 1);
17973        worktrees.pop().unwrap()
17974    });
17975    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17976
17977    let buffer_1 = project
17978        .update(cx, |project, cx| {
17979            project.open_buffer((worktree_id, "first.rs"), cx)
17980        })
17981        .await
17982        .unwrap();
17983    let buffer_2 = project
17984        .update(cx, |project, cx| {
17985            project.open_buffer((worktree_id, "second.rs"), cx)
17986        })
17987        .await
17988        .unwrap();
17989    let buffer_3 = project
17990        .update(cx, |project, cx| {
17991            project.open_buffer((worktree_id, "third.rs"), cx)
17992        })
17993        .await
17994        .unwrap();
17995
17996    let multi_buffer = cx.new(|cx| {
17997        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17998        multi_buffer.push_excerpts(
17999            buffer_1.clone(),
18000            [
18001                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18002                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18003                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18004            ],
18005            cx,
18006        );
18007        multi_buffer.push_excerpts(
18008            buffer_2.clone(),
18009            [
18010                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18011                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18012                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18013            ],
18014            cx,
18015        );
18016        multi_buffer.push_excerpts(
18017            buffer_3.clone(),
18018            [
18019                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
18020                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
18021                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
18022            ],
18023            cx,
18024        );
18025        multi_buffer
18026    });
18027    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18028        Editor::new(
18029            EditorMode::full(),
18030            multi_buffer.clone(),
18031            Some(project.clone()),
18032            window,
18033            cx,
18034        )
18035    });
18036
18037    assert_eq!(
18038        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18039        "\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",
18040    );
18041
18042    multi_buffer_editor.update(cx, |editor, cx| {
18043        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18044    });
18045    assert_eq!(
18046        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18047        "\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",
18048        "After folding the first buffer, its text should not be displayed"
18049    );
18050
18051    multi_buffer_editor.update(cx, |editor, cx| {
18052        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18053    });
18054    assert_eq!(
18055        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18056        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
18057        "After folding the second buffer, its text should not be displayed"
18058    );
18059
18060    multi_buffer_editor.update(cx, |editor, cx| {
18061        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18062    });
18063    assert_eq!(
18064        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18065        "\n\n\n\n\n",
18066        "After folding the third buffer, its text should not be displayed"
18067    );
18068
18069    // Emulate selection inside the fold logic, that should work
18070    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18071        editor
18072            .snapshot(window, cx)
18073            .next_line_boundary(Point::new(0, 4));
18074    });
18075
18076    multi_buffer_editor.update(cx, |editor, cx| {
18077        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18078    });
18079    assert_eq!(
18080        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18081        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18082        "After unfolding the second buffer, its text should be displayed"
18083    );
18084
18085    // Typing inside of buffer 1 causes that buffer to be unfolded.
18086    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18087        assert_eq!(
18088            multi_buffer
18089                .read(cx)
18090                .snapshot(cx)
18091                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
18092                .collect::<String>(),
18093            "bbbb"
18094        );
18095        editor.change_selections(None, window, cx, |selections| {
18096            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
18097        });
18098        editor.handle_input("B", window, cx);
18099    });
18100
18101    assert_eq!(
18102        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18103        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
18104        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
18105    );
18106
18107    multi_buffer_editor.update(cx, |editor, cx| {
18108        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18109    });
18110    assert_eq!(
18111        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18112        "\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",
18113        "After unfolding the all buffers, all original text should be displayed"
18114    );
18115}
18116
18117#[gpui::test]
18118async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
18119    init_test(cx, |_| {});
18120
18121    let sample_text_1 = "1111\n2222\n3333".to_string();
18122    let sample_text_2 = "4444\n5555\n6666".to_string();
18123    let sample_text_3 = "7777\n8888\n9999".to_string();
18124
18125    let fs = FakeFs::new(cx.executor());
18126    fs.insert_tree(
18127        path!("/a"),
18128        json!({
18129            "first.rs": sample_text_1,
18130            "second.rs": sample_text_2,
18131            "third.rs": sample_text_3,
18132        }),
18133    )
18134    .await;
18135    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18136    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18137    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18138    let worktree = project.update(cx, |project, cx| {
18139        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18140        assert_eq!(worktrees.len(), 1);
18141        worktrees.pop().unwrap()
18142    });
18143    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18144
18145    let buffer_1 = project
18146        .update(cx, |project, cx| {
18147            project.open_buffer((worktree_id, "first.rs"), cx)
18148        })
18149        .await
18150        .unwrap();
18151    let buffer_2 = project
18152        .update(cx, |project, cx| {
18153            project.open_buffer((worktree_id, "second.rs"), cx)
18154        })
18155        .await
18156        .unwrap();
18157    let buffer_3 = project
18158        .update(cx, |project, cx| {
18159            project.open_buffer((worktree_id, "third.rs"), cx)
18160        })
18161        .await
18162        .unwrap();
18163
18164    let multi_buffer = cx.new(|cx| {
18165        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18166        multi_buffer.push_excerpts(
18167            buffer_1.clone(),
18168            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18169            cx,
18170        );
18171        multi_buffer.push_excerpts(
18172            buffer_2.clone(),
18173            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18174            cx,
18175        );
18176        multi_buffer.push_excerpts(
18177            buffer_3.clone(),
18178            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
18179            cx,
18180        );
18181        multi_buffer
18182    });
18183
18184    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18185        Editor::new(
18186            EditorMode::full(),
18187            multi_buffer,
18188            Some(project.clone()),
18189            window,
18190            cx,
18191        )
18192    });
18193
18194    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
18195    assert_eq!(
18196        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18197        full_text,
18198    );
18199
18200    multi_buffer_editor.update(cx, |editor, cx| {
18201        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
18202    });
18203    assert_eq!(
18204        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18205        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
18206        "After folding the first buffer, its text should not be displayed"
18207    );
18208
18209    multi_buffer_editor.update(cx, |editor, cx| {
18210        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
18211    });
18212
18213    assert_eq!(
18214        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18215        "\n\n\n\n\n\n7777\n8888\n9999",
18216        "After folding the second buffer, its text should not be displayed"
18217    );
18218
18219    multi_buffer_editor.update(cx, |editor, cx| {
18220        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
18221    });
18222    assert_eq!(
18223        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18224        "\n\n\n\n\n",
18225        "After folding the third buffer, its text should not be displayed"
18226    );
18227
18228    multi_buffer_editor.update(cx, |editor, cx| {
18229        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
18230    });
18231    assert_eq!(
18232        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18233        "\n\n\n\n4444\n5555\n6666\n\n",
18234        "After unfolding the second buffer, its text should be displayed"
18235    );
18236
18237    multi_buffer_editor.update(cx, |editor, cx| {
18238        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18239    });
18240    assert_eq!(
18241        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18242        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18243        "After unfolding the first buffer, its text should be displayed"
18244    );
18245
18246    multi_buffer_editor.update(cx, |editor, cx| {
18247        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18248    });
18249    assert_eq!(
18250        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18251        full_text,
18252        "After unfolding all buffers, all original text should be displayed"
18253    );
18254}
18255
18256#[gpui::test]
18257async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18258    init_test(cx, |_| {});
18259
18260    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18261
18262    let fs = FakeFs::new(cx.executor());
18263    fs.insert_tree(
18264        path!("/a"),
18265        json!({
18266            "main.rs": sample_text,
18267        }),
18268    )
18269    .await;
18270    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18271    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18272    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18273    let worktree = project.update(cx, |project, cx| {
18274        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18275        assert_eq!(worktrees.len(), 1);
18276        worktrees.pop().unwrap()
18277    });
18278    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18279
18280    let buffer_1 = project
18281        .update(cx, |project, cx| {
18282            project.open_buffer((worktree_id, "main.rs"), cx)
18283        })
18284        .await
18285        .unwrap();
18286
18287    let multi_buffer = cx.new(|cx| {
18288        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18289        multi_buffer.push_excerpts(
18290            buffer_1.clone(),
18291            [ExcerptRange::new(
18292                Point::new(0, 0)
18293                    ..Point::new(
18294                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18295                        0,
18296                    ),
18297            )],
18298            cx,
18299        );
18300        multi_buffer
18301    });
18302    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18303        Editor::new(
18304            EditorMode::full(),
18305            multi_buffer,
18306            Some(project.clone()),
18307            window,
18308            cx,
18309        )
18310    });
18311
18312    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18313    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18314        enum TestHighlight {}
18315        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18316        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18317        editor.highlight_text::<TestHighlight>(
18318            vec![highlight_range.clone()],
18319            HighlightStyle::color(Hsla::green()),
18320            cx,
18321        );
18322        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18323    });
18324
18325    let full_text = format!("\n\n{sample_text}");
18326    assert_eq!(
18327        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18328        full_text,
18329    );
18330}
18331
18332#[gpui::test]
18333async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18334    init_test(cx, |_| {});
18335    cx.update(|cx| {
18336        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18337            "keymaps/default-linux.json",
18338            cx,
18339        )
18340        .unwrap();
18341        cx.bind_keys(default_key_bindings);
18342    });
18343
18344    let (editor, cx) = cx.add_window_view(|window, cx| {
18345        let multi_buffer = MultiBuffer::build_multi(
18346            [
18347                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18348                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18349                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18350                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18351            ],
18352            cx,
18353        );
18354        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18355
18356        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18357        // fold all but the second buffer, so that we test navigating between two
18358        // adjacent folded buffers, as well as folded buffers at the start and
18359        // end the multibuffer
18360        editor.fold_buffer(buffer_ids[0], cx);
18361        editor.fold_buffer(buffer_ids[2], cx);
18362        editor.fold_buffer(buffer_ids[3], cx);
18363
18364        editor
18365    });
18366    cx.simulate_resize(size(px(1000.), px(1000.)));
18367
18368    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18369    cx.assert_excerpts_with_selections(indoc! {"
18370        [EXCERPT]
18371        ˇ[FOLDED]
18372        [EXCERPT]
18373        a1
18374        b1
18375        [EXCERPT]
18376        [FOLDED]
18377        [EXCERPT]
18378        [FOLDED]
18379        "
18380    });
18381    cx.simulate_keystroke("down");
18382    cx.assert_excerpts_with_selections(indoc! {"
18383        [EXCERPT]
18384        [FOLDED]
18385        [EXCERPT]
18386        ˇa1
18387        b1
18388        [EXCERPT]
18389        [FOLDED]
18390        [EXCERPT]
18391        [FOLDED]
18392        "
18393    });
18394    cx.simulate_keystroke("down");
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    for _ in 0..5 {
18434        cx.simulate_keystroke("down");
18435        cx.assert_excerpts_with_selections(indoc! {"
18436            [EXCERPT]
18437            [FOLDED]
18438            [EXCERPT]
18439            a1
18440            b1
18441            [EXCERPT]
18442            [FOLDED]
18443            [EXCERPT]
18444            ˇ[FOLDED]
18445            "
18446        });
18447    }
18448
18449    cx.simulate_keystroke("up");
18450    cx.assert_excerpts_with_selections(indoc! {"
18451        [EXCERPT]
18452        [FOLDED]
18453        [EXCERPT]
18454        a1
18455        b1
18456        [EXCERPT]
18457        ˇ[FOLDED]
18458        [EXCERPT]
18459        [FOLDED]
18460        "
18461    });
18462    cx.simulate_keystroke("up");
18463    cx.assert_excerpts_with_selections(indoc! {"
18464        [EXCERPT]
18465        [FOLDED]
18466        [EXCERPT]
18467        a1
18468        b1
18469        ˇ[EXCERPT]
18470        [FOLDED]
18471        [EXCERPT]
18472        [FOLDED]
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    for _ in 0..5 {
18502        cx.simulate_keystroke("up");
18503        cx.assert_excerpts_with_selections(indoc! {"
18504            [EXCERPT]
18505            ˇ[FOLDED]
18506            [EXCERPT]
18507            a1
18508            b1
18509            [EXCERPT]
18510            [FOLDED]
18511            [EXCERPT]
18512            [FOLDED]
18513            "
18514        });
18515    }
18516}
18517
18518#[gpui::test]
18519async fn test_inline_completion_text(cx: &mut TestAppContext) {
18520    init_test(cx, |_| {});
18521
18522    // Simple insertion
18523    assert_highlighted_edits(
18524        "Hello, world!",
18525        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18526        true,
18527        cx,
18528        |highlighted_edits, cx| {
18529            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18530            assert_eq!(highlighted_edits.highlights.len(), 1);
18531            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18532            assert_eq!(
18533                highlighted_edits.highlights[0].1.background_color,
18534                Some(cx.theme().status().created_background)
18535            );
18536        },
18537    )
18538    .await;
18539
18540    // Replacement
18541    assert_highlighted_edits(
18542        "This is a test.",
18543        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18544        false,
18545        cx,
18546        |highlighted_edits, cx| {
18547            assert_eq!(highlighted_edits.text, "That is a test.");
18548            assert_eq!(highlighted_edits.highlights.len(), 1);
18549            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18550            assert_eq!(
18551                highlighted_edits.highlights[0].1.background_color,
18552                Some(cx.theme().status().created_background)
18553            );
18554        },
18555    )
18556    .await;
18557
18558    // Multiple edits
18559    assert_highlighted_edits(
18560        "Hello, world!",
18561        vec![
18562            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18563            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18564        ],
18565        false,
18566        cx,
18567        |highlighted_edits, cx| {
18568            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18569            assert_eq!(highlighted_edits.highlights.len(), 2);
18570            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18571            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18572            assert_eq!(
18573                highlighted_edits.highlights[0].1.background_color,
18574                Some(cx.theme().status().created_background)
18575            );
18576            assert_eq!(
18577                highlighted_edits.highlights[1].1.background_color,
18578                Some(cx.theme().status().created_background)
18579            );
18580        },
18581    )
18582    .await;
18583
18584    // Multiple lines with edits
18585    assert_highlighted_edits(
18586        "First line\nSecond line\nThird line\nFourth line",
18587        vec![
18588            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18589            (
18590                Point::new(2, 0)..Point::new(2, 10),
18591                "New third line".to_string(),
18592            ),
18593            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18594        ],
18595        false,
18596        cx,
18597        |highlighted_edits, cx| {
18598            assert_eq!(
18599                highlighted_edits.text,
18600                "Second modified\nNew third line\nFourth updated line"
18601            );
18602            assert_eq!(highlighted_edits.highlights.len(), 3);
18603            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18604            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18605            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18606            for highlight in &highlighted_edits.highlights {
18607                assert_eq!(
18608                    highlight.1.background_color,
18609                    Some(cx.theme().status().created_background)
18610                );
18611            }
18612        },
18613    )
18614    .await;
18615}
18616
18617#[gpui::test]
18618async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18619    init_test(cx, |_| {});
18620
18621    // Deletion
18622    assert_highlighted_edits(
18623        "Hello, world!",
18624        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18625        true,
18626        cx,
18627        |highlighted_edits, cx| {
18628            assert_eq!(highlighted_edits.text, "Hello, world!");
18629            assert_eq!(highlighted_edits.highlights.len(), 1);
18630            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18631            assert_eq!(
18632                highlighted_edits.highlights[0].1.background_color,
18633                Some(cx.theme().status().deleted_background)
18634            );
18635        },
18636    )
18637    .await;
18638
18639    // Insertion
18640    assert_highlighted_edits(
18641        "Hello, world!",
18642        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18643        true,
18644        cx,
18645        |highlighted_edits, cx| {
18646            assert_eq!(highlighted_edits.highlights.len(), 1);
18647            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18648            assert_eq!(
18649                highlighted_edits.highlights[0].1.background_color,
18650                Some(cx.theme().status().created_background)
18651            );
18652        },
18653    )
18654    .await;
18655}
18656
18657async fn assert_highlighted_edits(
18658    text: &str,
18659    edits: Vec<(Range<Point>, String)>,
18660    include_deletions: bool,
18661    cx: &mut TestAppContext,
18662    assertion_fn: impl Fn(HighlightedText, &App),
18663) {
18664    let window = cx.add_window(|window, cx| {
18665        let buffer = MultiBuffer::build_simple(text, cx);
18666        Editor::new(EditorMode::full(), buffer, None, window, cx)
18667    });
18668    let cx = &mut VisualTestContext::from_window(*window, cx);
18669
18670    let (buffer, snapshot) = window
18671        .update(cx, |editor, _window, cx| {
18672            (
18673                editor.buffer().clone(),
18674                editor.buffer().read(cx).snapshot(cx),
18675            )
18676        })
18677        .unwrap();
18678
18679    let edits = edits
18680        .into_iter()
18681        .map(|(range, edit)| {
18682            (
18683                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18684                edit,
18685            )
18686        })
18687        .collect::<Vec<_>>();
18688
18689    let text_anchor_edits = edits
18690        .clone()
18691        .into_iter()
18692        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18693        .collect::<Vec<_>>();
18694
18695    let edit_preview = window
18696        .update(cx, |_, _window, cx| {
18697            buffer
18698                .read(cx)
18699                .as_singleton()
18700                .unwrap()
18701                .read(cx)
18702                .preview_edits(text_anchor_edits.into(), cx)
18703        })
18704        .unwrap()
18705        .await;
18706
18707    cx.update(|_window, cx| {
18708        let highlighted_edits = inline_completion_edit_text(
18709            &snapshot.as_singleton().unwrap().2,
18710            &edits,
18711            &edit_preview,
18712            include_deletions,
18713            cx,
18714        );
18715        assertion_fn(highlighted_edits, cx)
18716    });
18717}
18718
18719#[track_caller]
18720fn assert_breakpoint(
18721    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18722    path: &Arc<Path>,
18723    expected: Vec<(u32, Breakpoint)>,
18724) {
18725    if expected.len() == 0usize {
18726        assert!(!breakpoints.contains_key(path), "{}", path.display());
18727    } else {
18728        let mut breakpoint = breakpoints
18729            .get(path)
18730            .unwrap()
18731            .into_iter()
18732            .map(|breakpoint| {
18733                (
18734                    breakpoint.row,
18735                    Breakpoint {
18736                        message: breakpoint.message.clone(),
18737                        state: breakpoint.state,
18738                        condition: breakpoint.condition.clone(),
18739                        hit_condition: breakpoint.hit_condition.clone(),
18740                    },
18741                )
18742            })
18743            .collect::<Vec<_>>();
18744
18745        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18746
18747        assert_eq!(expected, breakpoint);
18748    }
18749}
18750
18751fn add_log_breakpoint_at_cursor(
18752    editor: &mut Editor,
18753    log_message: &str,
18754    window: &mut Window,
18755    cx: &mut Context<Editor>,
18756) {
18757    let (anchor, bp) = editor
18758        .breakpoints_at_cursors(window, cx)
18759        .first()
18760        .and_then(|(anchor, bp)| {
18761            if let Some(bp) = bp {
18762                Some((*anchor, bp.clone()))
18763            } else {
18764                None
18765            }
18766        })
18767        .unwrap_or_else(|| {
18768            let cursor_position: Point = editor.selections.newest(cx).head();
18769
18770            let breakpoint_position = editor
18771                .snapshot(window, cx)
18772                .display_snapshot
18773                .buffer_snapshot
18774                .anchor_before(Point::new(cursor_position.row, 0));
18775
18776            (breakpoint_position, Breakpoint::new_log(&log_message))
18777        });
18778
18779    editor.edit_breakpoint_at_anchor(
18780        anchor,
18781        bp,
18782        BreakpointEditAction::EditLogMessage(log_message.into()),
18783        cx,
18784    );
18785}
18786
18787#[gpui::test]
18788async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18789    init_test(cx, |_| {});
18790
18791    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18792    let fs = FakeFs::new(cx.executor());
18793    fs.insert_tree(
18794        path!("/a"),
18795        json!({
18796            "main.rs": sample_text,
18797        }),
18798    )
18799    .await;
18800    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18801    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18802    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18803
18804    let fs = FakeFs::new(cx.executor());
18805    fs.insert_tree(
18806        path!("/a"),
18807        json!({
18808            "main.rs": sample_text,
18809        }),
18810    )
18811    .await;
18812    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18813    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18814    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18815    let worktree_id = workspace
18816        .update(cx, |workspace, _window, cx| {
18817            workspace.project().update(cx, |project, cx| {
18818                project.worktrees(cx).next().unwrap().read(cx).id()
18819            })
18820        })
18821        .unwrap();
18822
18823    let buffer = project
18824        .update(cx, |project, cx| {
18825            project.open_buffer((worktree_id, "main.rs"), cx)
18826        })
18827        .await
18828        .unwrap();
18829
18830    let (editor, cx) = cx.add_window_view(|window, cx| {
18831        Editor::new(
18832            EditorMode::full(),
18833            MultiBuffer::build_from_buffer(buffer, cx),
18834            Some(project.clone()),
18835            window,
18836            cx,
18837        )
18838    });
18839
18840    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18841    let abs_path = project.read_with(cx, |project, cx| {
18842        project
18843            .absolute_path(&project_path, cx)
18844            .map(|path_buf| Arc::from(path_buf.to_owned()))
18845            .unwrap()
18846    });
18847
18848    // assert we can add breakpoint on the first line
18849    editor.update_in(cx, |editor, window, cx| {
18850        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18851        editor.move_to_end(&MoveToEnd, window, cx);
18852        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18853    });
18854
18855    let breakpoints = editor.update(cx, |editor, cx| {
18856        editor
18857            .breakpoint_store()
18858            .as_ref()
18859            .unwrap()
18860            .read(cx)
18861            .all_source_breakpoints(cx)
18862            .clone()
18863    });
18864
18865    assert_eq!(1, breakpoints.len());
18866    assert_breakpoint(
18867        &breakpoints,
18868        &abs_path,
18869        vec![
18870            (0, Breakpoint::new_standard()),
18871            (3, Breakpoint::new_standard()),
18872        ],
18873    );
18874
18875    editor.update_in(cx, |editor, window, cx| {
18876        editor.move_to_beginning(&MoveToBeginning, window, cx);
18877        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18878    });
18879
18880    let breakpoints = editor.update(cx, |editor, cx| {
18881        editor
18882            .breakpoint_store()
18883            .as_ref()
18884            .unwrap()
18885            .read(cx)
18886            .all_source_breakpoints(cx)
18887            .clone()
18888    });
18889
18890    assert_eq!(1, breakpoints.len());
18891    assert_breakpoint(
18892        &breakpoints,
18893        &abs_path,
18894        vec![(3, Breakpoint::new_standard())],
18895    );
18896
18897    editor.update_in(cx, |editor, window, cx| {
18898        editor.move_to_end(&MoveToEnd, window, cx);
18899        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18900    });
18901
18902    let breakpoints = editor.update(cx, |editor, cx| {
18903        editor
18904            .breakpoint_store()
18905            .as_ref()
18906            .unwrap()
18907            .read(cx)
18908            .all_source_breakpoints(cx)
18909            .clone()
18910    });
18911
18912    assert_eq!(0, breakpoints.len());
18913    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18914}
18915
18916#[gpui::test]
18917async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18918    init_test(cx, |_| {});
18919
18920    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18921
18922    let fs = FakeFs::new(cx.executor());
18923    fs.insert_tree(
18924        path!("/a"),
18925        json!({
18926            "main.rs": sample_text,
18927        }),
18928    )
18929    .await;
18930    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18931    let (workspace, cx) =
18932        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18933
18934    let worktree_id = workspace.update(cx, |workspace, cx| {
18935        workspace.project().update(cx, |project, cx| {
18936            project.worktrees(cx).next().unwrap().read(cx).id()
18937        })
18938    });
18939
18940    let buffer = project
18941        .update(cx, |project, cx| {
18942            project.open_buffer((worktree_id, "main.rs"), cx)
18943        })
18944        .await
18945        .unwrap();
18946
18947    let (editor, cx) = cx.add_window_view(|window, cx| {
18948        Editor::new(
18949            EditorMode::full(),
18950            MultiBuffer::build_from_buffer(buffer, cx),
18951            Some(project.clone()),
18952            window,
18953            cx,
18954        )
18955    });
18956
18957    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18958    let abs_path = project.read_with(cx, |project, cx| {
18959        project
18960            .absolute_path(&project_path, cx)
18961            .map(|path_buf| Arc::from(path_buf.to_owned()))
18962            .unwrap()
18963    });
18964
18965    editor.update_in(cx, |editor, window, cx| {
18966        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18967    });
18968
18969    let breakpoints = editor.update(cx, |editor, cx| {
18970        editor
18971            .breakpoint_store()
18972            .as_ref()
18973            .unwrap()
18974            .read(cx)
18975            .all_source_breakpoints(cx)
18976            .clone()
18977    });
18978
18979    assert_breakpoint(
18980        &breakpoints,
18981        &abs_path,
18982        vec![(0, Breakpoint::new_log("hello world"))],
18983    );
18984
18985    // Removing a log message from a log breakpoint should remove it
18986    editor.update_in(cx, |editor, window, cx| {
18987        add_log_breakpoint_at_cursor(editor, "", window, cx);
18988    });
18989
18990    let breakpoints = editor.update(cx, |editor, cx| {
18991        editor
18992            .breakpoint_store()
18993            .as_ref()
18994            .unwrap()
18995            .read(cx)
18996            .all_source_breakpoints(cx)
18997            .clone()
18998    });
18999
19000    assert_breakpoint(&breakpoints, &abs_path, vec![]);
19001
19002    editor.update_in(cx, |editor, window, cx| {
19003        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19004        editor.move_to_end(&MoveToEnd, window, cx);
19005        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19006        // Not adding a log message to a standard breakpoint shouldn't remove it
19007        add_log_breakpoint_at_cursor(editor, "", window, cx);
19008    });
19009
19010    let breakpoints = editor.update(cx, |editor, cx| {
19011        editor
19012            .breakpoint_store()
19013            .as_ref()
19014            .unwrap()
19015            .read(cx)
19016            .all_source_breakpoints(cx)
19017            .clone()
19018    });
19019
19020    assert_breakpoint(
19021        &breakpoints,
19022        &abs_path,
19023        vec![
19024            (0, Breakpoint::new_standard()),
19025            (3, Breakpoint::new_standard()),
19026        ],
19027    );
19028
19029    editor.update_in(cx, |editor, window, cx| {
19030        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
19031    });
19032
19033    let breakpoints = editor.update(cx, |editor, cx| {
19034        editor
19035            .breakpoint_store()
19036            .as_ref()
19037            .unwrap()
19038            .read(cx)
19039            .all_source_breakpoints(cx)
19040            .clone()
19041    });
19042
19043    assert_breakpoint(
19044        &breakpoints,
19045        &abs_path,
19046        vec![
19047            (0, Breakpoint::new_standard()),
19048            (3, Breakpoint::new_log("hello world")),
19049        ],
19050    );
19051
19052    editor.update_in(cx, |editor, window, cx| {
19053        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
19054    });
19055
19056    let breakpoints = editor.update(cx, |editor, cx| {
19057        editor
19058            .breakpoint_store()
19059            .as_ref()
19060            .unwrap()
19061            .read(cx)
19062            .all_source_breakpoints(cx)
19063            .clone()
19064    });
19065
19066    assert_breakpoint(
19067        &breakpoints,
19068        &abs_path,
19069        vec![
19070            (0, Breakpoint::new_standard()),
19071            (3, Breakpoint::new_log("hello Earth!!")),
19072        ],
19073    );
19074}
19075
19076/// This also tests that Editor::breakpoint_at_cursor_head is working properly
19077/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
19078/// or when breakpoints were placed out of order. This tests for a regression too
19079#[gpui::test]
19080async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
19081    init_test(cx, |_| {});
19082
19083    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
19084    let fs = FakeFs::new(cx.executor());
19085    fs.insert_tree(
19086        path!("/a"),
19087        json!({
19088            "main.rs": sample_text,
19089        }),
19090    )
19091    .await;
19092    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19093    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19094    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19095
19096    let fs = FakeFs::new(cx.executor());
19097    fs.insert_tree(
19098        path!("/a"),
19099        json!({
19100            "main.rs": sample_text,
19101        }),
19102    )
19103    .await;
19104    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19105    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19106    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19107    let worktree_id = workspace
19108        .update(cx, |workspace, _window, cx| {
19109            workspace.project().update(cx, |project, cx| {
19110                project.worktrees(cx).next().unwrap().read(cx).id()
19111            })
19112        })
19113        .unwrap();
19114
19115    let buffer = project
19116        .update(cx, |project, cx| {
19117            project.open_buffer((worktree_id, "main.rs"), cx)
19118        })
19119        .await
19120        .unwrap();
19121
19122    let (editor, cx) = cx.add_window_view(|window, cx| {
19123        Editor::new(
19124            EditorMode::full(),
19125            MultiBuffer::build_from_buffer(buffer, cx),
19126            Some(project.clone()),
19127            window,
19128            cx,
19129        )
19130    });
19131
19132    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
19133    let abs_path = project.read_with(cx, |project, cx| {
19134        project
19135            .absolute_path(&project_path, cx)
19136            .map(|path_buf| Arc::from(path_buf.to_owned()))
19137            .unwrap()
19138    });
19139
19140    // assert we can add breakpoint on the first line
19141    editor.update_in(cx, |editor, window, cx| {
19142        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19143        editor.move_to_end(&MoveToEnd, window, cx);
19144        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19145        editor.move_up(&MoveUp, window, cx);
19146        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
19147    });
19148
19149    let breakpoints = editor.update(cx, |editor, cx| {
19150        editor
19151            .breakpoint_store()
19152            .as_ref()
19153            .unwrap()
19154            .read(cx)
19155            .all_source_breakpoints(cx)
19156            .clone()
19157    });
19158
19159    assert_eq!(1, breakpoints.len());
19160    assert_breakpoint(
19161        &breakpoints,
19162        &abs_path,
19163        vec![
19164            (0, Breakpoint::new_standard()),
19165            (2, Breakpoint::new_standard()),
19166            (3, Breakpoint::new_standard()),
19167        ],
19168    );
19169
19170    editor.update_in(cx, |editor, window, cx| {
19171        editor.move_to_beginning(&MoveToBeginning, window, cx);
19172        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19173        editor.move_to_end(&MoveToEnd, window, cx);
19174        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19175        // Disabling a breakpoint that doesn't exist should do nothing
19176        editor.move_up(&MoveUp, window, cx);
19177        editor.move_up(&MoveUp, window, cx);
19178        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19179    });
19180
19181    let breakpoints = editor.update(cx, |editor, cx| {
19182        editor
19183            .breakpoint_store()
19184            .as_ref()
19185            .unwrap()
19186            .read(cx)
19187            .all_source_breakpoints(cx)
19188            .clone()
19189    });
19190
19191    let disable_breakpoint = {
19192        let mut bp = Breakpoint::new_standard();
19193        bp.state = BreakpointState::Disabled;
19194        bp
19195    };
19196
19197    assert_eq!(1, breakpoints.len());
19198    assert_breakpoint(
19199        &breakpoints,
19200        &abs_path,
19201        vec![
19202            (0, disable_breakpoint.clone()),
19203            (2, Breakpoint::new_standard()),
19204            (3, disable_breakpoint.clone()),
19205        ],
19206    );
19207
19208    editor.update_in(cx, |editor, window, cx| {
19209        editor.move_to_beginning(&MoveToBeginning, window, cx);
19210        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19211        editor.move_to_end(&MoveToEnd, window, cx);
19212        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
19213        editor.move_up(&MoveUp, window, cx);
19214        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
19215    });
19216
19217    let breakpoints = editor.update(cx, |editor, cx| {
19218        editor
19219            .breakpoint_store()
19220            .as_ref()
19221            .unwrap()
19222            .read(cx)
19223            .all_source_breakpoints(cx)
19224            .clone()
19225    });
19226
19227    assert_eq!(1, breakpoints.len());
19228    assert_breakpoint(
19229        &breakpoints,
19230        &abs_path,
19231        vec![
19232            (0, Breakpoint::new_standard()),
19233            (2, disable_breakpoint),
19234            (3, Breakpoint::new_standard()),
19235        ],
19236    );
19237}
19238
19239#[gpui::test]
19240async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19241    init_test(cx, |_| {});
19242    let capabilities = lsp::ServerCapabilities {
19243        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19244            prepare_provider: Some(true),
19245            work_done_progress_options: Default::default(),
19246        })),
19247        ..Default::default()
19248    };
19249    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19250
19251    cx.set_state(indoc! {"
19252        struct Fˇoo {}
19253    "});
19254
19255    cx.update_editor(|editor, _, cx| {
19256        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19257        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19258        editor.highlight_background::<DocumentHighlightRead>(
19259            &[highlight_range],
19260            |c| c.editor_document_highlight_read_background,
19261            cx,
19262        );
19263    });
19264
19265    let mut prepare_rename_handler = cx
19266        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19267            move |_, _, _| async move {
19268                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19269                    start: lsp::Position {
19270                        line: 0,
19271                        character: 7,
19272                    },
19273                    end: lsp::Position {
19274                        line: 0,
19275                        character: 10,
19276                    },
19277                })))
19278            },
19279        );
19280    let prepare_rename_task = cx
19281        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19282        .expect("Prepare rename was not started");
19283    prepare_rename_handler.next().await.unwrap();
19284    prepare_rename_task.await.expect("Prepare rename failed");
19285
19286    let mut rename_handler =
19287        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19288            let edit = lsp::TextEdit {
19289                range: lsp::Range {
19290                    start: lsp::Position {
19291                        line: 0,
19292                        character: 7,
19293                    },
19294                    end: lsp::Position {
19295                        line: 0,
19296                        character: 10,
19297                    },
19298                },
19299                new_text: "FooRenamed".to_string(),
19300            };
19301            Ok(Some(lsp::WorkspaceEdit::new(
19302                // Specify the same edit twice
19303                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19304            )))
19305        });
19306    let rename_task = cx
19307        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19308        .expect("Confirm rename was not started");
19309    rename_handler.next().await.unwrap();
19310    rename_task.await.expect("Confirm rename failed");
19311    cx.run_until_parked();
19312
19313    // Despite two edits, only one is actually applied as those are identical
19314    cx.assert_editor_state(indoc! {"
19315        struct FooRenamedˇ {}
19316    "});
19317}
19318
19319#[gpui::test]
19320async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19321    init_test(cx, |_| {});
19322    // These capabilities indicate that the server does not support prepare rename.
19323    let capabilities = lsp::ServerCapabilities {
19324        rename_provider: Some(lsp::OneOf::Left(true)),
19325        ..Default::default()
19326    };
19327    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19328
19329    cx.set_state(indoc! {"
19330        struct Fˇoo {}
19331    "});
19332
19333    cx.update_editor(|editor, _window, cx| {
19334        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19335        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19336        editor.highlight_background::<DocumentHighlightRead>(
19337            &[highlight_range],
19338            |c| c.editor_document_highlight_read_background,
19339            cx,
19340        );
19341    });
19342
19343    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19344        .expect("Prepare rename was not started")
19345        .await
19346        .expect("Prepare rename failed");
19347
19348    let mut rename_handler =
19349        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19350            let edit = lsp::TextEdit {
19351                range: lsp::Range {
19352                    start: lsp::Position {
19353                        line: 0,
19354                        character: 7,
19355                    },
19356                    end: lsp::Position {
19357                        line: 0,
19358                        character: 10,
19359                    },
19360                },
19361                new_text: "FooRenamed".to_string(),
19362            };
19363            Ok(Some(lsp::WorkspaceEdit::new(
19364                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19365            )))
19366        });
19367    let rename_task = cx
19368        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19369        .expect("Confirm rename was not started");
19370    rename_handler.next().await.unwrap();
19371    rename_task.await.expect("Confirm rename failed");
19372    cx.run_until_parked();
19373
19374    // Correct range is renamed, as `surrounding_word` is used to find it.
19375    cx.assert_editor_state(indoc! {"
19376        struct FooRenamedˇ {}
19377    "});
19378}
19379
19380#[gpui::test]
19381async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19382    init_test(cx, |_| {});
19383    let mut cx = EditorTestContext::new(cx).await;
19384
19385    let language = Arc::new(
19386        Language::new(
19387            LanguageConfig::default(),
19388            Some(tree_sitter_html::LANGUAGE.into()),
19389        )
19390        .with_brackets_query(
19391            r#"
19392            ("<" @open "/>" @close)
19393            ("</" @open ">" @close)
19394            ("<" @open ">" @close)
19395            ("\"" @open "\"" @close)
19396            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19397        "#,
19398        )
19399        .unwrap(),
19400    );
19401    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19402
19403    cx.set_state(indoc! {"
19404        <span>ˇ</span>
19405    "});
19406    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19407    cx.assert_editor_state(indoc! {"
19408        <span>
19409        ˇ
19410        </span>
19411    "});
19412
19413    cx.set_state(indoc! {"
19414        <span><span></span>ˇ</span>
19415    "});
19416    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19417    cx.assert_editor_state(indoc! {"
19418        <span><span></span>
19419        ˇ</span>
19420    "});
19421
19422    cx.set_state(indoc! {"
19423        <span>ˇ
19424        </span>
19425    "});
19426    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19427    cx.assert_editor_state(indoc! {"
19428        <span>
19429        ˇ
19430        </span>
19431    "});
19432}
19433
19434#[gpui::test(iterations = 10)]
19435async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19436    init_test(cx, |_| {});
19437
19438    let fs = FakeFs::new(cx.executor());
19439    fs.insert_tree(
19440        path!("/dir"),
19441        json!({
19442            "a.ts": "a",
19443        }),
19444    )
19445    .await;
19446
19447    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19448    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19449    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19450
19451    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19452    language_registry.add(Arc::new(Language::new(
19453        LanguageConfig {
19454            name: "TypeScript".into(),
19455            matcher: LanguageMatcher {
19456                path_suffixes: vec!["ts".to_string()],
19457                ..Default::default()
19458            },
19459            ..Default::default()
19460        },
19461        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19462    )));
19463    let mut fake_language_servers = language_registry.register_fake_lsp(
19464        "TypeScript",
19465        FakeLspAdapter {
19466            capabilities: lsp::ServerCapabilities {
19467                code_lens_provider: Some(lsp::CodeLensOptions {
19468                    resolve_provider: Some(true),
19469                }),
19470                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19471                    commands: vec!["_the/command".to_string()],
19472                    ..lsp::ExecuteCommandOptions::default()
19473                }),
19474                ..lsp::ServerCapabilities::default()
19475            },
19476            ..FakeLspAdapter::default()
19477        },
19478    );
19479
19480    let (buffer, _handle) = project
19481        .update(cx, |p, cx| {
19482            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19483        })
19484        .await
19485        .unwrap();
19486    cx.executor().run_until_parked();
19487
19488    let fake_server = fake_language_servers.next().await.unwrap();
19489
19490    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19491    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19492    drop(buffer_snapshot);
19493    let actions = cx
19494        .update_window(*workspace, |_, window, cx| {
19495            project.code_actions(&buffer, anchor..anchor, window, cx)
19496        })
19497        .unwrap();
19498
19499    fake_server
19500        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19501            Ok(Some(vec![
19502                lsp::CodeLens {
19503                    range: lsp::Range::default(),
19504                    command: Some(lsp::Command {
19505                        title: "Code lens command".to_owned(),
19506                        command: "_the/command".to_owned(),
19507                        arguments: None,
19508                    }),
19509                    data: None,
19510                },
19511                lsp::CodeLens {
19512                    range: lsp::Range::default(),
19513                    command: Some(lsp::Command {
19514                        title: "Command not in capabilities".to_owned(),
19515                        command: "not in capabilities".to_owned(),
19516                        arguments: None,
19517                    }),
19518                    data: None,
19519                },
19520                lsp::CodeLens {
19521                    range: lsp::Range {
19522                        start: lsp::Position {
19523                            line: 1,
19524                            character: 1,
19525                        },
19526                        end: lsp::Position {
19527                            line: 1,
19528                            character: 1,
19529                        },
19530                    },
19531                    command: Some(lsp::Command {
19532                        title: "Command not in range".to_owned(),
19533                        command: "_the/command".to_owned(),
19534                        arguments: None,
19535                    }),
19536                    data: None,
19537                },
19538            ]))
19539        })
19540        .next()
19541        .await;
19542
19543    let actions = actions.await.unwrap();
19544    assert_eq!(
19545        actions.len(),
19546        1,
19547        "Should have only one valid action for the 0..0 range"
19548    );
19549    let action = actions[0].clone();
19550    let apply = project.update(cx, |project, cx| {
19551        project.apply_code_action(buffer.clone(), action, true, cx)
19552    });
19553
19554    // Resolving the code action does not populate its edits. In absence of
19555    // edits, we must execute the given command.
19556    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19557        |mut lens, _| async move {
19558            let lens_command = lens.command.as_mut().expect("should have a command");
19559            assert_eq!(lens_command.title, "Code lens command");
19560            lens_command.arguments = Some(vec![json!("the-argument")]);
19561            Ok(lens)
19562        },
19563    );
19564
19565    // While executing the command, the language server sends the editor
19566    // a `workspaceEdit` request.
19567    fake_server
19568        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19569            let fake = fake_server.clone();
19570            move |params, _| {
19571                assert_eq!(params.command, "_the/command");
19572                let fake = fake.clone();
19573                async move {
19574                    fake.server
19575                        .request::<lsp::request::ApplyWorkspaceEdit>(
19576                            lsp::ApplyWorkspaceEditParams {
19577                                label: None,
19578                                edit: lsp::WorkspaceEdit {
19579                                    changes: Some(
19580                                        [(
19581                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19582                                            vec![lsp::TextEdit {
19583                                                range: lsp::Range::new(
19584                                                    lsp::Position::new(0, 0),
19585                                                    lsp::Position::new(0, 0),
19586                                                ),
19587                                                new_text: "X".into(),
19588                                            }],
19589                                        )]
19590                                        .into_iter()
19591                                        .collect(),
19592                                    ),
19593                                    ..Default::default()
19594                                },
19595                            },
19596                        )
19597                        .await
19598                        .into_response()
19599                        .unwrap();
19600                    Ok(Some(json!(null)))
19601                }
19602            }
19603        })
19604        .next()
19605        .await;
19606
19607    // Applying the code lens command returns a project transaction containing the edits
19608    // sent by the language server in its `workspaceEdit` request.
19609    let transaction = apply.await.unwrap();
19610    assert!(transaction.0.contains_key(&buffer));
19611    buffer.update(cx, |buffer, cx| {
19612        assert_eq!(buffer.text(), "Xa");
19613        buffer.undo(cx);
19614        assert_eq!(buffer.text(), "a");
19615    });
19616}
19617
19618#[gpui::test]
19619async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19620    init_test(cx, |_| {});
19621
19622    let fs = FakeFs::new(cx.executor());
19623    let main_text = r#"fn main() {
19624println!("1");
19625println!("2");
19626println!("3");
19627println!("4");
19628println!("5");
19629}"#;
19630    let lib_text = "mod foo {}";
19631    fs.insert_tree(
19632        path!("/a"),
19633        json!({
19634            "lib.rs": lib_text,
19635            "main.rs": main_text,
19636        }),
19637    )
19638    .await;
19639
19640    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19641    let (workspace, cx) =
19642        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19643    let worktree_id = workspace.update(cx, |workspace, cx| {
19644        workspace.project().update(cx, |project, cx| {
19645            project.worktrees(cx).next().unwrap().read(cx).id()
19646        })
19647    });
19648
19649    let expected_ranges = vec![
19650        Point::new(0, 0)..Point::new(0, 0),
19651        Point::new(1, 0)..Point::new(1, 1),
19652        Point::new(2, 0)..Point::new(2, 2),
19653        Point::new(3, 0)..Point::new(3, 3),
19654    ];
19655
19656    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19657    let editor_1 = workspace
19658        .update_in(cx, |workspace, window, cx| {
19659            workspace.open_path(
19660                (worktree_id, "main.rs"),
19661                Some(pane_1.downgrade()),
19662                true,
19663                window,
19664                cx,
19665            )
19666        })
19667        .unwrap()
19668        .await
19669        .downcast::<Editor>()
19670        .unwrap();
19671    pane_1.update(cx, |pane, cx| {
19672        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19673        open_editor.update(cx, |editor, cx| {
19674            assert_eq!(
19675                editor.display_text(cx),
19676                main_text,
19677                "Original main.rs text on initial open",
19678            );
19679            assert_eq!(
19680                editor
19681                    .selections
19682                    .all::<Point>(cx)
19683                    .into_iter()
19684                    .map(|s| s.range())
19685                    .collect::<Vec<_>>(),
19686                vec![Point::zero()..Point::zero()],
19687                "Default selections on initial open",
19688            );
19689        })
19690    });
19691    editor_1.update_in(cx, |editor, window, cx| {
19692        editor.change_selections(None, window, cx, |s| {
19693            s.select_ranges(expected_ranges.clone());
19694        });
19695    });
19696
19697    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19698        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19699    });
19700    let editor_2 = workspace
19701        .update_in(cx, |workspace, window, cx| {
19702            workspace.open_path(
19703                (worktree_id, "main.rs"),
19704                Some(pane_2.downgrade()),
19705                true,
19706                window,
19707                cx,
19708            )
19709        })
19710        .unwrap()
19711        .await
19712        .downcast::<Editor>()
19713        .unwrap();
19714    pane_2.update(cx, |pane, cx| {
19715        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19716        open_editor.update(cx, |editor, cx| {
19717            assert_eq!(
19718                editor.display_text(cx),
19719                main_text,
19720                "Original main.rs text on initial open in another panel",
19721            );
19722            assert_eq!(
19723                editor
19724                    .selections
19725                    .all::<Point>(cx)
19726                    .into_iter()
19727                    .map(|s| s.range())
19728                    .collect::<Vec<_>>(),
19729                vec![Point::zero()..Point::zero()],
19730                "Default selections on initial open in another panel",
19731            );
19732        })
19733    });
19734
19735    editor_2.update_in(cx, |editor, window, cx| {
19736        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19737    });
19738
19739    let _other_editor_1 = workspace
19740        .update_in(cx, |workspace, window, cx| {
19741            workspace.open_path(
19742                (worktree_id, "lib.rs"),
19743                Some(pane_1.downgrade()),
19744                true,
19745                window,
19746                cx,
19747            )
19748        })
19749        .unwrap()
19750        .await
19751        .downcast::<Editor>()
19752        .unwrap();
19753    pane_1
19754        .update_in(cx, |pane, window, cx| {
19755            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19756                .unwrap()
19757        })
19758        .await
19759        .unwrap();
19760    drop(editor_1);
19761    pane_1.update(cx, |pane, cx| {
19762        pane.active_item()
19763            .unwrap()
19764            .downcast::<Editor>()
19765            .unwrap()
19766            .update(cx, |editor, cx| {
19767                assert_eq!(
19768                    editor.display_text(cx),
19769                    lib_text,
19770                    "Other file should be open and active",
19771                );
19772            });
19773        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19774    });
19775
19776    let _other_editor_2 = workspace
19777        .update_in(cx, |workspace, window, cx| {
19778            workspace.open_path(
19779                (worktree_id, "lib.rs"),
19780                Some(pane_2.downgrade()),
19781                true,
19782                window,
19783                cx,
19784            )
19785        })
19786        .unwrap()
19787        .await
19788        .downcast::<Editor>()
19789        .unwrap();
19790    pane_2
19791        .update_in(cx, |pane, window, cx| {
19792            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19793                .unwrap()
19794        })
19795        .await
19796        .unwrap();
19797    drop(editor_2);
19798    pane_2.update(cx, |pane, cx| {
19799        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19800        open_editor.update(cx, |editor, cx| {
19801            assert_eq!(
19802                editor.display_text(cx),
19803                lib_text,
19804                "Other file should be open and active in another panel too",
19805            );
19806        });
19807        assert_eq!(
19808            pane.items().count(),
19809            1,
19810            "No other editors should be open in another pane",
19811        );
19812    });
19813
19814    let _editor_1_reopened = workspace
19815        .update_in(cx, |workspace, window, cx| {
19816            workspace.open_path(
19817                (worktree_id, "main.rs"),
19818                Some(pane_1.downgrade()),
19819                true,
19820                window,
19821                cx,
19822            )
19823        })
19824        .unwrap()
19825        .await
19826        .downcast::<Editor>()
19827        .unwrap();
19828    let _editor_2_reopened = workspace
19829        .update_in(cx, |workspace, window, cx| {
19830            workspace.open_path(
19831                (worktree_id, "main.rs"),
19832                Some(pane_2.downgrade()),
19833                true,
19834                window,
19835                cx,
19836            )
19837        })
19838        .unwrap()
19839        .await
19840        .downcast::<Editor>()
19841        .unwrap();
19842    pane_1.update(cx, |pane, cx| {
19843        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19844        open_editor.update(cx, |editor, cx| {
19845            assert_eq!(
19846                editor.display_text(cx),
19847                main_text,
19848                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19849            );
19850            assert_eq!(
19851                editor
19852                    .selections
19853                    .all::<Point>(cx)
19854                    .into_iter()
19855                    .map(|s| s.range())
19856                    .collect::<Vec<_>>(),
19857                expected_ranges,
19858                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19859            );
19860        })
19861    });
19862    pane_2.update(cx, |pane, cx| {
19863        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19864        open_editor.update(cx, |editor, cx| {
19865            assert_eq!(
19866                editor.display_text(cx),
19867                r#"fn main() {
19868⋯rintln!("1");
19869⋯intln!("2");
19870⋯ntln!("3");
19871println!("4");
19872println!("5");
19873}"#,
19874                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19875            );
19876            assert_eq!(
19877                editor
19878                    .selections
19879                    .all::<Point>(cx)
19880                    .into_iter()
19881                    .map(|s| s.range())
19882                    .collect::<Vec<_>>(),
19883                vec![Point::zero()..Point::zero()],
19884                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19885            );
19886        })
19887    });
19888}
19889
19890#[gpui::test]
19891async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19892    init_test(cx, |_| {});
19893
19894    let fs = FakeFs::new(cx.executor());
19895    let main_text = r#"fn main() {
19896println!("1");
19897println!("2");
19898println!("3");
19899println!("4");
19900println!("5");
19901}"#;
19902    let lib_text = "mod foo {}";
19903    fs.insert_tree(
19904        path!("/a"),
19905        json!({
19906            "lib.rs": lib_text,
19907            "main.rs": main_text,
19908        }),
19909    )
19910    .await;
19911
19912    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19913    let (workspace, cx) =
19914        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19915    let worktree_id = workspace.update(cx, |workspace, cx| {
19916        workspace.project().update(cx, |project, cx| {
19917            project.worktrees(cx).next().unwrap().read(cx).id()
19918        })
19919    });
19920
19921    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19922    let editor = workspace
19923        .update_in(cx, |workspace, window, cx| {
19924            workspace.open_path(
19925                (worktree_id, "main.rs"),
19926                Some(pane.downgrade()),
19927                true,
19928                window,
19929                cx,
19930            )
19931        })
19932        .unwrap()
19933        .await
19934        .downcast::<Editor>()
19935        .unwrap();
19936    pane.update(cx, |pane, cx| {
19937        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19938        open_editor.update(cx, |editor, cx| {
19939            assert_eq!(
19940                editor.display_text(cx),
19941                main_text,
19942                "Original main.rs text on initial open",
19943            );
19944        })
19945    });
19946    editor.update_in(cx, |editor, window, cx| {
19947        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19948    });
19949
19950    cx.update_global(|store: &mut SettingsStore, cx| {
19951        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19952            s.restore_on_file_reopen = Some(false);
19953        });
19954    });
19955    editor.update_in(cx, |editor, window, cx| {
19956        editor.fold_ranges(
19957            vec![
19958                Point::new(1, 0)..Point::new(1, 1),
19959                Point::new(2, 0)..Point::new(2, 2),
19960                Point::new(3, 0)..Point::new(3, 3),
19961            ],
19962            false,
19963            window,
19964            cx,
19965        );
19966    });
19967    pane.update_in(cx, |pane, window, cx| {
19968        pane.close_all_items(&CloseAllItems::default(), window, cx)
19969            .unwrap()
19970    })
19971    .await
19972    .unwrap();
19973    pane.update(cx, |pane, _| {
19974        assert!(pane.active_item().is_none());
19975    });
19976    cx.update_global(|store: &mut SettingsStore, cx| {
19977        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19978            s.restore_on_file_reopen = Some(true);
19979        });
19980    });
19981
19982    let _editor_reopened = workspace
19983        .update_in(cx, |workspace, window, cx| {
19984            workspace.open_path(
19985                (worktree_id, "main.rs"),
19986                Some(pane.downgrade()),
19987                true,
19988                window,
19989                cx,
19990            )
19991        })
19992        .unwrap()
19993        .await
19994        .downcast::<Editor>()
19995        .unwrap();
19996    pane.update(cx, |pane, cx| {
19997        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19998        open_editor.update(cx, |editor, cx| {
19999            assert_eq!(
20000                editor.display_text(cx),
20001                main_text,
20002                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
20003            );
20004        })
20005    });
20006}
20007
20008#[gpui::test]
20009async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
20010    struct EmptyModalView {
20011        focus_handle: gpui::FocusHandle,
20012    }
20013    impl EventEmitter<DismissEvent> for EmptyModalView {}
20014    impl Render for EmptyModalView {
20015        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
20016            div()
20017        }
20018    }
20019    impl Focusable for EmptyModalView {
20020        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
20021            self.focus_handle.clone()
20022        }
20023    }
20024    impl workspace::ModalView for EmptyModalView {}
20025    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
20026        EmptyModalView {
20027            focus_handle: cx.focus_handle(),
20028        }
20029    }
20030
20031    init_test(cx, |_| {});
20032
20033    let fs = FakeFs::new(cx.executor());
20034    let project = Project::test(fs, [], cx).await;
20035    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20036    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
20037    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
20038    let editor = cx.new_window_entity(|window, cx| {
20039        Editor::new(
20040            EditorMode::full(),
20041            buffer,
20042            Some(project.clone()),
20043            window,
20044            cx,
20045        )
20046    });
20047    workspace
20048        .update(cx, |workspace, window, cx| {
20049            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
20050        })
20051        .unwrap();
20052    editor.update_in(cx, |editor, window, cx| {
20053        editor.open_context_menu(&OpenContextMenu, window, cx);
20054        assert!(editor.mouse_context_menu.is_some());
20055    });
20056    workspace
20057        .update(cx, |workspace, window, cx| {
20058            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
20059        })
20060        .unwrap();
20061    cx.read(|cx| {
20062        assert!(editor.read(cx).mouse_context_menu.is_none());
20063    });
20064}
20065
20066#[gpui::test]
20067async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
20068    init_test(cx, |_| {});
20069
20070    let fs = FakeFs::new(cx.executor());
20071    fs.insert_file(path!("/file.html"), Default::default())
20072        .await;
20073
20074    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
20075
20076    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20077    let html_language = Arc::new(Language::new(
20078        LanguageConfig {
20079            name: "HTML".into(),
20080            matcher: LanguageMatcher {
20081                path_suffixes: vec!["html".to_string()],
20082                ..LanguageMatcher::default()
20083            },
20084            brackets: BracketPairConfig {
20085                pairs: vec![BracketPair {
20086                    start: "<".into(),
20087                    end: ">".into(),
20088                    close: true,
20089                    ..Default::default()
20090                }],
20091                ..Default::default()
20092            },
20093            ..Default::default()
20094        },
20095        Some(tree_sitter_html::LANGUAGE.into()),
20096    ));
20097    language_registry.add(html_language);
20098    let mut fake_servers = language_registry.register_fake_lsp(
20099        "HTML",
20100        FakeLspAdapter {
20101            capabilities: lsp::ServerCapabilities {
20102                completion_provider: Some(lsp::CompletionOptions {
20103                    resolve_provider: Some(true),
20104                    ..Default::default()
20105                }),
20106                ..Default::default()
20107            },
20108            ..Default::default()
20109        },
20110    );
20111
20112    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
20113    let cx = &mut VisualTestContext::from_window(*workspace, cx);
20114
20115    let worktree_id = workspace
20116        .update(cx, |workspace, _window, cx| {
20117            workspace.project().update(cx, |project, cx| {
20118                project.worktrees(cx).next().unwrap().read(cx).id()
20119            })
20120        })
20121        .unwrap();
20122    project
20123        .update(cx, |project, cx| {
20124            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
20125        })
20126        .await
20127        .unwrap();
20128    let editor = workspace
20129        .update(cx, |workspace, window, cx| {
20130            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
20131        })
20132        .unwrap()
20133        .await
20134        .unwrap()
20135        .downcast::<Editor>()
20136        .unwrap();
20137
20138    let fake_server = fake_servers.next().await.unwrap();
20139    editor.update_in(cx, |editor, window, cx| {
20140        editor.set_text("<ad></ad>", window, cx);
20141        editor.change_selections(None, window, cx, |selections| {
20142            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
20143        });
20144        let Some((buffer, _)) = editor
20145            .buffer
20146            .read(cx)
20147            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
20148        else {
20149            panic!("Failed to get buffer for selection position");
20150        };
20151        let buffer = buffer.read(cx);
20152        let buffer_id = buffer.remote_id();
20153        let opening_range =
20154            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
20155        let closing_range =
20156            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
20157        let mut linked_ranges = HashMap::default();
20158        linked_ranges.insert(
20159            buffer_id,
20160            vec![(opening_range.clone(), vec![closing_range.clone()])],
20161        );
20162        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
20163    });
20164    let mut completion_handle =
20165        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
20166            Ok(Some(lsp::CompletionResponse::Array(vec![
20167                lsp::CompletionItem {
20168                    label: "head".to_string(),
20169                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20170                        lsp::InsertReplaceEdit {
20171                            new_text: "head".to_string(),
20172                            insert: lsp::Range::new(
20173                                lsp::Position::new(0, 1),
20174                                lsp::Position::new(0, 3),
20175                            ),
20176                            replace: lsp::Range::new(
20177                                lsp::Position::new(0, 1),
20178                                lsp::Position::new(0, 3),
20179                            ),
20180                        },
20181                    )),
20182                    ..Default::default()
20183                },
20184            ])))
20185        });
20186    editor.update_in(cx, |editor, window, cx| {
20187        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
20188    });
20189    cx.run_until_parked();
20190    completion_handle.next().await.unwrap();
20191    editor.update(cx, |editor, _| {
20192        assert!(
20193            editor.context_menu_visible(),
20194            "Completion menu should be visible"
20195        );
20196    });
20197    editor.update_in(cx, |editor, window, cx| {
20198        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
20199    });
20200    cx.executor().run_until_parked();
20201    editor.update(cx, |editor, cx| {
20202        assert_eq!(editor.text(cx), "<head></head>");
20203    });
20204}
20205
20206#[gpui::test]
20207async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
20208    init_test(cx, |_| {});
20209
20210    let fs = FakeFs::new(cx.executor());
20211    fs.insert_tree(
20212        path!("/root"),
20213        json!({
20214            "a": {
20215                "main.rs": "fn main() {}",
20216            },
20217            "foo": {
20218                "bar": {
20219                    "external_file.rs": "pub mod external {}",
20220                }
20221            }
20222        }),
20223    )
20224    .await;
20225
20226    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
20227    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
20228    language_registry.add(rust_lang());
20229    let _fake_servers = language_registry.register_fake_lsp(
20230        "Rust",
20231        FakeLspAdapter {
20232            ..FakeLspAdapter::default()
20233        },
20234    );
20235    let (workspace, cx) =
20236        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20237    let worktree_id = workspace.update(cx, |workspace, cx| {
20238        workspace.project().update(cx, |project, cx| {
20239            project.worktrees(cx).next().unwrap().read(cx).id()
20240        })
20241    });
20242
20243    let assert_language_servers_count =
20244        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20245            project.update(cx, |project, cx| {
20246                let current = project
20247                    .lsp_store()
20248                    .read(cx)
20249                    .as_local()
20250                    .unwrap()
20251                    .language_servers
20252                    .len();
20253                assert_eq!(expected, current, "{context}");
20254            });
20255        };
20256
20257    assert_language_servers_count(
20258        0,
20259        "No servers should be running before any file is open",
20260        cx,
20261    );
20262    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20263    let main_editor = workspace
20264        .update_in(cx, |workspace, window, cx| {
20265            workspace.open_path(
20266                (worktree_id, "main.rs"),
20267                Some(pane.downgrade()),
20268                true,
20269                window,
20270                cx,
20271            )
20272        })
20273        .unwrap()
20274        .await
20275        .downcast::<Editor>()
20276        .unwrap();
20277    pane.update(cx, |pane, cx| {
20278        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20279        open_editor.update(cx, |editor, cx| {
20280            assert_eq!(
20281                editor.display_text(cx),
20282                "fn main() {}",
20283                "Original main.rs text on initial open",
20284            );
20285        });
20286        assert_eq!(open_editor, main_editor);
20287    });
20288    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20289
20290    let external_editor = workspace
20291        .update_in(cx, |workspace, window, cx| {
20292            workspace.open_abs_path(
20293                PathBuf::from("/root/foo/bar/external_file.rs"),
20294                OpenOptions::default(),
20295                window,
20296                cx,
20297            )
20298        })
20299        .await
20300        .expect("opening external file")
20301        .downcast::<Editor>()
20302        .expect("downcasted external file's open element to editor");
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                "pub mod external {}",
20309                "External file is open now",
20310            );
20311        });
20312        assert_eq!(open_editor, external_editor);
20313    });
20314    assert_language_servers_count(
20315        1,
20316        "Second, external, *.rs file should join the existing server",
20317        cx,
20318    );
20319
20320    pane.update_in(cx, |pane, window, cx| {
20321        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20322    })
20323    .unwrap()
20324    .await
20325    .unwrap();
20326    pane.update_in(cx, |pane, window, cx| {
20327        pane.navigate_backward(window, cx);
20328    });
20329    cx.run_until_parked();
20330    pane.update(cx, |pane, cx| {
20331        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20332        open_editor.update(cx, |editor, cx| {
20333            assert_eq!(
20334                editor.display_text(cx),
20335                "pub mod external {}",
20336                "External file is open now",
20337            );
20338        });
20339    });
20340    assert_language_servers_count(
20341        1,
20342        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20343        cx,
20344    );
20345
20346    cx.update(|_, cx| {
20347        workspace::reload(&workspace::Reload::default(), cx);
20348    });
20349    assert_language_servers_count(
20350        1,
20351        "After reloading the worktree with local and external files opened, only one project should be started",
20352        cx,
20353    );
20354}
20355
20356#[gpui::test]
20357async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestAppContext) {
20358    init_test(cx, |_| {});
20359
20360    let mut cx = EditorTestContext::new(cx).await;
20361    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20362    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20363
20364    // test cursor move to start of each line on tab
20365    // for `if`, `elif`, `else`, `while`, `with` and `for`
20366    cx.set_state(indoc! {"
20367        def main():
20368        ˇ    for item in items:
20369        ˇ        while item.active:
20370        ˇ            if item.value > 10:
20371        ˇ                continue
20372        ˇ            elif item.value < 0:
20373        ˇ                break
20374        ˇ            else:
20375        ˇ                with item.context() as ctx:
20376        ˇ                    yield count
20377        ˇ        else:
20378        ˇ            log('while else')
20379        ˇ    else:
20380        ˇ        log('for else')
20381    "});
20382    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20383    cx.assert_editor_state(indoc! {"
20384        def main():
20385            ˇfor item in items:
20386                ˇwhile item.active:
20387                    ˇif item.value > 10:
20388                        ˇcontinue
20389                    ˇelif item.value < 0:
20390                        ˇbreak
20391                    ˇelse:
20392                        ˇwith item.context() as ctx:
20393                            ˇyield count
20394                ˇelse:
20395                    ˇlog('while else')
20396            ˇelse:
20397                ˇlog('for else')
20398    "});
20399    // test relative indent is preserved when tab
20400    // for `if`, `elif`, `else`, `while`, `with` and `for`
20401    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20402    cx.assert_editor_state(indoc! {"
20403        def main():
20404                ˇfor item in items:
20405                    ˇwhile item.active:
20406                        ˇif item.value > 10:
20407                            ˇcontinue
20408                        ˇelif item.value < 0:
20409                            ˇbreak
20410                        ˇelse:
20411                            ˇwith item.context() as ctx:
20412                                ˇyield count
20413                    ˇelse:
20414                        ˇlog('while else')
20415                ˇelse:
20416                    ˇlog('for else')
20417    "});
20418
20419    // test cursor move to start of each line on tab
20420    // for `try`, `except`, `else`, `finally`, `match` and `def`
20421    cx.set_state(indoc! {"
20422        def main():
20423        ˇ    try:
20424        ˇ       fetch()
20425        ˇ    except ValueError:
20426        ˇ       handle_error()
20427        ˇ    else:
20428        ˇ        match value:
20429        ˇ            case _:
20430        ˇ    finally:
20431        ˇ        def status():
20432        ˇ            return 0
20433    "});
20434    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20435    cx.assert_editor_state(indoc! {"
20436        def main():
20437            ˇtry:
20438                ˇfetch()
20439            ˇexcept ValueError:
20440                ˇhandle_error()
20441            ˇelse:
20442                ˇmatch value:
20443                    ˇcase _:
20444            ˇfinally:
20445                ˇdef status():
20446                    ˇreturn 0
20447    "});
20448    // test relative indent is preserved when tab
20449    // for `try`, `except`, `else`, `finally`, `match` and `def`
20450    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
20451    cx.assert_editor_state(indoc! {"
20452        def main():
20453                ˇtry:
20454                    ˇfetch()
20455                ˇexcept ValueError:
20456                    ˇhandle_error()
20457                ˇelse:
20458                    ˇmatch value:
20459                        ˇcase _:
20460                ˇfinally:
20461                    ˇdef status():
20462                        ˇreturn 0
20463    "});
20464}
20465
20466#[gpui::test]
20467async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
20468    init_test(cx, |_| {});
20469
20470    let mut cx = EditorTestContext::new(cx).await;
20471    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20472    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20473
20474    // test `else` auto outdents when typed inside `if` block
20475    cx.set_state(indoc! {"
20476        def main():
20477            if i == 2:
20478                return
20479                ˇ
20480    "});
20481    cx.update_editor(|editor, window, cx| {
20482        editor.handle_input("else:", window, cx);
20483    });
20484    cx.assert_editor_state(indoc! {"
20485        def main():
20486            if i == 2:
20487                return
20488            else:ˇ
20489    "});
20490
20491    // test `except` auto outdents when typed inside `try` block
20492    cx.set_state(indoc! {"
20493        def main():
20494            try:
20495                i = 2
20496                ˇ
20497    "});
20498    cx.update_editor(|editor, window, cx| {
20499        editor.handle_input("except:", window, cx);
20500    });
20501    cx.assert_editor_state(indoc! {"
20502        def main():
20503            try:
20504                i = 2
20505            except:ˇ
20506    "});
20507
20508    // test `else` auto outdents when typed inside `except` block
20509    cx.set_state(indoc! {"
20510        def main():
20511            try:
20512                i = 2
20513            except:
20514                j = 2
20515                ˇ
20516    "});
20517    cx.update_editor(|editor, window, cx| {
20518        editor.handle_input("else:", window, cx);
20519    });
20520    cx.assert_editor_state(indoc! {"
20521        def main():
20522            try:
20523                i = 2
20524            except:
20525                j = 2
20526            else:ˇ
20527    "});
20528
20529    // test `finally` auto outdents when typed inside `else` block
20530    cx.set_state(indoc! {"
20531        def main():
20532            try:
20533                i = 2
20534            except:
20535                j = 2
20536            else:
20537                k = 2
20538                ˇ
20539    "});
20540    cx.update_editor(|editor, window, cx| {
20541        editor.handle_input("finally:", window, cx);
20542    });
20543    cx.assert_editor_state(indoc! {"
20544        def main():
20545            try:
20546                i = 2
20547            except:
20548                j = 2
20549            else:
20550                k = 2
20551            finally:ˇ
20552    "});
20553
20554    // TODO: test `except` auto outdents when typed inside `try` block right after for block
20555    // cx.set_state(indoc! {"
20556    //     def main():
20557    //         try:
20558    //             for i in range(n):
20559    //                 pass
20560    //             ˇ
20561    // "});
20562    // cx.update_editor(|editor, window, cx| {
20563    //     editor.handle_input("except:", window, cx);
20564    // });
20565    // cx.assert_editor_state(indoc! {"
20566    //     def main():
20567    //         try:
20568    //             for i in range(n):
20569    //                 pass
20570    //         except:ˇ
20571    // "});
20572
20573    // TODO: test `else` auto outdents when typed inside `except` block right after for block
20574    // cx.set_state(indoc! {"
20575    //     def main():
20576    //         try:
20577    //             i = 2
20578    //         except:
20579    //             for i in range(n):
20580    //                 pass
20581    //             ˇ
20582    // "});
20583    // cx.update_editor(|editor, window, cx| {
20584    //     editor.handle_input("else:", window, cx);
20585    // });
20586    // cx.assert_editor_state(indoc! {"
20587    //     def main():
20588    //         try:
20589    //             i = 2
20590    //         except:
20591    //             for i in range(n):
20592    //                 pass
20593    //         else:ˇ
20594    // "});
20595
20596    // TODO: test `finally` auto outdents when typed inside `else` block right after for block
20597    // cx.set_state(indoc! {"
20598    //     def main():
20599    //         try:
20600    //             i = 2
20601    //         except:
20602    //             j = 2
20603    //         else:
20604    //             for i in range(n):
20605    //                 pass
20606    //             ˇ
20607    // "});
20608    // cx.update_editor(|editor, window, cx| {
20609    //     editor.handle_input("finally:", window, cx);
20610    // });
20611    // cx.assert_editor_state(indoc! {"
20612    //     def main():
20613    //         try:
20614    //             i = 2
20615    //         except:
20616    //             j = 2
20617    //         else:
20618    //             for i in range(n):
20619    //                 pass
20620    //         finally:ˇ
20621    // "});
20622
20623    // test `else` stays at correct indent when typed after `for` block
20624    cx.set_state(indoc! {"
20625        def main():
20626            for i in range(10):
20627                if i == 3:
20628                    break
20629            ˇ
20630    "});
20631    cx.update_editor(|editor, window, cx| {
20632        editor.handle_input("else:", window, cx);
20633    });
20634    cx.assert_editor_state(indoc! {"
20635        def main():
20636            for i in range(10):
20637                if i == 3:
20638                    break
20639            else:ˇ
20640    "});
20641}
20642
20643#[gpui::test]
20644async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
20645    init_test(cx, |_| {});
20646    update_test_language_settings(cx, |settings| {
20647        settings.defaults.extend_comment_on_newline = Some(false);
20648    });
20649    let mut cx = EditorTestContext::new(cx).await;
20650    let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
20651    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
20652
20653    // test correct indent after newline on comment
20654    cx.set_state(indoc! {"
20655        # COMMENT:ˇ
20656    "});
20657    cx.update_editor(|editor, window, cx| {
20658        editor.newline(&Newline, window, cx);
20659    });
20660    cx.assert_editor_state(indoc! {"
20661        # COMMENT:
20662        ˇ
20663    "});
20664
20665    // test correct indent after newline in curly brackets
20666    cx.set_state(indoc! {"
20667        {ˇ}
20668    "});
20669    cx.update_editor(|editor, window, cx| {
20670        editor.newline(&Newline, window, cx);
20671    });
20672    cx.run_until_parked();
20673    cx.assert_editor_state(indoc! {"
20674        {
20675            ˇ
20676        }
20677    "});
20678}
20679
20680fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20681    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20682    point..point
20683}
20684
20685fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20686    let (text, ranges) = marked_text_ranges(marked_text, true);
20687    assert_eq!(editor.text(cx), text);
20688    assert_eq!(
20689        editor.selections.ranges(cx),
20690        ranges,
20691        "Assert selections are {}",
20692        marked_text
20693    );
20694}
20695
20696pub fn handle_signature_help_request(
20697    cx: &mut EditorLspTestContext,
20698    mocked_response: lsp::SignatureHelp,
20699) -> impl Future<Output = ()> + use<> {
20700    let mut request =
20701        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20702            let mocked_response = mocked_response.clone();
20703            async move { Ok(Some(mocked_response)) }
20704        });
20705
20706    async move {
20707        request.next().await;
20708    }
20709}
20710
20711/// Handle completion request passing a marked string specifying where the completion
20712/// should be triggered from using '|' character, what range should be replaced, and what completions
20713/// should be returned using '<' and '>' to delimit the range.
20714///
20715/// Also see `handle_completion_request_with_insert_and_replace`.
20716#[track_caller]
20717pub fn handle_completion_request(
20718    cx: &mut EditorLspTestContext,
20719    marked_string: &str,
20720    completions: Vec<&'static str>,
20721    counter: Arc<AtomicUsize>,
20722) -> impl Future<Output = ()> {
20723    let complete_from_marker: TextRangeMarker = '|'.into();
20724    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20725    let (_, mut marked_ranges) = marked_text_ranges_by(
20726        marked_string,
20727        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20728    );
20729
20730    let complete_from_position =
20731        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20732    let replace_range =
20733        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20734
20735    let mut request =
20736        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20737            let completions = completions.clone();
20738            counter.fetch_add(1, atomic::Ordering::Release);
20739            async move {
20740                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20741                assert_eq!(
20742                    params.text_document_position.position,
20743                    complete_from_position
20744                );
20745                Ok(Some(lsp::CompletionResponse::Array(
20746                    completions
20747                        .iter()
20748                        .map(|completion_text| lsp::CompletionItem {
20749                            label: completion_text.to_string(),
20750                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20751                                range: replace_range,
20752                                new_text: completion_text.to_string(),
20753                            })),
20754                            ..Default::default()
20755                        })
20756                        .collect(),
20757                )))
20758            }
20759        });
20760
20761    async move {
20762        request.next().await;
20763    }
20764}
20765
20766/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20767/// given instead, which also contains an `insert` range.
20768///
20769/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20770/// that is, `replace_range.start..cursor_pos`.
20771pub fn handle_completion_request_with_insert_and_replace(
20772    cx: &mut EditorLspTestContext,
20773    marked_string: &str,
20774    completions: Vec<&'static str>,
20775    counter: Arc<AtomicUsize>,
20776) -> impl Future<Output = ()> {
20777    let complete_from_marker: TextRangeMarker = '|'.into();
20778    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20779    let (_, mut marked_ranges) = marked_text_ranges_by(
20780        marked_string,
20781        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20782    );
20783
20784    let complete_from_position =
20785        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20786    let replace_range =
20787        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20788
20789    let mut request =
20790        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20791            let completions = completions.clone();
20792            counter.fetch_add(1, atomic::Ordering::Release);
20793            async move {
20794                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20795                assert_eq!(
20796                    params.text_document_position.position, complete_from_position,
20797                    "marker `|` position doesn't match",
20798                );
20799                Ok(Some(lsp::CompletionResponse::Array(
20800                    completions
20801                        .iter()
20802                        .map(|completion_text| lsp::CompletionItem {
20803                            label: completion_text.to_string(),
20804                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20805                                lsp::InsertReplaceEdit {
20806                                    insert: lsp::Range {
20807                                        start: replace_range.start,
20808                                        end: complete_from_position,
20809                                    },
20810                                    replace: replace_range,
20811                                    new_text: completion_text.to_string(),
20812                                },
20813                            )),
20814                            ..Default::default()
20815                        })
20816                        .collect(),
20817                )))
20818            }
20819        });
20820
20821    async move {
20822        request.next().await;
20823    }
20824}
20825
20826fn handle_resolve_completion_request(
20827    cx: &mut EditorLspTestContext,
20828    edits: Option<Vec<(&'static str, &'static str)>>,
20829) -> impl Future<Output = ()> {
20830    let edits = edits.map(|edits| {
20831        edits
20832            .iter()
20833            .map(|(marked_string, new_text)| {
20834                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20835                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20836                lsp::TextEdit::new(replace_range, new_text.to_string())
20837            })
20838            .collect::<Vec<_>>()
20839    });
20840
20841    let mut request =
20842        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20843            let edits = edits.clone();
20844            async move {
20845                Ok(lsp::CompletionItem {
20846                    additional_text_edits: edits,
20847                    ..Default::default()
20848                })
20849            }
20850        });
20851
20852    async move {
20853        request.next().await;
20854    }
20855}
20856
20857pub(crate) fn update_test_language_settings(
20858    cx: &mut TestAppContext,
20859    f: impl Fn(&mut AllLanguageSettingsContent),
20860) {
20861    cx.update(|cx| {
20862        SettingsStore::update_global(cx, |store, cx| {
20863            store.update_user_settings::<AllLanguageSettings>(cx, f);
20864        });
20865    });
20866}
20867
20868pub(crate) fn update_test_project_settings(
20869    cx: &mut TestAppContext,
20870    f: impl Fn(&mut ProjectSettings),
20871) {
20872    cx.update(|cx| {
20873        SettingsStore::update_global(cx, |store, cx| {
20874            store.update_user_settings::<ProjectSettings>(cx, f);
20875        });
20876    });
20877}
20878
20879pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
20880    cx.update(|cx| {
20881        assets::Assets.load_test_fonts(cx);
20882        let store = SettingsStore::test(cx);
20883        cx.set_global(store);
20884        theme::init(theme::LoadThemes::JustBase, cx);
20885        release_channel::init(SemanticVersion::default(), cx);
20886        client::init_settings(cx);
20887        language::init(cx);
20888        Project::init_settings(cx);
20889        workspace::init_settings(cx);
20890        crate::init(cx);
20891    });
20892
20893    update_test_language_settings(cx, f);
20894}
20895
20896#[track_caller]
20897fn assert_hunk_revert(
20898    not_reverted_text_with_selections: &str,
20899    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
20900    expected_reverted_text_with_selections: &str,
20901    base_text: &str,
20902    cx: &mut EditorLspTestContext,
20903) {
20904    cx.set_state(not_reverted_text_with_selections);
20905    cx.set_head_text(base_text);
20906    cx.executor().run_until_parked();
20907
20908    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
20909        let snapshot = editor.snapshot(window, cx);
20910        let reverted_hunk_statuses = snapshot
20911            .buffer_snapshot
20912            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
20913            .map(|hunk| hunk.status().kind)
20914            .collect::<Vec<_>>();
20915
20916        editor.git_restore(&Default::default(), window, cx);
20917        reverted_hunk_statuses
20918    });
20919    cx.executor().run_until_parked();
20920    cx.assert_editor_state(expected_reverted_text_with_selections);
20921    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20922}