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};
   30use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   31use lsp::CompletionParams;
   32use multi_buffer::{IndentGuide, PathKey};
   33use parking_lot::Mutex;
   34use pretty_assertions::{assert_eq, assert_ne};
   35use project::{
   36    FakeFs,
   37    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   38    project_settings::{LspSettings, ProjectSettings},
   39};
   40use serde_json::{self, json};
   41use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   42use std::{
   43    iter,
   44    sync::atomic::{self, AtomicUsize},
   45};
   46use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   47use text::ToPoint as _;
   48use unindent::Unindent;
   49use util::{
   50    assert_set_eq, path,
   51    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   52    uri,
   53};
   54use workspace::{
   55    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   56    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   57};
   58
   59#[gpui::test]
   60fn test_edit_events(cx: &mut TestAppContext) {
   61    init_test(cx, |_| {});
   62
   63    let buffer = cx.new(|cx| {
   64        let mut buffer = language::Buffer::local("123456", cx);
   65        buffer.set_group_interval(Duration::from_secs(1));
   66        buffer
   67    });
   68
   69    let events = Rc::new(RefCell::new(Vec::new()));
   70    let editor1 = cx.add_window({
   71        let events = events.clone();
   72        |window, cx| {
   73            let entity = cx.entity().clone();
   74            cx.subscribe_in(
   75                &entity,
   76                window,
   77                move |_, _, event: &EditorEvent, _, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor1", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, window, cx)
   87        }
   88    });
   89
   90    let editor2 = cx.add_window({
   91        let events = events.clone();
   92        |window, cx| {
   93            cx.subscribe_in(
   94                &cx.entity().clone(),
   95                window,
   96                move |_, _, event: &EditorEvent, _, _| match event {
   97                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   98                    EditorEvent::BufferEdited => {
   99                        events.borrow_mut().push(("editor2", "buffer edited"))
  100                    }
  101                    _ => {}
  102                },
  103            )
  104            .detach();
  105            Editor::for_buffer(buffer.clone(), None, window, cx)
  106        }
  107    });
  108
  109    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  110
  111    // Mutating editor 1 will emit an `Edited` event only for that editor.
  112    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  113    assert_eq!(
  114        mem::take(&mut *events.borrow_mut()),
  115        [
  116            ("editor1", "edited"),
  117            ("editor1", "buffer edited"),
  118            ("editor2", "buffer edited"),
  119        ]
  120    );
  121
  122    // Mutating editor 2 will emit an `Edited` event only for that editor.
  123    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  124    assert_eq!(
  125        mem::take(&mut *events.borrow_mut()),
  126        [
  127            ("editor2", "edited"),
  128            ("editor1", "buffer edited"),
  129            ("editor2", "buffer edited"),
  130        ]
  131    );
  132
  133    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  134    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  135    assert_eq!(
  136        mem::take(&mut *events.borrow_mut()),
  137        [
  138            ("editor1", "edited"),
  139            ("editor1", "buffer edited"),
  140            ("editor2", "buffer edited"),
  141        ]
  142    );
  143
  144    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  145    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  146    assert_eq!(
  147        mem::take(&mut *events.borrow_mut()),
  148        [
  149            ("editor1", "edited"),
  150            ("editor1", "buffer edited"),
  151            ("editor2", "buffer edited"),
  152        ]
  153    );
  154
  155    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  156    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  157    assert_eq!(
  158        mem::take(&mut *events.borrow_mut()),
  159        [
  160            ("editor2", "edited"),
  161            ("editor1", "buffer edited"),
  162            ("editor2", "buffer edited"),
  163        ]
  164    );
  165
  166    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  167    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  168    assert_eq!(
  169        mem::take(&mut *events.borrow_mut()),
  170        [
  171            ("editor2", "edited"),
  172            ("editor1", "buffer edited"),
  173            ("editor2", "buffer edited"),
  174        ]
  175    );
  176
  177    // No event is emitted when the mutation is a no-op.
  178    _ = editor2.update(cx, |editor, window, cx| {
  179        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  180
  181        editor.backspace(&Backspace, window, cx);
  182    });
  183    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  184}
  185
  186#[gpui::test]
  187fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  188    init_test(cx, |_| {});
  189
  190    let mut now = Instant::now();
  191    let group_interval = Duration::from_millis(1);
  192    let buffer = cx.new(|cx| {
  193        let mut buf = language::Buffer::local("123456", cx);
  194        buf.set_group_interval(group_interval);
  195        buf
  196    });
  197    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  198    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  199
  200    _ = editor.update(cx, |editor, window, cx| {
  201        editor.start_transaction_at(now, window, cx);
  202        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  203
  204        editor.insert("cd", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cd56");
  207        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  208
  209        editor.start_transaction_at(now, window, cx);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  211        editor.insert("e", window, cx);
  212        editor.end_transaction_at(now, cx);
  213        assert_eq!(editor.text(cx), "12cde6");
  214        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  215
  216        now += group_interval + Duration::from_millis(1);
  217        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  218
  219        // Simulate an edit in another editor
  220        buffer.update(cx, |buffer, cx| {
  221            buffer.start_transaction_at(now, cx);
  222            buffer.edit([(0..1, "a")], None, cx);
  223            buffer.edit([(1..1, "b")], None, cx);
  224            buffer.end_transaction_at(now, cx);
  225        });
  226
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  229
  230        // Last transaction happened past the group interval in a different editor.
  231        // Undo it individually and don't restore selections.
  232        editor.undo(&Undo, window, cx);
  233        assert_eq!(editor.text(cx), "12cde6");
  234        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  235
  236        // First two transactions happened within the group interval in this editor.
  237        // Undo them together and restore selections.
  238        editor.undo(&Undo, window, cx);
  239        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  240        assert_eq!(editor.text(cx), "123456");
  241        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  242
  243        // Redo the first two transactions together.
  244        editor.redo(&Redo, window, cx);
  245        assert_eq!(editor.text(cx), "12cde6");
  246        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  247
  248        // Redo the last transaction on its own.
  249        editor.redo(&Redo, window, cx);
  250        assert_eq!(editor.text(cx), "ab2cde6");
  251        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  252
  253        // Test empty transactions.
  254        editor.start_transaction_at(now, window, cx);
  255        editor.end_transaction_at(now, cx);
  256        editor.undo(&Undo, window, cx);
  257        assert_eq!(editor.text(cx), "12cde6");
  258    });
  259}
  260
  261#[gpui::test]
  262fn test_ime_composition(cx: &mut TestAppContext) {
  263    init_test(cx, |_| {});
  264
  265    let buffer = cx.new(|cx| {
  266        let mut buffer = language::Buffer::local("abcde", cx);
  267        // Ensure automatic grouping doesn't occur.
  268        buffer.set_group_interval(Duration::ZERO);
  269        buffer
  270    });
  271
  272    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  273    cx.add_window(|window, cx| {
  274        let mut editor = build_editor(buffer.clone(), window, cx);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  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        assert_eq!(editor.text(cx), "äbcde");
  281        assert_eq!(
  282            editor.marked_text_ranges(cx),
  283            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  284        );
  285
  286        // Finalize IME composition.
  287        editor.replace_text_in_range(None, "ā", window, cx);
  288        assert_eq!(editor.text(cx), "ābcde");
  289        assert_eq!(editor.marked_text_ranges(cx), None);
  290
  291        // IME composition edits are grouped and are undone/redone at once.
  292        editor.undo(&Default::default(), window, cx);
  293        assert_eq!(editor.text(cx), "abcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295        editor.redo(&Default::default(), window, cx);
  296        assert_eq!(editor.text(cx), "ābcde");
  297        assert_eq!(editor.marked_text_ranges(cx), None);
  298
  299        // Start a new IME composition.
  300        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  301        assert_eq!(
  302            editor.marked_text_ranges(cx),
  303            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  304        );
  305
  306        // Undoing during an IME composition cancels it.
  307        editor.undo(&Default::default(), window, cx);
  308        assert_eq!(editor.text(cx), "ābcde");
  309        assert_eq!(editor.marked_text_ranges(cx), None);
  310
  311        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  312        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  313        assert_eq!(editor.text(cx), "ābcdè");
  314        assert_eq!(
  315            editor.marked_text_ranges(cx),
  316            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  317        );
  318
  319        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  320        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  321        assert_eq!(editor.text(cx), "ābcdę");
  322        assert_eq!(editor.marked_text_ranges(cx), None);
  323
  324        // Start a new IME composition with multiple cursors.
  325        editor.change_selections(None, window, cx, |s| {
  326            s.select_ranges([
  327                OffsetUtf16(1)..OffsetUtf16(1),
  328                OffsetUtf16(3)..OffsetUtf16(3),
  329                OffsetUtf16(5)..OffsetUtf16(5),
  330            ])
  331        });
  332        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  333        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  334        assert_eq!(
  335            editor.marked_text_ranges(cx),
  336            Some(vec![
  337                OffsetUtf16(0)..OffsetUtf16(3),
  338                OffsetUtf16(4)..OffsetUtf16(7),
  339                OffsetUtf16(8)..OffsetUtf16(11)
  340            ])
  341        );
  342
  343        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  344        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  345        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  346        assert_eq!(
  347            editor.marked_text_ranges(cx),
  348            Some(vec![
  349                OffsetUtf16(1)..OffsetUtf16(2),
  350                OffsetUtf16(5)..OffsetUtf16(6),
  351                OffsetUtf16(9)..OffsetUtf16(10)
  352            ])
  353        );
  354
  355        // Finalize IME composition with multiple cursors.
  356        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  357        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  358        assert_eq!(editor.marked_text_ranges(cx), None);
  359
  360        editor
  361    });
  362}
  363
  364#[gpui::test]
  365fn test_selection_with_mouse(cx: &mut TestAppContext) {
  366    init_test(cx, |_| {});
  367
  368    let editor = cx.add_window(|window, cx| {
  369        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  370        build_editor(buffer, window, cx)
  371    });
  372
  373    _ = editor.update(cx, |editor, window, cx| {
  374        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  375    });
  376    assert_eq!(
  377        editor
  378            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  379            .unwrap(),
  380        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  381    );
  382
  383    _ = editor.update(cx, |editor, window, cx| {
  384        editor.update_selection(
  385            DisplayPoint::new(DisplayRow(3), 3),
  386            0,
  387            gpui::Point::<f32>::default(),
  388            window,
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  398    );
  399
  400    _ = editor.update(cx, |editor, window, cx| {
  401        editor.update_selection(
  402            DisplayPoint::new(DisplayRow(1), 1),
  403            0,
  404            gpui::Point::<f32>::default(),
  405            window,
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |editor, window, cx| {
  418        editor.end_selection(window, cx);
  419        editor.update_selection(
  420            DisplayPoint::new(DisplayRow(3), 3),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            window,
  424            cx,
  425        );
  426    });
  427
  428    assert_eq!(
  429        editor
  430            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  431            .unwrap(),
  432        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  433    );
  434
  435    _ = editor.update(cx, |editor, window, cx| {
  436        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  437        editor.update_selection(
  438            DisplayPoint::new(DisplayRow(0), 0),
  439            0,
  440            gpui::Point::<f32>::default(),
  441            window,
  442            cx,
  443        );
  444    });
  445
  446    assert_eq!(
  447        editor
  448            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  449            .unwrap(),
  450        [
  451            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  452            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  453        ]
  454    );
  455
  456    _ = editor.update(cx, |editor, window, cx| {
  457        editor.end_selection(window, cx);
  458    });
  459
  460    assert_eq!(
  461        editor
  462            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  463            .unwrap(),
  464        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  465    );
  466}
  467
  468#[gpui::test]
  469fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  470    init_test(cx, |_| {});
  471
  472    let editor = cx.add_window(|window, cx| {
  473        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  474        build_editor(buffer, window, cx)
  475    });
  476
  477    _ = editor.update(cx, |editor, window, cx| {
  478        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  479    });
  480
  481    _ = editor.update(cx, |editor, window, cx| {
  482        editor.end_selection(window, cx);
  483    });
  484
  485    _ = editor.update(cx, |editor, window, cx| {
  486        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  487    });
  488
  489    _ = editor.update(cx, |editor, window, cx| {
  490        editor.end_selection(window, cx);
  491    });
  492
  493    assert_eq!(
  494        editor
  495            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  496            .unwrap(),
  497        [
  498            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  499            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  500        ]
  501    );
  502
  503    _ = editor.update(cx, |editor, window, cx| {
  504        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  505    });
  506
  507    _ = editor.update(cx, |editor, window, cx| {
  508        editor.end_selection(window, cx);
  509    });
  510
  511    assert_eq!(
  512        editor
  513            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  514            .unwrap(),
  515        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  516    );
  517}
  518
  519#[gpui::test]
  520fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  521    init_test(cx, |_| {});
  522
  523    let editor = cx.add_window(|window, cx| {
  524        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  525        build_editor(buffer, window, cx)
  526    });
  527
  528    _ = editor.update(cx, |editor, window, cx| {
  529        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  530        assert_eq!(
  531            editor.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  533        );
  534    });
  535
  536    _ = editor.update(cx, |editor, window, cx| {
  537        editor.update_selection(
  538            DisplayPoint::new(DisplayRow(3), 3),
  539            0,
  540            gpui::Point::<f32>::default(),
  541            window,
  542            cx,
  543        );
  544        assert_eq!(
  545            editor.selections.display_ranges(cx),
  546            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  547        );
  548    });
  549
  550    _ = editor.update(cx, |editor, window, cx| {
  551        editor.cancel(&Cancel, window, cx);
  552        editor.update_selection(
  553            DisplayPoint::new(DisplayRow(1), 1),
  554            0,
  555            gpui::Point::<f32>::default(),
  556            window,
  557            cx,
  558        );
  559        assert_eq!(
  560            editor.selections.display_ranges(cx),
  561            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  562        );
  563    });
  564}
  565
  566#[gpui::test]
  567fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  568    init_test(cx, |_| {});
  569
  570    let editor = cx.add_window(|window, cx| {
  571        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  572        build_editor(buffer, window, cx)
  573    });
  574
  575    _ = editor.update(cx, |editor, window, cx| {
  576        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  577        assert_eq!(
  578            editor.selections.display_ranges(cx),
  579            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  580        );
  581
  582        editor.move_down(&Default::default(), window, cx);
  583        assert_eq!(
  584            editor.selections.display_ranges(cx),
  585            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  586        );
  587
  588        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  589        assert_eq!(
  590            editor.selections.display_ranges(cx),
  591            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  592        );
  593
  594        editor.move_up(&Default::default(), window, cx);
  595        assert_eq!(
  596            editor.selections.display_ranges(cx),
  597            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  598        );
  599    });
  600}
  601
  602#[gpui::test]
  603fn test_clone(cx: &mut TestAppContext) {
  604    init_test(cx, |_| {});
  605
  606    let (text, selection_ranges) = marked_text_ranges(
  607        indoc! {"
  608            one
  609            two
  610            threeˇ
  611            four
  612            fiveˇ
  613        "},
  614        true,
  615    );
  616
  617    let editor = cx.add_window(|window, cx| {
  618        let buffer = MultiBuffer::build_simple(&text, cx);
  619        build_editor(buffer, window, cx)
  620    });
  621
  622    _ = editor.update(cx, |editor, window, cx| {
  623        editor.change_selections(None, window, cx, |s| {
  624            s.select_ranges(selection_ranges.clone())
  625        });
  626        editor.fold_creases(
  627            vec![
  628                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  629                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  630            ],
  631            true,
  632            window,
  633            cx,
  634        );
  635    });
  636
  637    let cloned_editor = editor
  638        .update(cx, |editor, _, cx| {
  639            cx.open_window(Default::default(), |window, cx| {
  640                cx.new(|cx| editor.clone(window, cx))
  641            })
  642        })
  643        .unwrap()
  644        .unwrap();
  645
  646    let snapshot = editor
  647        .update(cx, |e, window, cx| e.snapshot(window, cx))
  648        .unwrap();
  649    let cloned_snapshot = cloned_editor
  650        .update(cx, |e, window, cx| e.snapshot(window, cx))
  651        .unwrap();
  652
  653    assert_eq!(
  654        cloned_editor
  655            .update(cx, |e, _, cx| e.display_text(cx))
  656            .unwrap(),
  657        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  658    );
  659    assert_eq!(
  660        cloned_snapshot
  661            .folds_in_range(0..text.len())
  662            .collect::<Vec<_>>(),
  663        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  664    );
  665    assert_set_eq!(
  666        cloned_editor
  667            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  668            .unwrap(),
  669        editor
  670            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  671            .unwrap()
  672    );
  673    assert_set_eq!(
  674        cloned_editor
  675            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  676            .unwrap(),
  677        editor
  678            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  679            .unwrap()
  680    );
  681}
  682
  683#[gpui::test]
  684async fn test_navigation_history(cx: &mut TestAppContext) {
  685    init_test(cx, |_| {});
  686
  687    use workspace::item::Item;
  688
  689    let fs = FakeFs::new(cx.executor());
  690    let project = Project::test(fs, [], cx).await;
  691    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  692    let pane = workspace
  693        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  694        .unwrap();
  695
  696    _ = workspace.update(cx, |_v, window, cx| {
  697        cx.new(|cx| {
  698            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  699            let mut editor = build_editor(buffer.clone(), window, cx);
  700            let handle = cx.entity();
  701            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  702
  703            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  704                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  705            }
  706
  707            // Move the cursor a small distance.
  708            // Nothing is added to the navigation history.
  709            editor.change_selections(None, window, cx, |s| {
  710                s.select_display_ranges([
  711                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  712                ])
  713            });
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  717                ])
  718            });
  719            assert!(pop_history(&mut editor, cx).is_none());
  720
  721            // Move the cursor a large distance.
  722            // The history can jump back to the previous position.
  723            editor.change_selections(None, window, cx, |s| {
  724                s.select_display_ranges([
  725                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  726                ])
  727            });
  728            let nav_entry = pop_history(&mut editor, cx).unwrap();
  729            editor.navigate(nav_entry.data.unwrap(), window, cx);
  730            assert_eq!(nav_entry.item.id(), cx.entity_id());
  731            assert_eq!(
  732                editor.selections.display_ranges(cx),
  733                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  734            );
  735            assert!(pop_history(&mut editor, cx).is_none());
  736
  737            // Move the cursor a small distance via the mouse.
  738            // Nothing is added to the navigation history.
  739            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  740            editor.end_selection(window, cx);
  741            assert_eq!(
  742                editor.selections.display_ranges(cx),
  743                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  744            );
  745            assert!(pop_history(&mut editor, cx).is_none());
  746
  747            // Move the cursor a large distance via the mouse.
  748            // The history can jump back to the previous position.
  749            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  750            editor.end_selection(window, cx);
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  754            );
  755            let nav_entry = pop_history(&mut editor, cx).unwrap();
  756            editor.navigate(nav_entry.data.unwrap(), window, cx);
  757            assert_eq!(nav_entry.item.id(), cx.entity_id());
  758            assert_eq!(
  759                editor.selections.display_ranges(cx),
  760                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  761            );
  762            assert!(pop_history(&mut editor, cx).is_none());
  763
  764            // Set scroll position to check later
  765            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  766            let original_scroll_position = editor.scroll_manager.anchor();
  767
  768            // Jump to the end of the document and adjust scroll
  769            editor.move_to_end(&MoveToEnd, window, cx);
  770            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  771            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  772
  773            let nav_entry = pop_history(&mut editor, cx).unwrap();
  774            editor.navigate(nav_entry.data.unwrap(), window, cx);
  775            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  776
  777            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  778            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  779            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  780            let invalid_point = Point::new(9999, 0);
  781            editor.navigate(
  782                Box::new(NavigationData {
  783                    cursor_anchor: invalid_anchor,
  784                    cursor_position: invalid_point,
  785                    scroll_anchor: ScrollAnchor {
  786                        anchor: invalid_anchor,
  787                        offset: Default::default(),
  788                    },
  789                    scroll_top_row: invalid_point.row,
  790                }),
  791                window,
  792                cx,
  793            );
  794            assert_eq!(
  795                editor.selections.display_ranges(cx),
  796                &[editor.max_point(cx)..editor.max_point(cx)]
  797            );
  798            assert_eq!(
  799                editor.scroll_position(cx),
  800                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  801            );
  802
  803            editor
  804        })
  805    });
  806}
  807
  808#[gpui::test]
  809fn test_cancel(cx: &mut TestAppContext) {
  810    init_test(cx, |_| {});
  811
  812    let editor = cx.add_window(|window, cx| {
  813        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  814        build_editor(buffer, window, cx)
  815    });
  816
  817    _ = editor.update(cx, |editor, window, cx| {
  818        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  819        editor.update_selection(
  820            DisplayPoint::new(DisplayRow(1), 1),
  821            0,
  822            gpui::Point::<f32>::default(),
  823            window,
  824            cx,
  825        );
  826        editor.end_selection(window, cx);
  827
  828        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  829        editor.update_selection(
  830            DisplayPoint::new(DisplayRow(0), 3),
  831            0,
  832            gpui::Point::<f32>::default(),
  833            window,
  834            cx,
  835        );
  836        editor.end_selection(window, cx);
  837        assert_eq!(
  838            editor.selections.display_ranges(cx),
  839            [
  840                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  841                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  842            ]
  843        );
  844    });
  845
  846    _ = editor.update(cx, |editor, window, cx| {
  847        editor.cancel(&Cancel, window, cx);
  848        assert_eq!(
  849            editor.selections.display_ranges(cx),
  850            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  851        );
  852    });
  853
  854    _ = editor.update(cx, |editor, window, cx| {
  855        editor.cancel(&Cancel, window, cx);
  856        assert_eq!(
  857            editor.selections.display_ranges(cx),
  858            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  859        );
  860    });
  861}
  862
  863#[gpui::test]
  864fn test_fold_action(cx: &mut TestAppContext) {
  865    init_test(cx, |_| {});
  866
  867    let editor = cx.add_window(|window, cx| {
  868        let buffer = MultiBuffer::build_simple(
  869            &"
  870                impl Foo {
  871                    // Hello!
  872
  873                    fn a() {
  874                        1
  875                    }
  876
  877                    fn b() {
  878                        2
  879                    }
  880
  881                    fn c() {
  882                        3
  883                    }
  884                }
  885            "
  886            .unindent(),
  887            cx,
  888        );
  889        build_editor(buffer.clone(), window, cx)
  890    });
  891
  892    _ = editor.update(cx, |editor, window, cx| {
  893        editor.change_selections(None, window, cx, |s| {
  894            s.select_display_ranges([
  895                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  896            ]);
  897        });
  898        editor.fold(&Fold, window, cx);
  899        assert_eq!(
  900            editor.display_text(cx),
  901            "
  902                impl Foo {
  903                    // Hello!
  904
  905                    fn a() {
  906                        1
  907                    }
  908
  909                    fn b() {⋯
  910                    }
  911
  912                    fn c() {⋯
  913                    }
  914                }
  915            "
  916            .unindent(),
  917        );
  918
  919        editor.fold(&Fold, window, cx);
  920        assert_eq!(
  921            editor.display_text(cx),
  922            "
  923                impl Foo {⋯
  924                }
  925            "
  926            .unindent(),
  927        );
  928
  929        editor.unfold_lines(&UnfoldLines, window, cx);
  930        assert_eq!(
  931            editor.display_text(cx),
  932            "
  933                impl Foo {
  934                    // Hello!
  935
  936                    fn a() {
  937                        1
  938                    }
  939
  940                    fn b() {⋯
  941                    }
  942
  943                    fn c() {⋯
  944                    }
  945                }
  946            "
  947            .unindent(),
  948        );
  949
  950        editor.unfold_lines(&UnfoldLines, window, cx);
  951        assert_eq!(
  952            editor.display_text(cx),
  953            editor.buffer.read(cx).read(cx).text()
  954        );
  955    });
  956}
  957
  958#[gpui::test]
  959fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  960    init_test(cx, |_| {});
  961
  962    let editor = cx.add_window(|window, cx| {
  963        let buffer = MultiBuffer::build_simple(
  964            &"
  965                class Foo:
  966                    # Hello!
  967
  968                    def a():
  969                        print(1)
  970
  971                    def b():
  972                        print(2)
  973
  974                    def c():
  975                        print(3)
  976            "
  977            .unindent(),
  978            cx,
  979        );
  980        build_editor(buffer.clone(), window, cx)
  981    });
  982
  983    _ = editor.update(cx, |editor, window, cx| {
  984        editor.change_selections(None, window, cx, |s| {
  985            s.select_display_ranges([
  986                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  987            ]);
  988        });
  989        editor.fold(&Fold, window, cx);
  990        assert_eq!(
  991            editor.display_text(cx),
  992            "
  993                class Foo:
  994                    # Hello!
  995
  996                    def a():
  997                        print(1)
  998
  999                    def b():⋯
 1000
 1001                    def c():⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.fold(&Fold, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:⋯
 1011            "
 1012            .unindent(),
 1013        );
 1014
 1015        editor.unfold_lines(&UnfoldLines, window, cx);
 1016        assert_eq!(
 1017            editor.display_text(cx),
 1018            "
 1019                class Foo:
 1020                    # Hello!
 1021
 1022                    def a():
 1023                        print(1)
 1024
 1025                    def b():⋯
 1026
 1027                    def c():⋯
 1028            "
 1029            .unindent(),
 1030        );
 1031
 1032        editor.unfold_lines(&UnfoldLines, window, cx);
 1033        assert_eq!(
 1034            editor.display_text(cx),
 1035            editor.buffer.read(cx).read(cx).text()
 1036        );
 1037    });
 1038}
 1039
 1040#[gpui::test]
 1041fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1042    init_test(cx, |_| {});
 1043
 1044    let editor = cx.add_window(|window, cx| {
 1045        let buffer = MultiBuffer::build_simple(
 1046            &"
 1047                class Foo:
 1048                    # Hello!
 1049
 1050                    def a():
 1051                        print(1)
 1052
 1053                    def b():
 1054                        print(2)
 1055
 1056
 1057                    def c():
 1058                        print(3)
 1059
 1060
 1061            "
 1062            .unindent(),
 1063            cx,
 1064        );
 1065        build_editor(buffer.clone(), window, cx)
 1066    });
 1067
 1068    _ = editor.update(cx, |editor, window, cx| {
 1069        editor.change_selections(None, window, cx, |s| {
 1070            s.select_display_ranges([
 1071                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1072            ]);
 1073        });
 1074        editor.fold(&Fold, window, cx);
 1075        assert_eq!(
 1076            editor.display_text(cx),
 1077            "
 1078                class Foo:
 1079                    # Hello!
 1080
 1081                    def a():
 1082                        print(1)
 1083
 1084                    def b():⋯
 1085
 1086
 1087                    def c():⋯
 1088
 1089
 1090            "
 1091            .unindent(),
 1092        );
 1093
 1094        editor.fold(&Fold, window, cx);
 1095        assert_eq!(
 1096            editor.display_text(cx),
 1097            "
 1098                class Foo:⋯
 1099
 1100
 1101            "
 1102            .unindent(),
 1103        );
 1104
 1105        editor.unfold_lines(&UnfoldLines, window, cx);
 1106        assert_eq!(
 1107            editor.display_text(cx),
 1108            "
 1109                class Foo:
 1110                    # Hello!
 1111
 1112                    def a():
 1113                        print(1)
 1114
 1115                    def b():⋯
 1116
 1117
 1118                    def c():⋯
 1119
 1120
 1121            "
 1122            .unindent(),
 1123        );
 1124
 1125        editor.unfold_lines(&UnfoldLines, window, cx);
 1126        assert_eq!(
 1127            editor.display_text(cx),
 1128            editor.buffer.read(cx).read(cx).text()
 1129        );
 1130    });
 1131}
 1132
 1133#[gpui::test]
 1134fn test_fold_at_level(cx: &mut TestAppContext) {
 1135    init_test(cx, |_| {});
 1136
 1137    let editor = cx.add_window(|window, cx| {
 1138        let buffer = MultiBuffer::build_simple(
 1139            &"
 1140                class Foo:
 1141                    # Hello!
 1142
 1143                    def a():
 1144                        print(1)
 1145
 1146                    def b():
 1147                        print(2)
 1148
 1149
 1150                class Bar:
 1151                    # World!
 1152
 1153                    def a():
 1154                        print(1)
 1155
 1156                    def b():
 1157                        print(2)
 1158
 1159
 1160            "
 1161            .unindent(),
 1162            cx,
 1163        );
 1164        build_editor(buffer.clone(), window, cx)
 1165    });
 1166
 1167    _ = editor.update(cx, |editor, window, cx| {
 1168        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1169        assert_eq!(
 1170            editor.display_text(cx),
 1171            "
 1172                class Foo:
 1173                    # Hello!
 1174
 1175                    def a():⋯
 1176
 1177                    def b():⋯
 1178
 1179
 1180                class Bar:
 1181                    # World!
 1182
 1183                    def a():⋯
 1184
 1185                    def b():⋯
 1186
 1187
 1188            "
 1189            .unindent(),
 1190        );
 1191
 1192        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1193        assert_eq!(
 1194            editor.display_text(cx),
 1195            "
 1196                class Foo:⋯
 1197
 1198
 1199                class Bar:⋯
 1200
 1201
 1202            "
 1203            .unindent(),
 1204        );
 1205
 1206        editor.unfold_all(&UnfoldAll, window, cx);
 1207        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1208        assert_eq!(
 1209            editor.display_text(cx),
 1210            "
 1211                class Foo:
 1212                    # Hello!
 1213
 1214                    def a():
 1215                        print(1)
 1216
 1217                    def b():
 1218                        print(2)
 1219
 1220
 1221                class Bar:
 1222                    # World!
 1223
 1224                    def a():
 1225                        print(1)
 1226
 1227                    def b():
 1228                        print(2)
 1229
 1230
 1231            "
 1232            .unindent(),
 1233        );
 1234
 1235        assert_eq!(
 1236            editor.display_text(cx),
 1237            editor.buffer.read(cx).read(cx).text()
 1238        );
 1239    });
 1240}
 1241
 1242#[gpui::test]
 1243fn test_move_cursor(cx: &mut TestAppContext) {
 1244    init_test(cx, |_| {});
 1245
 1246    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1247    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1248
 1249    buffer.update(cx, |buffer, cx| {
 1250        buffer.edit(
 1251            vec![
 1252                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1253                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1254            ],
 1255            None,
 1256            cx,
 1257        );
 1258    });
 1259    _ = editor.update(cx, |editor, window, cx| {
 1260        assert_eq!(
 1261            editor.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1263        );
 1264
 1265        editor.move_down(&MoveDown, window, cx);
 1266        assert_eq!(
 1267            editor.selections.display_ranges(cx),
 1268            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1269        );
 1270
 1271        editor.move_right(&MoveRight, window, cx);
 1272        assert_eq!(
 1273            editor.selections.display_ranges(cx),
 1274            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1275        );
 1276
 1277        editor.move_left(&MoveLeft, window, cx);
 1278        assert_eq!(
 1279            editor.selections.display_ranges(cx),
 1280            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1281        );
 1282
 1283        editor.move_up(&MoveUp, window, cx);
 1284        assert_eq!(
 1285            editor.selections.display_ranges(cx),
 1286            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1287        );
 1288
 1289        editor.move_to_end(&MoveToEnd, window, cx);
 1290        assert_eq!(
 1291            editor.selections.display_ranges(cx),
 1292            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1293        );
 1294
 1295        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1296        assert_eq!(
 1297            editor.selections.display_ranges(cx),
 1298            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1299        );
 1300
 1301        editor.change_selections(None, window, cx, |s| {
 1302            s.select_display_ranges([
 1303                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1304            ]);
 1305        });
 1306        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1307        assert_eq!(
 1308            editor.selections.display_ranges(cx),
 1309            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1310        );
 1311
 1312        editor.select_to_end(&SelectToEnd, window, cx);
 1313        assert_eq!(
 1314            editor.selections.display_ranges(cx),
 1315            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1316        );
 1317    });
 1318}
 1319
 1320#[gpui::test]
 1321fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1322    init_test(cx, |_| {});
 1323
 1324    let editor = cx.add_window(|window, cx| {
 1325        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1326        build_editor(buffer.clone(), window, cx)
 1327    });
 1328
 1329    assert_eq!('🟥'.len_utf8(), 4);
 1330    assert_eq!('α'.len_utf8(), 2);
 1331
 1332    _ = editor.update(cx, |editor, window, cx| {
 1333        editor.fold_creases(
 1334            vec![
 1335                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1338            ],
 1339            true,
 1340            window,
 1341            cx,
 1342        );
 1343        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1344
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧".len())]
 1354        );
 1355        editor.move_right(&MoveRight, window, cx);
 1356        assert_eq!(
 1357            editor.selections.display_ranges(cx),
 1358            &[empty_range(0, "🟥🟧⋯".len())]
 1359        );
 1360
 1361        editor.move_down(&MoveDown, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "ab".len())]
 1375        );
 1376        editor.move_left(&MoveLeft, window, cx);
 1377        assert_eq!(
 1378            editor.selections.display_ranges(cx),
 1379            &[empty_range(1, "a".len())]
 1380        );
 1381
 1382        editor.move_down(&MoveDown, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "α".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯".len())]
 1396        );
 1397        editor.move_right(&MoveRight, window, cx);
 1398        assert_eq!(
 1399            editor.selections.display_ranges(cx),
 1400            &[empty_range(2, "αβ⋯ε".len())]
 1401        );
 1402
 1403        editor.move_up(&MoveUp, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(1, "ab⋯e".len())]
 1407        );
 1408        editor.move_down(&MoveDown, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(2, "αβ⋯ε".len())]
 1412        );
 1413        editor.move_up(&MoveUp, window, cx);
 1414        assert_eq!(
 1415            editor.selections.display_ranges(cx),
 1416            &[empty_range(1, "ab⋯e".len())]
 1417        );
 1418
 1419        editor.move_up(&MoveUp, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥🟧".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "🟥".len())]
 1428        );
 1429        editor.move_left(&MoveLeft, window, cx);
 1430        assert_eq!(
 1431            editor.selections.display_ranges(cx),
 1432            &[empty_range(0, "".len())]
 1433        );
 1434    });
 1435}
 1436
 1437#[gpui::test]
 1438fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1439    init_test(cx, |_| {});
 1440
 1441    let editor = cx.add_window(|window, cx| {
 1442        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1443        build_editor(buffer.clone(), window, cx)
 1444    });
 1445    _ = editor.update(cx, |editor, window, cx| {
 1446        editor.change_selections(None, window, cx, |s| {
 1447            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1448        });
 1449
 1450        // moving above start of document should move selection to start of document,
 1451        // but the next move down should still be at the original goal_x
 1452        editor.move_up(&MoveUp, window, cx);
 1453        assert_eq!(
 1454            editor.selections.display_ranges(cx),
 1455            &[empty_range(0, "".len())]
 1456        );
 1457
 1458        editor.move_down(&MoveDown, window, cx);
 1459        assert_eq!(
 1460            editor.selections.display_ranges(cx),
 1461            &[empty_range(1, "abcd".len())]
 1462        );
 1463
 1464        editor.move_down(&MoveDown, window, cx);
 1465        assert_eq!(
 1466            editor.selections.display_ranges(cx),
 1467            &[empty_range(2, "αβγ".len())]
 1468        );
 1469
 1470        editor.move_down(&MoveDown, window, cx);
 1471        assert_eq!(
 1472            editor.selections.display_ranges(cx),
 1473            &[empty_range(3, "abcd".len())]
 1474        );
 1475
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1480        );
 1481
 1482        // moving past end of document should not change goal_x
 1483        editor.move_down(&MoveDown, window, cx);
 1484        assert_eq!(
 1485            editor.selections.display_ranges(cx),
 1486            &[empty_range(5, "".len())]
 1487        );
 1488
 1489        editor.move_down(&MoveDown, window, cx);
 1490        assert_eq!(
 1491            editor.selections.display_ranges(cx),
 1492            &[empty_range(5, "".len())]
 1493        );
 1494
 1495        editor.move_up(&MoveUp, window, cx);
 1496        assert_eq!(
 1497            editor.selections.display_ranges(cx),
 1498            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1499        );
 1500
 1501        editor.move_up(&MoveUp, window, cx);
 1502        assert_eq!(
 1503            editor.selections.display_ranges(cx),
 1504            &[empty_range(3, "abcd".len())]
 1505        );
 1506
 1507        editor.move_up(&MoveUp, window, cx);
 1508        assert_eq!(
 1509            editor.selections.display_ranges(cx),
 1510            &[empty_range(2, "αβγ".len())]
 1511        );
 1512    });
 1513}
 1514
 1515#[gpui::test]
 1516fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1517    init_test(cx, |_| {});
 1518    let move_to_beg = MoveToBeginningOfLine {
 1519        stop_at_soft_wraps: true,
 1520        stop_at_indent: true,
 1521    };
 1522
 1523    let delete_to_beg = DeleteToBeginningOfLine {
 1524        stop_at_indent: false,
 1525    };
 1526
 1527    let move_to_end = MoveToEndOfLine {
 1528        stop_at_soft_wraps: true,
 1529    };
 1530
 1531    let editor = cx.add_window(|window, cx| {
 1532        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1533        build_editor(buffer, window, cx)
 1534    });
 1535    _ = editor.update(cx, |editor, window, cx| {
 1536        editor.change_selections(None, window, cx, |s| {
 1537            s.select_display_ranges([
 1538                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1539                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1540            ]);
 1541        });
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1572                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1573            ]
 1574        );
 1575    });
 1576
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    // Moving to the end of line again is a no-op.
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_to_end_of_line(&move_to_end, window, cx);
 1591        assert_eq!(
 1592            editor.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1595                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = editor.update(cx, |editor, window, cx| {
 1601        editor.move_left(&MoveLeft, window, cx);
 1602        editor.select_to_beginning_of_line(
 1603            &SelectToBeginningOfLine {
 1604                stop_at_soft_wraps: true,
 1605                stop_at_indent: true,
 1606            },
 1607            window,
 1608            cx,
 1609        );
 1610        assert_eq!(
 1611            editor.selections.display_ranges(cx),
 1612            &[
 1613                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1614                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1615            ]
 1616        );
 1617    });
 1618
 1619    _ = editor.update(cx, |editor, window, cx| {
 1620        editor.select_to_beginning_of_line(
 1621            &SelectToBeginningOfLine {
 1622                stop_at_soft_wraps: true,
 1623                stop_at_indent: true,
 1624            },
 1625            window,
 1626            cx,
 1627        );
 1628        assert_eq!(
 1629            editor.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636
 1637    _ = editor.update(cx, |editor, window, cx| {
 1638        editor.select_to_beginning_of_line(
 1639            &SelectToBeginningOfLine {
 1640                stop_at_soft_wraps: true,
 1641                stop_at_indent: true,
 1642            },
 1643            window,
 1644            cx,
 1645        );
 1646        assert_eq!(
 1647            editor.selections.display_ranges(cx),
 1648            &[
 1649                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1650                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1651            ]
 1652        );
 1653    });
 1654
 1655    _ = editor.update(cx, |editor, window, cx| {
 1656        editor.select_to_end_of_line(
 1657            &SelectToEndOfLine {
 1658                stop_at_soft_wraps: true,
 1659            },
 1660            window,
 1661            cx,
 1662        );
 1663        assert_eq!(
 1664            editor.selections.display_ranges(cx),
 1665            &[
 1666                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1667                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1668            ]
 1669        );
 1670    });
 1671
 1672    _ = editor.update(cx, |editor, window, cx| {
 1673        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1674        assert_eq!(editor.display_text(cx), "ab\n  de");
 1675        assert_eq!(
 1676            editor.selections.display_ranges(cx),
 1677            &[
 1678                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1679                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1680            ]
 1681        );
 1682    });
 1683
 1684    _ = editor.update(cx, |editor, window, cx| {
 1685        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1686        assert_eq!(editor.display_text(cx), "\n");
 1687        assert_eq!(
 1688            editor.selections.display_ranges(cx),
 1689            &[
 1690                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1691                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1692            ]
 1693        );
 1694    });
 1695}
 1696
 1697#[gpui::test]
 1698fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1699    init_test(cx, |_| {});
 1700    let move_to_beg = MoveToBeginningOfLine {
 1701        stop_at_soft_wraps: false,
 1702        stop_at_indent: false,
 1703    };
 1704
 1705    let move_to_end = MoveToEndOfLine {
 1706        stop_at_soft_wraps: false,
 1707    };
 1708
 1709    let editor = cx.add_window(|window, cx| {
 1710        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1711        build_editor(buffer, window, cx)
 1712    });
 1713
 1714    _ = editor.update(cx, |editor, window, cx| {
 1715        editor.set_wrap_width(Some(140.0.into()), cx);
 1716
 1717        // We expect the following lines after wrapping
 1718        // ```
 1719        // thequickbrownfox
 1720        // jumpedoverthelazydo
 1721        // gs
 1722        // ```
 1723        // The final `gs` was soft-wrapped onto a new line.
 1724        assert_eq!(
 1725            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1726            editor.display_text(cx),
 1727        );
 1728
 1729        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1730        // Start the cursor at the `k` on the first line
 1731        editor.change_selections(None, window, cx, |s| {
 1732            s.select_display_ranges([
 1733                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1734            ]);
 1735        });
 1736
 1737        // Moving to the beginning of the line should put us at the beginning of the line.
 1738        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1739        assert_eq!(
 1740            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1741            editor.selections.display_ranges(cx)
 1742        );
 1743
 1744        // Moving to the end of the line should put us at the end of the line.
 1745        editor.move_to_end_of_line(&move_to_end, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1752        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1753        editor.change_selections(None, window, cx, |s| {
 1754            s.select_display_ranges([
 1755                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1756            ]);
 1757        });
 1758
 1759        // Moving to the beginning of the line should put us at the start of the second line of
 1760        // display text, i.e., the `j`.
 1761        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the beginning of the line again should be a no-op.
 1768        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773
 1774        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1775        // next display line.
 1776        editor.move_to_end_of_line(&move_to_end, window, cx);
 1777        assert_eq!(
 1778            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1779            editor.selections.display_ranges(cx)
 1780        );
 1781
 1782        // Moving to the end of the line again should be a no-op.
 1783        editor.move_to_end_of_line(&move_to_end, window, cx);
 1784        assert_eq!(
 1785            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1786            editor.selections.display_ranges(cx)
 1787        );
 1788    });
 1789}
 1790
 1791#[gpui::test]
 1792fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1793    init_test(cx, |_| {});
 1794
 1795    let move_to_beg = MoveToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let select_to_beg = SelectToBeginningOfLine {
 1801        stop_at_soft_wraps: true,
 1802        stop_at_indent: true,
 1803    };
 1804
 1805    let delete_to_beg = DeleteToBeginningOfLine {
 1806        stop_at_indent: true,
 1807    };
 1808
 1809    let move_to_end = MoveToEndOfLine {
 1810        stop_at_soft_wraps: false,
 1811    };
 1812
 1813    let editor = cx.add_window(|window, cx| {
 1814        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1815        build_editor(buffer, window, cx)
 1816    });
 1817
 1818    _ = editor.update(cx, |editor, window, cx| {
 1819        editor.change_selections(None, window, cx, |s| {
 1820            s.select_display_ranges([
 1821                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1822                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1823            ]);
 1824        });
 1825
 1826        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1827        // and the second cursor at the first non-whitespace character in the line.
 1828        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1829        assert_eq!(
 1830            editor.selections.display_ranges(cx),
 1831            &[
 1832                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1834            ]
 1835        );
 1836
 1837        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1838        // and should move the second cursor to the beginning of the line.
 1839        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1840        assert_eq!(
 1841            editor.selections.display_ranges(cx),
 1842            &[
 1843                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1844                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1845            ]
 1846        );
 1847
 1848        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1849        // and should move the second cursor back to the first non-whitespace character in the line.
 1850        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1851        assert_eq!(
 1852            editor.selections.display_ranges(cx),
 1853            &[
 1854                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1855                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1856            ]
 1857        );
 1858
 1859        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1860        // and to the first non-whitespace character in the line for the second cursor.
 1861        editor.move_to_end_of_line(&move_to_end, window, cx);
 1862        editor.move_left(&MoveLeft, window, cx);
 1863        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1864        assert_eq!(
 1865            editor.selections.display_ranges(cx),
 1866            &[
 1867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1868                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1869            ]
 1870        );
 1871
 1872        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1873        // and should select to the beginning of the line for the second cursor.
 1874        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[
 1878                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1879                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1880            ]
 1881        );
 1882
 1883        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1884        // and should delete to the first non-whitespace character in the line for the second cursor.
 1885        editor.move_to_end_of_line(&move_to_end, window, cx);
 1886        editor.move_left(&MoveLeft, window, cx);
 1887        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1888        assert_eq!(editor.text(cx), "c\n  f");
 1889    });
 1890}
 1891
 1892#[gpui::test]
 1893fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1894    init_test(cx, |_| {});
 1895
 1896    let editor = cx.add_window(|window, cx| {
 1897        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1898        build_editor(buffer, window, cx)
 1899    });
 1900    _ = editor.update(cx, |editor, window, cx| {
 1901        editor.change_selections(None, window, cx, |s| {
 1902            s.select_display_ranges([
 1903                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1904                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1905            ])
 1906        });
 1907
 1908        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1909        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1918        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1921        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1924        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1925
 1926        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1927        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1928
 1929        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1930        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1931
 1932        editor.move_right(&MoveRight, window, cx);
 1933        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1934        assert_selection_ranges(
 1935            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1936            editor,
 1937            cx,
 1938        );
 1939
 1940        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1941        assert_selection_ranges(
 1942            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1943            editor,
 1944            cx,
 1945        );
 1946
 1947        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1948        assert_selection_ranges(
 1949            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1950            editor,
 1951            cx,
 1952        );
 1953    });
 1954}
 1955
 1956#[gpui::test]
 1957fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1958    init_test(cx, |_| {});
 1959
 1960    let editor = cx.add_window(|window, cx| {
 1961        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1962        build_editor(buffer, window, cx)
 1963    });
 1964
 1965    _ = editor.update(cx, |editor, window, cx| {
 1966        editor.set_wrap_width(Some(140.0.into()), cx);
 1967        assert_eq!(
 1968            editor.display_text(cx),
 1969            "use one::{\n    two::three::\n    four::five\n};"
 1970        );
 1971
 1972        editor.change_selections(None, window, cx, |s| {
 1973            s.select_display_ranges([
 1974                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1975            ]);
 1976        });
 1977
 1978        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1979        assert_eq!(
 1980            editor.selections.display_ranges(cx),
 1981            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1982        );
 1983
 1984        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1985        assert_eq!(
 1986            editor.selections.display_ranges(cx),
 1987            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1988        );
 1989
 1990        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1991        assert_eq!(
 1992            editor.selections.display_ranges(cx),
 1993            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1994        );
 1995
 1996        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1997        assert_eq!(
 1998            editor.selections.display_ranges(cx),
 1999            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2000        );
 2001
 2002        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2003        assert_eq!(
 2004            editor.selections.display_ranges(cx),
 2005            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2006        );
 2007
 2008        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2009        assert_eq!(
 2010            editor.selections.display_ranges(cx),
 2011            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2012        );
 2013    });
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2029
 2030    cx.set_state(
 2031        &r#"ˇone
 2032        two
 2033
 2034        three
 2035        fourˇ
 2036        five
 2037
 2038        six"#
 2039            .unindent(),
 2040    );
 2041
 2042    cx.update_editor(|editor, window, cx| {
 2043        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2044    });
 2045    cx.assert_editor_state(
 2046        &r#"one
 2047        two
 2048        ˇ
 2049        three
 2050        four
 2051        five
 2052        ˇ
 2053        six"#
 2054            .unindent(),
 2055    );
 2056
 2057    cx.update_editor(|editor, window, cx| {
 2058        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2059    });
 2060    cx.assert_editor_state(
 2061        &r#"one
 2062        two
 2063
 2064        three
 2065        four
 2066        five
 2067        ˇ
 2068        sixˇ"#
 2069            .unindent(),
 2070    );
 2071
 2072    cx.update_editor(|editor, window, cx| {
 2073        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2074    });
 2075    cx.assert_editor_state(
 2076        &r#"one
 2077        two
 2078
 2079        three
 2080        four
 2081        five
 2082
 2083        sixˇ"#
 2084            .unindent(),
 2085    );
 2086
 2087    cx.update_editor(|editor, window, cx| {
 2088        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2089    });
 2090    cx.assert_editor_state(
 2091        &r#"one
 2092        two
 2093
 2094        three
 2095        four
 2096        five
 2097        ˇ
 2098        six"#
 2099            .unindent(),
 2100    );
 2101
 2102    cx.update_editor(|editor, window, cx| {
 2103        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2104    });
 2105    cx.assert_editor_state(
 2106        &r#"one
 2107        two
 2108        ˇ
 2109        three
 2110        four
 2111        five
 2112
 2113        six"#
 2114            .unindent(),
 2115    );
 2116
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2119    });
 2120    cx.assert_editor_state(
 2121        &r#"ˇone
 2122        two
 2123
 2124        three
 2125        four
 2126        five
 2127
 2128        six"#
 2129            .unindent(),
 2130    );
 2131}
 2132
 2133#[gpui::test]
 2134async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136    let mut cx = EditorTestContext::new(cx).await;
 2137    let line_height = cx.editor(|editor, window, _| {
 2138        editor
 2139            .style()
 2140            .unwrap()
 2141            .text
 2142            .line_height_in_pixels(window.rem_size())
 2143    });
 2144    let window = cx.window;
 2145    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2146
 2147    cx.set_state(
 2148        r#"ˇone
 2149        two
 2150        three
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#,
 2159    );
 2160
 2161    cx.update_editor(|editor, window, cx| {
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 0.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 3.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 6.)
 2175        );
 2176        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2177        assert_eq!(
 2178            editor.snapshot(window, cx).scroll_position(),
 2179            gpui::Point::new(0., 3.)
 2180        );
 2181
 2182        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 1.)
 2186        );
 2187        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2188        assert_eq!(
 2189            editor.snapshot(window, cx).scroll_position(),
 2190            gpui::Point::new(0., 3.)
 2191        );
 2192    });
 2193}
 2194
 2195#[gpui::test]
 2196async fn test_autoscroll(cx: &mut TestAppContext) {
 2197    init_test(cx, |_| {});
 2198    let mut cx = EditorTestContext::new(cx).await;
 2199
 2200    let line_height = cx.update_editor(|editor, window, cx| {
 2201        editor.set_vertical_scroll_margin(2, cx);
 2202        editor
 2203            .style()
 2204            .unwrap()
 2205            .text
 2206            .line_height_in_pixels(window.rem_size())
 2207    });
 2208    let window = cx.window;
 2209    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2210
 2211    cx.set_state(
 2212        r#"ˇone
 2213            two
 2214            three
 2215            four
 2216            five
 2217            six
 2218            seven
 2219            eight
 2220            nine
 2221            ten
 2222        "#,
 2223    );
 2224    cx.update_editor(|editor, window, cx| {
 2225        assert_eq!(
 2226            editor.snapshot(window, cx).scroll_position(),
 2227            gpui::Point::new(0., 0.0)
 2228        );
 2229    });
 2230
 2231    // Add a cursor below the visible area. Since both cursors cannot fit
 2232    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2233    // allows the vertical scroll margin below that cursor.
 2234    cx.update_editor(|editor, window, cx| {
 2235        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2236            selections.select_ranges([
 2237                Point::new(0, 0)..Point::new(0, 0),
 2238                Point::new(6, 0)..Point::new(6, 0),
 2239            ]);
 2240        })
 2241    });
 2242    cx.update_editor(|editor, window, cx| {
 2243        assert_eq!(
 2244            editor.snapshot(window, cx).scroll_position(),
 2245            gpui::Point::new(0., 3.0)
 2246        );
 2247    });
 2248
 2249    // Move down. The editor cursor scrolls down to track the newest cursor.
 2250    cx.update_editor(|editor, window, cx| {
 2251        editor.move_down(&Default::default(), window, cx);
 2252    });
 2253    cx.update_editor(|editor, window, cx| {
 2254        assert_eq!(
 2255            editor.snapshot(window, cx).scroll_position(),
 2256            gpui::Point::new(0., 4.0)
 2257        );
 2258    });
 2259
 2260    // Add a cursor above the visible area. Since both cursors fit on screen,
 2261    // the editor scrolls to show both.
 2262    cx.update_editor(|editor, window, cx| {
 2263        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2264            selections.select_ranges([
 2265                Point::new(1, 0)..Point::new(1, 0),
 2266                Point::new(6, 0)..Point::new(6, 0),
 2267            ]);
 2268        })
 2269    });
 2270    cx.update_editor(|editor, window, cx| {
 2271        assert_eq!(
 2272            editor.snapshot(window, cx).scroll_position(),
 2273            gpui::Point::new(0., 1.0)
 2274        );
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281    let mut cx = EditorTestContext::new(cx).await;
 2282
 2283    let line_height = cx.editor(|editor, window, _cx| {
 2284        editor
 2285            .style()
 2286            .unwrap()
 2287            .text
 2288            .line_height_in_pixels(window.rem_size())
 2289    });
 2290    let window = cx.window;
 2291    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2292    cx.set_state(
 2293        &r#"
 2294        ˇone
 2295        two
 2296        threeˇ
 2297        four
 2298        five
 2299        six
 2300        seven
 2301        eight
 2302        nine
 2303        ten
 2304        "#
 2305        .unindent(),
 2306    );
 2307
 2308    cx.update_editor(|editor, window, cx| {
 2309        editor.move_page_down(&MovePageDown::default(), window, cx)
 2310    });
 2311    cx.assert_editor_state(
 2312        &r#"
 2313        one
 2314        two
 2315        three
 2316        ˇfour
 2317        five
 2318        sixˇ
 2319        seven
 2320        eight
 2321        nine
 2322        ten
 2323        "#
 2324        .unindent(),
 2325    );
 2326
 2327    cx.update_editor(|editor, window, cx| {
 2328        editor.move_page_down(&MovePageDown::default(), window, cx)
 2329    });
 2330    cx.assert_editor_state(
 2331        &r#"
 2332        one
 2333        two
 2334        three
 2335        four
 2336        five
 2337        six
 2338        ˇseven
 2339        eight
 2340        nineˇ
 2341        ten
 2342        "#
 2343        .unindent(),
 2344    );
 2345
 2346    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2347    cx.assert_editor_state(
 2348        &r#"
 2349        one
 2350        two
 2351        three
 2352        ˇfour
 2353        five
 2354        sixˇ
 2355        seven
 2356        eight
 2357        nine
 2358        ten
 2359        "#
 2360        .unindent(),
 2361    );
 2362
 2363    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2364    cx.assert_editor_state(
 2365        &r#"
 2366        ˇone
 2367        two
 2368        threeˇ
 2369        four
 2370        five
 2371        six
 2372        seven
 2373        eight
 2374        nine
 2375        ten
 2376        "#
 2377        .unindent(),
 2378    );
 2379
 2380    // Test select collapsing
 2381    cx.update_editor(|editor, window, cx| {
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385    });
 2386    cx.assert_editor_state(
 2387        &r#"
 2388        one
 2389        two
 2390        three
 2391        four
 2392        five
 2393        six
 2394        seven
 2395        eight
 2396        nine
 2397        ˇten
 2398        ˇ"#
 2399        .unindent(),
 2400    );
 2401}
 2402
 2403#[gpui::test]
 2404async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2405    init_test(cx, |_| {});
 2406    let mut cx = EditorTestContext::new(cx).await;
 2407    cx.set_state("one «two threeˇ» four");
 2408    cx.update_editor(|editor, window, cx| {
 2409        editor.delete_to_beginning_of_line(
 2410            &DeleteToBeginningOfLine {
 2411                stop_at_indent: false,
 2412            },
 2413            window,
 2414            cx,
 2415        );
 2416        assert_eq!(editor.text(cx), " four");
 2417    });
 2418}
 2419
 2420#[gpui::test]
 2421fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2422    init_test(cx, |_| {});
 2423
 2424    let editor = cx.add_window(|window, cx| {
 2425        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2426        build_editor(buffer.clone(), window, cx)
 2427    });
 2428
 2429    _ = editor.update(cx, |editor, window, cx| {
 2430        editor.change_selections(None, window, cx, |s| {
 2431            s.select_display_ranges([
 2432                // an empty selection - the preceding word fragment is deleted
 2433                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2434                // characters selected - they are deleted
 2435                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2436            ])
 2437        });
 2438        editor.delete_to_previous_word_start(
 2439            &DeleteToPreviousWordStart {
 2440                ignore_newlines: false,
 2441            },
 2442            window,
 2443            cx,
 2444        );
 2445        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2446    });
 2447
 2448    _ = editor.update(cx, |editor, window, cx| {
 2449        editor.change_selections(None, window, cx, |s| {
 2450            s.select_display_ranges([
 2451                // an empty selection - the following word fragment is deleted
 2452                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2453                // characters selected - they are deleted
 2454                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2455            ])
 2456        });
 2457        editor.delete_to_next_word_end(
 2458            &DeleteToNextWordEnd {
 2459                ignore_newlines: false,
 2460            },
 2461            window,
 2462            cx,
 2463        );
 2464        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2465    });
 2466}
 2467
 2468#[gpui::test]
 2469fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2470    init_test(cx, |_| {});
 2471
 2472    let editor = cx.add_window(|window, cx| {
 2473        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2474        build_editor(buffer.clone(), window, cx)
 2475    });
 2476    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2477        ignore_newlines: false,
 2478    };
 2479    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2480        ignore_newlines: true,
 2481    };
 2482
 2483    _ = editor.update(cx, |editor, window, cx| {
 2484        editor.change_selections(None, window, cx, |s| {
 2485            s.select_display_ranges([
 2486                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2487            ])
 2488        });
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2501    });
 2502}
 2503
 2504#[gpui::test]
 2505fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2506    init_test(cx, |_| {});
 2507
 2508    let editor = cx.add_window(|window, cx| {
 2509        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2510        build_editor(buffer.clone(), window, cx)
 2511    });
 2512    let del_to_next_word_end = DeleteToNextWordEnd {
 2513        ignore_newlines: false,
 2514    };
 2515    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2516        ignore_newlines: true,
 2517    };
 2518
 2519    _ = editor.update(cx, |editor, window, cx| {
 2520        editor.change_selections(None, window, cx, |s| {
 2521            s.select_display_ranges([
 2522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2523            ])
 2524        });
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "one\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "\n   two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(
 2537            editor.buffer.read(cx).read(cx).text(),
 2538            "two\nthree\n   four"
 2539        );
 2540        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2541        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2542        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2546    });
 2547}
 2548
 2549#[gpui::test]
 2550fn test_newline(cx: &mut TestAppContext) {
 2551    init_test(cx, |_| {});
 2552
 2553    let editor = cx.add_window(|window, cx| {
 2554        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2555        build_editor(buffer.clone(), window, cx)
 2556    });
 2557
 2558    _ = editor.update(cx, |editor, window, cx| {
 2559        editor.change_selections(None, window, cx, |s| {
 2560            s.select_display_ranges([
 2561                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2563                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2564            ])
 2565        });
 2566
 2567        editor.newline(&Newline, window, cx);
 2568        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2569    });
 2570}
 2571
 2572#[gpui::test]
 2573fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2574    init_test(cx, |_| {});
 2575
 2576    let editor = cx.add_window(|window, cx| {
 2577        let buffer = MultiBuffer::build_simple(
 2578            "
 2579                a
 2580                b(
 2581                    X
 2582                )
 2583                c(
 2584                    X
 2585                )
 2586            "
 2587            .unindent()
 2588            .as_str(),
 2589            cx,
 2590        );
 2591        let mut editor = build_editor(buffer.clone(), window, cx);
 2592        editor.change_selections(None, window, cx, |s| {
 2593            s.select_ranges([
 2594                Point::new(2, 4)..Point::new(2, 5),
 2595                Point::new(5, 4)..Point::new(5, 5),
 2596            ])
 2597        });
 2598        editor
 2599    });
 2600
 2601    _ = editor.update(cx, |editor, window, cx| {
 2602        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2603        editor.buffer.update(cx, |buffer, cx| {
 2604            buffer.edit(
 2605                [
 2606                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2607                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2608                ],
 2609                None,
 2610                cx,
 2611            );
 2612            assert_eq!(
 2613                buffer.read(cx).text(),
 2614                "
 2615                    a
 2616                    b()
 2617                    c()
 2618                "
 2619                .unindent()
 2620            );
 2621        });
 2622        assert_eq!(
 2623            editor.selections.ranges(cx),
 2624            &[
 2625                Point::new(1, 2)..Point::new(1, 2),
 2626                Point::new(2, 2)..Point::new(2, 2),
 2627            ],
 2628        );
 2629
 2630        editor.newline(&Newline, window, cx);
 2631        assert_eq!(
 2632            editor.text(cx),
 2633            "
 2634                a
 2635                b(
 2636                )
 2637                c(
 2638                )
 2639            "
 2640            .unindent()
 2641        );
 2642
 2643        // The selections are moved after the inserted newlines
 2644        assert_eq!(
 2645            editor.selections.ranges(cx),
 2646            &[
 2647                Point::new(2, 0)..Point::new(2, 0),
 2648                Point::new(4, 0)..Point::new(4, 0),
 2649            ],
 2650        );
 2651    });
 2652}
 2653
 2654#[gpui::test]
 2655async fn test_newline_above(cx: &mut TestAppContext) {
 2656    init_test(cx, |settings| {
 2657        settings.defaults.tab_size = NonZeroU32::new(4)
 2658    });
 2659
 2660    let language = Arc::new(
 2661        Language::new(
 2662            LanguageConfig::default(),
 2663            Some(tree_sitter_rust::LANGUAGE.into()),
 2664        )
 2665        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2666        .unwrap(),
 2667    );
 2668
 2669    let mut cx = EditorTestContext::new(cx).await;
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671    cx.set_state(indoc! {"
 2672        const a: ˇA = (
 2673 2674                «const_functionˇ»(ˇ),
 2675                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2676 2677        ˇ);ˇ
 2678    "});
 2679
 2680    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2681    cx.assert_editor_state(indoc! {"
 2682        ˇ
 2683        const a: A = (
 2684            ˇ
 2685            (
 2686                ˇ
 2687                ˇ
 2688                const_function(),
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                something_else,
 2694                ˇ
 2695            )
 2696            ˇ
 2697            ˇ
 2698        );
 2699    "});
 2700}
 2701
 2702#[gpui::test]
 2703async fn test_newline_below(cx: &mut TestAppContext) {
 2704    init_test(cx, |settings| {
 2705        settings.defaults.tab_size = NonZeroU32::new(4)
 2706    });
 2707
 2708    let language = Arc::new(
 2709        Language::new(
 2710            LanguageConfig::default(),
 2711            Some(tree_sitter_rust::LANGUAGE.into()),
 2712        )
 2713        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2714        .unwrap(),
 2715    );
 2716
 2717    let mut cx = EditorTestContext::new(cx).await;
 2718    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2719    cx.set_state(indoc! {"
 2720        const a: ˇA = (
 2721 2722                «const_functionˇ»(ˇ),
 2723                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2724 2725        ˇ);ˇ
 2726    "});
 2727
 2728    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2729    cx.assert_editor_state(indoc! {"
 2730        const a: A = (
 2731            ˇ
 2732            (
 2733                ˇ
 2734                const_function(),
 2735                ˇ
 2736                ˇ
 2737                something_else,
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742            )
 2743            ˇ
 2744        );
 2745        ˇ
 2746        ˇ
 2747    "});
 2748}
 2749
 2750#[gpui::test]
 2751async fn test_newline_comments(cx: &mut TestAppContext) {
 2752    init_test(cx, |settings| {
 2753        settings.defaults.tab_size = NonZeroU32::new(4)
 2754    });
 2755
 2756    let language = Arc::new(Language::new(
 2757        LanguageConfig {
 2758            line_comments: vec!["//".into()],
 2759            ..LanguageConfig::default()
 2760        },
 2761        None,
 2762    ));
 2763    {
 2764        let mut cx = EditorTestContext::new(cx).await;
 2765        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2766        cx.set_state(indoc! {"
 2767        // Fooˇ
 2768    "});
 2769
 2770        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2771        cx.assert_editor_state(indoc! {"
 2772        // Foo
 2773        //ˇ
 2774    "});
 2775        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2776        cx.set_state(indoc! {"
 2777        ˇ// Foo
 2778    "});
 2779        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2780        cx.assert_editor_state(indoc! {"
 2781
 2782        ˇ// Foo
 2783    "});
 2784    }
 2785    // Ensure that comment continuations can be disabled.
 2786    update_test_language_settings(cx, |settings| {
 2787        settings.defaults.extend_comment_on_newline = Some(false);
 2788    });
 2789    let mut cx = EditorTestContext::new(cx).await;
 2790    cx.set_state(indoc! {"
 2791        // Fooˇ
 2792    "});
 2793    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794    cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        ˇ
 2797    "});
 2798}
 2799
 2800#[gpui::test]
 2801fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2802    init_test(cx, |_| {});
 2803
 2804    let editor = cx.add_window(|window, cx| {
 2805        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2806        let mut editor = build_editor(buffer.clone(), window, cx);
 2807        editor.change_selections(None, window, cx, |s| {
 2808            s.select_ranges([3..4, 11..12, 19..20])
 2809        });
 2810        editor
 2811    });
 2812
 2813    _ = editor.update(cx, |editor, window, cx| {
 2814        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2815        editor.buffer.update(cx, |buffer, cx| {
 2816            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2817            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2818        });
 2819        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2820
 2821        editor.insert("Z", window, cx);
 2822        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2823
 2824        // The selections are moved after the inserted characters
 2825        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2826    });
 2827}
 2828
 2829#[gpui::test]
 2830async fn test_tab(cx: &mut TestAppContext) {
 2831    init_test(cx, |settings| {
 2832        settings.defaults.tab_size = NonZeroU32::new(3)
 2833    });
 2834
 2835    let mut cx = EditorTestContext::new(cx).await;
 2836    cx.set_state(indoc! {"
 2837        ˇabˇc
 2838        ˇ🏀ˇ🏀ˇefg
 2839 2840    "});
 2841    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843           ˇab ˇc
 2844           ˇ🏀  ˇ🏀  ˇefg
 2845        d  ˇ
 2846    "});
 2847
 2848    cx.set_state(indoc! {"
 2849        a
 2850        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        a
 2855           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2856    "});
 2857}
 2858
 2859#[gpui::test]
 2860async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2861    init_test(cx, |_| {});
 2862
 2863    let mut cx = EditorTestContext::new(cx).await;
 2864    let language = Arc::new(
 2865        Language::new(
 2866            LanguageConfig::default(),
 2867            Some(tree_sitter_rust::LANGUAGE.into()),
 2868        )
 2869        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2870        .unwrap(),
 2871    );
 2872    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2873
 2874    // when all cursors are to the left of the suggested indent, then auto-indent all.
 2875    cx.set_state(indoc! {"
 2876        const a: B = (
 2877            c(
 2878        ˇ
 2879        ˇ    )
 2880        );
 2881    "});
 2882    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884        const a: B = (
 2885            c(
 2886                ˇ
 2887            ˇ)
 2888        );
 2889    "});
 2890
 2891    // cursors that are already at the suggested indent level do not move
 2892    // until other cursors that are to the left of the suggested indent
 2893    // auto-indent.
 2894    cx.set_state(indoc! {"
 2895        ˇ
 2896        const a: B = (
 2897            c(
 2898                d(
 2899        ˇ
 2900                )
 2901        ˇ
 2902        ˇ    )
 2903        );
 2904    "});
 2905    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2906    cx.assert_editor_state(indoc! {"
 2907        ˇ
 2908        const a: B = (
 2909            c(
 2910                d(
 2911                    ˇ
 2912                )
 2913                ˇ
 2914            ˇ)
 2915        );
 2916    "});
 2917    // once all multi-cursors are at the suggested
 2918    // indent level, they all insert a soft tab together.
 2919    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2920    cx.assert_editor_state(indoc! {"
 2921            ˇ
 2922        const a: B = (
 2923            c(
 2924                d(
 2925                        ˇ
 2926                )
 2927                    ˇ
 2928                ˇ)
 2929        );
 2930    "});
 2931
 2932    // handle auto-indent when there are multiple cursors on the same line
 2933    cx.set_state(indoc! {"
 2934        const a: B = (
 2935            c(
 2936        ˇ    ˇ
 2937        ˇ    )
 2938        );
 2939    "});
 2940    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2941    cx.assert_editor_state(indoc! {"
 2942        const a: B = (
 2943            c(
 2944                ˇ
 2945            ˇ)
 2946        );
 2947    "});
 2948}
 2949
 2950#[gpui::test]
 2951async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2952    init_test(cx, |settings| {
 2953        settings.defaults.tab_size = NonZeroU32::new(3)
 2954    });
 2955
 2956    let mut cx = EditorTestContext::new(cx).await;
 2957    cx.set_state(indoc! {"
 2958         ˇ
 2959        \t ˇ
 2960        \t  ˇ
 2961        \t   ˇ
 2962         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2963    "});
 2964
 2965    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2966    cx.assert_editor_state(indoc! {"
 2967           ˇ
 2968        \t   ˇ
 2969        \t   ˇ
 2970        \t      ˇ
 2971         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2972    "});
 2973}
 2974
 2975#[gpui::test]
 2976async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2977    init_test(cx, |settings| {
 2978        settings.defaults.tab_size = NonZeroU32::new(4)
 2979    });
 2980
 2981    let language = Arc::new(
 2982        Language::new(
 2983            LanguageConfig::default(),
 2984            Some(tree_sitter_rust::LANGUAGE.into()),
 2985        )
 2986        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2987        .unwrap(),
 2988    );
 2989
 2990    let mut cx = EditorTestContext::new(cx).await;
 2991    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2992    cx.set_state(indoc! {"
 2993        fn a() {
 2994            if b {
 2995        \t ˇc
 2996            }
 2997        }
 2998    "});
 2999
 3000    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3001    cx.assert_editor_state(indoc! {"
 3002        fn a() {
 3003            if b {
 3004                ˇc
 3005            }
 3006        }
 3007    "});
 3008}
 3009
 3010#[gpui::test]
 3011async fn test_indent_outdent(cx: &mut TestAppContext) {
 3012    init_test(cx, |settings| {
 3013        settings.defaults.tab_size = NonZeroU32::new(4);
 3014    });
 3015
 3016    let mut cx = EditorTestContext::new(cx).await;
 3017
 3018    cx.set_state(indoc! {"
 3019          «oneˇ» «twoˇ»
 3020        three
 3021         four
 3022    "});
 3023    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3024    cx.assert_editor_state(indoc! {"
 3025            «oneˇ» «twoˇ»
 3026        three
 3027         four
 3028    "});
 3029
 3030    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3031    cx.assert_editor_state(indoc! {"
 3032        «oneˇ» «twoˇ»
 3033        three
 3034         four
 3035    "});
 3036
 3037    // select across line ending
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        t«hree
 3041        ˇ» four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046            t«hree
 3047        ˇ» four
 3048    "});
 3049
 3050    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        one two
 3053        t«hree
 3054        ˇ» four
 3055    "});
 3056
 3057    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3058    cx.set_state(indoc! {"
 3059        one two
 3060        ˇthree
 3061            four
 3062    "});
 3063    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3064    cx.assert_editor_state(indoc! {"
 3065        one two
 3066            ˇthree
 3067            four
 3068    "});
 3069
 3070    cx.set_state(indoc! {"
 3071        one two
 3072        ˇ    three
 3073            four
 3074    "});
 3075    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3076    cx.assert_editor_state(indoc! {"
 3077        one two
 3078        ˇthree
 3079            four
 3080    "});
 3081}
 3082
 3083#[gpui::test]
 3084async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3085    init_test(cx, |settings| {
 3086        settings.defaults.hard_tabs = Some(true);
 3087    });
 3088
 3089    let mut cx = EditorTestContext::new(cx).await;
 3090
 3091    // select two ranges on one line
 3092    cx.set_state(indoc! {"
 3093        «oneˇ» «twoˇ»
 3094        three
 3095        four
 3096    "});
 3097    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        \t«oneˇ» «twoˇ»
 3100        three
 3101        four
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        \t\t«oneˇ» «twoˇ»
 3106        three
 3107        four
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        \t«oneˇ» «twoˇ»
 3112        three
 3113        four
 3114    "});
 3115    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3116    cx.assert_editor_state(indoc! {"
 3117        «oneˇ» «twoˇ»
 3118        three
 3119        four
 3120    "});
 3121
 3122    // select across a line ending
 3123    cx.set_state(indoc! {"
 3124        one two
 3125        t«hree
 3126        ˇ»four
 3127    "});
 3128    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3129    cx.assert_editor_state(indoc! {"
 3130        one two
 3131        \tt«hree
 3132        ˇ»four
 3133    "});
 3134    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3135    cx.assert_editor_state(indoc! {"
 3136        one two
 3137        \t\tt«hree
 3138        ˇ»four
 3139    "});
 3140    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3141    cx.assert_editor_state(indoc! {"
 3142        one two
 3143        \tt«hree
 3144        ˇ»four
 3145    "});
 3146    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3147    cx.assert_editor_state(indoc! {"
 3148        one two
 3149        t«hree
 3150        ˇ»four
 3151    "});
 3152
 3153    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3154    cx.set_state(indoc! {"
 3155        one two
 3156        ˇthree
 3157        four
 3158    "});
 3159    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3160    cx.assert_editor_state(indoc! {"
 3161        one two
 3162        ˇthree
 3163        four
 3164    "});
 3165    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3166    cx.assert_editor_state(indoc! {"
 3167        one two
 3168        \tˇthree
 3169        four
 3170    "});
 3171    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3172    cx.assert_editor_state(indoc! {"
 3173        one two
 3174        ˇthree
 3175        four
 3176    "});
 3177}
 3178
 3179#[gpui::test]
 3180fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3181    init_test(cx, |settings| {
 3182        settings.languages.extend([
 3183            (
 3184                "TOML".into(),
 3185                LanguageSettingsContent {
 3186                    tab_size: NonZeroU32::new(2),
 3187                    ..Default::default()
 3188                },
 3189            ),
 3190            (
 3191                "Rust".into(),
 3192                LanguageSettingsContent {
 3193                    tab_size: NonZeroU32::new(4),
 3194                    ..Default::default()
 3195                },
 3196            ),
 3197        ]);
 3198    });
 3199
 3200    let toml_language = Arc::new(Language::new(
 3201        LanguageConfig {
 3202            name: "TOML".into(),
 3203            ..Default::default()
 3204        },
 3205        None,
 3206    ));
 3207    let rust_language = Arc::new(Language::new(
 3208        LanguageConfig {
 3209            name: "Rust".into(),
 3210            ..Default::default()
 3211        },
 3212        None,
 3213    ));
 3214
 3215    let toml_buffer =
 3216        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3217    let rust_buffer =
 3218        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3219    let multibuffer = cx.new(|cx| {
 3220        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3221        multibuffer.push_excerpts(
 3222            toml_buffer.clone(),
 3223            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3224            cx,
 3225        );
 3226        multibuffer.push_excerpts(
 3227            rust_buffer.clone(),
 3228            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3229            cx,
 3230        );
 3231        multibuffer
 3232    });
 3233
 3234    cx.add_window(|window, cx| {
 3235        let mut editor = build_editor(multibuffer, window, cx);
 3236
 3237        assert_eq!(
 3238            editor.text(cx),
 3239            indoc! {"
 3240                a = 1
 3241                b = 2
 3242
 3243                const c: usize = 3;
 3244            "}
 3245        );
 3246
 3247        select_ranges(
 3248            &mut editor,
 3249            indoc! {"
 3250                «aˇ» = 1
 3251                b = 2
 3252
 3253                «const c:ˇ» usize = 3;
 3254            "},
 3255            window,
 3256            cx,
 3257        );
 3258
 3259        editor.tab(&Tab, window, cx);
 3260        assert_text_with_selections(
 3261            &mut editor,
 3262            indoc! {"
 3263                  «aˇ» = 1
 3264                b = 2
 3265
 3266                    «const c:ˇ» usize = 3;
 3267            "},
 3268            cx,
 3269        );
 3270        editor.backtab(&Backtab, window, cx);
 3271        assert_text_with_selections(
 3272            &mut editor,
 3273            indoc! {"
 3274                «aˇ» = 1
 3275                b = 2
 3276
 3277                «const c:ˇ» usize = 3;
 3278            "},
 3279            cx,
 3280        );
 3281
 3282        editor
 3283    });
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_backspace(cx: &mut TestAppContext) {
 3288    init_test(cx, |_| {});
 3289
 3290    let mut cx = EditorTestContext::new(cx).await;
 3291
 3292    // Basic backspace
 3293    cx.set_state(indoc! {"
 3294        onˇe two three
 3295        fou«rˇ» five six
 3296        seven «ˇeight nine
 3297        »ten
 3298    "});
 3299    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3300    cx.assert_editor_state(indoc! {"
 3301        oˇe two three
 3302        fouˇ five six
 3303        seven ˇten
 3304    "});
 3305
 3306    // Test backspace inside and around indents
 3307    cx.set_state(indoc! {"
 3308        zero
 3309            ˇone
 3310                ˇtwo
 3311            ˇ ˇ ˇ  three
 3312        ˇ  ˇ  four
 3313    "});
 3314    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3315    cx.assert_editor_state(indoc! {"
 3316        zero
 3317        ˇone
 3318            ˇtwo
 3319        ˇ  threeˇ  four
 3320    "});
 3321}
 3322
 3323#[gpui::test]
 3324async fn test_delete(cx: &mut TestAppContext) {
 3325    init_test(cx, |_| {});
 3326
 3327    let mut cx = EditorTestContext::new(cx).await;
 3328    cx.set_state(indoc! {"
 3329        onˇe two three
 3330        fou«rˇ» five six
 3331        seven «ˇeight nine
 3332        »ten
 3333    "});
 3334    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3335    cx.assert_editor_state(indoc! {"
 3336        onˇ two three
 3337        fouˇ five six
 3338        seven ˇten
 3339    "});
 3340}
 3341
 3342#[gpui::test]
 3343fn test_delete_line(cx: &mut TestAppContext) {
 3344    init_test(cx, |_| {});
 3345
 3346    let editor = cx.add_window(|window, cx| {
 3347        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3348        build_editor(buffer, window, cx)
 3349    });
 3350    _ = editor.update(cx, |editor, window, cx| {
 3351        editor.change_selections(None, window, cx, |s| {
 3352            s.select_display_ranges([
 3353                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3354                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3355                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3356            ])
 3357        });
 3358        editor.delete_line(&DeleteLine, window, cx);
 3359        assert_eq!(editor.display_text(cx), "ghi");
 3360        assert_eq!(
 3361            editor.selections.display_ranges(cx),
 3362            vec![
 3363                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3364                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3365            ]
 3366        );
 3367    });
 3368
 3369    let editor = cx.add_window(|window, cx| {
 3370        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3371        build_editor(buffer, window, cx)
 3372    });
 3373    _ = editor.update(cx, |editor, window, cx| {
 3374        editor.change_selections(None, window, cx, |s| {
 3375            s.select_display_ranges([
 3376                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3377            ])
 3378        });
 3379        editor.delete_line(&DeleteLine, window, cx);
 3380        assert_eq!(editor.display_text(cx), "ghi\n");
 3381        assert_eq!(
 3382            editor.selections.display_ranges(cx),
 3383            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3384        );
 3385    });
 3386}
 3387
 3388#[gpui::test]
 3389fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3390    init_test(cx, |_| {});
 3391
 3392    cx.add_window(|window, cx| {
 3393        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3394        let mut editor = build_editor(buffer.clone(), window, cx);
 3395        let buffer = buffer.read(cx).as_singleton().unwrap();
 3396
 3397        assert_eq!(
 3398            editor.selections.ranges::<Point>(cx),
 3399            &[Point::new(0, 0)..Point::new(0, 0)]
 3400        );
 3401
 3402        // When on single line, replace newline at end by space
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            &[Point::new(0, 3)..Point::new(0, 3)]
 3408        );
 3409
 3410        // When multiple lines are selected, remove newlines that are spanned by the selection
 3411        editor.change_selections(None, window, cx, |s| {
 3412            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3413        });
 3414        editor.join_lines(&JoinLines, window, cx);
 3415        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3416        assert_eq!(
 3417            editor.selections.ranges::<Point>(cx),
 3418            &[Point::new(0, 11)..Point::new(0, 11)]
 3419        );
 3420
 3421        // Undo should be transactional
 3422        editor.undo(&Undo, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3424        assert_eq!(
 3425            editor.selections.ranges::<Point>(cx),
 3426            &[Point::new(0, 5)..Point::new(2, 2)]
 3427        );
 3428
 3429        // When joining an empty line don't insert a space
 3430        editor.change_selections(None, window, cx, |s| {
 3431            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3432        });
 3433        editor.join_lines(&JoinLines, window, cx);
 3434        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3435        assert_eq!(
 3436            editor.selections.ranges::<Point>(cx),
 3437            [Point::new(2, 3)..Point::new(2, 3)]
 3438        );
 3439
 3440        // We can remove trailing newlines
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3443        assert_eq!(
 3444            editor.selections.ranges::<Point>(cx),
 3445            [Point::new(2, 3)..Point::new(2, 3)]
 3446        );
 3447
 3448        // We don't blow up on the last line
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3451        assert_eq!(
 3452            editor.selections.ranges::<Point>(cx),
 3453            [Point::new(2, 3)..Point::new(2, 3)]
 3454        );
 3455
 3456        // reset to test indentation
 3457        editor.buffer.update(cx, |buffer, cx| {
 3458            buffer.edit(
 3459                [
 3460                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3461                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3462                ],
 3463                None,
 3464                cx,
 3465            )
 3466        });
 3467
 3468        // We remove any leading spaces
 3469        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3470        editor.change_selections(None, window, cx, |s| {
 3471            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3472        });
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3475
 3476        // We don't insert a space for a line containing only spaces
 3477        editor.join_lines(&JoinLines, window, cx);
 3478        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3479
 3480        // We ignore any leading tabs
 3481        editor.join_lines(&JoinLines, window, cx);
 3482        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3483
 3484        editor
 3485    });
 3486}
 3487
 3488#[gpui::test]
 3489fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3490    init_test(cx, |_| {});
 3491
 3492    cx.add_window(|window, cx| {
 3493        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3494        let mut editor = build_editor(buffer.clone(), window, cx);
 3495        let buffer = buffer.read(cx).as_singleton().unwrap();
 3496
 3497        editor.change_selections(None, window, cx, |s| {
 3498            s.select_ranges([
 3499                Point::new(0, 2)..Point::new(1, 1),
 3500                Point::new(1, 2)..Point::new(1, 2),
 3501                Point::new(3, 1)..Point::new(3, 2),
 3502            ])
 3503        });
 3504
 3505        editor.join_lines(&JoinLines, window, cx);
 3506        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3507
 3508        assert_eq!(
 3509            editor.selections.ranges::<Point>(cx),
 3510            [
 3511                Point::new(0, 7)..Point::new(0, 7),
 3512                Point::new(1, 3)..Point::new(1, 3)
 3513            ]
 3514        );
 3515        editor
 3516    });
 3517}
 3518
 3519#[gpui::test]
 3520async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3521    init_test(cx, |_| {});
 3522
 3523    let mut cx = EditorTestContext::new(cx).await;
 3524
 3525    let diff_base = r#"
 3526        Line 0
 3527        Line 1
 3528        Line 2
 3529        Line 3
 3530        "#
 3531    .unindent();
 3532
 3533    cx.set_state(
 3534        &r#"
 3535        ˇLine 0
 3536        Line 1
 3537        Line 2
 3538        Line 3
 3539        "#
 3540        .unindent(),
 3541    );
 3542
 3543    cx.set_head_text(&diff_base);
 3544    executor.run_until_parked();
 3545
 3546    // Join lines
 3547    cx.update_editor(|editor, window, cx| {
 3548        editor.join_lines(&JoinLines, window, cx);
 3549    });
 3550    executor.run_until_parked();
 3551
 3552    cx.assert_editor_state(
 3553        &r#"
 3554        Line 0ˇ Line 1
 3555        Line 2
 3556        Line 3
 3557        "#
 3558        .unindent(),
 3559    );
 3560    // Join again
 3561    cx.update_editor(|editor, window, cx| {
 3562        editor.join_lines(&JoinLines, window, cx);
 3563    });
 3564    executor.run_until_parked();
 3565
 3566    cx.assert_editor_state(
 3567        &r#"
 3568        Line 0 Line 1ˇ Line 2
 3569        Line 3
 3570        "#
 3571        .unindent(),
 3572    );
 3573}
 3574
 3575#[gpui::test]
 3576async fn test_custom_newlines_cause_no_false_positive_diffs(
 3577    executor: BackgroundExecutor,
 3578    cx: &mut TestAppContext,
 3579) {
 3580    init_test(cx, |_| {});
 3581    let mut cx = EditorTestContext::new(cx).await;
 3582    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3583    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3584    executor.run_until_parked();
 3585
 3586    cx.update_editor(|editor, window, cx| {
 3587        let snapshot = editor.snapshot(window, cx);
 3588        assert_eq!(
 3589            snapshot
 3590                .buffer_snapshot
 3591                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3592                .collect::<Vec<_>>(),
 3593            Vec::new(),
 3594            "Should not have any diffs for files with custom newlines"
 3595        );
 3596    });
 3597}
 3598
 3599#[gpui::test]
 3600async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3601    init_test(cx, |_| {});
 3602
 3603    let mut cx = EditorTestContext::new(cx).await;
 3604
 3605    // Test sort_lines_case_insensitive()
 3606    cx.set_state(indoc! {"
 3607        «z
 3608        y
 3609        x
 3610        Z
 3611        Y
 3612        Xˇ»
 3613    "});
 3614    cx.update_editor(|e, window, cx| {
 3615        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3616    });
 3617    cx.assert_editor_state(indoc! {"
 3618        «x
 3619        X
 3620        y
 3621        Y
 3622        z
 3623        Zˇ»
 3624    "});
 3625
 3626    // Test reverse_lines()
 3627    cx.set_state(indoc! {"
 3628        «5
 3629        4
 3630        3
 3631        2
 3632        1ˇ»
 3633    "});
 3634    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3635    cx.assert_editor_state(indoc! {"
 3636        «1
 3637        2
 3638        3
 3639        4
 3640        5ˇ»
 3641    "});
 3642
 3643    // Skip testing shuffle_line()
 3644
 3645    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3646    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3647
 3648    // Don't manipulate when cursor is on single line, but expand the selection
 3649    cx.set_state(indoc! {"
 3650        ddˇdd
 3651        ccc
 3652        bb
 3653        a
 3654    "});
 3655    cx.update_editor(|e, window, cx| {
 3656        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3657    });
 3658    cx.assert_editor_state(indoc! {"
 3659        «ddddˇ»
 3660        ccc
 3661        bb
 3662        a
 3663    "});
 3664
 3665    // Basic manipulate case
 3666    // Start selection moves to column 0
 3667    // End of selection shrinks to fit shorter line
 3668    cx.set_state(indoc! {"
 3669        dd«d
 3670        ccc
 3671        bb
 3672        aaaaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «aaaaa
 3679        bb
 3680        ccc
 3681        dddˇ»
 3682    "});
 3683
 3684    // Manipulate case with newlines
 3685    cx.set_state(indoc! {"
 3686        dd«d
 3687        ccc
 3688
 3689        bb
 3690        aaaaa
 3691
 3692        ˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3696    });
 3697    cx.assert_editor_state(indoc! {"
 3698        «
 3699
 3700        aaaaa
 3701        bb
 3702        ccc
 3703        dddˇ»
 3704
 3705    "});
 3706
 3707    // Adding new line
 3708    cx.set_state(indoc! {"
 3709        aa«a
 3710        bbˇ»b
 3711    "});
 3712    cx.update_editor(|e, window, cx| {
 3713        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        «aaa
 3717        bbb
 3718        added_lineˇ»
 3719    "});
 3720
 3721    // Removing line
 3722    cx.set_state(indoc! {"
 3723        aa«a
 3724        bbbˇ»
 3725    "});
 3726    cx.update_editor(|e, window, cx| {
 3727        e.manipulate_lines(window, cx, |lines| {
 3728            lines.pop();
 3729        })
 3730    });
 3731    cx.assert_editor_state(indoc! {"
 3732        «aaaˇ»
 3733    "});
 3734
 3735    // Removing all lines
 3736    cx.set_state(indoc! {"
 3737        aa«a
 3738        bbbˇ»
 3739    "});
 3740    cx.update_editor(|e, window, cx| {
 3741        e.manipulate_lines(window, cx, |lines| {
 3742            lines.drain(..);
 3743        })
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        ˇ
 3747    "});
 3748}
 3749
 3750#[gpui::test]
 3751async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3752    init_test(cx, |_| {});
 3753
 3754    let mut cx = EditorTestContext::new(cx).await;
 3755
 3756    // Consider continuous selection as single selection
 3757    cx.set_state(indoc! {"
 3758        Aaa«aa
 3759        cˇ»c«c
 3760        bb
 3761        aaaˇ»aa
 3762    "});
 3763    cx.update_editor(|e, window, cx| {
 3764        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3765    });
 3766    cx.assert_editor_state(indoc! {"
 3767        «Aaaaa
 3768        ccc
 3769        bb
 3770        aaaaaˇ»
 3771    "});
 3772
 3773    cx.set_state(indoc! {"
 3774        Aaa«aa
 3775        cˇ»c«c
 3776        bb
 3777        aaaˇ»aa
 3778    "});
 3779    cx.update_editor(|e, window, cx| {
 3780        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3781    });
 3782    cx.assert_editor_state(indoc! {"
 3783        «Aaaaa
 3784        ccc
 3785        bbˇ»
 3786    "});
 3787
 3788    // Consider non continuous selection as distinct dedup operations
 3789    cx.set_state(indoc! {"
 3790        «aaaaa
 3791        bb
 3792        aaaaa
 3793        aaaaaˇ»
 3794
 3795        aaa«aaˇ»
 3796    "});
 3797    cx.update_editor(|e, window, cx| {
 3798        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3799    });
 3800    cx.assert_editor_state(indoc! {"
 3801        «aaaaa
 3802        bbˇ»
 3803
 3804        «aaaaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    cx.set_state(indoc! {"
 3815        «Aaa
 3816        aAa
 3817        Aaaˇ»
 3818    "});
 3819    cx.update_editor(|e, window, cx| {
 3820        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3821    });
 3822    cx.assert_editor_state(indoc! {"
 3823        «Aaa
 3824        aAaˇ»
 3825    "});
 3826
 3827    cx.set_state(indoc! {"
 3828        «Aaa
 3829        aAa
 3830        aaAˇ»
 3831    "});
 3832    cx.update_editor(|e, window, cx| {
 3833        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3834    });
 3835    cx.assert_editor_state(indoc! {"
 3836        «Aaaˇ»
 3837    "});
 3838}
 3839
 3840#[gpui::test]
 3841async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3842    init_test(cx, |_| {});
 3843
 3844    let mut cx = EditorTestContext::new(cx).await;
 3845
 3846    // Manipulate with multiple selections on a single line
 3847    cx.set_state(indoc! {"
 3848        dd«dd
 3849        cˇ»c«c
 3850        bb
 3851        aaaˇ»aa
 3852    "});
 3853    cx.update_editor(|e, window, cx| {
 3854        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3855    });
 3856    cx.assert_editor_state(indoc! {"
 3857        «aaaaa
 3858        bb
 3859        ccc
 3860        ddddˇ»
 3861    "});
 3862
 3863    // Manipulate with multiple disjoin selections
 3864    cx.set_state(indoc! {"
 3865 3866        4
 3867        3
 3868        2
 3869        1ˇ»
 3870
 3871        dd«dd
 3872        ccc
 3873        bb
 3874        aaaˇ»aa
 3875    "});
 3876    cx.update_editor(|e, window, cx| {
 3877        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3878    });
 3879    cx.assert_editor_state(indoc! {"
 3880        «1
 3881        2
 3882        3
 3883        4
 3884        5ˇ»
 3885
 3886        «aaaaa
 3887        bb
 3888        ccc
 3889        ddddˇ»
 3890    "});
 3891
 3892    // Adding lines on each selection
 3893    cx.set_state(indoc! {"
 3894 3895        1ˇ»
 3896
 3897        bb«bb
 3898        aaaˇ»aa
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «2
 3905        1
 3906        added lineˇ»
 3907
 3908        «bbbb
 3909        aaaaa
 3910        added lineˇ»
 3911    "});
 3912
 3913    // Removing lines on each selection
 3914    cx.set_state(indoc! {"
 3915 3916        1ˇ»
 3917
 3918        bb«bb
 3919        aaaˇ»aa
 3920    "});
 3921    cx.update_editor(|e, window, cx| {
 3922        e.manipulate_lines(window, cx, |lines| {
 3923            lines.pop();
 3924        })
 3925    });
 3926    cx.assert_editor_state(indoc! {"
 3927        «2ˇ»
 3928
 3929        «bbbbˇ»
 3930    "});
 3931}
 3932
 3933#[gpui::test]
 3934async fn test_toggle_case(cx: &mut TestAppContext) {
 3935    init_test(cx, |_| {});
 3936
 3937    let mut cx = EditorTestContext::new(cx).await;
 3938
 3939    // If all lower case -> upper case
 3940    cx.set_state(indoc! {"
 3941        «hello worldˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3944    cx.assert_editor_state(indoc! {"
 3945        «HELLO WORLDˇ»
 3946    "});
 3947
 3948    // If all upper case -> lower case
 3949    cx.set_state(indoc! {"
 3950        «HELLO WORLDˇ»
 3951    "});
 3952    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3953    cx.assert_editor_state(indoc! {"
 3954        «hello worldˇ»
 3955    "});
 3956
 3957    // If any upper case characters are identified -> lower case
 3958    // This matches JetBrains IDEs
 3959    cx.set_state(indoc! {"
 3960        «hEllo worldˇ»
 3961    "});
 3962    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3963    cx.assert_editor_state(indoc! {"
 3964        «hello worldˇ»
 3965    "});
 3966}
 3967
 3968#[gpui::test]
 3969async fn test_manipulate_text(cx: &mut TestAppContext) {
 3970    init_test(cx, |_| {});
 3971
 3972    let mut cx = EditorTestContext::new(cx).await;
 3973
 3974    // Test convert_to_upper_case()
 3975    cx.set_state(indoc! {"
 3976        «hello worldˇ»
 3977    "});
 3978    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3979    cx.assert_editor_state(indoc! {"
 3980        «HELLO WORLDˇ»
 3981    "});
 3982
 3983    // Test convert_to_lower_case()
 3984    cx.set_state(indoc! {"
 3985        «HELLO WORLDˇ»
 3986    "});
 3987    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3988    cx.assert_editor_state(indoc! {"
 3989        «hello worldˇ»
 3990    "});
 3991
 3992    // Test multiple line, single selection case
 3993    cx.set_state(indoc! {"
 3994        «The quick brown
 3995        fox jumps over
 3996        the lazy dogˇ»
 3997    "});
 3998    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3999    cx.assert_editor_state(indoc! {"
 4000        «The Quick Brown
 4001        Fox Jumps Over
 4002        The Lazy Dogˇ»
 4003    "});
 4004
 4005    // Test multiple line, single selection case
 4006    cx.set_state(indoc! {"
 4007        «The quick brown
 4008        fox jumps over
 4009        the lazy dogˇ»
 4010    "});
 4011    cx.update_editor(|e, window, cx| {
 4012        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4013    });
 4014    cx.assert_editor_state(indoc! {"
 4015        «TheQuickBrown
 4016        FoxJumpsOver
 4017        TheLazyDogˇ»
 4018    "});
 4019
 4020    // From here on out, test more complex cases of manipulate_text()
 4021
 4022    // Test no selection case - should affect words cursors are in
 4023    // Cursor at beginning, middle, and end of word
 4024    cx.set_state(indoc! {"
 4025        ˇhello big beauˇtiful worldˇ
 4026    "});
 4027    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4028    cx.assert_editor_state(indoc! {"
 4029        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4030    "});
 4031
 4032    // Test multiple selections on a single line and across multiple lines
 4033    cx.set_state(indoc! {"
 4034        «Theˇ» quick «brown
 4035        foxˇ» jumps «overˇ»
 4036        the «lazyˇ» dog
 4037    "});
 4038    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4039    cx.assert_editor_state(indoc! {"
 4040        «THEˇ» quick «BROWN
 4041        FOXˇ» jumps «OVERˇ»
 4042        the «LAZYˇ» dog
 4043    "});
 4044
 4045    // Test case where text length grows
 4046    cx.set_state(indoc! {"
 4047        «tschüߡ»
 4048    "});
 4049    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        «TSCHÜSSˇ»
 4052    "});
 4053
 4054    // Test to make sure we don't crash when text shrinks
 4055    cx.set_state(indoc! {"
 4056        aaa_bbbˇ
 4057    "});
 4058    cx.update_editor(|e, window, cx| {
 4059        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4060    });
 4061    cx.assert_editor_state(indoc! {"
 4062        «aaaBbbˇ»
 4063    "});
 4064
 4065    // Test to make sure we all aware of the fact that each word can grow and shrink
 4066    // Final selections should be aware of this fact
 4067    cx.set_state(indoc! {"
 4068        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4069    "});
 4070    cx.update_editor(|e, window, cx| {
 4071        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4072    });
 4073    cx.assert_editor_state(indoc! {"
 4074        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4075    "});
 4076
 4077    cx.set_state(indoc! {"
 4078        «hElLo, WoRld!ˇ»
 4079    "});
 4080    cx.update_editor(|e, window, cx| {
 4081        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4082    });
 4083    cx.assert_editor_state(indoc! {"
 4084        «HeLlO, wOrLD!ˇ»
 4085    "});
 4086}
 4087
 4088#[gpui::test]
 4089fn test_duplicate_line(cx: &mut TestAppContext) {
 4090    init_test(cx, |_| {});
 4091
 4092    let editor = cx.add_window(|window, cx| {
 4093        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4094        build_editor(buffer, window, cx)
 4095    });
 4096    _ = editor.update(cx, |editor, window, cx| {
 4097        editor.change_selections(None, window, cx, |s| {
 4098            s.select_display_ranges([
 4099                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4101                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4102                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4103            ])
 4104        });
 4105        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4106        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4107        assert_eq!(
 4108            editor.selections.display_ranges(cx),
 4109            vec![
 4110                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4111                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4112                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4113                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4114            ]
 4115        );
 4116    });
 4117
 4118    let editor = cx.add_window(|window, cx| {
 4119        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4120        build_editor(buffer, window, cx)
 4121    });
 4122    _ = editor.update(cx, |editor, window, cx| {
 4123        editor.change_selections(None, window, cx, |s| {
 4124            s.select_display_ranges([
 4125                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4126                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4127            ])
 4128        });
 4129        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4130        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4131        assert_eq!(
 4132            editor.selections.display_ranges(cx),
 4133            vec![
 4134                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4135                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4136            ]
 4137        );
 4138    });
 4139
 4140    // With `move_upwards` the selections stay in place, except for
 4141    // the lines inserted above them
 4142    let editor = cx.add_window(|window, cx| {
 4143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4144        build_editor(buffer, window, cx)
 4145    });
 4146    _ = editor.update(cx, |editor, window, cx| {
 4147        editor.change_selections(None, window, cx, |s| {
 4148            s.select_display_ranges([
 4149                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4150                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4151                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4152                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4153            ])
 4154        });
 4155        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4156        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4157        assert_eq!(
 4158            editor.selections.display_ranges(cx),
 4159            vec![
 4160                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4161                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4162                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4163                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4164            ]
 4165        );
 4166    });
 4167
 4168    let editor = cx.add_window(|window, cx| {
 4169        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4170        build_editor(buffer, window, cx)
 4171    });
 4172    _ = editor.update(cx, |editor, window, cx| {
 4173        editor.change_selections(None, window, cx, |s| {
 4174            s.select_display_ranges([
 4175                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4176                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4177            ])
 4178        });
 4179        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4180        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4181        assert_eq!(
 4182            editor.selections.display_ranges(cx),
 4183            vec![
 4184                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4185                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4186            ]
 4187        );
 4188    });
 4189
 4190    let editor = cx.add_window(|window, cx| {
 4191        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4192        build_editor(buffer, window, cx)
 4193    });
 4194    _ = editor.update(cx, |editor, window, cx| {
 4195        editor.change_selections(None, window, cx, |s| {
 4196            s.select_display_ranges([
 4197                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4198                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4199            ])
 4200        });
 4201        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4202        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4203        assert_eq!(
 4204            editor.selections.display_ranges(cx),
 4205            vec![
 4206                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4207                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4208            ]
 4209        );
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214fn test_move_line_up_down(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let editor = cx.add_window(|window, cx| {
 4218        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4219        build_editor(buffer, window, cx)
 4220    });
 4221    _ = editor.update(cx, |editor, window, cx| {
 4222        editor.fold_creases(
 4223            vec![
 4224                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4225                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4226                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4227            ],
 4228            true,
 4229            window,
 4230            cx,
 4231        );
 4232        editor.change_selections(None, window, cx, |s| {
 4233            s.select_display_ranges([
 4234                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4235                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4236                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4237                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4238            ])
 4239        });
 4240        assert_eq!(
 4241            editor.display_text(cx),
 4242            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4243        );
 4244
 4245        editor.move_line_up(&MoveLineUp, window, cx);
 4246        assert_eq!(
 4247            editor.display_text(cx),
 4248            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4249        );
 4250        assert_eq!(
 4251            editor.selections.display_ranges(cx),
 4252            vec![
 4253                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4254                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4255                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4256                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4257            ]
 4258        );
 4259    });
 4260
 4261    _ = editor.update(cx, |editor, window, cx| {
 4262        editor.move_line_down(&MoveLineDown, window, cx);
 4263        assert_eq!(
 4264            editor.display_text(cx),
 4265            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4266        );
 4267        assert_eq!(
 4268            editor.selections.display_ranges(cx),
 4269            vec![
 4270                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4271                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4272                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4273                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4274            ]
 4275        );
 4276    });
 4277
 4278    _ = editor.update(cx, |editor, window, cx| {
 4279        editor.move_line_down(&MoveLineDown, window, cx);
 4280        assert_eq!(
 4281            editor.display_text(cx),
 4282            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4283        );
 4284        assert_eq!(
 4285            editor.selections.display_ranges(cx),
 4286            vec![
 4287                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4288                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4289                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4290                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4291            ]
 4292        );
 4293    });
 4294
 4295    _ = editor.update(cx, |editor, window, cx| {
 4296        editor.move_line_up(&MoveLineUp, window, cx);
 4297        assert_eq!(
 4298            editor.display_text(cx),
 4299            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4300        );
 4301        assert_eq!(
 4302            editor.selections.display_ranges(cx),
 4303            vec![
 4304                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4305                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4306                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4307                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4308            ]
 4309        );
 4310    });
 4311}
 4312
 4313#[gpui::test]
 4314fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4315    init_test(cx, |_| {});
 4316
 4317    let editor = cx.add_window(|window, cx| {
 4318        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4319        build_editor(buffer, window, cx)
 4320    });
 4321    _ = editor.update(cx, |editor, window, cx| {
 4322        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4323        editor.insert_blocks(
 4324            [BlockProperties {
 4325                style: BlockStyle::Fixed,
 4326                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4327                height: Some(1),
 4328                render: Arc::new(|_| div().into_any()),
 4329                priority: 0,
 4330            }],
 4331            Some(Autoscroll::fit()),
 4332            cx,
 4333        );
 4334        editor.change_selections(None, window, cx, |s| {
 4335            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4336        });
 4337        editor.move_line_down(&MoveLineDown, window, cx);
 4338    });
 4339}
 4340
 4341#[gpui::test]
 4342async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4343    init_test(cx, |_| {});
 4344
 4345    let mut cx = EditorTestContext::new(cx).await;
 4346    cx.set_state(
 4347        &"
 4348            ˇzero
 4349            one
 4350            two
 4351            three
 4352            four
 4353            five
 4354        "
 4355        .unindent(),
 4356    );
 4357
 4358    // Create a four-line block that replaces three lines of text.
 4359    cx.update_editor(|editor, window, cx| {
 4360        let snapshot = editor.snapshot(window, cx);
 4361        let snapshot = &snapshot.buffer_snapshot;
 4362        let placement = BlockPlacement::Replace(
 4363            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4364        );
 4365        editor.insert_blocks(
 4366            [BlockProperties {
 4367                placement,
 4368                height: Some(4),
 4369                style: BlockStyle::Sticky,
 4370                render: Arc::new(|_| gpui::div().into_any_element()),
 4371                priority: 0,
 4372            }],
 4373            None,
 4374            cx,
 4375        );
 4376    });
 4377
 4378    // Move down so that the cursor touches the block.
 4379    cx.update_editor(|editor, window, cx| {
 4380        editor.move_down(&Default::default(), window, cx);
 4381    });
 4382    cx.assert_editor_state(
 4383        &"
 4384            zero
 4385            «one
 4386            two
 4387            threeˇ»
 4388            four
 4389            five
 4390        "
 4391        .unindent(),
 4392    );
 4393
 4394    // Move down past the block.
 4395    cx.update_editor(|editor, window, cx| {
 4396        editor.move_down(&Default::default(), window, cx);
 4397    });
 4398    cx.assert_editor_state(
 4399        &"
 4400            zero
 4401            one
 4402            two
 4403            three
 4404            ˇfour
 4405            five
 4406        "
 4407        .unindent(),
 4408    );
 4409}
 4410
 4411#[gpui::test]
 4412fn test_transpose(cx: &mut TestAppContext) {
 4413    init_test(cx, |_| {});
 4414
 4415    _ = cx.add_window(|window, cx| {
 4416        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4417        editor.set_style(EditorStyle::default(), window, cx);
 4418        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "bac");
 4421        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4422
 4423        editor.transpose(&Default::default(), window, cx);
 4424        assert_eq!(editor.text(cx), "bca");
 4425        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4426
 4427        editor.transpose(&Default::default(), window, cx);
 4428        assert_eq!(editor.text(cx), "bac");
 4429        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4430
 4431        editor
 4432    });
 4433
 4434    _ = cx.add_window(|window, cx| {
 4435        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4436        editor.set_style(EditorStyle::default(), window, cx);
 4437        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "acb\nde");
 4440        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4441
 4442        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4443        editor.transpose(&Default::default(), window, cx);
 4444        assert_eq!(editor.text(cx), "acbd\ne");
 4445        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4446
 4447        editor.transpose(&Default::default(), window, cx);
 4448        assert_eq!(editor.text(cx), "acbde\n");
 4449        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4450
 4451        editor.transpose(&Default::default(), window, cx);
 4452        assert_eq!(editor.text(cx), "acbd\ne");
 4453        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4454
 4455        editor
 4456    });
 4457
 4458    _ = cx.add_window(|window, cx| {
 4459        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4460        editor.set_style(EditorStyle::default(), window, cx);
 4461        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4462        editor.transpose(&Default::default(), window, cx);
 4463        assert_eq!(editor.text(cx), "bacd\ne");
 4464        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4465
 4466        editor.transpose(&Default::default(), window, cx);
 4467        assert_eq!(editor.text(cx), "bcade\n");
 4468        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4469
 4470        editor.transpose(&Default::default(), window, cx);
 4471        assert_eq!(editor.text(cx), "bcda\ne");
 4472        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4473
 4474        editor.transpose(&Default::default(), window, cx);
 4475        assert_eq!(editor.text(cx), "bcade\n");
 4476        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4477
 4478        editor.transpose(&Default::default(), window, cx);
 4479        assert_eq!(editor.text(cx), "bcaed\n");
 4480        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4481
 4482        editor
 4483    });
 4484
 4485    _ = cx.add_window(|window, cx| {
 4486        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4487        editor.set_style(EditorStyle::default(), window, cx);
 4488        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4489        editor.transpose(&Default::default(), window, cx);
 4490        assert_eq!(editor.text(cx), "🏀🍐✋");
 4491        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4492
 4493        editor.transpose(&Default::default(), window, cx);
 4494        assert_eq!(editor.text(cx), "🏀✋🍐");
 4495        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4496
 4497        editor.transpose(&Default::default(), window, cx);
 4498        assert_eq!(editor.text(cx), "🏀🍐✋");
 4499        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4500
 4501        editor
 4502    });
 4503}
 4504
 4505#[gpui::test]
 4506async fn test_rewrap(cx: &mut TestAppContext) {
 4507    init_test(cx, |settings| {
 4508        settings.languages.extend([
 4509            (
 4510                "Markdown".into(),
 4511                LanguageSettingsContent {
 4512                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4513                    ..Default::default()
 4514                },
 4515            ),
 4516            (
 4517                "Plain Text".into(),
 4518                LanguageSettingsContent {
 4519                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4520                    ..Default::default()
 4521                },
 4522            ),
 4523        ])
 4524    });
 4525
 4526    let mut cx = EditorTestContext::new(cx).await;
 4527
 4528    let language_with_c_comments = Arc::new(Language::new(
 4529        LanguageConfig {
 4530            line_comments: vec!["// ".into()],
 4531            ..LanguageConfig::default()
 4532        },
 4533        None,
 4534    ));
 4535    let language_with_pound_comments = Arc::new(Language::new(
 4536        LanguageConfig {
 4537            line_comments: vec!["# ".into()],
 4538            ..LanguageConfig::default()
 4539        },
 4540        None,
 4541    ));
 4542    let markdown_language = Arc::new(Language::new(
 4543        LanguageConfig {
 4544            name: "Markdown".into(),
 4545            ..LanguageConfig::default()
 4546        },
 4547        None,
 4548    ));
 4549    let language_with_doc_comments = Arc::new(Language::new(
 4550        LanguageConfig {
 4551            line_comments: vec!["// ".into(), "/// ".into()],
 4552            ..LanguageConfig::default()
 4553        },
 4554        Some(tree_sitter_rust::LANGUAGE.into()),
 4555    ));
 4556
 4557    let plaintext_language = Arc::new(Language::new(
 4558        LanguageConfig {
 4559            name: "Plain Text".into(),
 4560            ..LanguageConfig::default()
 4561        },
 4562        None,
 4563    ));
 4564
 4565    assert_rewrap(
 4566        indoc! {"
 4567            // ˇ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.
 4568        "},
 4569        indoc! {"
 4570            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4571            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4572            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4573            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4574            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4575            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4576            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4577            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4578            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4579            // porttitor id. Aliquam id accumsan eros.
 4580        "},
 4581        language_with_c_comments.clone(),
 4582        &mut cx,
 4583    );
 4584
 4585    // Test that rewrapping works inside of a selection
 4586    assert_rewrap(
 4587        indoc! {"
 4588            «// 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.ˇ»
 4589        "},
 4590        indoc! {"
 4591            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4592            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4593            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4594            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4595            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4596            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4597            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4598            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4599            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4600            // porttitor id. Aliquam id accumsan eros.ˇ»
 4601        "},
 4602        language_with_c_comments.clone(),
 4603        &mut cx,
 4604    );
 4605
 4606    // Test that cursors that expand to the same region are collapsed.
 4607    assert_rewrap(
 4608        indoc! {"
 4609            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4610            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4611            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4612            // ˇ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.
 4613        "},
 4614        indoc! {"
 4615            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4616            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4617            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4618            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4619            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4620            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4621            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4622            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4623            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4624            // porttitor id. Aliquam id accumsan eros.
 4625        "},
 4626        language_with_c_comments.clone(),
 4627        &mut cx,
 4628    );
 4629
 4630    // Test that non-contiguous selections are treated separately.
 4631    assert_rewrap(
 4632        indoc! {"
 4633            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4634            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4635            //
 4636            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4637            // ˇ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.
 4638        "},
 4639        indoc! {"
 4640            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4641            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4642            // auctor, eu lacinia sapien scelerisque.
 4643            //
 4644            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4645            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4646            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4647            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4648            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4649            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4650            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4651        "},
 4652        language_with_c_comments.clone(),
 4653        &mut cx,
 4654    );
 4655
 4656    // Test that different comment prefixes are supported.
 4657    assert_rewrap(
 4658        indoc! {"
 4659            # ˇ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.
 4660        "},
 4661        indoc! {"
 4662            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4663            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4664            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4665            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4666            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4667            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4668            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4669            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4670            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4671            # accumsan eros.
 4672        "},
 4673        language_with_pound_comments.clone(),
 4674        &mut cx,
 4675    );
 4676
 4677    // Test that rewrapping is ignored outside of comments in most languages.
 4678    assert_rewrap(
 4679        indoc! {"
 4680            /// Adds two numbers.
 4681            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4682            fn add(a: u32, b: u32) -> u32 {
 4683                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ˇ
 4684            }
 4685        "},
 4686        indoc! {"
 4687            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4688            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4689            fn add(a: u32, b: u32) -> u32 {
 4690                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ˇ
 4691            }
 4692        "},
 4693        language_with_doc_comments.clone(),
 4694        &mut cx,
 4695    );
 4696
 4697    // Test that rewrapping works in Markdown and Plain Text languages.
 4698    assert_rewrap(
 4699        indoc! {"
 4700            # Hello
 4701
 4702            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.
 4703        "},
 4704        indoc! {"
 4705            # Hello
 4706
 4707            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4708            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4709            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4710            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4711            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4712            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4713            Integer sit amet scelerisque nisi.
 4714        "},
 4715        markdown_language,
 4716        &mut cx,
 4717    );
 4718
 4719    assert_rewrap(
 4720        indoc! {"
 4721            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.
 4722        "},
 4723        indoc! {"
 4724            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4725            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4726            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4727            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4728            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4729            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4730            Integer sit amet scelerisque nisi.
 4731        "},
 4732        plaintext_language,
 4733        &mut cx,
 4734    );
 4735
 4736    // Test rewrapping unaligned comments in a selection.
 4737    assert_rewrap(
 4738        indoc! {"
 4739            fn foo() {
 4740                if true {
 4741            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4742            // Praesent semper egestas tellus id dignissim.ˇ»
 4743                    do_something();
 4744                } else {
 4745                    //
 4746                }
 4747            }
 4748        "},
 4749        indoc! {"
 4750            fn foo() {
 4751                if true {
 4752            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4753                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4754                    // egestas tellus id dignissim.ˇ»
 4755                    do_something();
 4756                } else {
 4757                    //
 4758                }
 4759            }
 4760        "},
 4761        language_with_doc_comments.clone(),
 4762        &mut cx,
 4763    );
 4764
 4765    assert_rewrap(
 4766        indoc! {"
 4767            fn foo() {
 4768                if true {
 4769            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4770            // Praesent semper egestas tellus id dignissim.»
 4771                    do_something();
 4772                } else {
 4773                    //
 4774                }
 4775
 4776            }
 4777        "},
 4778        indoc! {"
 4779            fn foo() {
 4780                if true {
 4781            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4782                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4783                    // egestas tellus id dignissim.»
 4784                    do_something();
 4785                } else {
 4786                    //
 4787                }
 4788
 4789            }
 4790        "},
 4791        language_with_doc_comments.clone(),
 4792        &mut cx,
 4793    );
 4794
 4795    #[track_caller]
 4796    fn assert_rewrap(
 4797        unwrapped_text: &str,
 4798        wrapped_text: &str,
 4799        language: Arc<Language>,
 4800        cx: &mut EditorTestContext,
 4801    ) {
 4802        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4803        cx.set_state(unwrapped_text);
 4804        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4805        cx.assert_editor_state(wrapped_text);
 4806    }
 4807}
 4808
 4809#[gpui::test]
 4810async fn test_hard_wrap(cx: &mut TestAppContext) {
 4811    init_test(cx, |_| {});
 4812    let mut cx = EditorTestContext::new(cx).await;
 4813
 4814    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4815    cx.update_editor(|editor, _, cx| {
 4816        editor.set_hard_wrap(Some(14), cx);
 4817    });
 4818
 4819    cx.set_state(indoc!(
 4820        "
 4821        one two three ˇ
 4822        "
 4823    ));
 4824    cx.simulate_input("four");
 4825    cx.run_until_parked();
 4826
 4827    cx.assert_editor_state(indoc!(
 4828        "
 4829        one two three
 4830        fourˇ
 4831        "
 4832    ));
 4833
 4834    cx.update_editor(|editor, window, cx| {
 4835        editor.newline(&Default::default(), window, cx);
 4836    });
 4837    cx.run_until_parked();
 4838    cx.assert_editor_state(indoc!(
 4839        "
 4840        one two three
 4841        four
 4842        ˇ
 4843        "
 4844    ));
 4845
 4846    cx.simulate_input("five");
 4847    cx.run_until_parked();
 4848    cx.assert_editor_state(indoc!(
 4849        "
 4850        one two three
 4851        four
 4852        fiveˇ
 4853        "
 4854    ));
 4855
 4856    cx.update_editor(|editor, window, cx| {
 4857        editor.newline(&Default::default(), window, cx);
 4858    });
 4859    cx.run_until_parked();
 4860    cx.simulate_input("# ");
 4861    cx.run_until_parked();
 4862    cx.assert_editor_state(indoc!(
 4863        "
 4864        one two three
 4865        four
 4866        five
 4867        # ˇ
 4868        "
 4869    ));
 4870
 4871    cx.update_editor(|editor, window, cx| {
 4872        editor.newline(&Default::default(), window, cx);
 4873    });
 4874    cx.run_until_parked();
 4875    cx.assert_editor_state(indoc!(
 4876        "
 4877        one two three
 4878        four
 4879        five
 4880        #\x20
 4881 4882        "
 4883    ));
 4884
 4885    cx.simulate_input(" 6");
 4886    cx.run_until_parked();
 4887    cx.assert_editor_state(indoc!(
 4888        "
 4889        one two three
 4890        four
 4891        five
 4892        #
 4893        # 6ˇ
 4894        "
 4895    ));
 4896}
 4897
 4898#[gpui::test]
 4899async fn test_clipboard(cx: &mut TestAppContext) {
 4900    init_test(cx, |_| {});
 4901
 4902    let mut cx = EditorTestContext::new(cx).await;
 4903
 4904    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4905    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4906    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4907
 4908    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4909    cx.set_state("two ˇfour ˇsix ˇ");
 4910    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4911    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4912
 4913    // Paste again but with only two cursors. Since the number of cursors doesn't
 4914    // match the number of slices in the clipboard, the entire clipboard text
 4915    // is pasted at each cursor.
 4916    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4917    cx.update_editor(|e, window, cx| {
 4918        e.handle_input("( ", window, cx);
 4919        e.paste(&Paste, window, cx);
 4920        e.handle_input(") ", window, cx);
 4921    });
 4922    cx.assert_editor_state(
 4923        &([
 4924            "( one✅ ",
 4925            "three ",
 4926            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4927            "three ",
 4928            "five ) ˇ",
 4929        ]
 4930        .join("\n")),
 4931    );
 4932
 4933    // Cut with three selections, one of which is full-line.
 4934    cx.set_state(indoc! {"
 4935        1«2ˇ»3
 4936        4ˇ567
 4937        «8ˇ»9"});
 4938    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4939    cx.assert_editor_state(indoc! {"
 4940        1ˇ3
 4941        ˇ9"});
 4942
 4943    // Paste with three selections, noticing how the copied selection that was full-line
 4944    // gets inserted before the second cursor.
 4945    cx.set_state(indoc! {"
 4946        1ˇ3
 4947 4948        «oˇ»ne"});
 4949    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4950    cx.assert_editor_state(indoc! {"
 4951        12ˇ3
 4952        4567
 4953 4954        8ˇne"});
 4955
 4956    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4957    cx.set_state(indoc! {"
 4958        The quick brown
 4959        fox juˇmps over
 4960        the lazy dog"});
 4961    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4962    assert_eq!(
 4963        cx.read_from_clipboard()
 4964            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4965        Some("fox jumps over\n".to_string())
 4966    );
 4967
 4968    // Paste with three selections, noticing how the copied full-line selection is inserted
 4969    // before the empty selections but replaces the selection that is non-empty.
 4970    cx.set_state(indoc! {"
 4971        Tˇhe quick brown
 4972        «foˇ»x jumps over
 4973        tˇhe lazy dog"});
 4974    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4975    cx.assert_editor_state(indoc! {"
 4976        fox jumps over
 4977        Tˇhe quick brown
 4978        fox jumps over
 4979        ˇx jumps over
 4980        fox jumps over
 4981        tˇhe lazy dog"});
 4982}
 4983
 4984#[gpui::test]
 4985async fn test_copy_trim(cx: &mut TestAppContext) {
 4986    init_test(cx, |_| {});
 4987
 4988    let mut cx = EditorTestContext::new(cx).await;
 4989    cx.set_state(
 4990        r#"            «for selection in selections.iter() {
 4991            let mut start = selection.start;
 4992            let mut end = selection.end;
 4993            let is_entire_line = selection.is_empty();
 4994            if is_entire_line {
 4995                start = Point::new(start.row, 0);ˇ»
 4996                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4997            }
 4998        "#,
 4999    );
 5000    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5001    assert_eq!(
 5002        cx.read_from_clipboard()
 5003            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5004        Some(
 5005            "for selection in selections.iter() {
 5006            let mut start = selection.start;
 5007            let mut end = selection.end;
 5008            let is_entire_line = selection.is_empty();
 5009            if is_entire_line {
 5010                start = Point::new(start.row, 0);"
 5011                .to_string()
 5012        ),
 5013        "Regular copying preserves all indentation selected",
 5014    );
 5015    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5016    assert_eq!(
 5017        cx.read_from_clipboard()
 5018            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5019        Some(
 5020            "for selection in selections.iter() {
 5021let mut start = selection.start;
 5022let mut end = selection.end;
 5023let is_entire_line = selection.is_empty();
 5024if is_entire_line {
 5025    start = Point::new(start.row, 0);"
 5026                .to_string()
 5027        ),
 5028        "Copying with stripping should strip all leading whitespaces"
 5029    );
 5030
 5031    cx.set_state(
 5032        r#"       «     for selection in selections.iter() {
 5033            let mut start = selection.start;
 5034            let mut end = selection.end;
 5035            let is_entire_line = selection.is_empty();
 5036            if is_entire_line {
 5037                start = Point::new(start.row, 0);ˇ»
 5038                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5039            }
 5040        "#,
 5041    );
 5042    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5043    assert_eq!(
 5044        cx.read_from_clipboard()
 5045            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5046        Some(
 5047            "     for selection in selections.iter() {
 5048            let mut start = selection.start;
 5049            let mut end = selection.end;
 5050            let is_entire_line = selection.is_empty();
 5051            if is_entire_line {
 5052                start = Point::new(start.row, 0);"
 5053                .to_string()
 5054        ),
 5055        "Regular copying preserves all indentation selected",
 5056    );
 5057    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5058    assert_eq!(
 5059        cx.read_from_clipboard()
 5060            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5061        Some(
 5062            "for selection in selections.iter() {
 5063let mut start = selection.start;
 5064let mut end = selection.end;
 5065let is_entire_line = selection.is_empty();
 5066if is_entire_line {
 5067    start = Point::new(start.row, 0);"
 5068                .to_string()
 5069        ),
 5070        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5071    );
 5072
 5073    cx.set_state(
 5074        r#"       «ˇ     for selection in selections.iter() {
 5075            let mut start = selection.start;
 5076            let mut end = selection.end;
 5077            let is_entire_line = selection.is_empty();
 5078            if is_entire_line {
 5079                start = Point::new(start.row, 0);»
 5080                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5081            }
 5082        "#,
 5083    );
 5084    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5085    assert_eq!(
 5086        cx.read_from_clipboard()
 5087            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5088        Some(
 5089            "     for selection in selections.iter() {
 5090            let mut start = selection.start;
 5091            let mut end = selection.end;
 5092            let is_entire_line = selection.is_empty();
 5093            if is_entire_line {
 5094                start = Point::new(start.row, 0);"
 5095                .to_string()
 5096        ),
 5097        "Regular copying for reverse selection works the same",
 5098    );
 5099    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5100    assert_eq!(
 5101        cx.read_from_clipboard()
 5102            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5103        Some(
 5104            "for selection in selections.iter() {
 5105let mut start = selection.start;
 5106let mut end = selection.end;
 5107let is_entire_line = selection.is_empty();
 5108if is_entire_line {
 5109    start = Point::new(start.row, 0);"
 5110                .to_string()
 5111        ),
 5112        "Copying with stripping for reverse selection works the same"
 5113    );
 5114
 5115    cx.set_state(
 5116        r#"            for selection «in selections.iter() {
 5117            let mut start = selection.start;
 5118            let mut end = selection.end;
 5119            let is_entire_line = selection.is_empty();
 5120            if is_entire_line {
 5121                start = Point::new(start.row, 0);ˇ»
 5122                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5123            }
 5124        "#,
 5125    );
 5126    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5127    assert_eq!(
 5128        cx.read_from_clipboard()
 5129            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5130        Some(
 5131            "in selections.iter() {
 5132            let mut start = selection.start;
 5133            let mut end = selection.end;
 5134            let is_entire_line = selection.is_empty();
 5135            if is_entire_line {
 5136                start = Point::new(start.row, 0);"
 5137                .to_string()
 5138        ),
 5139        "When selecting past the indent, the copying works as usual",
 5140    );
 5141    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5142    assert_eq!(
 5143        cx.read_from_clipboard()
 5144            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5145        Some(
 5146            "in selections.iter() {
 5147            let mut start = selection.start;
 5148            let mut end = selection.end;
 5149            let is_entire_line = selection.is_empty();
 5150            if is_entire_line {
 5151                start = Point::new(start.row, 0);"
 5152                .to_string()
 5153        ),
 5154        "When selecting past the indent, nothing is trimmed"
 5155    );
 5156
 5157    cx.set_state(
 5158        r#"            «for selection in selections.iter() {
 5159            let mut start = selection.start;
 5160
 5161            let mut end = selection.end;
 5162            let is_entire_line = selection.is_empty();
 5163            if is_entire_line {
 5164                start = Point::new(start.row, 0);
 5165ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5166            }
 5167        "#,
 5168    );
 5169    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5170    assert_eq!(
 5171        cx.read_from_clipboard()
 5172            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5173        Some(
 5174            "for selection in selections.iter() {
 5175let mut start = selection.start;
 5176
 5177let mut end = selection.end;
 5178let is_entire_line = selection.is_empty();
 5179if is_entire_line {
 5180    start = Point::new(start.row, 0);
 5181"
 5182            .to_string()
 5183        ),
 5184        "Copying with stripping should ignore empty lines"
 5185    );
 5186}
 5187
 5188#[gpui::test]
 5189async fn test_paste_multiline(cx: &mut TestAppContext) {
 5190    init_test(cx, |_| {});
 5191
 5192    let mut cx = EditorTestContext::new(cx).await;
 5193    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5194
 5195    // Cut an indented block, without the leading whitespace.
 5196    cx.set_state(indoc! {"
 5197        const a: B = (
 5198            c(),
 5199            «d(
 5200                e,
 5201                f
 5202            )ˇ»
 5203        );
 5204    "});
 5205    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5206    cx.assert_editor_state(indoc! {"
 5207        const a: B = (
 5208            c(),
 5209            ˇ
 5210        );
 5211    "});
 5212
 5213    // Paste it at the same position.
 5214    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5215    cx.assert_editor_state(indoc! {"
 5216        const a: B = (
 5217            c(),
 5218            d(
 5219                e,
 5220                f
 5221 5222        );
 5223    "});
 5224
 5225    // Paste it at a line with a lower indent level.
 5226    cx.set_state(indoc! {"
 5227        ˇ
 5228        const a: B = (
 5229            c(),
 5230        );
 5231    "});
 5232    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5233    cx.assert_editor_state(indoc! {"
 5234        d(
 5235            e,
 5236            f
 5237 5238        const a: B = (
 5239            c(),
 5240        );
 5241    "});
 5242
 5243    // Cut an indented block, with the leading whitespace.
 5244    cx.set_state(indoc! {"
 5245        const a: B = (
 5246            c(),
 5247        «    d(
 5248                e,
 5249                f
 5250            )
 5251        ˇ»);
 5252    "});
 5253    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5254    cx.assert_editor_state(indoc! {"
 5255        const a: B = (
 5256            c(),
 5257        ˇ);
 5258    "});
 5259
 5260    // Paste it at the same position.
 5261    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5262    cx.assert_editor_state(indoc! {"
 5263        const a: B = (
 5264            c(),
 5265            d(
 5266                e,
 5267                f
 5268            )
 5269        ˇ);
 5270    "});
 5271
 5272    // Paste it at a line with a higher indent level.
 5273    cx.set_state(indoc! {"
 5274        const a: B = (
 5275            c(),
 5276            d(
 5277                e,
 5278 5279            )
 5280        );
 5281    "});
 5282    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5283    cx.assert_editor_state(indoc! {"
 5284        const a: B = (
 5285            c(),
 5286            d(
 5287                e,
 5288                f    d(
 5289                    e,
 5290                    f
 5291                )
 5292        ˇ
 5293            )
 5294        );
 5295    "});
 5296
 5297    // Copy an indented block, starting mid-line
 5298    cx.set_state(indoc! {"
 5299        const a: B = (
 5300            c(),
 5301            somethin«g(
 5302                e,
 5303                f
 5304            )ˇ»
 5305        );
 5306    "});
 5307    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5308
 5309    // Paste it on a line with a lower indent level
 5310    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5311    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5312    cx.assert_editor_state(indoc! {"
 5313        const a: B = (
 5314            c(),
 5315            something(
 5316                e,
 5317                f
 5318            )
 5319        );
 5320        g(
 5321            e,
 5322            f
 5323"});
 5324}
 5325
 5326#[gpui::test]
 5327async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5328    init_test(cx, |_| {});
 5329
 5330    cx.write_to_clipboard(ClipboardItem::new_string(
 5331        "    d(\n        e\n    );\n".into(),
 5332    ));
 5333
 5334    let mut cx = EditorTestContext::new(cx).await;
 5335    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5336
 5337    cx.set_state(indoc! {"
 5338        fn a() {
 5339            b();
 5340            if c() {
 5341                ˇ
 5342            }
 5343        }
 5344    "});
 5345
 5346    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5347    cx.assert_editor_state(indoc! {"
 5348        fn a() {
 5349            b();
 5350            if c() {
 5351                d(
 5352                    e
 5353                );
 5354        ˇ
 5355            }
 5356        }
 5357    "});
 5358
 5359    cx.set_state(indoc! {"
 5360        fn a() {
 5361            b();
 5362            ˇ
 5363        }
 5364    "});
 5365
 5366    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5367    cx.assert_editor_state(indoc! {"
 5368        fn a() {
 5369            b();
 5370            d(
 5371                e
 5372            );
 5373        ˇ
 5374        }
 5375    "});
 5376}
 5377
 5378#[gpui::test]
 5379fn test_select_all(cx: &mut TestAppContext) {
 5380    init_test(cx, |_| {});
 5381
 5382    let editor = cx.add_window(|window, cx| {
 5383        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5384        build_editor(buffer, window, cx)
 5385    });
 5386    _ = editor.update(cx, |editor, window, cx| {
 5387        editor.select_all(&SelectAll, window, cx);
 5388        assert_eq!(
 5389            editor.selections.display_ranges(cx),
 5390            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5391        );
 5392    });
 5393}
 5394
 5395#[gpui::test]
 5396fn test_select_line(cx: &mut TestAppContext) {
 5397    init_test(cx, |_| {});
 5398
 5399    let editor = cx.add_window(|window, cx| {
 5400        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5401        build_editor(buffer, window, cx)
 5402    });
 5403    _ = editor.update(cx, |editor, window, cx| {
 5404        editor.change_selections(None, window, cx, |s| {
 5405            s.select_display_ranges([
 5406                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5407                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5408                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5409                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5410            ])
 5411        });
 5412        editor.select_line(&SelectLine, window, cx);
 5413        assert_eq!(
 5414            editor.selections.display_ranges(cx),
 5415            vec![
 5416                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5417                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5418            ]
 5419        );
 5420    });
 5421
 5422    _ = editor.update(cx, |editor, window, cx| {
 5423        editor.select_line(&SelectLine, window, cx);
 5424        assert_eq!(
 5425            editor.selections.display_ranges(cx),
 5426            vec![
 5427                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5428                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5429            ]
 5430        );
 5431    });
 5432
 5433    _ = editor.update(cx, |editor, window, cx| {
 5434        editor.select_line(&SelectLine, window, cx);
 5435        assert_eq!(
 5436            editor.selections.display_ranges(cx),
 5437            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5438        );
 5439    });
 5440}
 5441
 5442#[gpui::test]
 5443async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5444    init_test(cx, |_| {});
 5445    let mut cx = EditorTestContext::new(cx).await;
 5446
 5447    #[track_caller]
 5448    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5449        cx.set_state(initial_state);
 5450        cx.update_editor(|e, window, cx| {
 5451            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5452        });
 5453        cx.assert_editor_state(expected_state);
 5454    }
 5455
 5456    // Selection starts and ends at the middle of lines, left-to-right
 5457    test(
 5458        &mut cx,
 5459        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5460        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5461    );
 5462    // Same thing, right-to-left
 5463    test(
 5464        &mut cx,
 5465        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5466        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5467    );
 5468
 5469    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5470    test(
 5471        &mut cx,
 5472        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5473        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5474    );
 5475    // Same thing, right-to-left
 5476    test(
 5477        &mut cx,
 5478        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5479        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5480    );
 5481
 5482    // Whole buffer, left-to-right, last line ends with newline
 5483    test(
 5484        &mut cx,
 5485        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5486        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5487    );
 5488    // Same thing, right-to-left
 5489    test(
 5490        &mut cx,
 5491        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5492        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5493    );
 5494
 5495    // Starts at the end of a line, ends at the start of another
 5496    test(
 5497        &mut cx,
 5498        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5499        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5500    );
 5501}
 5502
 5503#[gpui::test]
 5504async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5505    init_test(cx, |_| {});
 5506
 5507    let editor = cx.add_window(|window, cx| {
 5508        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5509        build_editor(buffer, window, cx)
 5510    });
 5511
 5512    // setup
 5513    _ = editor.update(cx, |editor, window, cx| {
 5514        editor.fold_creases(
 5515            vec![
 5516                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5517                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5518                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5519            ],
 5520            true,
 5521            window,
 5522            cx,
 5523        );
 5524        assert_eq!(
 5525            editor.display_text(cx),
 5526            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5527        );
 5528    });
 5529
 5530    _ = editor.update(cx, |editor, window, cx| {
 5531        editor.change_selections(None, window, cx, |s| {
 5532            s.select_display_ranges([
 5533                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5534                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5535                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5536                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5537            ])
 5538        });
 5539        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5540        assert_eq!(
 5541            editor.display_text(cx),
 5542            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5543        );
 5544    });
 5545    EditorTestContext::for_editor(editor, cx)
 5546        .await
 5547        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5548
 5549    _ = editor.update(cx, |editor, window, cx| {
 5550        editor.change_selections(None, window, cx, |s| {
 5551            s.select_display_ranges([
 5552                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5553            ])
 5554        });
 5555        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5556        assert_eq!(
 5557            editor.display_text(cx),
 5558            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5559        );
 5560        assert_eq!(
 5561            editor.selections.display_ranges(cx),
 5562            [
 5563                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5564                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5565                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5566                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5567                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5568                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5569                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5570            ]
 5571        );
 5572    });
 5573    EditorTestContext::for_editor(editor, cx)
 5574        .await
 5575        .assert_editor_state(
 5576            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5577        );
 5578}
 5579
 5580#[gpui::test]
 5581async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5582    init_test(cx, |_| {});
 5583
 5584    let mut cx = EditorTestContext::new(cx).await;
 5585
 5586    cx.set_state(indoc!(
 5587        r#"abc
 5588           defˇghi
 5589
 5590           jk
 5591           nlmo
 5592           "#
 5593    ));
 5594
 5595    cx.update_editor(|editor, window, cx| {
 5596        editor.add_selection_above(&Default::default(), window, cx);
 5597    });
 5598
 5599    cx.assert_editor_state(indoc!(
 5600        r#"abcˇ
 5601           defˇghi
 5602
 5603           jk
 5604           nlmo
 5605           "#
 5606    ));
 5607
 5608    cx.update_editor(|editor, window, cx| {
 5609        editor.add_selection_above(&Default::default(), window, cx);
 5610    });
 5611
 5612    cx.assert_editor_state(indoc!(
 5613        r#"abcˇ
 5614            defˇghi
 5615
 5616            jk
 5617            nlmo
 5618            "#
 5619    ));
 5620
 5621    cx.update_editor(|editor, window, cx| {
 5622        editor.add_selection_below(&Default::default(), window, cx);
 5623    });
 5624
 5625    cx.assert_editor_state(indoc!(
 5626        r#"abc
 5627           defˇghi
 5628
 5629           jk
 5630           nlmo
 5631           "#
 5632    ));
 5633
 5634    cx.update_editor(|editor, window, cx| {
 5635        editor.undo_selection(&Default::default(), window, cx);
 5636    });
 5637
 5638    cx.assert_editor_state(indoc!(
 5639        r#"abcˇ
 5640           defˇghi
 5641
 5642           jk
 5643           nlmo
 5644           "#
 5645    ));
 5646
 5647    cx.update_editor(|editor, window, cx| {
 5648        editor.redo_selection(&Default::default(), window, cx);
 5649    });
 5650
 5651    cx.assert_editor_state(indoc!(
 5652        r#"abc
 5653           defˇghi
 5654
 5655           jk
 5656           nlmo
 5657           "#
 5658    ));
 5659
 5660    cx.update_editor(|editor, window, cx| {
 5661        editor.add_selection_below(&Default::default(), window, cx);
 5662    });
 5663
 5664    cx.assert_editor_state(indoc!(
 5665        r#"abc
 5666           defˇghi
 5667
 5668           jk
 5669           nlmˇo
 5670           "#
 5671    ));
 5672
 5673    cx.update_editor(|editor, window, cx| {
 5674        editor.add_selection_below(&Default::default(), window, cx);
 5675    });
 5676
 5677    cx.assert_editor_state(indoc!(
 5678        r#"abc
 5679           defˇghi
 5680
 5681           jk
 5682           nlmˇo
 5683           "#
 5684    ));
 5685
 5686    // change selections
 5687    cx.set_state(indoc!(
 5688        r#"abc
 5689           def«ˇg»hi
 5690
 5691           jk
 5692           nlmo
 5693           "#
 5694    ));
 5695
 5696    cx.update_editor(|editor, window, cx| {
 5697        editor.add_selection_below(&Default::default(), window, cx);
 5698    });
 5699
 5700    cx.assert_editor_state(indoc!(
 5701        r#"abc
 5702           def«ˇg»hi
 5703
 5704           jk
 5705           nlm«ˇo»
 5706           "#
 5707    ));
 5708
 5709    cx.update_editor(|editor, window, cx| {
 5710        editor.add_selection_below(&Default::default(), window, cx);
 5711    });
 5712
 5713    cx.assert_editor_state(indoc!(
 5714        r#"abc
 5715           def«ˇg»hi
 5716
 5717           jk
 5718           nlm«ˇo»
 5719           "#
 5720    ));
 5721
 5722    cx.update_editor(|editor, window, cx| {
 5723        editor.add_selection_above(&Default::default(), window, cx);
 5724    });
 5725
 5726    cx.assert_editor_state(indoc!(
 5727        r#"abc
 5728           def«ˇg»hi
 5729
 5730           jk
 5731           nlmo
 5732           "#
 5733    ));
 5734
 5735    cx.update_editor(|editor, window, cx| {
 5736        editor.add_selection_above(&Default::default(), window, cx);
 5737    });
 5738
 5739    cx.assert_editor_state(indoc!(
 5740        r#"abc
 5741           def«ˇg»hi
 5742
 5743           jk
 5744           nlmo
 5745           "#
 5746    ));
 5747
 5748    // Change selections again
 5749    cx.set_state(indoc!(
 5750        r#"a«bc
 5751           defgˇ»hi
 5752
 5753           jk
 5754           nlmo
 5755           "#
 5756    ));
 5757
 5758    cx.update_editor(|editor, window, cx| {
 5759        editor.add_selection_below(&Default::default(), window, cx);
 5760    });
 5761
 5762    cx.assert_editor_state(indoc!(
 5763        r#"a«bcˇ»
 5764           d«efgˇ»hi
 5765
 5766           j«kˇ»
 5767           nlmo
 5768           "#
 5769    ));
 5770
 5771    cx.update_editor(|editor, window, cx| {
 5772        editor.add_selection_below(&Default::default(), window, cx);
 5773    });
 5774    cx.assert_editor_state(indoc!(
 5775        r#"a«bcˇ»
 5776           d«efgˇ»hi
 5777
 5778           j«kˇ»
 5779           n«lmoˇ»
 5780           "#
 5781    ));
 5782    cx.update_editor(|editor, window, cx| {
 5783        editor.add_selection_above(&Default::default(), window, cx);
 5784    });
 5785
 5786    cx.assert_editor_state(indoc!(
 5787        r#"a«bcˇ»
 5788           d«efgˇ»hi
 5789
 5790           j«kˇ»
 5791           nlmo
 5792           "#
 5793    ));
 5794
 5795    // Change selections again
 5796    cx.set_state(indoc!(
 5797        r#"abc
 5798           d«ˇefghi
 5799
 5800           jk
 5801           nlm»o
 5802           "#
 5803    ));
 5804
 5805    cx.update_editor(|editor, window, cx| {
 5806        editor.add_selection_above(&Default::default(), window, cx);
 5807    });
 5808
 5809    cx.assert_editor_state(indoc!(
 5810        r#"a«ˇbc»
 5811           d«ˇef»ghi
 5812
 5813           j«ˇk»
 5814           n«ˇlm»o
 5815           "#
 5816    ));
 5817
 5818    cx.update_editor(|editor, window, cx| {
 5819        editor.add_selection_below(&Default::default(), window, cx);
 5820    });
 5821
 5822    cx.assert_editor_state(indoc!(
 5823        r#"abc
 5824           d«ˇef»ghi
 5825
 5826           j«ˇk»
 5827           n«ˇlm»o
 5828           "#
 5829    ));
 5830}
 5831
 5832#[gpui::test]
 5833async fn test_select_next(cx: &mut TestAppContext) {
 5834    init_test(cx, |_| {});
 5835
 5836    let mut cx = EditorTestContext::new(cx).await;
 5837    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5838
 5839    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5840        .unwrap();
 5841    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5842
 5843    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5844        .unwrap();
 5845    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5846
 5847    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5848    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5849
 5850    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5851    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5852
 5853    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5854        .unwrap();
 5855    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5856
 5857    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5858        .unwrap();
 5859    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5860
 5861    // Test selection direction should be preserved
 5862    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5863
 5864    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5865        .unwrap();
 5866    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5867}
 5868
 5869#[gpui::test]
 5870async fn test_select_all_matches(cx: &mut TestAppContext) {
 5871    init_test(cx, |_| {});
 5872
 5873    let mut cx = EditorTestContext::new(cx).await;
 5874
 5875    // Test caret-only selections
 5876    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5877    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5878        .unwrap();
 5879    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5880
 5881    // Test left-to-right selections
 5882    cx.set_state("abc\n«abcˇ»\nabc");
 5883    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5884        .unwrap();
 5885    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5886
 5887    // Test right-to-left selections
 5888    cx.set_state("abc\n«ˇabc»\nabc");
 5889    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5890        .unwrap();
 5891    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5892
 5893    // Test selecting whitespace with caret selection
 5894    cx.set_state("abc\nˇ   abc\nabc");
 5895    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5896        .unwrap();
 5897    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5898
 5899    // Test selecting whitespace with left-to-right selection
 5900    cx.set_state("abc\n«ˇ  »abc\nabc");
 5901    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5902        .unwrap();
 5903    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5904
 5905    // Test no matches with right-to-left selection
 5906    cx.set_state("abc\n«  ˇ»abc\nabc");
 5907    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5908        .unwrap();
 5909    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5910}
 5911
 5912#[gpui::test]
 5913async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5914    init_test(cx, |_| {});
 5915
 5916    let mut cx = EditorTestContext::new(cx).await;
 5917
 5918    let large_body_1 = "\nd".repeat(200);
 5919    let large_body_2 = "\ne".repeat(200);
 5920
 5921    cx.set_state(&format!(
 5922        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5923    ));
 5924    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5925        let scroll_position = editor.scroll_position(cx);
 5926        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5927        scroll_position
 5928    });
 5929
 5930    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5931        .unwrap();
 5932    cx.assert_editor_state(&format!(
 5933        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5934    ));
 5935    let scroll_position_after_selection =
 5936        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5937    assert_eq!(
 5938        initial_scroll_position, scroll_position_after_selection,
 5939        "Scroll position should not change after selecting all matches"
 5940    );
 5941}
 5942
 5943#[gpui::test]
 5944async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5945    init_test(cx, |_| {});
 5946
 5947    let mut cx = EditorLspTestContext::new_rust(
 5948        lsp::ServerCapabilities {
 5949            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5950            ..Default::default()
 5951        },
 5952        cx,
 5953    )
 5954    .await;
 5955
 5956    cx.set_state(indoc! {"
 5957        line 1
 5958        line 2
 5959        linˇe 3
 5960        line 4
 5961        line 5
 5962    "});
 5963
 5964    // Make an edit
 5965    cx.update_editor(|editor, window, cx| {
 5966        editor.handle_input("X", window, cx);
 5967    });
 5968
 5969    // Move cursor to a different position
 5970    cx.update_editor(|editor, window, cx| {
 5971        editor.change_selections(None, window, cx, |s| {
 5972            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5973        });
 5974    });
 5975
 5976    cx.assert_editor_state(indoc! {"
 5977        line 1
 5978        line 2
 5979        linXe 3
 5980        line 4
 5981        liˇne 5
 5982    "});
 5983
 5984    cx.lsp
 5985        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5986            Ok(Some(vec![lsp::TextEdit::new(
 5987                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5988                "PREFIX ".to_string(),
 5989            )]))
 5990        });
 5991
 5992    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5993        .unwrap()
 5994        .await
 5995        .unwrap();
 5996
 5997    cx.assert_editor_state(indoc! {"
 5998        PREFIX line 1
 5999        line 2
 6000        linXe 3
 6001        line 4
 6002        liˇne 5
 6003    "});
 6004
 6005    // Undo formatting
 6006    cx.update_editor(|editor, window, cx| {
 6007        editor.undo(&Default::default(), window, cx);
 6008    });
 6009
 6010    // Verify cursor moved back to position after edit
 6011    cx.assert_editor_state(indoc! {"
 6012        line 1
 6013        line 2
 6014        linXˇe 3
 6015        line 4
 6016        line 5
 6017    "});
 6018}
 6019
 6020#[gpui::test]
 6021async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6022    init_test(cx, |_| {});
 6023
 6024    let mut cx = EditorTestContext::new(cx).await;
 6025    cx.set_state(
 6026        r#"let foo = 2;
 6027lˇet foo = 2;
 6028let fooˇ = 2;
 6029let foo = 2;
 6030let foo = ˇ2;"#,
 6031    );
 6032
 6033    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6034        .unwrap();
 6035    cx.assert_editor_state(
 6036        r#"let foo = 2;
 6037«letˇ» foo = 2;
 6038let «fooˇ» = 2;
 6039let foo = 2;
 6040let foo = «2ˇ»;"#,
 6041    );
 6042
 6043    // noop for multiple selections with different contents
 6044    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6045        .unwrap();
 6046    cx.assert_editor_state(
 6047        r#"let foo = 2;
 6048«letˇ» foo = 2;
 6049let «fooˇ» = 2;
 6050let foo = 2;
 6051let foo = «2ˇ»;"#,
 6052    );
 6053
 6054    // Test last selection direction should be preserved
 6055    cx.set_state(
 6056        r#"let foo = 2;
 6057let foo = 2;
 6058let «fooˇ» = 2;
 6059let «ˇfoo» = 2;
 6060let foo = 2;"#,
 6061    );
 6062
 6063    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6064        .unwrap();
 6065    cx.assert_editor_state(
 6066        r#"let foo = 2;
 6067let foo = 2;
 6068let «fooˇ» = 2;
 6069let «ˇfoo» = 2;
 6070let «ˇfoo» = 2;"#,
 6071    );
 6072}
 6073
 6074#[gpui::test]
 6075async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6076    init_test(cx, |_| {});
 6077
 6078    let mut cx =
 6079        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6080
 6081    cx.assert_editor_state(indoc! {"
 6082        ˇbbb
 6083        ccc
 6084
 6085        bbb
 6086        ccc
 6087        "});
 6088    cx.dispatch_action(SelectPrevious::default());
 6089    cx.assert_editor_state(indoc! {"
 6090                «bbbˇ»
 6091                ccc
 6092
 6093                bbb
 6094                ccc
 6095                "});
 6096    cx.dispatch_action(SelectPrevious::default());
 6097    cx.assert_editor_state(indoc! {"
 6098                «bbbˇ»
 6099                ccc
 6100
 6101                «bbbˇ»
 6102                ccc
 6103                "});
 6104}
 6105
 6106#[gpui::test]
 6107async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6108    init_test(cx, |_| {});
 6109
 6110    let mut cx = EditorTestContext::new(cx).await;
 6111    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6112
 6113    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6114        .unwrap();
 6115    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6116
 6117    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6118        .unwrap();
 6119    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6120
 6121    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6122    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6123
 6124    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6125    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6126
 6127    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6128        .unwrap();
 6129    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6130
 6131    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6132        .unwrap();
 6133    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6134}
 6135
 6136#[gpui::test]
 6137async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6138    init_test(cx, |_| {});
 6139
 6140    let mut cx = EditorTestContext::new(cx).await;
 6141    cx.set_state("");
 6142
 6143    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6144        .unwrap();
 6145    cx.assert_editor_state("«aˇ»");
 6146    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6147        .unwrap();
 6148    cx.assert_editor_state("«aˇ»");
 6149}
 6150
 6151#[gpui::test]
 6152async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6153    init_test(cx, |_| {});
 6154
 6155    let mut cx = EditorTestContext::new(cx).await;
 6156    cx.set_state(
 6157        r#"let foo = 2;
 6158lˇet foo = 2;
 6159let fooˇ = 2;
 6160let foo = 2;
 6161let foo = ˇ2;"#,
 6162    );
 6163
 6164    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6165        .unwrap();
 6166    cx.assert_editor_state(
 6167        r#"let foo = 2;
 6168«letˇ» foo = 2;
 6169let «fooˇ» = 2;
 6170let foo = 2;
 6171let foo = «2ˇ»;"#,
 6172    );
 6173
 6174    // noop for multiple selections with different contents
 6175    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6176        .unwrap();
 6177    cx.assert_editor_state(
 6178        r#"let foo = 2;
 6179«letˇ» foo = 2;
 6180let «fooˇ» = 2;
 6181let foo = 2;
 6182let foo = «2ˇ»;"#,
 6183    );
 6184}
 6185
 6186#[gpui::test]
 6187async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6188    init_test(cx, |_| {});
 6189
 6190    let mut cx = EditorTestContext::new(cx).await;
 6191    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6192
 6193    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6194        .unwrap();
 6195    // selection direction is preserved
 6196    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6197
 6198    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6199        .unwrap();
 6200    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6201
 6202    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6203    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6204
 6205    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6206    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6207
 6208    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6209        .unwrap();
 6210    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6211
 6212    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6213        .unwrap();
 6214    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6215}
 6216
 6217#[gpui::test]
 6218async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6219    init_test(cx, |_| {});
 6220
 6221    let language = Arc::new(Language::new(
 6222        LanguageConfig::default(),
 6223        Some(tree_sitter_rust::LANGUAGE.into()),
 6224    ));
 6225
 6226    let text = r#"
 6227        use mod1::mod2::{mod3, mod4};
 6228
 6229        fn fn_1(param1: bool, param2: &str) {
 6230            let var1 = "text";
 6231        }
 6232    "#
 6233    .unindent();
 6234
 6235    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6236    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6237    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6238
 6239    editor
 6240        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6241        .await;
 6242
 6243    editor.update_in(cx, |editor, window, cx| {
 6244        editor.change_selections(None, window, cx, |s| {
 6245            s.select_display_ranges([
 6246                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6247                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6248                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6249            ]);
 6250        });
 6251        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6252    });
 6253    editor.update(cx, |editor, cx| {
 6254        assert_text_with_selections(
 6255            editor,
 6256            indoc! {r#"
 6257                use mod1::mod2::{mod3, «mod4ˇ»};
 6258
 6259                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6260                    let var1 = "«ˇtext»";
 6261                }
 6262            "#},
 6263            cx,
 6264        );
 6265    });
 6266
 6267    editor.update_in(cx, |editor, window, cx| {
 6268        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6269    });
 6270    editor.update(cx, |editor, cx| {
 6271        assert_text_with_selections(
 6272            editor,
 6273            indoc! {r#"
 6274                use mod1::mod2::«{mod3, mod4}ˇ»;
 6275
 6276                «ˇfn fn_1(param1: bool, param2: &str) {
 6277                    let var1 = "text";
 6278 6279            "#},
 6280            cx,
 6281        );
 6282    });
 6283
 6284    editor.update_in(cx, |editor, window, cx| {
 6285        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6286    });
 6287    assert_eq!(
 6288        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6289        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6290    );
 6291
 6292    // Trying to expand the selected syntax node one more time has no effect.
 6293    editor.update_in(cx, |editor, window, cx| {
 6294        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6295    });
 6296    assert_eq!(
 6297        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6298        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6299    );
 6300
 6301    editor.update_in(cx, |editor, window, cx| {
 6302        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6303    });
 6304    editor.update(cx, |editor, cx| {
 6305        assert_text_with_selections(
 6306            editor,
 6307            indoc! {r#"
 6308                use mod1::mod2::«{mod3, mod4}ˇ»;
 6309
 6310                «ˇfn fn_1(param1: bool, param2: &str) {
 6311                    let var1 = "text";
 6312 6313            "#},
 6314            cx,
 6315        );
 6316    });
 6317
 6318    editor.update_in(cx, |editor, window, cx| {
 6319        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6320    });
 6321    editor.update(cx, |editor, cx| {
 6322        assert_text_with_selections(
 6323            editor,
 6324            indoc! {r#"
 6325                use mod1::mod2::{mod3, «mod4ˇ»};
 6326
 6327                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6328                    let var1 = "«ˇtext»";
 6329                }
 6330            "#},
 6331            cx,
 6332        );
 6333    });
 6334
 6335    editor.update_in(cx, |editor, window, cx| {
 6336        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6337    });
 6338    editor.update(cx, |editor, cx| {
 6339        assert_text_with_selections(
 6340            editor,
 6341            indoc! {r#"
 6342                use mod1::mod2::{mod3, mo«ˇ»d4};
 6343
 6344                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6345                    let var1 = "te«ˇ»xt";
 6346                }
 6347            "#},
 6348            cx,
 6349        );
 6350    });
 6351
 6352    // Trying to shrink the selected syntax node one more time has no effect.
 6353    editor.update_in(cx, |editor, window, cx| {
 6354        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6355    });
 6356    editor.update_in(cx, |editor, _, cx| {
 6357        assert_text_with_selections(
 6358            editor,
 6359            indoc! {r#"
 6360                use mod1::mod2::{mod3, mo«ˇ»d4};
 6361
 6362                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6363                    let var1 = "te«ˇ»xt";
 6364                }
 6365            "#},
 6366            cx,
 6367        );
 6368    });
 6369
 6370    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6371    // a fold.
 6372    editor.update_in(cx, |editor, window, cx| {
 6373        editor.fold_creases(
 6374            vec![
 6375                Crease::simple(
 6376                    Point::new(0, 21)..Point::new(0, 24),
 6377                    FoldPlaceholder::test(),
 6378                ),
 6379                Crease::simple(
 6380                    Point::new(3, 20)..Point::new(3, 22),
 6381                    FoldPlaceholder::test(),
 6382                ),
 6383            ],
 6384            true,
 6385            window,
 6386            cx,
 6387        );
 6388        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6389    });
 6390    editor.update(cx, |editor, cx| {
 6391        assert_text_with_selections(
 6392            editor,
 6393            indoc! {r#"
 6394                use mod1::mod2::«{mod3, mod4}ˇ»;
 6395
 6396                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6397                    let var1 = "«ˇtext»";
 6398                }
 6399            "#},
 6400            cx,
 6401        );
 6402    });
 6403}
 6404
 6405#[gpui::test]
 6406async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6407    init_test(cx, |_| {});
 6408
 6409    let language = Arc::new(Language::new(
 6410        LanguageConfig::default(),
 6411        Some(tree_sitter_rust::LANGUAGE.into()),
 6412    ));
 6413
 6414    let text = r#"
 6415        use mod1::mod2::{mod3, mod4};
 6416
 6417        fn fn_1(param1: bool, param2: &str) {
 6418            let var1 = "hello world";
 6419        }
 6420    "#
 6421    .unindent();
 6422
 6423    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6424    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6425    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6426
 6427    editor
 6428        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6429        .await;
 6430
 6431    // Test 1: Cursor on a letter of a string word
 6432    editor.update_in(cx, |editor, window, cx| {
 6433        editor.change_selections(None, window, cx, |s| {
 6434            s.select_display_ranges([
 6435                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6436            ]);
 6437        });
 6438    });
 6439    editor.update_in(cx, |editor, window, cx| {
 6440        assert_text_with_selections(
 6441            editor,
 6442            indoc! {r#"
 6443                use mod1::mod2::{mod3, mod4};
 6444
 6445                fn fn_1(param1: bool, param2: &str) {
 6446                    let var1 = "hˇello world";
 6447                }
 6448            "#},
 6449            cx,
 6450        );
 6451        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6452        assert_text_with_selections(
 6453            editor,
 6454            indoc! {r#"
 6455                use mod1::mod2::{mod3, mod4};
 6456
 6457                fn fn_1(param1: bool, param2: &str) {
 6458                    let var1 = "«ˇhello» world";
 6459                }
 6460            "#},
 6461            cx,
 6462        );
 6463    });
 6464
 6465    // Test 2: Partial selection within a word
 6466    editor.update_in(cx, |editor, window, cx| {
 6467        editor.change_selections(None, window, cx, |s| {
 6468            s.select_display_ranges([
 6469                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6470            ]);
 6471        });
 6472    });
 6473    editor.update_in(cx, |editor, window, cx| {
 6474        assert_text_with_selections(
 6475            editor,
 6476            indoc! {r#"
 6477                use mod1::mod2::{mod3, mod4};
 6478
 6479                fn fn_1(param1: bool, param2: &str) {
 6480                    let var1 = "h«elˇ»lo world";
 6481                }
 6482            "#},
 6483            cx,
 6484        );
 6485        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6486        assert_text_with_selections(
 6487            editor,
 6488            indoc! {r#"
 6489                use mod1::mod2::{mod3, mod4};
 6490
 6491                fn fn_1(param1: bool, param2: &str) {
 6492                    let var1 = "«ˇhello» world";
 6493                }
 6494            "#},
 6495            cx,
 6496        );
 6497    });
 6498
 6499    // Test 3: Complete word already selected
 6500    editor.update_in(cx, |editor, window, cx| {
 6501        editor.change_selections(None, window, cx, |s| {
 6502            s.select_display_ranges([
 6503                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6504            ]);
 6505        });
 6506    });
 6507    editor.update_in(cx, |editor, window, cx| {
 6508        assert_text_with_selections(
 6509            editor,
 6510            indoc! {r#"
 6511                use mod1::mod2::{mod3, mod4};
 6512
 6513                fn fn_1(param1: bool, param2: &str) {
 6514                    let var1 = "«helloˇ» world";
 6515                }
 6516            "#},
 6517            cx,
 6518        );
 6519        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6520        assert_text_with_selections(
 6521            editor,
 6522            indoc! {r#"
 6523                use mod1::mod2::{mod3, mod4};
 6524
 6525                fn fn_1(param1: bool, param2: &str) {
 6526                    let var1 = "«hello worldˇ»";
 6527                }
 6528            "#},
 6529            cx,
 6530        );
 6531    });
 6532
 6533    // Test 4: Selection spanning across words
 6534    editor.update_in(cx, |editor, window, cx| {
 6535        editor.change_selections(None, window, cx, |s| {
 6536            s.select_display_ranges([
 6537                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6538            ]);
 6539        });
 6540    });
 6541    editor.update_in(cx, |editor, window, cx| {
 6542        assert_text_with_selections(
 6543            editor,
 6544            indoc! {r#"
 6545                use mod1::mod2::{mod3, mod4};
 6546
 6547                fn fn_1(param1: bool, param2: &str) {
 6548                    let var1 = "hel«lo woˇ»rld";
 6549                }
 6550            "#},
 6551            cx,
 6552        );
 6553        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6554        assert_text_with_selections(
 6555            editor,
 6556            indoc! {r#"
 6557                use mod1::mod2::{mod3, mod4};
 6558
 6559                fn fn_1(param1: bool, param2: &str) {
 6560                    let var1 = "«ˇhello world»";
 6561                }
 6562            "#},
 6563            cx,
 6564        );
 6565    });
 6566
 6567    // Test 5: Expansion beyond string
 6568    editor.update_in(cx, |editor, window, cx| {
 6569        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6570        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6571        assert_text_with_selections(
 6572            editor,
 6573            indoc! {r#"
 6574                use mod1::mod2::{mod3, mod4};
 6575
 6576                fn fn_1(param1: bool, param2: &str) {
 6577                    «ˇlet var1 = "hello world";»
 6578                }
 6579            "#},
 6580            cx,
 6581        );
 6582    });
 6583}
 6584
 6585#[gpui::test]
 6586async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6587    init_test(cx, |_| {});
 6588
 6589    let base_text = r#"
 6590        impl A {
 6591            // this is an uncommitted comment
 6592
 6593            fn b() {
 6594                c();
 6595            }
 6596
 6597            // this is another uncommitted comment
 6598
 6599            fn d() {
 6600                // e
 6601                // f
 6602            }
 6603        }
 6604
 6605        fn g() {
 6606            // h
 6607        }
 6608    "#
 6609    .unindent();
 6610
 6611    let text = r#"
 6612        ˇimpl A {
 6613
 6614            fn b() {
 6615                c();
 6616            }
 6617
 6618            fn d() {
 6619                // e
 6620                // f
 6621            }
 6622        }
 6623
 6624        fn g() {
 6625            // h
 6626        }
 6627    "#
 6628    .unindent();
 6629
 6630    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6631    cx.set_state(&text);
 6632    cx.set_head_text(&base_text);
 6633    cx.update_editor(|editor, window, cx| {
 6634        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6635    });
 6636
 6637    cx.assert_state_with_diff(
 6638        "
 6639        ˇimpl A {
 6640      -     // this is an uncommitted comment
 6641
 6642            fn b() {
 6643                c();
 6644            }
 6645
 6646      -     // this is another uncommitted comment
 6647      -
 6648            fn d() {
 6649                // e
 6650                // f
 6651            }
 6652        }
 6653
 6654        fn g() {
 6655            // h
 6656        }
 6657    "
 6658        .unindent(),
 6659    );
 6660
 6661    let expected_display_text = "
 6662        impl A {
 6663            // this is an uncommitted comment
 6664
 6665            fn b() {
 6666 6667            }
 6668
 6669            // this is another uncommitted comment
 6670
 6671            fn d() {
 6672 6673            }
 6674        }
 6675
 6676        fn g() {
 6677 6678        }
 6679        "
 6680    .unindent();
 6681
 6682    cx.update_editor(|editor, window, cx| {
 6683        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6684        assert_eq!(editor.display_text(cx), expected_display_text);
 6685    });
 6686}
 6687
 6688#[gpui::test]
 6689async fn test_autoindent(cx: &mut TestAppContext) {
 6690    init_test(cx, |_| {});
 6691
 6692    let language = Arc::new(
 6693        Language::new(
 6694            LanguageConfig {
 6695                brackets: BracketPairConfig {
 6696                    pairs: vec![
 6697                        BracketPair {
 6698                            start: "{".to_string(),
 6699                            end: "}".to_string(),
 6700                            close: false,
 6701                            surround: false,
 6702                            newline: true,
 6703                        },
 6704                        BracketPair {
 6705                            start: "(".to_string(),
 6706                            end: ")".to_string(),
 6707                            close: false,
 6708                            surround: false,
 6709                            newline: true,
 6710                        },
 6711                    ],
 6712                    ..Default::default()
 6713                },
 6714                ..Default::default()
 6715            },
 6716            Some(tree_sitter_rust::LANGUAGE.into()),
 6717        )
 6718        .with_indents_query(
 6719            r#"
 6720                (_ "(" ")" @end) @indent
 6721                (_ "{" "}" @end) @indent
 6722            "#,
 6723        )
 6724        .unwrap(),
 6725    );
 6726
 6727    let text = "fn a() {}";
 6728
 6729    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6730    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6731    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6732    editor
 6733        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6734        .await;
 6735
 6736    editor.update_in(cx, |editor, window, cx| {
 6737        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6738        editor.newline(&Newline, window, cx);
 6739        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6740        assert_eq!(
 6741            editor.selections.ranges(cx),
 6742            &[
 6743                Point::new(1, 4)..Point::new(1, 4),
 6744                Point::new(3, 4)..Point::new(3, 4),
 6745                Point::new(5, 0)..Point::new(5, 0)
 6746            ]
 6747        );
 6748    });
 6749}
 6750
 6751#[gpui::test]
 6752async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6753    init_test(cx, |_| {});
 6754
 6755    {
 6756        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6757        cx.set_state(indoc! {"
 6758            impl A {
 6759
 6760                fn b() {}
 6761
 6762            «fn c() {
 6763
 6764            }ˇ»
 6765            }
 6766        "});
 6767
 6768        cx.update_editor(|editor, window, cx| {
 6769            editor.autoindent(&Default::default(), window, cx);
 6770        });
 6771
 6772        cx.assert_editor_state(indoc! {"
 6773            impl A {
 6774
 6775                fn b() {}
 6776
 6777                «fn c() {
 6778
 6779                }ˇ»
 6780            }
 6781        "});
 6782    }
 6783
 6784    {
 6785        let mut cx = EditorTestContext::new_multibuffer(
 6786            cx,
 6787            [indoc! { "
 6788                impl A {
 6789                «
 6790                // a
 6791                fn b(){}
 6792                »
 6793                «
 6794                    }
 6795                    fn c(){}
 6796                »
 6797            "}],
 6798        );
 6799
 6800        let buffer = cx.update_editor(|editor, _, cx| {
 6801            let buffer = editor.buffer().update(cx, |buffer, _| {
 6802                buffer.all_buffers().iter().next().unwrap().clone()
 6803            });
 6804            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6805            buffer
 6806        });
 6807
 6808        cx.run_until_parked();
 6809        cx.update_editor(|editor, window, cx| {
 6810            editor.select_all(&Default::default(), window, cx);
 6811            editor.autoindent(&Default::default(), window, cx)
 6812        });
 6813        cx.run_until_parked();
 6814
 6815        cx.update(|_, cx| {
 6816            assert_eq!(
 6817                buffer.read(cx).text(),
 6818                indoc! { "
 6819                    impl A {
 6820
 6821                        // a
 6822                        fn b(){}
 6823
 6824
 6825                    }
 6826                    fn c(){}
 6827
 6828                " }
 6829            )
 6830        });
 6831    }
 6832}
 6833
 6834#[gpui::test]
 6835async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6836    init_test(cx, |_| {});
 6837
 6838    let mut cx = EditorTestContext::new(cx).await;
 6839
 6840    let language = Arc::new(Language::new(
 6841        LanguageConfig {
 6842            brackets: BracketPairConfig {
 6843                pairs: vec![
 6844                    BracketPair {
 6845                        start: "{".to_string(),
 6846                        end: "}".to_string(),
 6847                        close: true,
 6848                        surround: true,
 6849                        newline: true,
 6850                    },
 6851                    BracketPair {
 6852                        start: "(".to_string(),
 6853                        end: ")".to_string(),
 6854                        close: true,
 6855                        surround: true,
 6856                        newline: true,
 6857                    },
 6858                    BracketPair {
 6859                        start: "/*".to_string(),
 6860                        end: " */".to_string(),
 6861                        close: true,
 6862                        surround: true,
 6863                        newline: true,
 6864                    },
 6865                    BracketPair {
 6866                        start: "[".to_string(),
 6867                        end: "]".to_string(),
 6868                        close: false,
 6869                        surround: false,
 6870                        newline: true,
 6871                    },
 6872                    BracketPair {
 6873                        start: "\"".to_string(),
 6874                        end: "\"".to_string(),
 6875                        close: true,
 6876                        surround: true,
 6877                        newline: false,
 6878                    },
 6879                    BracketPair {
 6880                        start: "<".to_string(),
 6881                        end: ">".to_string(),
 6882                        close: false,
 6883                        surround: true,
 6884                        newline: true,
 6885                    },
 6886                ],
 6887                ..Default::default()
 6888            },
 6889            autoclose_before: "})]".to_string(),
 6890            ..Default::default()
 6891        },
 6892        Some(tree_sitter_rust::LANGUAGE.into()),
 6893    ));
 6894
 6895    cx.language_registry().add(language.clone());
 6896    cx.update_buffer(|buffer, cx| {
 6897        buffer.set_language(Some(language), cx);
 6898    });
 6899
 6900    cx.set_state(
 6901        &r#"
 6902            🏀ˇ
 6903            εˇ
 6904            ❤️ˇ
 6905        "#
 6906        .unindent(),
 6907    );
 6908
 6909    // autoclose multiple nested brackets at multiple cursors
 6910    cx.update_editor(|editor, window, cx| {
 6911        editor.handle_input("{", window, cx);
 6912        editor.handle_input("{", window, cx);
 6913        editor.handle_input("{", window, cx);
 6914    });
 6915    cx.assert_editor_state(
 6916        &"
 6917            🏀{{{ˇ}}}
 6918            ε{{{ˇ}}}
 6919            ❤️{{{ˇ}}}
 6920        "
 6921        .unindent(),
 6922    );
 6923
 6924    // insert a different closing bracket
 6925    cx.update_editor(|editor, window, cx| {
 6926        editor.handle_input(")", window, cx);
 6927    });
 6928    cx.assert_editor_state(
 6929        &"
 6930            🏀{{{)ˇ}}}
 6931            ε{{{)ˇ}}}
 6932            ❤️{{{)ˇ}}}
 6933        "
 6934        .unindent(),
 6935    );
 6936
 6937    // skip over the auto-closed brackets when typing a closing bracket
 6938    cx.update_editor(|editor, window, cx| {
 6939        editor.move_right(&MoveRight, window, cx);
 6940        editor.handle_input("}", window, cx);
 6941        editor.handle_input("}", window, cx);
 6942        editor.handle_input("}", window, cx);
 6943    });
 6944    cx.assert_editor_state(
 6945        &"
 6946            🏀{{{)}}}}ˇ
 6947            ε{{{)}}}}ˇ
 6948            ❤️{{{)}}}}ˇ
 6949        "
 6950        .unindent(),
 6951    );
 6952
 6953    // autoclose multi-character pairs
 6954    cx.set_state(
 6955        &"
 6956            ˇ
 6957            ˇ
 6958        "
 6959        .unindent(),
 6960    );
 6961    cx.update_editor(|editor, window, cx| {
 6962        editor.handle_input("/", window, cx);
 6963        editor.handle_input("*", window, cx);
 6964    });
 6965    cx.assert_editor_state(
 6966        &"
 6967            /*ˇ */
 6968            /*ˇ */
 6969        "
 6970        .unindent(),
 6971    );
 6972
 6973    // one cursor autocloses a multi-character pair, one cursor
 6974    // does not autoclose.
 6975    cx.set_state(
 6976        &"
 6977 6978            ˇ
 6979        "
 6980        .unindent(),
 6981    );
 6982    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6983    cx.assert_editor_state(
 6984        &"
 6985            /*ˇ */
 6986 6987        "
 6988        .unindent(),
 6989    );
 6990
 6991    // Don't autoclose if the next character isn't whitespace and isn't
 6992    // listed in the language's "autoclose_before" section.
 6993    cx.set_state("ˇa b");
 6994    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6995    cx.assert_editor_state("{ˇa b");
 6996
 6997    // Don't autoclose if `close` is false for the bracket pair
 6998    cx.set_state("ˇ");
 6999    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7000    cx.assert_editor_state("");
 7001
 7002    // Surround with brackets if text is selected
 7003    cx.set_state("«aˇ» b");
 7004    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7005    cx.assert_editor_state("{«aˇ»} b");
 7006
 7007    // Autoclose when not immediately after a word character
 7008    cx.set_state("a ˇ");
 7009    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7010    cx.assert_editor_state("a \"ˇ\"");
 7011
 7012    // Autoclose pair where the start and end characters are the same
 7013    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7014    cx.assert_editor_state("a \"\"ˇ");
 7015
 7016    // Don't autoclose when immediately after a word character
 7017    cx.set_state("");
 7018    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7019    cx.assert_editor_state("a\"ˇ");
 7020
 7021    // Do autoclose when after a non-word character
 7022    cx.set_state("");
 7023    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7024    cx.assert_editor_state("{\"ˇ\"");
 7025
 7026    // Non identical pairs autoclose regardless of preceding character
 7027    cx.set_state("");
 7028    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7029    cx.assert_editor_state("a{ˇ}");
 7030
 7031    // Don't autoclose pair if autoclose is disabled
 7032    cx.set_state("ˇ");
 7033    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7034    cx.assert_editor_state("");
 7035
 7036    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7037    cx.set_state("«aˇ» b");
 7038    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7039    cx.assert_editor_state("<«aˇ»> b");
 7040}
 7041
 7042#[gpui::test]
 7043async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7044    init_test(cx, |settings| {
 7045        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7046    });
 7047
 7048    let mut cx = EditorTestContext::new(cx).await;
 7049
 7050    let language = Arc::new(Language::new(
 7051        LanguageConfig {
 7052            brackets: BracketPairConfig {
 7053                pairs: vec![
 7054                    BracketPair {
 7055                        start: "{".to_string(),
 7056                        end: "}".to_string(),
 7057                        close: true,
 7058                        surround: true,
 7059                        newline: true,
 7060                    },
 7061                    BracketPair {
 7062                        start: "(".to_string(),
 7063                        end: ")".to_string(),
 7064                        close: true,
 7065                        surround: true,
 7066                        newline: true,
 7067                    },
 7068                    BracketPair {
 7069                        start: "[".to_string(),
 7070                        end: "]".to_string(),
 7071                        close: false,
 7072                        surround: false,
 7073                        newline: true,
 7074                    },
 7075                ],
 7076                ..Default::default()
 7077            },
 7078            autoclose_before: "})]".to_string(),
 7079            ..Default::default()
 7080        },
 7081        Some(tree_sitter_rust::LANGUAGE.into()),
 7082    ));
 7083
 7084    cx.language_registry().add(language.clone());
 7085    cx.update_buffer(|buffer, cx| {
 7086        buffer.set_language(Some(language), cx);
 7087    });
 7088
 7089    cx.set_state(
 7090        &"
 7091            ˇ
 7092            ˇ
 7093            ˇ
 7094        "
 7095        .unindent(),
 7096    );
 7097
 7098    // ensure only matching closing brackets are skipped over
 7099    cx.update_editor(|editor, window, cx| {
 7100        editor.handle_input("}", window, cx);
 7101        editor.move_left(&MoveLeft, window, cx);
 7102        editor.handle_input(")", window, cx);
 7103        editor.move_left(&MoveLeft, window, cx);
 7104    });
 7105    cx.assert_editor_state(
 7106        &"
 7107            ˇ)}
 7108            ˇ)}
 7109            ˇ)}
 7110        "
 7111        .unindent(),
 7112    );
 7113
 7114    // skip-over closing brackets at multiple cursors
 7115    cx.update_editor(|editor, window, cx| {
 7116        editor.handle_input(")", window, cx);
 7117        editor.handle_input("}", window, cx);
 7118    });
 7119    cx.assert_editor_state(
 7120        &"
 7121            )}ˇ
 7122            )}ˇ
 7123            )}ˇ
 7124        "
 7125        .unindent(),
 7126    );
 7127
 7128    // ignore non-close brackets
 7129    cx.update_editor(|editor, window, cx| {
 7130        editor.handle_input("]", window, cx);
 7131        editor.move_left(&MoveLeft, window, cx);
 7132        editor.handle_input("]", window, cx);
 7133    });
 7134    cx.assert_editor_state(
 7135        &"
 7136            )}]ˇ]
 7137            )}]ˇ]
 7138            )}]ˇ]
 7139        "
 7140        .unindent(),
 7141    );
 7142}
 7143
 7144#[gpui::test]
 7145async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7146    init_test(cx, |_| {});
 7147
 7148    let mut cx = EditorTestContext::new(cx).await;
 7149
 7150    let html_language = Arc::new(
 7151        Language::new(
 7152            LanguageConfig {
 7153                name: "HTML".into(),
 7154                brackets: BracketPairConfig {
 7155                    pairs: vec![
 7156                        BracketPair {
 7157                            start: "<".into(),
 7158                            end: ">".into(),
 7159                            close: true,
 7160                            ..Default::default()
 7161                        },
 7162                        BracketPair {
 7163                            start: "{".into(),
 7164                            end: "}".into(),
 7165                            close: true,
 7166                            ..Default::default()
 7167                        },
 7168                        BracketPair {
 7169                            start: "(".into(),
 7170                            end: ")".into(),
 7171                            close: true,
 7172                            ..Default::default()
 7173                        },
 7174                    ],
 7175                    ..Default::default()
 7176                },
 7177                autoclose_before: "})]>".into(),
 7178                ..Default::default()
 7179            },
 7180            Some(tree_sitter_html::LANGUAGE.into()),
 7181        )
 7182        .with_injection_query(
 7183            r#"
 7184            (script_element
 7185                (raw_text) @injection.content
 7186                (#set! injection.language "javascript"))
 7187            "#,
 7188        )
 7189        .unwrap(),
 7190    );
 7191
 7192    let javascript_language = Arc::new(Language::new(
 7193        LanguageConfig {
 7194            name: "JavaScript".into(),
 7195            brackets: BracketPairConfig {
 7196                pairs: vec![
 7197                    BracketPair {
 7198                        start: "/*".into(),
 7199                        end: " */".into(),
 7200                        close: true,
 7201                        ..Default::default()
 7202                    },
 7203                    BracketPair {
 7204                        start: "{".into(),
 7205                        end: "}".into(),
 7206                        close: true,
 7207                        ..Default::default()
 7208                    },
 7209                    BracketPair {
 7210                        start: "(".into(),
 7211                        end: ")".into(),
 7212                        close: true,
 7213                        ..Default::default()
 7214                    },
 7215                ],
 7216                ..Default::default()
 7217            },
 7218            autoclose_before: "})]>".into(),
 7219            ..Default::default()
 7220        },
 7221        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7222    ));
 7223
 7224    cx.language_registry().add(html_language.clone());
 7225    cx.language_registry().add(javascript_language.clone());
 7226
 7227    cx.update_buffer(|buffer, cx| {
 7228        buffer.set_language(Some(html_language), cx);
 7229    });
 7230
 7231    cx.set_state(
 7232        &r#"
 7233            <body>ˇ
 7234                <script>
 7235                    var x = 1;ˇ
 7236                </script>
 7237            </body>ˇ
 7238        "#
 7239        .unindent(),
 7240    );
 7241
 7242    // Precondition: different languages are active at different locations.
 7243    cx.update_editor(|editor, window, cx| {
 7244        let snapshot = editor.snapshot(window, cx);
 7245        let cursors = editor.selections.ranges::<usize>(cx);
 7246        let languages = cursors
 7247            .iter()
 7248            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7249            .collect::<Vec<_>>();
 7250        assert_eq!(
 7251            languages,
 7252            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7253        );
 7254    });
 7255
 7256    // Angle brackets autoclose in HTML, but not JavaScript.
 7257    cx.update_editor(|editor, window, cx| {
 7258        editor.handle_input("<", window, cx);
 7259        editor.handle_input("a", window, cx);
 7260    });
 7261    cx.assert_editor_state(
 7262        &r#"
 7263            <body><aˇ>
 7264                <script>
 7265                    var x = 1;<aˇ
 7266                </script>
 7267            </body><aˇ>
 7268        "#
 7269        .unindent(),
 7270    );
 7271
 7272    // Curly braces and parens autoclose in both HTML and JavaScript.
 7273    cx.update_editor(|editor, window, cx| {
 7274        editor.handle_input(" b=", window, cx);
 7275        editor.handle_input("{", window, cx);
 7276        editor.handle_input("c", window, cx);
 7277        editor.handle_input("(", window, cx);
 7278    });
 7279    cx.assert_editor_state(
 7280        &r#"
 7281            <body><a b={c(ˇ)}>
 7282                <script>
 7283                    var x = 1;<a b={c(ˇ)}
 7284                </script>
 7285            </body><a b={c(ˇ)}>
 7286        "#
 7287        .unindent(),
 7288    );
 7289
 7290    // Brackets that were already autoclosed are skipped.
 7291    cx.update_editor(|editor, window, cx| {
 7292        editor.handle_input(")", window, cx);
 7293        editor.handle_input("d", window, cx);
 7294        editor.handle_input("}", window, cx);
 7295    });
 7296    cx.assert_editor_state(
 7297        &r#"
 7298            <body><a b={c()d}ˇ>
 7299                <script>
 7300                    var x = 1;<a b={c()d}ˇ
 7301                </script>
 7302            </body><a b={c()d}ˇ>
 7303        "#
 7304        .unindent(),
 7305    );
 7306    cx.update_editor(|editor, window, cx| {
 7307        editor.handle_input(">", window, cx);
 7308    });
 7309    cx.assert_editor_state(
 7310        &r#"
 7311            <body><a b={c()d}>ˇ
 7312                <script>
 7313                    var x = 1;<a b={c()d}>ˇ
 7314                </script>
 7315            </body><a b={c()d}>ˇ
 7316        "#
 7317        .unindent(),
 7318    );
 7319
 7320    // Reset
 7321    cx.set_state(
 7322        &r#"
 7323            <body>ˇ
 7324                <script>
 7325                    var x = 1;ˇ
 7326                </script>
 7327            </body>ˇ
 7328        "#
 7329        .unindent(),
 7330    );
 7331
 7332    cx.update_editor(|editor, window, cx| {
 7333        editor.handle_input("<", window, cx);
 7334    });
 7335    cx.assert_editor_state(
 7336        &r#"
 7337            <body><ˇ>
 7338                <script>
 7339                    var x = 1;<ˇ
 7340                </script>
 7341            </body><ˇ>
 7342        "#
 7343        .unindent(),
 7344    );
 7345
 7346    // When backspacing, the closing angle brackets are removed.
 7347    cx.update_editor(|editor, window, cx| {
 7348        editor.backspace(&Backspace, window, cx);
 7349    });
 7350    cx.assert_editor_state(
 7351        &r#"
 7352            <body>ˇ
 7353                <script>
 7354                    var x = 1;ˇ
 7355                </script>
 7356            </body>ˇ
 7357        "#
 7358        .unindent(),
 7359    );
 7360
 7361    // Block comments autoclose in JavaScript, but not HTML.
 7362    cx.update_editor(|editor, window, cx| {
 7363        editor.handle_input("/", window, cx);
 7364        editor.handle_input("*", window, cx);
 7365    });
 7366    cx.assert_editor_state(
 7367        &r#"
 7368            <body>/*ˇ
 7369                <script>
 7370                    var x = 1;/*ˇ */
 7371                </script>
 7372            </body>/*ˇ
 7373        "#
 7374        .unindent(),
 7375    );
 7376}
 7377
 7378#[gpui::test]
 7379async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7380    init_test(cx, |_| {});
 7381
 7382    let mut cx = EditorTestContext::new(cx).await;
 7383
 7384    let rust_language = Arc::new(
 7385        Language::new(
 7386            LanguageConfig {
 7387                name: "Rust".into(),
 7388                brackets: serde_json::from_value(json!([
 7389                    { "start": "{", "end": "}", "close": true, "newline": true },
 7390                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7391                ]))
 7392                .unwrap(),
 7393                autoclose_before: "})]>".into(),
 7394                ..Default::default()
 7395            },
 7396            Some(tree_sitter_rust::LANGUAGE.into()),
 7397        )
 7398        .with_override_query("(string_literal) @string")
 7399        .unwrap(),
 7400    );
 7401
 7402    cx.language_registry().add(rust_language.clone());
 7403    cx.update_buffer(|buffer, cx| {
 7404        buffer.set_language(Some(rust_language), cx);
 7405    });
 7406
 7407    cx.set_state(
 7408        &r#"
 7409            let x = ˇ
 7410        "#
 7411        .unindent(),
 7412    );
 7413
 7414    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7415    cx.update_editor(|editor, window, cx| {
 7416        editor.handle_input("\"", window, cx);
 7417    });
 7418    cx.assert_editor_state(
 7419        &r#"
 7420            let x = "ˇ"
 7421        "#
 7422        .unindent(),
 7423    );
 7424
 7425    // Inserting another quotation mark. The cursor moves across the existing
 7426    // automatically-inserted quotation mark.
 7427    cx.update_editor(|editor, window, cx| {
 7428        editor.handle_input("\"", window, cx);
 7429    });
 7430    cx.assert_editor_state(
 7431        &r#"
 7432            let x = ""ˇ
 7433        "#
 7434        .unindent(),
 7435    );
 7436
 7437    // Reset
 7438    cx.set_state(
 7439        &r#"
 7440            let x = ˇ
 7441        "#
 7442        .unindent(),
 7443    );
 7444
 7445    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7446    cx.update_editor(|editor, window, cx| {
 7447        editor.handle_input("\"", window, cx);
 7448        editor.handle_input(" ", window, cx);
 7449        editor.move_left(&Default::default(), window, cx);
 7450        editor.handle_input("\\", window, cx);
 7451        editor.handle_input("\"", window, cx);
 7452    });
 7453    cx.assert_editor_state(
 7454        &r#"
 7455            let x = "\"ˇ "
 7456        "#
 7457        .unindent(),
 7458    );
 7459
 7460    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7461    // mark. Nothing is inserted.
 7462    cx.update_editor(|editor, window, cx| {
 7463        editor.move_right(&Default::default(), window, cx);
 7464        editor.handle_input("\"", window, cx);
 7465    });
 7466    cx.assert_editor_state(
 7467        &r#"
 7468            let x = "\" "ˇ
 7469        "#
 7470        .unindent(),
 7471    );
 7472}
 7473
 7474#[gpui::test]
 7475async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7476    init_test(cx, |_| {});
 7477
 7478    let language = Arc::new(Language::new(
 7479        LanguageConfig {
 7480            brackets: BracketPairConfig {
 7481                pairs: vec![
 7482                    BracketPair {
 7483                        start: "{".to_string(),
 7484                        end: "}".to_string(),
 7485                        close: true,
 7486                        surround: true,
 7487                        newline: true,
 7488                    },
 7489                    BracketPair {
 7490                        start: "/* ".to_string(),
 7491                        end: "*/".to_string(),
 7492                        close: true,
 7493                        surround: true,
 7494                        ..Default::default()
 7495                    },
 7496                ],
 7497                ..Default::default()
 7498            },
 7499            ..Default::default()
 7500        },
 7501        Some(tree_sitter_rust::LANGUAGE.into()),
 7502    ));
 7503
 7504    let text = r#"
 7505        a
 7506        b
 7507        c
 7508    "#
 7509    .unindent();
 7510
 7511    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7512    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7513    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7514    editor
 7515        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7516        .await;
 7517
 7518    editor.update_in(cx, |editor, window, cx| {
 7519        editor.change_selections(None, window, cx, |s| {
 7520            s.select_display_ranges([
 7521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7522                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7523                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7524            ])
 7525        });
 7526
 7527        editor.handle_input("{", window, cx);
 7528        editor.handle_input("{", window, cx);
 7529        editor.handle_input("{", window, cx);
 7530        assert_eq!(
 7531            editor.text(cx),
 7532            "
 7533                {{{a}}}
 7534                {{{b}}}
 7535                {{{c}}}
 7536            "
 7537            .unindent()
 7538        );
 7539        assert_eq!(
 7540            editor.selections.display_ranges(cx),
 7541            [
 7542                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7543                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7544                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7545            ]
 7546        );
 7547
 7548        editor.undo(&Undo, window, cx);
 7549        editor.undo(&Undo, window, cx);
 7550        editor.undo(&Undo, window, cx);
 7551        assert_eq!(
 7552            editor.text(cx),
 7553            "
 7554                a
 7555                b
 7556                c
 7557            "
 7558            .unindent()
 7559        );
 7560        assert_eq!(
 7561            editor.selections.display_ranges(cx),
 7562            [
 7563                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7564                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7565                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7566            ]
 7567        );
 7568
 7569        // Ensure inserting the first character of a multi-byte bracket pair
 7570        // doesn't surround the selections with the bracket.
 7571        editor.handle_input("/", window, cx);
 7572        assert_eq!(
 7573            editor.text(cx),
 7574            "
 7575                /
 7576                /
 7577                /
 7578            "
 7579            .unindent()
 7580        );
 7581        assert_eq!(
 7582            editor.selections.display_ranges(cx),
 7583            [
 7584                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7585                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7586                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7587            ]
 7588        );
 7589
 7590        editor.undo(&Undo, window, cx);
 7591        assert_eq!(
 7592            editor.text(cx),
 7593            "
 7594                a
 7595                b
 7596                c
 7597            "
 7598            .unindent()
 7599        );
 7600        assert_eq!(
 7601            editor.selections.display_ranges(cx),
 7602            [
 7603                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7604                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7605                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7606            ]
 7607        );
 7608
 7609        // Ensure inserting the last character of a multi-byte bracket pair
 7610        // doesn't surround the selections with the bracket.
 7611        editor.handle_input("*", window, cx);
 7612        assert_eq!(
 7613            editor.text(cx),
 7614            "
 7615                *
 7616                *
 7617                *
 7618            "
 7619            .unindent()
 7620        );
 7621        assert_eq!(
 7622            editor.selections.display_ranges(cx),
 7623            [
 7624                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7625                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7626                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7627            ]
 7628        );
 7629    });
 7630}
 7631
 7632#[gpui::test]
 7633async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7634    init_test(cx, |_| {});
 7635
 7636    let language = Arc::new(Language::new(
 7637        LanguageConfig {
 7638            brackets: BracketPairConfig {
 7639                pairs: vec![BracketPair {
 7640                    start: "{".to_string(),
 7641                    end: "}".to_string(),
 7642                    close: true,
 7643                    surround: true,
 7644                    newline: true,
 7645                }],
 7646                ..Default::default()
 7647            },
 7648            autoclose_before: "}".to_string(),
 7649            ..Default::default()
 7650        },
 7651        Some(tree_sitter_rust::LANGUAGE.into()),
 7652    ));
 7653
 7654    let text = r#"
 7655        a
 7656        b
 7657        c
 7658    "#
 7659    .unindent();
 7660
 7661    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7662    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7663    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7664    editor
 7665        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7666        .await;
 7667
 7668    editor.update_in(cx, |editor, window, cx| {
 7669        editor.change_selections(None, window, cx, |s| {
 7670            s.select_ranges([
 7671                Point::new(0, 1)..Point::new(0, 1),
 7672                Point::new(1, 1)..Point::new(1, 1),
 7673                Point::new(2, 1)..Point::new(2, 1),
 7674            ])
 7675        });
 7676
 7677        editor.handle_input("{", window, cx);
 7678        editor.handle_input("{", window, cx);
 7679        editor.handle_input("_", window, cx);
 7680        assert_eq!(
 7681            editor.text(cx),
 7682            "
 7683                a{{_}}
 7684                b{{_}}
 7685                c{{_}}
 7686            "
 7687            .unindent()
 7688        );
 7689        assert_eq!(
 7690            editor.selections.ranges::<Point>(cx),
 7691            [
 7692                Point::new(0, 4)..Point::new(0, 4),
 7693                Point::new(1, 4)..Point::new(1, 4),
 7694                Point::new(2, 4)..Point::new(2, 4)
 7695            ]
 7696        );
 7697
 7698        editor.backspace(&Default::default(), window, cx);
 7699        editor.backspace(&Default::default(), window, cx);
 7700        assert_eq!(
 7701            editor.text(cx),
 7702            "
 7703                a{}
 7704                b{}
 7705                c{}
 7706            "
 7707            .unindent()
 7708        );
 7709        assert_eq!(
 7710            editor.selections.ranges::<Point>(cx),
 7711            [
 7712                Point::new(0, 2)..Point::new(0, 2),
 7713                Point::new(1, 2)..Point::new(1, 2),
 7714                Point::new(2, 2)..Point::new(2, 2)
 7715            ]
 7716        );
 7717
 7718        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7719        assert_eq!(
 7720            editor.text(cx),
 7721            "
 7722                a
 7723                b
 7724                c
 7725            "
 7726            .unindent()
 7727        );
 7728        assert_eq!(
 7729            editor.selections.ranges::<Point>(cx),
 7730            [
 7731                Point::new(0, 1)..Point::new(0, 1),
 7732                Point::new(1, 1)..Point::new(1, 1),
 7733                Point::new(2, 1)..Point::new(2, 1)
 7734            ]
 7735        );
 7736    });
 7737}
 7738
 7739#[gpui::test]
 7740async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7741    init_test(cx, |settings| {
 7742        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7743    });
 7744
 7745    let mut cx = EditorTestContext::new(cx).await;
 7746
 7747    let language = Arc::new(Language::new(
 7748        LanguageConfig {
 7749            brackets: BracketPairConfig {
 7750                pairs: vec![
 7751                    BracketPair {
 7752                        start: "{".to_string(),
 7753                        end: "}".to_string(),
 7754                        close: true,
 7755                        surround: true,
 7756                        newline: true,
 7757                    },
 7758                    BracketPair {
 7759                        start: "(".to_string(),
 7760                        end: ")".to_string(),
 7761                        close: true,
 7762                        surround: true,
 7763                        newline: true,
 7764                    },
 7765                    BracketPair {
 7766                        start: "[".to_string(),
 7767                        end: "]".to_string(),
 7768                        close: false,
 7769                        surround: true,
 7770                        newline: true,
 7771                    },
 7772                ],
 7773                ..Default::default()
 7774            },
 7775            autoclose_before: "})]".to_string(),
 7776            ..Default::default()
 7777        },
 7778        Some(tree_sitter_rust::LANGUAGE.into()),
 7779    ));
 7780
 7781    cx.language_registry().add(language.clone());
 7782    cx.update_buffer(|buffer, cx| {
 7783        buffer.set_language(Some(language), cx);
 7784    });
 7785
 7786    cx.set_state(
 7787        &"
 7788            {(ˇ)}
 7789            [[ˇ]]
 7790            {(ˇ)}
 7791        "
 7792        .unindent(),
 7793    );
 7794
 7795    cx.update_editor(|editor, window, cx| {
 7796        editor.backspace(&Default::default(), window, cx);
 7797        editor.backspace(&Default::default(), window, cx);
 7798    });
 7799
 7800    cx.assert_editor_state(
 7801        &"
 7802            ˇ
 7803            ˇ]]
 7804            ˇ
 7805        "
 7806        .unindent(),
 7807    );
 7808
 7809    cx.update_editor(|editor, window, cx| {
 7810        editor.handle_input("{", window, cx);
 7811        editor.handle_input("{", window, cx);
 7812        editor.move_right(&MoveRight, window, cx);
 7813        editor.move_right(&MoveRight, window, cx);
 7814        editor.move_left(&MoveLeft, window, cx);
 7815        editor.move_left(&MoveLeft, window, cx);
 7816        editor.backspace(&Default::default(), window, cx);
 7817    });
 7818
 7819    cx.assert_editor_state(
 7820        &"
 7821            {ˇ}
 7822            {ˇ}]]
 7823            {ˇ}
 7824        "
 7825        .unindent(),
 7826    );
 7827
 7828    cx.update_editor(|editor, window, cx| {
 7829        editor.backspace(&Default::default(), window, cx);
 7830    });
 7831
 7832    cx.assert_editor_state(
 7833        &"
 7834            ˇ
 7835            ˇ]]
 7836            ˇ
 7837        "
 7838        .unindent(),
 7839    );
 7840}
 7841
 7842#[gpui::test]
 7843async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7844    init_test(cx, |_| {});
 7845
 7846    let language = Arc::new(Language::new(
 7847        LanguageConfig::default(),
 7848        Some(tree_sitter_rust::LANGUAGE.into()),
 7849    ));
 7850
 7851    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7852    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7853    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7854    editor
 7855        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7856        .await;
 7857
 7858    editor.update_in(cx, |editor, window, cx| {
 7859        editor.set_auto_replace_emoji_shortcode(true);
 7860
 7861        editor.handle_input("Hello ", window, cx);
 7862        editor.handle_input(":wave", window, cx);
 7863        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7864
 7865        editor.handle_input(":", window, cx);
 7866        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7867
 7868        editor.handle_input(" :smile", window, cx);
 7869        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7870
 7871        editor.handle_input(":", window, cx);
 7872        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7873
 7874        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7875        editor.handle_input(":wave", window, cx);
 7876        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7877
 7878        editor.handle_input(":", window, cx);
 7879        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7880
 7881        editor.handle_input(":1", window, cx);
 7882        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7883
 7884        editor.handle_input(":", window, cx);
 7885        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7886
 7887        // Ensure shortcode does not get replaced when it is part of a word
 7888        editor.handle_input(" Test:wave", window, cx);
 7889        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7890
 7891        editor.handle_input(":", window, cx);
 7892        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7893
 7894        editor.set_auto_replace_emoji_shortcode(false);
 7895
 7896        // Ensure shortcode does not get replaced when auto replace is off
 7897        editor.handle_input(" :wave", window, cx);
 7898        assert_eq!(
 7899            editor.text(cx),
 7900            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7901        );
 7902
 7903        editor.handle_input(":", window, cx);
 7904        assert_eq!(
 7905            editor.text(cx),
 7906            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7907        );
 7908    });
 7909}
 7910
 7911#[gpui::test]
 7912async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7913    init_test(cx, |_| {});
 7914
 7915    let (text, insertion_ranges) = marked_text_ranges(
 7916        indoc! {"
 7917            ˇ
 7918        "},
 7919        false,
 7920    );
 7921
 7922    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7923    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7924
 7925    _ = editor.update_in(cx, |editor, window, cx| {
 7926        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7927
 7928        editor
 7929            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7930            .unwrap();
 7931
 7932        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7933            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7934            assert_eq!(editor.text(cx), expected_text);
 7935            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7936        }
 7937
 7938        assert(
 7939            editor,
 7940            cx,
 7941            indoc! {"
 7942            type «» =•
 7943            "},
 7944        );
 7945
 7946        assert!(editor.context_menu_visible(), "There should be a matches");
 7947    });
 7948}
 7949
 7950#[gpui::test]
 7951async fn test_snippets(cx: &mut TestAppContext) {
 7952    init_test(cx, |_| {});
 7953
 7954    let (text, insertion_ranges) = marked_text_ranges(
 7955        indoc! {"
 7956            a.ˇ b
 7957            a.ˇ b
 7958            a.ˇ b
 7959        "},
 7960        false,
 7961    );
 7962
 7963    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7964    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7965
 7966    editor.update_in(cx, |editor, window, cx| {
 7967        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7968
 7969        editor
 7970            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7971            .unwrap();
 7972
 7973        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7974            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7975            assert_eq!(editor.text(cx), expected_text);
 7976            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7977        }
 7978
 7979        assert(
 7980            editor,
 7981            cx,
 7982            indoc! {"
 7983                a.f(«one», two, «three») b
 7984                a.f(«one», two, «three») b
 7985                a.f(«one», two, «three») b
 7986            "},
 7987        );
 7988
 7989        // Can't move earlier than the first tab stop
 7990        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7991        assert(
 7992            editor,
 7993            cx,
 7994            indoc! {"
 7995                a.f(«one», two, «three») b
 7996                a.f(«one», two, «three») b
 7997                a.f(«one», two, «three») b
 7998            "},
 7999        );
 8000
 8001        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8002        assert(
 8003            editor,
 8004            cx,
 8005            indoc! {"
 8006                a.f(one, «two», three) b
 8007                a.f(one, «two», three) b
 8008                a.f(one, «two», three) b
 8009            "},
 8010        );
 8011
 8012        editor.move_to_prev_snippet_tabstop(window, cx);
 8013        assert(
 8014            editor,
 8015            cx,
 8016            indoc! {"
 8017                a.f(«one», two, «three») b
 8018                a.f(«one», two, «three») b
 8019                a.f(«one», two, «three») b
 8020            "},
 8021        );
 8022
 8023        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8024        assert(
 8025            editor,
 8026            cx,
 8027            indoc! {"
 8028                a.f(one, «two», three) b
 8029                a.f(one, «two», three) b
 8030                a.f(one, «two», three) b
 8031            "},
 8032        );
 8033        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8034        assert(
 8035            editor,
 8036            cx,
 8037            indoc! {"
 8038                a.f(one, two, three)ˇ b
 8039                a.f(one, two, three)ˇ b
 8040                a.f(one, two, three)ˇ b
 8041            "},
 8042        );
 8043
 8044        // As soon as the last tab stop is reached, snippet state is gone
 8045        editor.move_to_prev_snippet_tabstop(window, cx);
 8046        assert(
 8047            editor,
 8048            cx,
 8049            indoc! {"
 8050                a.f(one, two, three)ˇ b
 8051                a.f(one, two, three)ˇ b
 8052                a.f(one, two, three)ˇ b
 8053            "},
 8054        );
 8055    });
 8056}
 8057
 8058#[gpui::test]
 8059async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8060    init_test(cx, |_| {});
 8061
 8062    let fs = FakeFs::new(cx.executor());
 8063    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8064
 8065    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8066
 8067    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8068    language_registry.add(rust_lang());
 8069    let mut fake_servers = language_registry.register_fake_lsp(
 8070        "Rust",
 8071        FakeLspAdapter {
 8072            capabilities: lsp::ServerCapabilities {
 8073                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8074                ..Default::default()
 8075            },
 8076            ..Default::default()
 8077        },
 8078    );
 8079
 8080    let buffer = project
 8081        .update(cx, |project, cx| {
 8082            project.open_local_buffer(path!("/file.rs"), cx)
 8083        })
 8084        .await
 8085        .unwrap();
 8086
 8087    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8088    let (editor, cx) = cx.add_window_view(|window, cx| {
 8089        build_editor_with_project(project.clone(), buffer, window, cx)
 8090    });
 8091    editor.update_in(cx, |editor, window, cx| {
 8092        editor.set_text("one\ntwo\nthree\n", window, cx)
 8093    });
 8094    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8095
 8096    cx.executor().start_waiting();
 8097    let fake_server = fake_servers.next().await.unwrap();
 8098
 8099    {
 8100        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8101            move |params, _| async move {
 8102                assert_eq!(
 8103                    params.text_document.uri,
 8104                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8105                );
 8106                assert_eq!(params.options.tab_size, 4);
 8107                Ok(Some(vec![lsp::TextEdit::new(
 8108                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8109                    ", ".to_string(),
 8110                )]))
 8111            },
 8112        );
 8113        let save = editor
 8114            .update_in(cx, |editor, window, cx| {
 8115                editor.save(true, project.clone(), window, cx)
 8116            })
 8117            .unwrap();
 8118        cx.executor().start_waiting();
 8119        save.await;
 8120
 8121        assert_eq!(
 8122            editor.update(cx, |editor, cx| editor.text(cx)),
 8123            "one, two\nthree\n"
 8124        );
 8125        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8126    }
 8127
 8128    {
 8129        editor.update_in(cx, |editor, window, cx| {
 8130            editor.set_text("one\ntwo\nthree\n", window, cx)
 8131        });
 8132        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8133
 8134        // Ensure we can still save even if formatting hangs.
 8135        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8136            move |params, _| async move {
 8137                assert_eq!(
 8138                    params.text_document.uri,
 8139                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8140                );
 8141                futures::future::pending::<()>().await;
 8142                unreachable!()
 8143            },
 8144        );
 8145        let save = editor
 8146            .update_in(cx, |editor, window, cx| {
 8147                editor.save(true, project.clone(), window, cx)
 8148            })
 8149            .unwrap();
 8150        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8151        cx.executor().start_waiting();
 8152        save.await;
 8153        assert_eq!(
 8154            editor.update(cx, |editor, cx| editor.text(cx)),
 8155            "one\ntwo\nthree\n"
 8156        );
 8157    }
 8158
 8159    // For non-dirty buffer, no formatting request should be sent
 8160    {
 8161        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8162
 8163        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8164            panic!("Should not be invoked on non-dirty buffer");
 8165        });
 8166        let save = editor
 8167            .update_in(cx, |editor, window, cx| {
 8168                editor.save(true, project.clone(), window, cx)
 8169            })
 8170            .unwrap();
 8171        cx.executor().start_waiting();
 8172        save.await;
 8173    }
 8174
 8175    // Set rust language override and assert overridden tabsize is sent to language server
 8176    update_test_language_settings(cx, |settings| {
 8177        settings.languages.insert(
 8178            "Rust".into(),
 8179            LanguageSettingsContent {
 8180                tab_size: NonZeroU32::new(8),
 8181                ..Default::default()
 8182            },
 8183        );
 8184    });
 8185
 8186    {
 8187        editor.update_in(cx, |editor, window, cx| {
 8188            editor.set_text("somehting_new\n", window, cx)
 8189        });
 8190        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8191        let _formatting_request_signal = fake_server
 8192            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8193                assert_eq!(
 8194                    params.text_document.uri,
 8195                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8196                );
 8197                assert_eq!(params.options.tab_size, 8);
 8198                Ok(Some(vec![]))
 8199            });
 8200        let save = editor
 8201            .update_in(cx, |editor, window, cx| {
 8202                editor.save(true, project.clone(), window, cx)
 8203            })
 8204            .unwrap();
 8205        cx.executor().start_waiting();
 8206        save.await;
 8207    }
 8208}
 8209
 8210#[gpui::test]
 8211async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8212    init_test(cx, |_| {});
 8213
 8214    let cols = 4;
 8215    let rows = 10;
 8216    let sample_text_1 = sample_text(rows, cols, 'a');
 8217    assert_eq!(
 8218        sample_text_1,
 8219        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8220    );
 8221    let sample_text_2 = sample_text(rows, cols, 'l');
 8222    assert_eq!(
 8223        sample_text_2,
 8224        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8225    );
 8226    let sample_text_3 = sample_text(rows, cols, 'v');
 8227    assert_eq!(
 8228        sample_text_3,
 8229        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8230    );
 8231
 8232    let fs = FakeFs::new(cx.executor());
 8233    fs.insert_tree(
 8234        path!("/a"),
 8235        json!({
 8236            "main.rs": sample_text_1,
 8237            "other.rs": sample_text_2,
 8238            "lib.rs": sample_text_3,
 8239        }),
 8240    )
 8241    .await;
 8242
 8243    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8244    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8245    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8246
 8247    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8248    language_registry.add(rust_lang());
 8249    let mut fake_servers = language_registry.register_fake_lsp(
 8250        "Rust",
 8251        FakeLspAdapter {
 8252            capabilities: lsp::ServerCapabilities {
 8253                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8254                ..Default::default()
 8255            },
 8256            ..Default::default()
 8257        },
 8258    );
 8259
 8260    let worktree = project.update(cx, |project, cx| {
 8261        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8262        assert_eq!(worktrees.len(), 1);
 8263        worktrees.pop().unwrap()
 8264    });
 8265    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8266
 8267    let buffer_1 = project
 8268        .update(cx, |project, cx| {
 8269            project.open_buffer((worktree_id, "main.rs"), cx)
 8270        })
 8271        .await
 8272        .unwrap();
 8273    let buffer_2 = project
 8274        .update(cx, |project, cx| {
 8275            project.open_buffer((worktree_id, "other.rs"), cx)
 8276        })
 8277        .await
 8278        .unwrap();
 8279    let buffer_3 = project
 8280        .update(cx, |project, cx| {
 8281            project.open_buffer((worktree_id, "lib.rs"), cx)
 8282        })
 8283        .await
 8284        .unwrap();
 8285
 8286    let multi_buffer = cx.new(|cx| {
 8287        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8288        multi_buffer.push_excerpts(
 8289            buffer_1.clone(),
 8290            [
 8291                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8292                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8293                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8294            ],
 8295            cx,
 8296        );
 8297        multi_buffer.push_excerpts(
 8298            buffer_2.clone(),
 8299            [
 8300                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8301                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8302                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8303            ],
 8304            cx,
 8305        );
 8306        multi_buffer.push_excerpts(
 8307            buffer_3.clone(),
 8308            [
 8309                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8310                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8311                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8312            ],
 8313            cx,
 8314        );
 8315        multi_buffer
 8316    });
 8317    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8318        Editor::new(
 8319            EditorMode::full(),
 8320            multi_buffer,
 8321            Some(project.clone()),
 8322            window,
 8323            cx,
 8324        )
 8325    });
 8326
 8327    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8328        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8329            s.select_ranges(Some(1..2))
 8330        });
 8331        editor.insert("|one|two|three|", window, cx);
 8332    });
 8333    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8334    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8335        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8336            s.select_ranges(Some(60..70))
 8337        });
 8338        editor.insert("|four|five|six|", window, cx);
 8339    });
 8340    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8341
 8342    // First two buffers should be edited, but not the third one.
 8343    assert_eq!(
 8344        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8345        "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}",
 8346    );
 8347    buffer_1.update(cx, |buffer, _| {
 8348        assert!(buffer.is_dirty());
 8349        assert_eq!(
 8350            buffer.text(),
 8351            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8352        )
 8353    });
 8354    buffer_2.update(cx, |buffer, _| {
 8355        assert!(buffer.is_dirty());
 8356        assert_eq!(
 8357            buffer.text(),
 8358            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8359        )
 8360    });
 8361    buffer_3.update(cx, |buffer, _| {
 8362        assert!(!buffer.is_dirty());
 8363        assert_eq!(buffer.text(), sample_text_3,)
 8364    });
 8365    cx.executor().run_until_parked();
 8366
 8367    cx.executor().start_waiting();
 8368    let save = multi_buffer_editor
 8369        .update_in(cx, |editor, window, cx| {
 8370            editor.save(true, project.clone(), window, cx)
 8371        })
 8372        .unwrap();
 8373
 8374    let fake_server = fake_servers.next().await.unwrap();
 8375    fake_server
 8376        .server
 8377        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8378            Ok(Some(vec![lsp::TextEdit::new(
 8379                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8380                format!("[{} formatted]", params.text_document.uri),
 8381            )]))
 8382        })
 8383        .detach();
 8384    save.await;
 8385
 8386    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8387    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8388    assert_eq!(
 8389        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8390        uri!(
 8391            "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}"
 8392        ),
 8393    );
 8394    buffer_1.update(cx, |buffer, _| {
 8395        assert!(!buffer.is_dirty());
 8396        assert_eq!(
 8397            buffer.text(),
 8398            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8399        )
 8400    });
 8401    buffer_2.update(cx, |buffer, _| {
 8402        assert!(!buffer.is_dirty());
 8403        assert_eq!(
 8404            buffer.text(),
 8405            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8406        )
 8407    });
 8408    buffer_3.update(cx, |buffer, _| {
 8409        assert!(!buffer.is_dirty());
 8410        assert_eq!(buffer.text(), sample_text_3,)
 8411    });
 8412}
 8413
 8414#[gpui::test]
 8415async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8416    init_test(cx, |_| {});
 8417
 8418    let fs = FakeFs::new(cx.executor());
 8419    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8420
 8421    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8422
 8423    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8424    language_registry.add(rust_lang());
 8425    let mut fake_servers = language_registry.register_fake_lsp(
 8426        "Rust",
 8427        FakeLspAdapter {
 8428            capabilities: lsp::ServerCapabilities {
 8429                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8430                ..Default::default()
 8431            },
 8432            ..Default::default()
 8433        },
 8434    );
 8435
 8436    let buffer = project
 8437        .update(cx, |project, cx| {
 8438            project.open_local_buffer(path!("/file.rs"), cx)
 8439        })
 8440        .await
 8441        .unwrap();
 8442
 8443    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8444    let (editor, cx) = cx.add_window_view(|window, cx| {
 8445        build_editor_with_project(project.clone(), buffer, window, cx)
 8446    });
 8447    editor.update_in(cx, |editor, window, cx| {
 8448        editor.set_text("one\ntwo\nthree\n", window, cx)
 8449    });
 8450    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8451
 8452    cx.executor().start_waiting();
 8453    let fake_server = fake_servers.next().await.unwrap();
 8454
 8455    let save = editor
 8456        .update_in(cx, |editor, window, cx| {
 8457            editor.save(true, project.clone(), window, cx)
 8458        })
 8459        .unwrap();
 8460    fake_server
 8461        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8462            assert_eq!(
 8463                params.text_document.uri,
 8464                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8465            );
 8466            assert_eq!(params.options.tab_size, 4);
 8467            Ok(Some(vec![lsp::TextEdit::new(
 8468                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8469                ", ".to_string(),
 8470            )]))
 8471        })
 8472        .next()
 8473        .await;
 8474    cx.executor().start_waiting();
 8475    save.await;
 8476    assert_eq!(
 8477        editor.update(cx, |editor, cx| editor.text(cx)),
 8478        "one, two\nthree\n"
 8479    );
 8480    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8481
 8482    editor.update_in(cx, |editor, window, cx| {
 8483        editor.set_text("one\ntwo\nthree\n", window, cx)
 8484    });
 8485    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8486
 8487    // Ensure we can still save even if formatting hangs.
 8488    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8489        move |params, _| async move {
 8490            assert_eq!(
 8491                params.text_document.uri,
 8492                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8493            );
 8494            futures::future::pending::<()>().await;
 8495            unreachable!()
 8496        },
 8497    );
 8498    let save = editor
 8499        .update_in(cx, |editor, window, cx| {
 8500            editor.save(true, project.clone(), window, cx)
 8501        })
 8502        .unwrap();
 8503    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8504    cx.executor().start_waiting();
 8505    save.await;
 8506    assert_eq!(
 8507        editor.update(cx, |editor, cx| editor.text(cx)),
 8508        "one\ntwo\nthree\n"
 8509    );
 8510    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8511
 8512    // For non-dirty buffer, no formatting request should be sent
 8513    let save = editor
 8514        .update_in(cx, |editor, window, cx| {
 8515            editor.save(true, project.clone(), window, cx)
 8516        })
 8517        .unwrap();
 8518    let _pending_format_request = fake_server
 8519        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8520            panic!("Should not be invoked on non-dirty buffer");
 8521        })
 8522        .next();
 8523    cx.executor().start_waiting();
 8524    save.await;
 8525
 8526    // Set Rust language override and assert overridden tabsize is sent to language server
 8527    update_test_language_settings(cx, |settings| {
 8528        settings.languages.insert(
 8529            "Rust".into(),
 8530            LanguageSettingsContent {
 8531                tab_size: NonZeroU32::new(8),
 8532                ..Default::default()
 8533            },
 8534        );
 8535    });
 8536
 8537    editor.update_in(cx, |editor, window, cx| {
 8538        editor.set_text("somehting_new\n", window, cx)
 8539    });
 8540    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8541    let save = editor
 8542        .update_in(cx, |editor, window, cx| {
 8543            editor.save(true, project.clone(), window, cx)
 8544        })
 8545        .unwrap();
 8546    fake_server
 8547        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8548            assert_eq!(
 8549                params.text_document.uri,
 8550                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8551            );
 8552            assert_eq!(params.options.tab_size, 8);
 8553            Ok(Some(vec![]))
 8554        })
 8555        .next()
 8556        .await;
 8557    cx.executor().start_waiting();
 8558    save.await;
 8559}
 8560
 8561#[gpui::test]
 8562async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8563    init_test(cx, |settings| {
 8564        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8565            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8566        ))
 8567    });
 8568
 8569    let fs = FakeFs::new(cx.executor());
 8570    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8571
 8572    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8573
 8574    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8575    language_registry.add(Arc::new(Language::new(
 8576        LanguageConfig {
 8577            name: "Rust".into(),
 8578            matcher: LanguageMatcher {
 8579                path_suffixes: vec!["rs".to_string()],
 8580                ..Default::default()
 8581            },
 8582            ..LanguageConfig::default()
 8583        },
 8584        Some(tree_sitter_rust::LANGUAGE.into()),
 8585    )));
 8586    update_test_language_settings(cx, |settings| {
 8587        // Enable Prettier formatting for the same buffer, and ensure
 8588        // LSP is called instead of Prettier.
 8589        settings.defaults.prettier = Some(PrettierSettings {
 8590            allowed: true,
 8591            ..PrettierSettings::default()
 8592        });
 8593    });
 8594    let mut fake_servers = language_registry.register_fake_lsp(
 8595        "Rust",
 8596        FakeLspAdapter {
 8597            capabilities: lsp::ServerCapabilities {
 8598                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8599                ..Default::default()
 8600            },
 8601            ..Default::default()
 8602        },
 8603    );
 8604
 8605    let buffer = project
 8606        .update(cx, |project, cx| {
 8607            project.open_local_buffer(path!("/file.rs"), cx)
 8608        })
 8609        .await
 8610        .unwrap();
 8611
 8612    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8613    let (editor, cx) = cx.add_window_view(|window, cx| {
 8614        build_editor_with_project(project.clone(), buffer, window, cx)
 8615    });
 8616    editor.update_in(cx, |editor, window, cx| {
 8617        editor.set_text("one\ntwo\nthree\n", window, cx)
 8618    });
 8619
 8620    cx.executor().start_waiting();
 8621    let fake_server = fake_servers.next().await.unwrap();
 8622
 8623    let format = editor
 8624        .update_in(cx, |editor, window, cx| {
 8625            editor.perform_format(
 8626                project.clone(),
 8627                FormatTrigger::Manual,
 8628                FormatTarget::Buffers,
 8629                window,
 8630                cx,
 8631            )
 8632        })
 8633        .unwrap();
 8634    fake_server
 8635        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8636            assert_eq!(
 8637                params.text_document.uri,
 8638                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8639            );
 8640            assert_eq!(params.options.tab_size, 4);
 8641            Ok(Some(vec![lsp::TextEdit::new(
 8642                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8643                ", ".to_string(),
 8644            )]))
 8645        })
 8646        .next()
 8647        .await;
 8648    cx.executor().start_waiting();
 8649    format.await;
 8650    assert_eq!(
 8651        editor.update(cx, |editor, cx| editor.text(cx)),
 8652        "one, two\nthree\n"
 8653    );
 8654
 8655    editor.update_in(cx, |editor, window, cx| {
 8656        editor.set_text("one\ntwo\nthree\n", window, cx)
 8657    });
 8658    // Ensure we don't lock if formatting hangs.
 8659    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8660        move |params, _| async move {
 8661            assert_eq!(
 8662                params.text_document.uri,
 8663                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8664            );
 8665            futures::future::pending::<()>().await;
 8666            unreachable!()
 8667        },
 8668    );
 8669    let format = editor
 8670        .update_in(cx, |editor, window, cx| {
 8671            editor.perform_format(
 8672                project,
 8673                FormatTrigger::Manual,
 8674                FormatTarget::Buffers,
 8675                window,
 8676                cx,
 8677            )
 8678        })
 8679        .unwrap();
 8680    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8681    cx.executor().start_waiting();
 8682    format.await;
 8683    assert_eq!(
 8684        editor.update(cx, |editor, cx| editor.text(cx)),
 8685        "one\ntwo\nthree\n"
 8686    );
 8687}
 8688
 8689#[gpui::test]
 8690async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8691    init_test(cx, |settings| {
 8692        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8693        settings.defaults.formatter =
 8694            Some(language_settings::SelectedFormatter::List(FormatterList(
 8695                vec![
 8696                    Formatter::LanguageServer { name: None },
 8697                    Formatter::CodeActions(
 8698                        [
 8699                            ("code-action-1".into(), true),
 8700                            ("code-action-2".into(), true),
 8701                        ]
 8702                        .into_iter()
 8703                        .collect(),
 8704                    ),
 8705                ]
 8706                .into(),
 8707            )))
 8708    });
 8709
 8710    let fs = FakeFs::new(cx.executor());
 8711    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8712        .await;
 8713
 8714    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8715    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8716    language_registry.add(rust_lang());
 8717
 8718    let mut fake_servers = language_registry.register_fake_lsp(
 8719        "Rust",
 8720        FakeLspAdapter {
 8721            capabilities: lsp::ServerCapabilities {
 8722                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8723                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8724                    commands: vec!["the-command-for-code-action-1".into()],
 8725                    ..Default::default()
 8726                }),
 8727                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8728                ..Default::default()
 8729            },
 8730            ..Default::default()
 8731        },
 8732    );
 8733
 8734    let buffer = project
 8735        .update(cx, |project, cx| {
 8736            project.open_local_buffer(path!("/file.rs"), cx)
 8737        })
 8738        .await
 8739        .unwrap();
 8740
 8741    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8742    let (editor, cx) = cx.add_window_view(|window, cx| {
 8743        build_editor_with_project(project.clone(), buffer, window, cx)
 8744    });
 8745
 8746    cx.executor().start_waiting();
 8747
 8748    let fake_server = fake_servers.next().await.unwrap();
 8749    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8750        move |_params, _| async move {
 8751            Ok(Some(vec![lsp::TextEdit::new(
 8752                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8753                "applied-formatting\n".to_string(),
 8754            )]))
 8755        },
 8756    );
 8757    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8758        move |params, _| async move {
 8759            assert_eq!(
 8760                params.context.only,
 8761                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8762            );
 8763            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8764            Ok(Some(vec![
 8765                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8766                    kind: Some("code-action-1".into()),
 8767                    edit: Some(lsp::WorkspaceEdit::new(
 8768                        [(
 8769                            uri.clone(),
 8770                            vec![lsp::TextEdit::new(
 8771                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8772                                "applied-code-action-1-edit\n".to_string(),
 8773                            )],
 8774                        )]
 8775                        .into_iter()
 8776                        .collect(),
 8777                    )),
 8778                    command: Some(lsp::Command {
 8779                        command: "the-command-for-code-action-1".into(),
 8780                        ..Default::default()
 8781                    }),
 8782                    ..Default::default()
 8783                }),
 8784                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8785                    kind: Some("code-action-2".into()),
 8786                    edit: Some(lsp::WorkspaceEdit::new(
 8787                        [(
 8788                            uri.clone(),
 8789                            vec![lsp::TextEdit::new(
 8790                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8791                                "applied-code-action-2-edit\n".to_string(),
 8792                            )],
 8793                        )]
 8794                        .into_iter()
 8795                        .collect(),
 8796                    )),
 8797                    ..Default::default()
 8798                }),
 8799            ]))
 8800        },
 8801    );
 8802
 8803    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8804        move |params, _| async move { Ok(params) }
 8805    });
 8806
 8807    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8808    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8809        let fake = fake_server.clone();
 8810        let lock = command_lock.clone();
 8811        move |params, _| {
 8812            assert_eq!(params.command, "the-command-for-code-action-1");
 8813            let fake = fake.clone();
 8814            let lock = lock.clone();
 8815            async move {
 8816                lock.lock().await;
 8817                fake.server
 8818                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8819                        label: None,
 8820                        edit: lsp::WorkspaceEdit {
 8821                            changes: Some(
 8822                                [(
 8823                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8824                                    vec![lsp::TextEdit {
 8825                                        range: lsp::Range::new(
 8826                                            lsp::Position::new(0, 0),
 8827                                            lsp::Position::new(0, 0),
 8828                                        ),
 8829                                        new_text: "applied-code-action-1-command\n".into(),
 8830                                    }],
 8831                                )]
 8832                                .into_iter()
 8833                                .collect(),
 8834                            ),
 8835                            ..Default::default()
 8836                        },
 8837                    })
 8838                    .await
 8839                    .unwrap();
 8840                Ok(Some(json!(null)))
 8841            }
 8842        }
 8843    });
 8844
 8845    cx.executor().start_waiting();
 8846    editor
 8847        .update_in(cx, |editor, window, cx| {
 8848            editor.perform_format(
 8849                project.clone(),
 8850                FormatTrigger::Manual,
 8851                FormatTarget::Buffers,
 8852                window,
 8853                cx,
 8854            )
 8855        })
 8856        .unwrap()
 8857        .await;
 8858    editor.update(cx, |editor, cx| {
 8859        assert_eq!(
 8860            editor.text(cx),
 8861            r#"
 8862                applied-code-action-2-edit
 8863                applied-code-action-1-command
 8864                applied-code-action-1-edit
 8865                applied-formatting
 8866                one
 8867                two
 8868                three
 8869            "#
 8870            .unindent()
 8871        );
 8872    });
 8873
 8874    editor.update_in(cx, |editor, window, cx| {
 8875        editor.undo(&Default::default(), window, cx);
 8876        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8877    });
 8878
 8879    // Perform a manual edit while waiting for an LSP command
 8880    // that's being run as part of a formatting code action.
 8881    let lock_guard = command_lock.lock().await;
 8882    let format = editor
 8883        .update_in(cx, |editor, window, cx| {
 8884            editor.perform_format(
 8885                project.clone(),
 8886                FormatTrigger::Manual,
 8887                FormatTarget::Buffers,
 8888                window,
 8889                cx,
 8890            )
 8891        })
 8892        .unwrap();
 8893    cx.run_until_parked();
 8894    editor.update(cx, |editor, cx| {
 8895        assert_eq!(
 8896            editor.text(cx),
 8897            r#"
 8898                applied-code-action-1-edit
 8899                applied-formatting
 8900                one
 8901                two
 8902                three
 8903            "#
 8904            .unindent()
 8905        );
 8906
 8907        editor.buffer.update(cx, |buffer, cx| {
 8908            let ix = buffer.len(cx);
 8909            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8910        });
 8911    });
 8912
 8913    // Allow the LSP command to proceed. Because the buffer was edited,
 8914    // the second code action will not be run.
 8915    drop(lock_guard);
 8916    format.await;
 8917    editor.update_in(cx, |editor, window, cx| {
 8918        assert_eq!(
 8919            editor.text(cx),
 8920            r#"
 8921                applied-code-action-1-command
 8922                applied-code-action-1-edit
 8923                applied-formatting
 8924                one
 8925                two
 8926                three
 8927                edited
 8928            "#
 8929            .unindent()
 8930        );
 8931
 8932        // The manual edit is undone first, because it is the last thing the user did
 8933        // (even though the command completed afterwards).
 8934        editor.undo(&Default::default(), window, cx);
 8935        assert_eq!(
 8936            editor.text(cx),
 8937            r#"
 8938                applied-code-action-1-command
 8939                applied-code-action-1-edit
 8940                applied-formatting
 8941                one
 8942                two
 8943                three
 8944            "#
 8945            .unindent()
 8946        );
 8947
 8948        // All the formatting (including the command, which completed after the manual edit)
 8949        // is undone together.
 8950        editor.undo(&Default::default(), window, cx);
 8951        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8952    });
 8953}
 8954
 8955#[gpui::test]
 8956async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8957    init_test(cx, |settings| {
 8958        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8959            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8960        ))
 8961    });
 8962
 8963    let fs = FakeFs::new(cx.executor());
 8964    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8965
 8966    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8967
 8968    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8969    language_registry.add(Arc::new(Language::new(
 8970        LanguageConfig {
 8971            name: "TypeScript".into(),
 8972            matcher: LanguageMatcher {
 8973                path_suffixes: vec!["ts".to_string()],
 8974                ..Default::default()
 8975            },
 8976            ..LanguageConfig::default()
 8977        },
 8978        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8979    )));
 8980    update_test_language_settings(cx, |settings| {
 8981        settings.defaults.prettier = Some(PrettierSettings {
 8982            allowed: true,
 8983            ..PrettierSettings::default()
 8984        });
 8985    });
 8986    let mut fake_servers = language_registry.register_fake_lsp(
 8987        "TypeScript",
 8988        FakeLspAdapter {
 8989            capabilities: lsp::ServerCapabilities {
 8990                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8991                ..Default::default()
 8992            },
 8993            ..Default::default()
 8994        },
 8995    );
 8996
 8997    let buffer = project
 8998        .update(cx, |project, cx| {
 8999            project.open_local_buffer(path!("/file.ts"), cx)
 9000        })
 9001        .await
 9002        .unwrap();
 9003
 9004    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9005    let (editor, cx) = cx.add_window_view(|window, cx| {
 9006        build_editor_with_project(project.clone(), buffer, window, cx)
 9007    });
 9008    editor.update_in(cx, |editor, window, cx| {
 9009        editor.set_text(
 9010            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9011            window,
 9012            cx,
 9013        )
 9014    });
 9015
 9016    cx.executor().start_waiting();
 9017    let fake_server = fake_servers.next().await.unwrap();
 9018
 9019    let format = editor
 9020        .update_in(cx, |editor, window, cx| {
 9021            editor.perform_code_action_kind(
 9022                project.clone(),
 9023                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9024                window,
 9025                cx,
 9026            )
 9027        })
 9028        .unwrap();
 9029    fake_server
 9030        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9031            assert_eq!(
 9032                params.text_document.uri,
 9033                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9034            );
 9035            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9036                lsp::CodeAction {
 9037                    title: "Organize Imports".to_string(),
 9038                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9039                    edit: Some(lsp::WorkspaceEdit {
 9040                        changes: Some(
 9041                            [(
 9042                                params.text_document.uri.clone(),
 9043                                vec![lsp::TextEdit::new(
 9044                                    lsp::Range::new(
 9045                                        lsp::Position::new(1, 0),
 9046                                        lsp::Position::new(2, 0),
 9047                                    ),
 9048                                    "".to_string(),
 9049                                )],
 9050                            )]
 9051                            .into_iter()
 9052                            .collect(),
 9053                        ),
 9054                        ..Default::default()
 9055                    }),
 9056                    ..Default::default()
 9057                },
 9058            )]))
 9059        })
 9060        .next()
 9061        .await;
 9062    cx.executor().start_waiting();
 9063    format.await;
 9064    assert_eq!(
 9065        editor.update(cx, |editor, cx| editor.text(cx)),
 9066        "import { a } from 'module';\n\nconst x = a;\n"
 9067    );
 9068
 9069    editor.update_in(cx, |editor, window, cx| {
 9070        editor.set_text(
 9071            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9072            window,
 9073            cx,
 9074        )
 9075    });
 9076    // Ensure we don't lock if code action hangs.
 9077    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9078        move |params, _| async move {
 9079            assert_eq!(
 9080                params.text_document.uri,
 9081                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9082            );
 9083            futures::future::pending::<()>().await;
 9084            unreachable!()
 9085        },
 9086    );
 9087    let format = editor
 9088        .update_in(cx, |editor, window, cx| {
 9089            editor.perform_code_action_kind(
 9090                project,
 9091                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9092                window,
 9093                cx,
 9094            )
 9095        })
 9096        .unwrap();
 9097    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9098    cx.executor().start_waiting();
 9099    format.await;
 9100    assert_eq!(
 9101        editor.update(cx, |editor, cx| editor.text(cx)),
 9102        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9103    );
 9104}
 9105
 9106#[gpui::test]
 9107async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9108    init_test(cx, |_| {});
 9109
 9110    let mut cx = EditorLspTestContext::new_rust(
 9111        lsp::ServerCapabilities {
 9112            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9113            ..Default::default()
 9114        },
 9115        cx,
 9116    )
 9117    .await;
 9118
 9119    cx.set_state(indoc! {"
 9120        one.twoˇ
 9121    "});
 9122
 9123    // The format request takes a long time. When it completes, it inserts
 9124    // a newline and an indent before the `.`
 9125    cx.lsp
 9126        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9127            let executor = cx.background_executor().clone();
 9128            async move {
 9129                executor.timer(Duration::from_millis(100)).await;
 9130                Ok(Some(vec![lsp::TextEdit {
 9131                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9132                    new_text: "\n    ".into(),
 9133                }]))
 9134            }
 9135        });
 9136
 9137    // Submit a format request.
 9138    let format_1 = cx
 9139        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9140        .unwrap();
 9141    cx.executor().run_until_parked();
 9142
 9143    // Submit a second format request.
 9144    let format_2 = cx
 9145        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9146        .unwrap();
 9147    cx.executor().run_until_parked();
 9148
 9149    // Wait for both format requests to complete
 9150    cx.executor().advance_clock(Duration::from_millis(200));
 9151    cx.executor().start_waiting();
 9152    format_1.await.unwrap();
 9153    cx.executor().start_waiting();
 9154    format_2.await.unwrap();
 9155
 9156    // The formatting edits only happens once.
 9157    cx.assert_editor_state(indoc! {"
 9158        one
 9159            .twoˇ
 9160    "});
 9161}
 9162
 9163#[gpui::test]
 9164async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9165    init_test(cx, |settings| {
 9166        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9167    });
 9168
 9169    let mut cx = EditorLspTestContext::new_rust(
 9170        lsp::ServerCapabilities {
 9171            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9172            ..Default::default()
 9173        },
 9174        cx,
 9175    )
 9176    .await;
 9177
 9178    // Set up a buffer white some trailing whitespace and no trailing newline.
 9179    cx.set_state(
 9180        &[
 9181            "one ",   //
 9182            "twoˇ",   //
 9183            "three ", //
 9184            "four",   //
 9185        ]
 9186        .join("\n"),
 9187    );
 9188
 9189    // Submit a format request.
 9190    let format = cx
 9191        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9192        .unwrap();
 9193
 9194    // Record which buffer changes have been sent to the language server
 9195    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9196    cx.lsp
 9197        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9198            let buffer_changes = buffer_changes.clone();
 9199            move |params, _| {
 9200                buffer_changes.lock().extend(
 9201                    params
 9202                        .content_changes
 9203                        .into_iter()
 9204                        .map(|e| (e.range.unwrap(), e.text)),
 9205                );
 9206            }
 9207        });
 9208
 9209    // Handle formatting requests to the language server.
 9210    cx.lsp
 9211        .set_request_handler::<lsp::request::Formatting, _, _>({
 9212            let buffer_changes = buffer_changes.clone();
 9213            move |_, _| {
 9214                // When formatting is requested, trailing whitespace has already been stripped,
 9215                // and the trailing newline has already been added.
 9216                assert_eq!(
 9217                    &buffer_changes.lock()[1..],
 9218                    &[
 9219                        (
 9220                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9221                            "".into()
 9222                        ),
 9223                        (
 9224                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9225                            "".into()
 9226                        ),
 9227                        (
 9228                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9229                            "\n".into()
 9230                        ),
 9231                    ]
 9232                );
 9233
 9234                // Insert blank lines between each line of the buffer.
 9235                async move {
 9236                    Ok(Some(vec![
 9237                        lsp::TextEdit {
 9238                            range: lsp::Range::new(
 9239                                lsp::Position::new(1, 0),
 9240                                lsp::Position::new(1, 0),
 9241                            ),
 9242                            new_text: "\n".into(),
 9243                        },
 9244                        lsp::TextEdit {
 9245                            range: lsp::Range::new(
 9246                                lsp::Position::new(2, 0),
 9247                                lsp::Position::new(2, 0),
 9248                            ),
 9249                            new_text: "\n".into(),
 9250                        },
 9251                    ]))
 9252                }
 9253            }
 9254        });
 9255
 9256    // After formatting the buffer, the trailing whitespace is stripped,
 9257    // a newline is appended, and the edits provided by the language server
 9258    // have been applied.
 9259    format.await.unwrap();
 9260    cx.assert_editor_state(
 9261        &[
 9262            "one",   //
 9263            "",      //
 9264            "twoˇ",  //
 9265            "",      //
 9266            "three", //
 9267            "four",  //
 9268            "",      //
 9269        ]
 9270        .join("\n"),
 9271    );
 9272
 9273    // Undoing the formatting undoes the trailing whitespace removal, the
 9274    // trailing newline, and the LSP edits.
 9275    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9276    cx.assert_editor_state(
 9277        &[
 9278            "one ",   //
 9279            "twoˇ",   //
 9280            "three ", //
 9281            "four",   //
 9282        ]
 9283        .join("\n"),
 9284    );
 9285}
 9286
 9287#[gpui::test]
 9288async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9289    cx: &mut TestAppContext,
 9290) {
 9291    init_test(cx, |_| {});
 9292
 9293    cx.update(|cx| {
 9294        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9295            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9296                settings.auto_signature_help = Some(true);
 9297            });
 9298        });
 9299    });
 9300
 9301    let mut cx = EditorLspTestContext::new_rust(
 9302        lsp::ServerCapabilities {
 9303            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9304                ..Default::default()
 9305            }),
 9306            ..Default::default()
 9307        },
 9308        cx,
 9309    )
 9310    .await;
 9311
 9312    let language = Language::new(
 9313        LanguageConfig {
 9314            name: "Rust".into(),
 9315            brackets: BracketPairConfig {
 9316                pairs: vec![
 9317                    BracketPair {
 9318                        start: "{".to_string(),
 9319                        end: "}".to_string(),
 9320                        close: true,
 9321                        surround: true,
 9322                        newline: true,
 9323                    },
 9324                    BracketPair {
 9325                        start: "(".to_string(),
 9326                        end: ")".to_string(),
 9327                        close: true,
 9328                        surround: true,
 9329                        newline: true,
 9330                    },
 9331                    BracketPair {
 9332                        start: "/*".to_string(),
 9333                        end: " */".to_string(),
 9334                        close: true,
 9335                        surround: true,
 9336                        newline: true,
 9337                    },
 9338                    BracketPair {
 9339                        start: "[".to_string(),
 9340                        end: "]".to_string(),
 9341                        close: false,
 9342                        surround: false,
 9343                        newline: true,
 9344                    },
 9345                    BracketPair {
 9346                        start: "\"".to_string(),
 9347                        end: "\"".to_string(),
 9348                        close: true,
 9349                        surround: true,
 9350                        newline: false,
 9351                    },
 9352                    BracketPair {
 9353                        start: "<".to_string(),
 9354                        end: ">".to_string(),
 9355                        close: false,
 9356                        surround: true,
 9357                        newline: true,
 9358                    },
 9359                ],
 9360                ..Default::default()
 9361            },
 9362            autoclose_before: "})]".to_string(),
 9363            ..Default::default()
 9364        },
 9365        Some(tree_sitter_rust::LANGUAGE.into()),
 9366    );
 9367    let language = Arc::new(language);
 9368
 9369    cx.language_registry().add(language.clone());
 9370    cx.update_buffer(|buffer, cx| {
 9371        buffer.set_language(Some(language), cx);
 9372    });
 9373
 9374    cx.set_state(
 9375        &r#"
 9376            fn main() {
 9377                sampleˇ
 9378            }
 9379        "#
 9380        .unindent(),
 9381    );
 9382
 9383    cx.update_editor(|editor, window, cx| {
 9384        editor.handle_input("(", window, cx);
 9385    });
 9386    cx.assert_editor_state(
 9387        &"
 9388            fn main() {
 9389                sample(ˇ)
 9390            }
 9391        "
 9392        .unindent(),
 9393    );
 9394
 9395    let mocked_response = lsp::SignatureHelp {
 9396        signatures: vec![lsp::SignatureInformation {
 9397            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9398            documentation: None,
 9399            parameters: Some(vec![
 9400                lsp::ParameterInformation {
 9401                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9402                    documentation: None,
 9403                },
 9404                lsp::ParameterInformation {
 9405                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9406                    documentation: None,
 9407                },
 9408            ]),
 9409            active_parameter: None,
 9410        }],
 9411        active_signature: Some(0),
 9412        active_parameter: Some(0),
 9413    };
 9414    handle_signature_help_request(&mut cx, mocked_response).await;
 9415
 9416    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9417        .await;
 9418
 9419    cx.editor(|editor, _, _| {
 9420        let signature_help_state = editor.signature_help_state.popover().cloned();
 9421        assert_eq!(
 9422            signature_help_state.unwrap().label,
 9423            "param1: u8, param2: u8"
 9424        );
 9425    });
 9426}
 9427
 9428#[gpui::test]
 9429async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9430    init_test(cx, |_| {});
 9431
 9432    cx.update(|cx| {
 9433        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9434            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9435                settings.auto_signature_help = Some(false);
 9436                settings.show_signature_help_after_edits = Some(false);
 9437            });
 9438        });
 9439    });
 9440
 9441    let mut cx = EditorLspTestContext::new_rust(
 9442        lsp::ServerCapabilities {
 9443            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9444                ..Default::default()
 9445            }),
 9446            ..Default::default()
 9447        },
 9448        cx,
 9449    )
 9450    .await;
 9451
 9452    let language = Language::new(
 9453        LanguageConfig {
 9454            name: "Rust".into(),
 9455            brackets: BracketPairConfig {
 9456                pairs: vec![
 9457                    BracketPair {
 9458                        start: "{".to_string(),
 9459                        end: "}".to_string(),
 9460                        close: true,
 9461                        surround: true,
 9462                        newline: true,
 9463                    },
 9464                    BracketPair {
 9465                        start: "(".to_string(),
 9466                        end: ")".to_string(),
 9467                        close: true,
 9468                        surround: true,
 9469                        newline: true,
 9470                    },
 9471                    BracketPair {
 9472                        start: "/*".to_string(),
 9473                        end: " */".to_string(),
 9474                        close: true,
 9475                        surround: true,
 9476                        newline: true,
 9477                    },
 9478                    BracketPair {
 9479                        start: "[".to_string(),
 9480                        end: "]".to_string(),
 9481                        close: false,
 9482                        surround: false,
 9483                        newline: true,
 9484                    },
 9485                    BracketPair {
 9486                        start: "\"".to_string(),
 9487                        end: "\"".to_string(),
 9488                        close: true,
 9489                        surround: true,
 9490                        newline: false,
 9491                    },
 9492                    BracketPair {
 9493                        start: "<".to_string(),
 9494                        end: ">".to_string(),
 9495                        close: false,
 9496                        surround: true,
 9497                        newline: true,
 9498                    },
 9499                ],
 9500                ..Default::default()
 9501            },
 9502            autoclose_before: "})]".to_string(),
 9503            ..Default::default()
 9504        },
 9505        Some(tree_sitter_rust::LANGUAGE.into()),
 9506    );
 9507    let language = Arc::new(language);
 9508
 9509    cx.language_registry().add(language.clone());
 9510    cx.update_buffer(|buffer, cx| {
 9511        buffer.set_language(Some(language), cx);
 9512    });
 9513
 9514    // Ensure that signature_help is not called when no signature help is enabled.
 9515    cx.set_state(
 9516        &r#"
 9517            fn main() {
 9518                sampleˇ
 9519            }
 9520        "#
 9521        .unindent(),
 9522    );
 9523    cx.update_editor(|editor, window, cx| {
 9524        editor.handle_input("(", window, cx);
 9525    });
 9526    cx.assert_editor_state(
 9527        &"
 9528            fn main() {
 9529                sample(ˇ)
 9530            }
 9531        "
 9532        .unindent(),
 9533    );
 9534    cx.editor(|editor, _, _| {
 9535        assert!(editor.signature_help_state.task().is_none());
 9536    });
 9537
 9538    let mocked_response = lsp::SignatureHelp {
 9539        signatures: vec![lsp::SignatureInformation {
 9540            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9541            documentation: None,
 9542            parameters: Some(vec![
 9543                lsp::ParameterInformation {
 9544                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9545                    documentation: None,
 9546                },
 9547                lsp::ParameterInformation {
 9548                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9549                    documentation: None,
 9550                },
 9551            ]),
 9552            active_parameter: None,
 9553        }],
 9554        active_signature: Some(0),
 9555        active_parameter: Some(0),
 9556    };
 9557
 9558    // Ensure that signature_help is called when enabled afte edits
 9559    cx.update(|_, cx| {
 9560        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9561            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9562                settings.auto_signature_help = Some(false);
 9563                settings.show_signature_help_after_edits = Some(true);
 9564            });
 9565        });
 9566    });
 9567    cx.set_state(
 9568        &r#"
 9569            fn main() {
 9570                sampleˇ
 9571            }
 9572        "#
 9573        .unindent(),
 9574    );
 9575    cx.update_editor(|editor, window, cx| {
 9576        editor.handle_input("(", window, cx);
 9577    });
 9578    cx.assert_editor_state(
 9579        &"
 9580            fn main() {
 9581                sample(ˇ)
 9582            }
 9583        "
 9584        .unindent(),
 9585    );
 9586    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9587    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9588        .await;
 9589    cx.update_editor(|editor, _, _| {
 9590        let signature_help_state = editor.signature_help_state.popover().cloned();
 9591        assert!(signature_help_state.is_some());
 9592        assert_eq!(
 9593            signature_help_state.unwrap().label,
 9594            "param1: u8, param2: u8"
 9595        );
 9596        editor.signature_help_state = SignatureHelpState::default();
 9597    });
 9598
 9599    // Ensure that signature_help is called when auto signature help override is enabled
 9600    cx.update(|_, cx| {
 9601        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9602            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9603                settings.auto_signature_help = Some(true);
 9604                settings.show_signature_help_after_edits = Some(false);
 9605            });
 9606        });
 9607    });
 9608    cx.set_state(
 9609        &r#"
 9610            fn main() {
 9611                sampleˇ
 9612            }
 9613        "#
 9614        .unindent(),
 9615    );
 9616    cx.update_editor(|editor, window, cx| {
 9617        editor.handle_input("(", window, cx);
 9618    });
 9619    cx.assert_editor_state(
 9620        &"
 9621            fn main() {
 9622                sample(ˇ)
 9623            }
 9624        "
 9625        .unindent(),
 9626    );
 9627    handle_signature_help_request(&mut cx, mocked_response).await;
 9628    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9629        .await;
 9630    cx.editor(|editor, _, _| {
 9631        let signature_help_state = editor.signature_help_state.popover().cloned();
 9632        assert!(signature_help_state.is_some());
 9633        assert_eq!(
 9634            signature_help_state.unwrap().label,
 9635            "param1: u8, param2: u8"
 9636        );
 9637    });
 9638}
 9639
 9640#[gpui::test]
 9641async fn test_signature_help(cx: &mut TestAppContext) {
 9642    init_test(cx, |_| {});
 9643    cx.update(|cx| {
 9644        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9645            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9646                settings.auto_signature_help = Some(true);
 9647            });
 9648        });
 9649    });
 9650
 9651    let mut cx = EditorLspTestContext::new_rust(
 9652        lsp::ServerCapabilities {
 9653            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9654                ..Default::default()
 9655            }),
 9656            ..Default::default()
 9657        },
 9658        cx,
 9659    )
 9660    .await;
 9661
 9662    // A test that directly calls `show_signature_help`
 9663    cx.update_editor(|editor, window, cx| {
 9664        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9665    });
 9666
 9667    let mocked_response = lsp::SignatureHelp {
 9668        signatures: vec![lsp::SignatureInformation {
 9669            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9670            documentation: None,
 9671            parameters: Some(vec![
 9672                lsp::ParameterInformation {
 9673                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9674                    documentation: None,
 9675                },
 9676                lsp::ParameterInformation {
 9677                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9678                    documentation: None,
 9679                },
 9680            ]),
 9681            active_parameter: None,
 9682        }],
 9683        active_signature: Some(0),
 9684        active_parameter: Some(0),
 9685    };
 9686    handle_signature_help_request(&mut cx, mocked_response).await;
 9687
 9688    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9689        .await;
 9690
 9691    cx.editor(|editor, _, _| {
 9692        let signature_help_state = editor.signature_help_state.popover().cloned();
 9693        assert!(signature_help_state.is_some());
 9694        assert_eq!(
 9695            signature_help_state.unwrap().label,
 9696            "param1: u8, param2: u8"
 9697        );
 9698    });
 9699
 9700    // When exiting outside from inside the brackets, `signature_help` is closed.
 9701    cx.set_state(indoc! {"
 9702        fn main() {
 9703            sample(ˇ);
 9704        }
 9705
 9706        fn sample(param1: u8, param2: u8) {}
 9707    "});
 9708
 9709    cx.update_editor(|editor, window, cx| {
 9710        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9711    });
 9712
 9713    let mocked_response = lsp::SignatureHelp {
 9714        signatures: Vec::new(),
 9715        active_signature: None,
 9716        active_parameter: None,
 9717    };
 9718    handle_signature_help_request(&mut cx, mocked_response).await;
 9719
 9720    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9721        .await;
 9722
 9723    cx.editor(|editor, _, _| {
 9724        assert!(!editor.signature_help_state.is_shown());
 9725    });
 9726
 9727    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9728    cx.set_state(indoc! {"
 9729        fn main() {
 9730            sample(ˇ);
 9731        }
 9732
 9733        fn sample(param1: u8, param2: u8) {}
 9734    "});
 9735
 9736    let mocked_response = lsp::SignatureHelp {
 9737        signatures: vec![lsp::SignatureInformation {
 9738            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9739            documentation: None,
 9740            parameters: Some(vec![
 9741                lsp::ParameterInformation {
 9742                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9743                    documentation: None,
 9744                },
 9745                lsp::ParameterInformation {
 9746                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9747                    documentation: None,
 9748                },
 9749            ]),
 9750            active_parameter: None,
 9751        }],
 9752        active_signature: Some(0),
 9753        active_parameter: Some(0),
 9754    };
 9755    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9756    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9757        .await;
 9758    cx.editor(|editor, _, _| {
 9759        assert!(editor.signature_help_state.is_shown());
 9760    });
 9761
 9762    // Restore the popover with more parameter input
 9763    cx.set_state(indoc! {"
 9764        fn main() {
 9765            sample(param1, param2ˇ);
 9766        }
 9767
 9768        fn sample(param1: u8, param2: u8) {}
 9769    "});
 9770
 9771    let mocked_response = lsp::SignatureHelp {
 9772        signatures: vec![lsp::SignatureInformation {
 9773            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9774            documentation: None,
 9775            parameters: Some(vec![
 9776                lsp::ParameterInformation {
 9777                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9778                    documentation: None,
 9779                },
 9780                lsp::ParameterInformation {
 9781                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9782                    documentation: None,
 9783                },
 9784            ]),
 9785            active_parameter: None,
 9786        }],
 9787        active_signature: Some(0),
 9788        active_parameter: Some(1),
 9789    };
 9790    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9791    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9792        .await;
 9793
 9794    // When selecting a range, the popover is gone.
 9795    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9796    cx.update_editor(|editor, window, cx| {
 9797        editor.change_selections(None, window, cx, |s| {
 9798            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9799        })
 9800    });
 9801    cx.assert_editor_state(indoc! {"
 9802        fn main() {
 9803            sample(param1, «ˇparam2»);
 9804        }
 9805
 9806        fn sample(param1: u8, param2: u8) {}
 9807    "});
 9808    cx.editor(|editor, _, _| {
 9809        assert!(!editor.signature_help_state.is_shown());
 9810    });
 9811
 9812    // When unselecting again, the popover is back if within the brackets.
 9813    cx.update_editor(|editor, window, cx| {
 9814        editor.change_selections(None, window, cx, |s| {
 9815            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9816        })
 9817    });
 9818    cx.assert_editor_state(indoc! {"
 9819        fn main() {
 9820            sample(param1, ˇparam2);
 9821        }
 9822
 9823        fn sample(param1: u8, param2: u8) {}
 9824    "});
 9825    handle_signature_help_request(&mut cx, mocked_response).await;
 9826    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9827        .await;
 9828    cx.editor(|editor, _, _| {
 9829        assert!(editor.signature_help_state.is_shown());
 9830    });
 9831
 9832    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9833    cx.update_editor(|editor, window, cx| {
 9834        editor.change_selections(None, window, cx, |s| {
 9835            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9836            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9837        })
 9838    });
 9839    cx.assert_editor_state(indoc! {"
 9840        fn main() {
 9841            sample(param1, ˇparam2);
 9842        }
 9843
 9844        fn sample(param1: u8, param2: u8) {}
 9845    "});
 9846
 9847    let mocked_response = lsp::SignatureHelp {
 9848        signatures: vec![lsp::SignatureInformation {
 9849            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9850            documentation: None,
 9851            parameters: Some(vec![
 9852                lsp::ParameterInformation {
 9853                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9854                    documentation: None,
 9855                },
 9856                lsp::ParameterInformation {
 9857                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9858                    documentation: None,
 9859                },
 9860            ]),
 9861            active_parameter: None,
 9862        }],
 9863        active_signature: Some(0),
 9864        active_parameter: Some(1),
 9865    };
 9866    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9867    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9868        .await;
 9869    cx.update_editor(|editor, _, cx| {
 9870        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9871    });
 9872    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9873        .await;
 9874    cx.update_editor(|editor, window, cx| {
 9875        editor.change_selections(None, window, cx, |s| {
 9876            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9877        })
 9878    });
 9879    cx.assert_editor_state(indoc! {"
 9880        fn main() {
 9881            sample(param1, «ˇparam2»);
 9882        }
 9883
 9884        fn sample(param1: u8, param2: u8) {}
 9885    "});
 9886    cx.update_editor(|editor, window, cx| {
 9887        editor.change_selections(None, window, cx, |s| {
 9888            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9889        })
 9890    });
 9891    cx.assert_editor_state(indoc! {"
 9892        fn main() {
 9893            sample(param1, ˇparam2);
 9894        }
 9895
 9896        fn sample(param1: u8, param2: u8) {}
 9897    "});
 9898    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9899        .await;
 9900}
 9901
 9902#[gpui::test]
 9903async fn test_completion_mode(cx: &mut TestAppContext) {
 9904    init_test(cx, |_| {});
 9905    let mut cx = EditorLspTestContext::new_rust(
 9906        lsp::ServerCapabilities {
 9907            completion_provider: Some(lsp::CompletionOptions {
 9908                resolve_provider: Some(true),
 9909                ..Default::default()
 9910            }),
 9911            ..Default::default()
 9912        },
 9913        cx,
 9914    )
 9915    .await;
 9916
 9917    struct Run {
 9918        run_description: &'static str,
 9919        initial_state: String,
 9920        buffer_marked_text: String,
 9921        completion_text: &'static str,
 9922        expected_with_insert_mode: String,
 9923        expected_with_replace_mode: String,
 9924        expected_with_replace_subsequence_mode: String,
 9925        expected_with_replace_suffix_mode: String,
 9926    }
 9927
 9928    let runs = [
 9929        Run {
 9930            run_description: "Start of word matches completion text",
 9931            initial_state: "before ediˇ after".into(),
 9932            buffer_marked_text: "before <edi|> after".into(),
 9933            completion_text: "editor",
 9934            expected_with_insert_mode: "before editorˇ after".into(),
 9935            expected_with_replace_mode: "before editorˇ after".into(),
 9936            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9937            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9938        },
 9939        Run {
 9940            run_description: "Accept same text at the middle of the word",
 9941            initial_state: "before ediˇtor after".into(),
 9942            buffer_marked_text: "before <edi|tor> after".into(),
 9943            completion_text: "editor",
 9944            expected_with_insert_mode: "before editorˇtor after".into(),
 9945            expected_with_replace_mode: "before editorˇ after".into(),
 9946            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9947            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9948        },
 9949        Run {
 9950            run_description: "End of word matches completion text -- cursor at end",
 9951            initial_state: "before torˇ after".into(),
 9952            buffer_marked_text: "before <tor|> after".into(),
 9953            completion_text: "editor",
 9954            expected_with_insert_mode: "before editorˇ after".into(),
 9955            expected_with_replace_mode: "before editorˇ after".into(),
 9956            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9957            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9958        },
 9959        Run {
 9960            run_description: "End of word matches completion text -- cursor at start",
 9961            initial_state: "before ˇtor after".into(),
 9962            buffer_marked_text: "before <|tor> after".into(),
 9963            completion_text: "editor",
 9964            expected_with_insert_mode: "before editorˇtor after".into(),
 9965            expected_with_replace_mode: "before editorˇ after".into(),
 9966            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9967            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9968        },
 9969        Run {
 9970            run_description: "Prepend text containing whitespace",
 9971            initial_state: "pˇfield: bool".into(),
 9972            buffer_marked_text: "<p|field>: bool".into(),
 9973            completion_text: "pub ",
 9974            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9975            expected_with_replace_mode: "pub ˇ: bool".into(),
 9976            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9977            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9978        },
 9979        Run {
 9980            run_description: "Add element to start of list",
 9981            initial_state: "[element_ˇelement_2]".into(),
 9982            buffer_marked_text: "[<element_|element_2>]".into(),
 9983            completion_text: "element_1",
 9984            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9985            expected_with_replace_mode: "[element_1ˇ]".into(),
 9986            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9987            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9988        },
 9989        Run {
 9990            run_description: "Add element to start of list -- first and second elements are equal",
 9991            initial_state: "[elˇelement]".into(),
 9992            buffer_marked_text: "[<el|element>]".into(),
 9993            completion_text: "element",
 9994            expected_with_insert_mode: "[elementˇelement]".into(),
 9995            expected_with_replace_mode: "[elementˇ]".into(),
 9996            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9997            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9998        },
 9999        Run {
10000            run_description: "Ends with matching suffix",
10001            initial_state: "SubˇError".into(),
10002            buffer_marked_text: "<Sub|Error>".into(),
10003            completion_text: "SubscriptionError",
10004            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10005            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10006            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10007            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10008        },
10009        Run {
10010            run_description: "Suffix is a subsequence -- contiguous",
10011            initial_state: "SubˇErr".into(),
10012            buffer_marked_text: "<Sub|Err>".into(),
10013            completion_text: "SubscriptionError",
10014            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10015            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10016            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10017            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10018        },
10019        Run {
10020            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10021            initial_state: "Suˇscrirr".into(),
10022            buffer_marked_text: "<Su|scrirr>".into(),
10023            completion_text: "SubscriptionError",
10024            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10025            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10026            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10027            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10028        },
10029        Run {
10030            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10031            initial_state: "foo(indˇix)".into(),
10032            buffer_marked_text: "foo(<ind|ix>)".into(),
10033            completion_text: "node_index",
10034            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10035            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10036            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10037            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10038        },
10039    ];
10040
10041    for run in runs {
10042        let run_variations = [
10043            (LspInsertMode::Insert, run.expected_with_insert_mode),
10044            (LspInsertMode::Replace, run.expected_with_replace_mode),
10045            (
10046                LspInsertMode::ReplaceSubsequence,
10047                run.expected_with_replace_subsequence_mode,
10048            ),
10049            (
10050                LspInsertMode::ReplaceSuffix,
10051                run.expected_with_replace_suffix_mode,
10052            ),
10053        ];
10054
10055        for (lsp_insert_mode, expected_text) in run_variations {
10056            eprintln!(
10057                "run = {:?}, mode = {lsp_insert_mode:.?}",
10058                run.run_description,
10059            );
10060
10061            update_test_language_settings(&mut cx, |settings| {
10062                settings.defaults.completions = Some(CompletionSettings {
10063                    lsp_insert_mode,
10064                    words: WordsCompletionMode::Disabled,
10065                    lsp: true,
10066                    lsp_fetch_timeout_ms: 0,
10067                });
10068            });
10069
10070            cx.set_state(&run.initial_state);
10071            cx.update_editor(|editor, window, cx| {
10072                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10073            });
10074
10075            let counter = Arc::new(AtomicUsize::new(0));
10076            handle_completion_request_with_insert_and_replace(
10077                &mut cx,
10078                &run.buffer_marked_text,
10079                vec![run.completion_text],
10080                counter.clone(),
10081            )
10082            .await;
10083            cx.condition(|editor, _| editor.context_menu_visible())
10084                .await;
10085            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10086
10087            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10088                editor
10089                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10090                    .unwrap()
10091            });
10092            cx.assert_editor_state(&expected_text);
10093            handle_resolve_completion_request(&mut cx, None).await;
10094            apply_additional_edits.await.unwrap();
10095        }
10096    }
10097}
10098
10099#[gpui::test]
10100async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10101    init_test(cx, |_| {});
10102    let mut cx = EditorLspTestContext::new_rust(
10103        lsp::ServerCapabilities {
10104            completion_provider: Some(lsp::CompletionOptions {
10105                resolve_provider: Some(true),
10106                ..Default::default()
10107            }),
10108            ..Default::default()
10109        },
10110        cx,
10111    )
10112    .await;
10113
10114    let initial_state = "SubˇError";
10115    let buffer_marked_text = "<Sub|Error>";
10116    let completion_text = "SubscriptionError";
10117    let expected_with_insert_mode = "SubscriptionErrorˇError";
10118    let expected_with_replace_mode = "SubscriptionErrorˇ";
10119
10120    update_test_language_settings(&mut cx, |settings| {
10121        settings.defaults.completions = Some(CompletionSettings {
10122            words: WordsCompletionMode::Disabled,
10123            // set the opposite here to ensure that the action is overriding the default behavior
10124            lsp_insert_mode: LspInsertMode::Insert,
10125            lsp: true,
10126            lsp_fetch_timeout_ms: 0,
10127        });
10128    });
10129
10130    cx.set_state(initial_state);
10131    cx.update_editor(|editor, window, cx| {
10132        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10133    });
10134
10135    let counter = Arc::new(AtomicUsize::new(0));
10136    handle_completion_request_with_insert_and_replace(
10137        &mut cx,
10138        &buffer_marked_text,
10139        vec![completion_text],
10140        counter.clone(),
10141    )
10142    .await;
10143    cx.condition(|editor, _| editor.context_menu_visible())
10144        .await;
10145    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10146
10147    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10148        editor
10149            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10150            .unwrap()
10151    });
10152    cx.assert_editor_state(&expected_with_replace_mode);
10153    handle_resolve_completion_request(&mut cx, None).await;
10154    apply_additional_edits.await.unwrap();
10155
10156    update_test_language_settings(&mut cx, |settings| {
10157        settings.defaults.completions = Some(CompletionSettings {
10158            words: WordsCompletionMode::Disabled,
10159            // set the opposite here to ensure that the action is overriding the default behavior
10160            lsp_insert_mode: LspInsertMode::Replace,
10161            lsp: true,
10162            lsp_fetch_timeout_ms: 0,
10163        });
10164    });
10165
10166    cx.set_state(initial_state);
10167    cx.update_editor(|editor, window, cx| {
10168        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10169    });
10170    handle_completion_request_with_insert_and_replace(
10171        &mut cx,
10172        &buffer_marked_text,
10173        vec![completion_text],
10174        counter.clone(),
10175    )
10176    .await;
10177    cx.condition(|editor, _| editor.context_menu_visible())
10178        .await;
10179    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10180
10181    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10182        editor
10183            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10184            .unwrap()
10185    });
10186    cx.assert_editor_state(&expected_with_insert_mode);
10187    handle_resolve_completion_request(&mut cx, None).await;
10188    apply_additional_edits.await.unwrap();
10189}
10190
10191#[gpui::test]
10192async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10193    init_test(cx, |_| {});
10194    let mut cx = EditorLspTestContext::new_rust(
10195        lsp::ServerCapabilities {
10196            completion_provider: Some(lsp::CompletionOptions {
10197                resolve_provider: Some(true),
10198                ..Default::default()
10199            }),
10200            ..Default::default()
10201        },
10202        cx,
10203    )
10204    .await;
10205
10206    // scenario: surrounding text matches completion text
10207    let completion_text = "to_offset";
10208    let initial_state = indoc! {"
10209        1. buf.to_offˇsuffix
10210        2. buf.to_offˇsuf
10211        3. buf.to_offˇfix
10212        4. buf.to_offˇ
10213        5. into_offˇensive
10214        6. ˇsuffix
10215        7. let ˇ //
10216        8. aaˇzz
10217        9. buf.to_off«zzzzzˇ»suffix
10218        10. buf.«ˇzzzzz»suffix
10219        11. to_off«ˇzzzzz»
10220
10221        buf.to_offˇsuffix  // newest cursor
10222    "};
10223    let completion_marked_buffer = indoc! {"
10224        1. buf.to_offsuffix
10225        2. buf.to_offsuf
10226        3. buf.to_offfix
10227        4. buf.to_off
10228        5. into_offensive
10229        6. suffix
10230        7. let  //
10231        8. aazz
10232        9. buf.to_offzzzzzsuffix
10233        10. buf.zzzzzsuffix
10234        11. to_offzzzzz
10235
10236        buf.<to_off|suffix>  // newest cursor
10237    "};
10238    let expected = indoc! {"
10239        1. buf.to_offsetˇ
10240        2. buf.to_offsetˇsuf
10241        3. buf.to_offsetˇfix
10242        4. buf.to_offsetˇ
10243        5. into_offsetˇensive
10244        6. to_offsetˇsuffix
10245        7. let to_offsetˇ //
10246        8. aato_offsetˇzz
10247        9. buf.to_offsetˇ
10248        10. buf.to_offsetˇsuffix
10249        11. to_offsetˇ
10250
10251        buf.to_offsetˇ  // newest cursor
10252    "};
10253    cx.set_state(initial_state);
10254    cx.update_editor(|editor, window, cx| {
10255        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10256    });
10257    handle_completion_request_with_insert_and_replace(
10258        &mut cx,
10259        completion_marked_buffer,
10260        vec![completion_text],
10261        Arc::new(AtomicUsize::new(0)),
10262    )
10263    .await;
10264    cx.condition(|editor, _| editor.context_menu_visible())
10265        .await;
10266    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10267        editor
10268            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10269            .unwrap()
10270    });
10271    cx.assert_editor_state(expected);
10272    handle_resolve_completion_request(&mut cx, None).await;
10273    apply_additional_edits.await.unwrap();
10274
10275    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10276    let completion_text = "foo_and_bar";
10277    let initial_state = indoc! {"
10278        1. ooanbˇ
10279        2. zooanbˇ
10280        3. ooanbˇz
10281        4. zooanbˇz
10282        5. ooanˇ
10283        6. oanbˇ
10284
10285        ooanbˇ
10286    "};
10287    let completion_marked_buffer = indoc! {"
10288        1. ooanb
10289        2. zooanb
10290        3. ooanbz
10291        4. zooanbz
10292        5. ooan
10293        6. oanb
10294
10295        <ooanb|>
10296    "};
10297    let expected = indoc! {"
10298        1. foo_and_barˇ
10299        2. zfoo_and_barˇ
10300        3. foo_and_barˇz
10301        4. zfoo_and_barˇz
10302        5. ooanfoo_and_barˇ
10303        6. oanbfoo_and_barˇ
10304
10305        foo_and_barˇ
10306    "};
10307    cx.set_state(initial_state);
10308    cx.update_editor(|editor, window, cx| {
10309        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10310    });
10311    handle_completion_request_with_insert_and_replace(
10312        &mut cx,
10313        completion_marked_buffer,
10314        vec![completion_text],
10315        Arc::new(AtomicUsize::new(0)),
10316    )
10317    .await;
10318    cx.condition(|editor, _| editor.context_menu_visible())
10319        .await;
10320    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10321        editor
10322            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10323            .unwrap()
10324    });
10325    cx.assert_editor_state(expected);
10326    handle_resolve_completion_request(&mut cx, None).await;
10327    apply_additional_edits.await.unwrap();
10328
10329    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10330    // (expects the same as if it was inserted at the end)
10331    let completion_text = "foo_and_bar";
10332    let initial_state = indoc! {"
10333        1. ooˇanb
10334        2. zooˇanb
10335        3. ooˇanbz
10336        4. zooˇanbz
10337
10338        ooˇanb
10339    "};
10340    let completion_marked_buffer = indoc! {"
10341        1. ooanb
10342        2. zooanb
10343        3. ooanbz
10344        4. zooanbz
10345
10346        <oo|anb>
10347    "};
10348    let expected = indoc! {"
10349        1. foo_and_barˇ
10350        2. zfoo_and_barˇ
10351        3. foo_and_barˇz
10352        4. zfoo_and_barˇz
10353
10354        foo_and_barˇ
10355    "};
10356    cx.set_state(initial_state);
10357    cx.update_editor(|editor, window, cx| {
10358        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10359    });
10360    handle_completion_request_with_insert_and_replace(
10361        &mut cx,
10362        completion_marked_buffer,
10363        vec![completion_text],
10364        Arc::new(AtomicUsize::new(0)),
10365    )
10366    .await;
10367    cx.condition(|editor, _| editor.context_menu_visible())
10368        .await;
10369    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10370        editor
10371            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10372            .unwrap()
10373    });
10374    cx.assert_editor_state(expected);
10375    handle_resolve_completion_request(&mut cx, None).await;
10376    apply_additional_edits.await.unwrap();
10377}
10378
10379// This used to crash
10380#[gpui::test]
10381async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10382    init_test(cx, |_| {});
10383
10384    let buffer_text = indoc! {"
10385        fn main() {
10386            10.satu;
10387
10388            //
10389            // separate cursors so they open in different excerpts (manually reproducible)
10390            //
10391
10392            10.satu20;
10393        }
10394    "};
10395    let multibuffer_text_with_selections = indoc! {"
10396        fn main() {
10397            10.satuˇ;
10398
10399            //
10400
10401            //
10402
10403            10.satuˇ20;
10404        }
10405    "};
10406    let expected_multibuffer = indoc! {"
10407        fn main() {
10408            10.saturating_sub()ˇ;
10409
10410            //
10411
10412            //
10413
10414            10.saturating_sub()ˇ;
10415        }
10416    "};
10417
10418    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10419    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10420
10421    let fs = FakeFs::new(cx.executor());
10422    fs.insert_tree(
10423        path!("/a"),
10424        json!({
10425            "main.rs": buffer_text,
10426        }),
10427    )
10428    .await;
10429
10430    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10431    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10432    language_registry.add(rust_lang());
10433    let mut fake_servers = language_registry.register_fake_lsp(
10434        "Rust",
10435        FakeLspAdapter {
10436            capabilities: lsp::ServerCapabilities {
10437                completion_provider: Some(lsp::CompletionOptions {
10438                    resolve_provider: None,
10439                    ..lsp::CompletionOptions::default()
10440                }),
10441                ..lsp::ServerCapabilities::default()
10442            },
10443            ..FakeLspAdapter::default()
10444        },
10445    );
10446    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10447    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10448    let buffer = project
10449        .update(cx, |project, cx| {
10450            project.open_local_buffer(path!("/a/main.rs"), cx)
10451        })
10452        .await
10453        .unwrap();
10454
10455    let multi_buffer = cx.new(|cx| {
10456        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10457        multi_buffer.push_excerpts(
10458            buffer.clone(),
10459            [ExcerptRange::new(0..first_excerpt_end)],
10460            cx,
10461        );
10462        multi_buffer.push_excerpts(
10463            buffer.clone(),
10464            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10465            cx,
10466        );
10467        multi_buffer
10468    });
10469
10470    let editor = workspace
10471        .update(cx, |_, window, cx| {
10472            cx.new(|cx| {
10473                Editor::new(
10474                    EditorMode::Full {
10475                        scale_ui_elements_with_buffer_font_size: false,
10476                        show_active_line_background: false,
10477                        sized_by_content: false,
10478                    },
10479                    multi_buffer.clone(),
10480                    Some(project.clone()),
10481                    window,
10482                    cx,
10483                )
10484            })
10485        })
10486        .unwrap();
10487
10488    let pane = workspace
10489        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10490        .unwrap();
10491    pane.update_in(cx, |pane, window, cx| {
10492        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10493    });
10494
10495    let fake_server = fake_servers.next().await.unwrap();
10496
10497    editor.update_in(cx, |editor, window, cx| {
10498        editor.change_selections(None, window, cx, |s| {
10499            s.select_ranges([
10500                Point::new(1, 11)..Point::new(1, 11),
10501                Point::new(7, 11)..Point::new(7, 11),
10502            ])
10503        });
10504
10505        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10506    });
10507
10508    editor.update_in(cx, |editor, window, cx| {
10509        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10510    });
10511
10512    fake_server
10513        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10514            let completion_item = lsp::CompletionItem {
10515                label: "saturating_sub()".into(),
10516                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10517                    lsp::InsertReplaceEdit {
10518                        new_text: "saturating_sub()".to_owned(),
10519                        insert: lsp::Range::new(
10520                            lsp::Position::new(7, 7),
10521                            lsp::Position::new(7, 11),
10522                        ),
10523                        replace: lsp::Range::new(
10524                            lsp::Position::new(7, 7),
10525                            lsp::Position::new(7, 13),
10526                        ),
10527                    },
10528                )),
10529                ..lsp::CompletionItem::default()
10530            };
10531
10532            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10533        })
10534        .next()
10535        .await
10536        .unwrap();
10537
10538    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10539        .await;
10540
10541    editor
10542        .update_in(cx, |editor, window, cx| {
10543            editor
10544                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10545                .unwrap()
10546        })
10547        .await
10548        .unwrap();
10549
10550    editor.update(cx, |editor, cx| {
10551        assert_text_with_selections(editor, expected_multibuffer, cx);
10552    })
10553}
10554
10555#[gpui::test]
10556async fn test_completion(cx: &mut TestAppContext) {
10557    init_test(cx, |_| {});
10558
10559    let mut cx = EditorLspTestContext::new_rust(
10560        lsp::ServerCapabilities {
10561            completion_provider: Some(lsp::CompletionOptions {
10562                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10563                resolve_provider: Some(true),
10564                ..Default::default()
10565            }),
10566            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10567            ..Default::default()
10568        },
10569        cx,
10570    )
10571    .await;
10572    let counter = Arc::new(AtomicUsize::new(0));
10573
10574    cx.set_state(indoc! {"
10575        oneˇ
10576        two
10577        three
10578    "});
10579    cx.simulate_keystroke(".");
10580    handle_completion_request(
10581        &mut cx,
10582        indoc! {"
10583            one.|<>
10584            two
10585            three
10586        "},
10587        vec!["first_completion", "second_completion"],
10588        counter.clone(),
10589    )
10590    .await;
10591    cx.condition(|editor, _| editor.context_menu_visible())
10592        .await;
10593    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10594
10595    let _handler = handle_signature_help_request(
10596        &mut cx,
10597        lsp::SignatureHelp {
10598            signatures: vec![lsp::SignatureInformation {
10599                label: "test signature".to_string(),
10600                documentation: None,
10601                parameters: Some(vec![lsp::ParameterInformation {
10602                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10603                    documentation: None,
10604                }]),
10605                active_parameter: None,
10606            }],
10607            active_signature: None,
10608            active_parameter: None,
10609        },
10610    );
10611    cx.update_editor(|editor, window, cx| {
10612        assert!(
10613            !editor.signature_help_state.is_shown(),
10614            "No signature help was called for"
10615        );
10616        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10617    });
10618    cx.run_until_parked();
10619    cx.update_editor(|editor, _, _| {
10620        assert!(
10621            !editor.signature_help_state.is_shown(),
10622            "No signature help should be shown when completions menu is open"
10623        );
10624    });
10625
10626    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10627        editor.context_menu_next(&Default::default(), window, cx);
10628        editor
10629            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10630            .unwrap()
10631    });
10632    cx.assert_editor_state(indoc! {"
10633        one.second_completionˇ
10634        two
10635        three
10636    "});
10637
10638    handle_resolve_completion_request(
10639        &mut cx,
10640        Some(vec![
10641            (
10642                //This overlaps with the primary completion edit which is
10643                //misbehavior from the LSP spec, test that we filter it out
10644                indoc! {"
10645                    one.second_ˇcompletion
10646                    two
10647                    threeˇ
10648                "},
10649                "overlapping additional edit",
10650            ),
10651            (
10652                indoc! {"
10653                    one.second_completion
10654                    two
10655                    threeˇ
10656                "},
10657                "\nadditional edit",
10658            ),
10659        ]),
10660    )
10661    .await;
10662    apply_additional_edits.await.unwrap();
10663    cx.assert_editor_state(indoc! {"
10664        one.second_completionˇ
10665        two
10666        three
10667        additional edit
10668    "});
10669
10670    cx.set_state(indoc! {"
10671        one.second_completion
10672        twoˇ
10673        threeˇ
10674        additional edit
10675    "});
10676    cx.simulate_keystroke(" ");
10677    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10678    cx.simulate_keystroke("s");
10679    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10680
10681    cx.assert_editor_state(indoc! {"
10682        one.second_completion
10683        two sˇ
10684        three sˇ
10685        additional edit
10686    "});
10687    handle_completion_request(
10688        &mut cx,
10689        indoc! {"
10690            one.second_completion
10691            two s
10692            three <s|>
10693            additional edit
10694        "},
10695        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10696        counter.clone(),
10697    )
10698    .await;
10699    cx.condition(|editor, _| editor.context_menu_visible())
10700        .await;
10701    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10702
10703    cx.simulate_keystroke("i");
10704
10705    handle_completion_request(
10706        &mut cx,
10707        indoc! {"
10708            one.second_completion
10709            two si
10710            three <si|>
10711            additional edit
10712        "},
10713        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10714        counter.clone(),
10715    )
10716    .await;
10717    cx.condition(|editor, _| editor.context_menu_visible())
10718        .await;
10719    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10720
10721    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10722        editor
10723            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10724            .unwrap()
10725    });
10726    cx.assert_editor_state(indoc! {"
10727        one.second_completion
10728        two sixth_completionˇ
10729        three sixth_completionˇ
10730        additional edit
10731    "});
10732
10733    apply_additional_edits.await.unwrap();
10734
10735    update_test_language_settings(&mut cx, |settings| {
10736        settings.defaults.show_completions_on_input = Some(false);
10737    });
10738    cx.set_state("editorˇ");
10739    cx.simulate_keystroke(".");
10740    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10741    cx.simulate_keystrokes("c l o");
10742    cx.assert_editor_state("editor.cloˇ");
10743    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10744    cx.update_editor(|editor, window, cx| {
10745        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10746    });
10747    handle_completion_request(
10748        &mut cx,
10749        "editor.<clo|>",
10750        vec!["close", "clobber"],
10751        counter.clone(),
10752    )
10753    .await;
10754    cx.condition(|editor, _| editor.context_menu_visible())
10755        .await;
10756    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10757
10758    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10759        editor
10760            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10761            .unwrap()
10762    });
10763    cx.assert_editor_state("editor.closeˇ");
10764    handle_resolve_completion_request(&mut cx, None).await;
10765    apply_additional_edits.await.unwrap();
10766}
10767
10768#[gpui::test]
10769async fn test_word_completion(cx: &mut TestAppContext) {
10770    let lsp_fetch_timeout_ms = 10;
10771    init_test(cx, |language_settings| {
10772        language_settings.defaults.completions = Some(CompletionSettings {
10773            words: WordsCompletionMode::Fallback,
10774            lsp: true,
10775            lsp_fetch_timeout_ms: 10,
10776            lsp_insert_mode: LspInsertMode::Insert,
10777        });
10778    });
10779
10780    let mut cx = EditorLspTestContext::new_rust(
10781        lsp::ServerCapabilities {
10782            completion_provider: Some(lsp::CompletionOptions {
10783                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10784                ..lsp::CompletionOptions::default()
10785            }),
10786            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10787            ..lsp::ServerCapabilities::default()
10788        },
10789        cx,
10790    )
10791    .await;
10792
10793    let throttle_completions = Arc::new(AtomicBool::new(false));
10794
10795    let lsp_throttle_completions = throttle_completions.clone();
10796    let _completion_requests_handler =
10797        cx.lsp
10798            .server
10799            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10800                let lsp_throttle_completions = lsp_throttle_completions.clone();
10801                let cx = cx.clone();
10802                async move {
10803                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10804                        cx.background_executor()
10805                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10806                            .await;
10807                    }
10808                    Ok(Some(lsp::CompletionResponse::Array(vec![
10809                        lsp::CompletionItem {
10810                            label: "first".into(),
10811                            ..lsp::CompletionItem::default()
10812                        },
10813                        lsp::CompletionItem {
10814                            label: "last".into(),
10815                            ..lsp::CompletionItem::default()
10816                        },
10817                    ])))
10818                }
10819            });
10820
10821    cx.set_state(indoc! {"
10822        oneˇ
10823        two
10824        three
10825    "});
10826    cx.simulate_keystroke(".");
10827    cx.executor().run_until_parked();
10828    cx.condition(|editor, _| editor.context_menu_visible())
10829        .await;
10830    cx.update_editor(|editor, window, cx| {
10831        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10832        {
10833            assert_eq!(
10834                completion_menu_entries(&menu),
10835                &["first", "last"],
10836                "When LSP server is fast to reply, no fallback word completions are used"
10837            );
10838        } else {
10839            panic!("expected completion menu to be open");
10840        }
10841        editor.cancel(&Cancel, window, cx);
10842    });
10843    cx.executor().run_until_parked();
10844    cx.condition(|editor, _| !editor.context_menu_visible())
10845        .await;
10846
10847    throttle_completions.store(true, atomic::Ordering::Release);
10848    cx.simulate_keystroke(".");
10849    cx.executor()
10850        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10851    cx.executor().run_until_parked();
10852    cx.condition(|editor, _| editor.context_menu_visible())
10853        .await;
10854    cx.update_editor(|editor, _, _| {
10855        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10856        {
10857            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10858                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10859        } else {
10860            panic!("expected completion menu to be open");
10861        }
10862    });
10863}
10864
10865#[gpui::test]
10866async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10867    init_test(cx, |language_settings| {
10868        language_settings.defaults.completions = Some(CompletionSettings {
10869            words: WordsCompletionMode::Enabled,
10870            lsp: true,
10871            lsp_fetch_timeout_ms: 0,
10872            lsp_insert_mode: LspInsertMode::Insert,
10873        });
10874    });
10875
10876    let mut cx = EditorLspTestContext::new_rust(
10877        lsp::ServerCapabilities {
10878            completion_provider: Some(lsp::CompletionOptions {
10879                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10880                ..lsp::CompletionOptions::default()
10881            }),
10882            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10883            ..lsp::ServerCapabilities::default()
10884        },
10885        cx,
10886    )
10887    .await;
10888
10889    let _completion_requests_handler =
10890        cx.lsp
10891            .server
10892            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10893                Ok(Some(lsp::CompletionResponse::Array(vec![
10894                    lsp::CompletionItem {
10895                        label: "first".into(),
10896                        ..lsp::CompletionItem::default()
10897                    },
10898                    lsp::CompletionItem {
10899                        label: "last".into(),
10900                        ..lsp::CompletionItem::default()
10901                    },
10902                ])))
10903            });
10904
10905    cx.set_state(indoc! {"ˇ
10906        first
10907        last
10908        second
10909    "});
10910    cx.simulate_keystroke(".");
10911    cx.executor().run_until_parked();
10912    cx.condition(|editor, _| editor.context_menu_visible())
10913        .await;
10914    cx.update_editor(|editor, _, _| {
10915        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10916        {
10917            assert_eq!(
10918                completion_menu_entries(&menu),
10919                &["first", "last", "second"],
10920                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10921            );
10922        } else {
10923            panic!("expected completion menu to be open");
10924        }
10925    });
10926}
10927
10928#[gpui::test]
10929async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10930    init_test(cx, |language_settings| {
10931        language_settings.defaults.completions = Some(CompletionSettings {
10932            words: WordsCompletionMode::Disabled,
10933            lsp: true,
10934            lsp_fetch_timeout_ms: 0,
10935            lsp_insert_mode: LspInsertMode::Insert,
10936        });
10937    });
10938
10939    let mut cx = EditorLspTestContext::new_rust(
10940        lsp::ServerCapabilities {
10941            completion_provider: Some(lsp::CompletionOptions {
10942                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10943                ..lsp::CompletionOptions::default()
10944            }),
10945            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10946            ..lsp::ServerCapabilities::default()
10947        },
10948        cx,
10949    )
10950    .await;
10951
10952    let _completion_requests_handler =
10953        cx.lsp
10954            .server
10955            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10956                panic!("LSP completions should not be queried when dealing with word completions")
10957            });
10958
10959    cx.set_state(indoc! {"ˇ
10960        first
10961        last
10962        second
10963    "});
10964    cx.update_editor(|editor, window, cx| {
10965        editor.show_word_completions(&ShowWordCompletions, window, cx);
10966    });
10967    cx.executor().run_until_parked();
10968    cx.condition(|editor, _| editor.context_menu_visible())
10969        .await;
10970    cx.update_editor(|editor, _, _| {
10971        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10972        {
10973            assert_eq!(
10974                completion_menu_entries(&menu),
10975                &["first", "last", "second"],
10976                "`ShowWordCompletions` action should show word completions"
10977            );
10978        } else {
10979            panic!("expected completion menu to be open");
10980        }
10981    });
10982
10983    cx.simulate_keystroke("l");
10984    cx.executor().run_until_parked();
10985    cx.condition(|editor, _| editor.context_menu_visible())
10986        .await;
10987    cx.update_editor(|editor, _, _| {
10988        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10989        {
10990            assert_eq!(
10991                completion_menu_entries(&menu),
10992                &["last"],
10993                "After showing word completions, further editing should filter them and not query the LSP"
10994            );
10995        } else {
10996            panic!("expected completion menu to be open");
10997        }
10998    });
10999}
11000
11001#[gpui::test]
11002async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11003    init_test(cx, |language_settings| {
11004        language_settings.defaults.completions = Some(CompletionSettings {
11005            words: WordsCompletionMode::Fallback,
11006            lsp: false,
11007            lsp_fetch_timeout_ms: 0,
11008            lsp_insert_mode: LspInsertMode::Insert,
11009        });
11010    });
11011
11012    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11013
11014    cx.set_state(indoc! {"ˇ
11015        0_usize
11016        let
11017        33
11018        4.5f32
11019    "});
11020    cx.update_editor(|editor, window, cx| {
11021        editor.show_completions(&ShowCompletions::default(), window, cx);
11022    });
11023    cx.executor().run_until_parked();
11024    cx.condition(|editor, _| editor.context_menu_visible())
11025        .await;
11026    cx.update_editor(|editor, window, cx| {
11027        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11028        {
11029            assert_eq!(
11030                completion_menu_entries(&menu),
11031                &["let"],
11032                "With no digits in the completion query, no digits should be in the word completions"
11033            );
11034        } else {
11035            panic!("expected completion menu to be open");
11036        }
11037        editor.cancel(&Cancel, window, cx);
11038    });
11039
11040    cx.set_state(indoc! {"11041        0_usize
11042        let
11043        3
11044        33.35f32
11045    "});
11046    cx.update_editor(|editor, window, cx| {
11047        editor.show_completions(&ShowCompletions::default(), window, cx);
11048    });
11049    cx.executor().run_until_parked();
11050    cx.condition(|editor, _| editor.context_menu_visible())
11051        .await;
11052    cx.update_editor(|editor, _, _| {
11053        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11054        {
11055            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11056                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11057        } else {
11058            panic!("expected completion menu to be open");
11059        }
11060    });
11061}
11062
11063fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11064    let position = || lsp::Position {
11065        line: params.text_document_position.position.line,
11066        character: params.text_document_position.position.character,
11067    };
11068    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11069        range: lsp::Range {
11070            start: position(),
11071            end: position(),
11072        },
11073        new_text: text.to_string(),
11074    }))
11075}
11076
11077#[gpui::test]
11078async fn test_multiline_completion(cx: &mut TestAppContext) {
11079    init_test(cx, |_| {});
11080
11081    let fs = FakeFs::new(cx.executor());
11082    fs.insert_tree(
11083        path!("/a"),
11084        json!({
11085            "main.ts": "a",
11086        }),
11087    )
11088    .await;
11089
11090    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11091    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11092    let typescript_language = Arc::new(Language::new(
11093        LanguageConfig {
11094            name: "TypeScript".into(),
11095            matcher: LanguageMatcher {
11096                path_suffixes: vec!["ts".to_string()],
11097                ..LanguageMatcher::default()
11098            },
11099            line_comments: vec!["// ".into()],
11100            ..LanguageConfig::default()
11101        },
11102        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11103    ));
11104    language_registry.add(typescript_language.clone());
11105    let mut fake_servers = language_registry.register_fake_lsp(
11106        "TypeScript",
11107        FakeLspAdapter {
11108            capabilities: lsp::ServerCapabilities {
11109                completion_provider: Some(lsp::CompletionOptions {
11110                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11111                    ..lsp::CompletionOptions::default()
11112                }),
11113                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11114                ..lsp::ServerCapabilities::default()
11115            },
11116            // Emulate vtsls label generation
11117            label_for_completion: Some(Box::new(|item, _| {
11118                let text = if let Some(description) = item
11119                    .label_details
11120                    .as_ref()
11121                    .and_then(|label_details| label_details.description.as_ref())
11122                {
11123                    format!("{} {}", item.label, description)
11124                } else if let Some(detail) = &item.detail {
11125                    format!("{} {}", item.label, detail)
11126                } else {
11127                    item.label.clone()
11128                };
11129                let len = text.len();
11130                Some(language::CodeLabel {
11131                    text,
11132                    runs: Vec::new(),
11133                    filter_range: 0..len,
11134                })
11135            })),
11136            ..FakeLspAdapter::default()
11137        },
11138    );
11139    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11140    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11141    let worktree_id = workspace
11142        .update(cx, |workspace, _window, cx| {
11143            workspace.project().update(cx, |project, cx| {
11144                project.worktrees(cx).next().unwrap().read(cx).id()
11145            })
11146        })
11147        .unwrap();
11148    let _buffer = project
11149        .update(cx, |project, cx| {
11150            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11151        })
11152        .await
11153        .unwrap();
11154    let editor = workspace
11155        .update(cx, |workspace, window, cx| {
11156            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11157        })
11158        .unwrap()
11159        .await
11160        .unwrap()
11161        .downcast::<Editor>()
11162        .unwrap();
11163    let fake_server = fake_servers.next().await.unwrap();
11164
11165    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11166    let multiline_label_2 = "a\nb\nc\n";
11167    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11168    let multiline_description = "d\ne\nf\n";
11169    let multiline_detail_2 = "g\nh\ni\n";
11170
11171    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11172        move |params, _| async move {
11173            Ok(Some(lsp::CompletionResponse::Array(vec![
11174                lsp::CompletionItem {
11175                    label: multiline_label.to_string(),
11176                    text_edit: gen_text_edit(&params, "new_text_1"),
11177                    ..lsp::CompletionItem::default()
11178                },
11179                lsp::CompletionItem {
11180                    label: "single line label 1".to_string(),
11181                    detail: Some(multiline_detail.to_string()),
11182                    text_edit: gen_text_edit(&params, "new_text_2"),
11183                    ..lsp::CompletionItem::default()
11184                },
11185                lsp::CompletionItem {
11186                    label: "single line label 2".to_string(),
11187                    label_details: Some(lsp::CompletionItemLabelDetails {
11188                        description: Some(multiline_description.to_string()),
11189                        detail: None,
11190                    }),
11191                    text_edit: gen_text_edit(&params, "new_text_2"),
11192                    ..lsp::CompletionItem::default()
11193                },
11194                lsp::CompletionItem {
11195                    label: multiline_label_2.to_string(),
11196                    detail: Some(multiline_detail_2.to_string()),
11197                    text_edit: gen_text_edit(&params, "new_text_3"),
11198                    ..lsp::CompletionItem::default()
11199                },
11200                lsp::CompletionItem {
11201                    label: "Label with many     spaces and \t but without newlines".to_string(),
11202                    detail: Some(
11203                        "Details with many     spaces and \t but without newlines".to_string(),
11204                    ),
11205                    text_edit: gen_text_edit(&params, "new_text_4"),
11206                    ..lsp::CompletionItem::default()
11207                },
11208            ])))
11209        },
11210    );
11211
11212    editor.update_in(cx, |editor, window, cx| {
11213        cx.focus_self(window);
11214        editor.move_to_end(&MoveToEnd, window, cx);
11215        editor.handle_input(".", window, cx);
11216    });
11217    cx.run_until_parked();
11218    completion_handle.next().await.unwrap();
11219
11220    editor.update(cx, |editor, _| {
11221        assert!(editor.context_menu_visible());
11222        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11223        {
11224            let completion_labels = menu
11225                .completions
11226                .borrow()
11227                .iter()
11228                .map(|c| c.label.text.clone())
11229                .collect::<Vec<_>>();
11230            assert_eq!(
11231                completion_labels,
11232                &[
11233                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11234                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11235                    "single line label 2 d e f ",
11236                    "a b c g h i ",
11237                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11238                ],
11239                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11240            );
11241
11242            for completion in menu
11243                .completions
11244                .borrow()
11245                .iter() {
11246                    assert_eq!(
11247                        completion.label.filter_range,
11248                        0..completion.label.text.len(),
11249                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11250                    );
11251                }
11252        } else {
11253            panic!("expected completion menu to be open");
11254        }
11255    });
11256}
11257
11258#[gpui::test]
11259async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11260    init_test(cx, |_| {});
11261    let mut cx = EditorLspTestContext::new_rust(
11262        lsp::ServerCapabilities {
11263            completion_provider: Some(lsp::CompletionOptions {
11264                trigger_characters: Some(vec![".".to_string()]),
11265                ..Default::default()
11266            }),
11267            ..Default::default()
11268        },
11269        cx,
11270    )
11271    .await;
11272    cx.lsp
11273        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11274            Ok(Some(lsp::CompletionResponse::Array(vec![
11275                lsp::CompletionItem {
11276                    label: "first".into(),
11277                    ..Default::default()
11278                },
11279                lsp::CompletionItem {
11280                    label: "last".into(),
11281                    ..Default::default()
11282                },
11283            ])))
11284        });
11285    cx.set_state("variableˇ");
11286    cx.simulate_keystroke(".");
11287    cx.executor().run_until_parked();
11288
11289    cx.update_editor(|editor, _, _| {
11290        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11291        {
11292            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11293        } else {
11294            panic!("expected completion menu to be open");
11295        }
11296    });
11297
11298    cx.update_editor(|editor, window, cx| {
11299        editor.move_page_down(&MovePageDown::default(), window, cx);
11300        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11301        {
11302            assert!(
11303                menu.selected_item == 1,
11304                "expected PageDown to select the last item from the context menu"
11305            );
11306        } else {
11307            panic!("expected completion menu to stay open after PageDown");
11308        }
11309    });
11310
11311    cx.update_editor(|editor, window, cx| {
11312        editor.move_page_up(&MovePageUp::default(), window, cx);
11313        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11314        {
11315            assert!(
11316                menu.selected_item == 0,
11317                "expected PageUp to select the first item from the context menu"
11318            );
11319        } else {
11320            panic!("expected completion menu to stay open after PageUp");
11321        }
11322    });
11323}
11324
11325#[gpui::test]
11326async fn test_as_is_completions(cx: &mut TestAppContext) {
11327    init_test(cx, |_| {});
11328    let mut cx = EditorLspTestContext::new_rust(
11329        lsp::ServerCapabilities {
11330            completion_provider: Some(lsp::CompletionOptions {
11331                ..Default::default()
11332            }),
11333            ..Default::default()
11334        },
11335        cx,
11336    )
11337    .await;
11338    cx.lsp
11339        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11340            Ok(Some(lsp::CompletionResponse::Array(vec![
11341                lsp::CompletionItem {
11342                    label: "unsafe".into(),
11343                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11344                        range: lsp::Range {
11345                            start: lsp::Position {
11346                                line: 1,
11347                                character: 2,
11348                            },
11349                            end: lsp::Position {
11350                                line: 1,
11351                                character: 3,
11352                            },
11353                        },
11354                        new_text: "unsafe".to_string(),
11355                    })),
11356                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11357                    ..Default::default()
11358                },
11359            ])))
11360        });
11361    cx.set_state("fn a() {}\n");
11362    cx.executor().run_until_parked();
11363    cx.update_editor(|editor, window, cx| {
11364        editor.show_completions(
11365            &ShowCompletions {
11366                trigger: Some("\n".into()),
11367            },
11368            window,
11369            cx,
11370        );
11371    });
11372    cx.executor().run_until_parked();
11373
11374    cx.update_editor(|editor, window, cx| {
11375        editor.confirm_completion(&Default::default(), window, cx)
11376    });
11377    cx.executor().run_until_parked();
11378    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11379}
11380
11381#[gpui::test]
11382async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11383    init_test(cx, |_| {});
11384
11385    let mut cx = EditorLspTestContext::new_rust(
11386        lsp::ServerCapabilities {
11387            completion_provider: Some(lsp::CompletionOptions {
11388                trigger_characters: Some(vec![".".to_string()]),
11389                resolve_provider: Some(true),
11390                ..Default::default()
11391            }),
11392            ..Default::default()
11393        },
11394        cx,
11395    )
11396    .await;
11397
11398    cx.set_state("fn main() { let a = 2ˇ; }");
11399    cx.simulate_keystroke(".");
11400    let completion_item = lsp::CompletionItem {
11401        label: "Some".into(),
11402        kind: Some(lsp::CompletionItemKind::SNIPPET),
11403        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11404        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11405            kind: lsp::MarkupKind::Markdown,
11406            value: "```rust\nSome(2)\n```".to_string(),
11407        })),
11408        deprecated: Some(false),
11409        sort_text: Some("Some".to_string()),
11410        filter_text: Some("Some".to_string()),
11411        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11412        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11413            range: lsp::Range {
11414                start: lsp::Position {
11415                    line: 0,
11416                    character: 22,
11417                },
11418                end: lsp::Position {
11419                    line: 0,
11420                    character: 22,
11421                },
11422            },
11423            new_text: "Some(2)".to_string(),
11424        })),
11425        additional_text_edits: Some(vec![lsp::TextEdit {
11426            range: lsp::Range {
11427                start: lsp::Position {
11428                    line: 0,
11429                    character: 20,
11430                },
11431                end: lsp::Position {
11432                    line: 0,
11433                    character: 22,
11434                },
11435            },
11436            new_text: "".to_string(),
11437        }]),
11438        ..Default::default()
11439    };
11440
11441    let closure_completion_item = completion_item.clone();
11442    let counter = Arc::new(AtomicUsize::new(0));
11443    let counter_clone = counter.clone();
11444    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11445        let task_completion_item = closure_completion_item.clone();
11446        counter_clone.fetch_add(1, atomic::Ordering::Release);
11447        async move {
11448            Ok(Some(lsp::CompletionResponse::Array(vec![
11449                task_completion_item,
11450            ])))
11451        }
11452    });
11453
11454    cx.condition(|editor, _| editor.context_menu_visible())
11455        .await;
11456    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11457    assert!(request.next().await.is_some());
11458    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11459
11460    cx.simulate_keystrokes("S o m");
11461    cx.condition(|editor, _| editor.context_menu_visible())
11462        .await;
11463    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11464    assert!(request.next().await.is_some());
11465    assert!(request.next().await.is_some());
11466    assert!(request.next().await.is_some());
11467    request.close();
11468    assert!(request.next().await.is_none());
11469    assert_eq!(
11470        counter.load(atomic::Ordering::Acquire),
11471        4,
11472        "With the completions menu open, only one LSP request should happen per input"
11473    );
11474}
11475
11476#[gpui::test]
11477async fn test_toggle_comment(cx: &mut TestAppContext) {
11478    init_test(cx, |_| {});
11479    let mut cx = EditorTestContext::new(cx).await;
11480    let language = Arc::new(Language::new(
11481        LanguageConfig {
11482            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11483            ..Default::default()
11484        },
11485        Some(tree_sitter_rust::LANGUAGE.into()),
11486    ));
11487    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11488
11489    // If multiple selections intersect a line, the line is only toggled once.
11490    cx.set_state(indoc! {"
11491        fn a() {
11492            «//b();
11493            ˇ»// «c();
11494            //ˇ»  d();
11495        }
11496    "});
11497
11498    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11499
11500    cx.assert_editor_state(indoc! {"
11501        fn a() {
11502            «b();
11503            c();
11504            ˇ» d();
11505        }
11506    "});
11507
11508    // The comment prefix is inserted at the same column for every line in a
11509    // selection.
11510    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11511
11512    cx.assert_editor_state(indoc! {"
11513        fn a() {
11514            // «b();
11515            // c();
11516            ˇ»//  d();
11517        }
11518    "});
11519
11520    // If a selection ends at the beginning of a line, that line is not toggled.
11521    cx.set_selections_state(indoc! {"
11522        fn a() {
11523            // b();
11524            «// c();
11525        ˇ»    //  d();
11526        }
11527    "});
11528
11529    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11530
11531    cx.assert_editor_state(indoc! {"
11532        fn a() {
11533            // b();
11534            «c();
11535        ˇ»    //  d();
11536        }
11537    "});
11538
11539    // If a selection span a single line and is empty, the line is toggled.
11540    cx.set_state(indoc! {"
11541        fn a() {
11542            a();
11543            b();
11544        ˇ
11545        }
11546    "});
11547
11548    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11549
11550    cx.assert_editor_state(indoc! {"
11551        fn a() {
11552            a();
11553            b();
11554        //•ˇ
11555        }
11556    "});
11557
11558    // If a selection span multiple lines, empty lines are not toggled.
11559    cx.set_state(indoc! {"
11560        fn a() {
11561            «a();
11562
11563            c();ˇ»
11564        }
11565    "});
11566
11567    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11568
11569    cx.assert_editor_state(indoc! {"
11570        fn a() {
11571            // «a();
11572
11573            // c();ˇ»
11574        }
11575    "});
11576
11577    // If a selection includes multiple comment prefixes, all lines are uncommented.
11578    cx.set_state(indoc! {"
11579        fn a() {
11580            «// a();
11581            /// b();
11582            //! c();ˇ»
11583        }
11584    "});
11585
11586    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11587
11588    cx.assert_editor_state(indoc! {"
11589        fn a() {
11590            «a();
11591            b();
11592            c();ˇ»
11593        }
11594    "});
11595}
11596
11597#[gpui::test]
11598async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11599    init_test(cx, |_| {});
11600    let mut cx = EditorTestContext::new(cx).await;
11601    let language = Arc::new(Language::new(
11602        LanguageConfig {
11603            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11604            ..Default::default()
11605        },
11606        Some(tree_sitter_rust::LANGUAGE.into()),
11607    ));
11608    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11609
11610    let toggle_comments = &ToggleComments {
11611        advance_downwards: false,
11612        ignore_indent: true,
11613    };
11614
11615    // If multiple selections intersect a line, the line is only toggled once.
11616    cx.set_state(indoc! {"
11617        fn a() {
11618        //    «b();
11619        //    c();
11620        //    ˇ» d();
11621        }
11622    "});
11623
11624    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11625
11626    cx.assert_editor_state(indoc! {"
11627        fn a() {
11628            «b();
11629            c();
11630            ˇ» d();
11631        }
11632    "});
11633
11634    // The comment prefix is inserted at the beginning of each line
11635    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11636
11637    cx.assert_editor_state(indoc! {"
11638        fn a() {
11639        //    «b();
11640        //    c();
11641        //    ˇ» d();
11642        }
11643    "});
11644
11645    // If a selection ends at the beginning of a line, that line is not toggled.
11646    cx.set_selections_state(indoc! {"
11647        fn a() {
11648        //    b();
11649        //    «c();
11650        ˇ»//     d();
11651        }
11652    "});
11653
11654    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11655
11656    cx.assert_editor_state(indoc! {"
11657        fn a() {
11658        //    b();
11659            «c();
11660        ˇ»//     d();
11661        }
11662    "});
11663
11664    // If a selection span a single line and is empty, the line is toggled.
11665    cx.set_state(indoc! {"
11666        fn a() {
11667            a();
11668            b();
11669        ˇ
11670        }
11671    "});
11672
11673    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11674
11675    cx.assert_editor_state(indoc! {"
11676        fn a() {
11677            a();
11678            b();
11679        //ˇ
11680        }
11681    "});
11682
11683    // If a selection span multiple lines, empty lines are not toggled.
11684    cx.set_state(indoc! {"
11685        fn a() {
11686            «a();
11687
11688            c();ˇ»
11689        }
11690    "});
11691
11692    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11693
11694    cx.assert_editor_state(indoc! {"
11695        fn a() {
11696        //    «a();
11697
11698        //    c();ˇ»
11699        }
11700    "});
11701
11702    // If a selection includes multiple comment prefixes, all lines are uncommented.
11703    cx.set_state(indoc! {"
11704        fn a() {
11705        //    «a();
11706        ///    b();
11707        //!    c();ˇ»
11708        }
11709    "});
11710
11711    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11712
11713    cx.assert_editor_state(indoc! {"
11714        fn a() {
11715            «a();
11716            b();
11717            c();ˇ»
11718        }
11719    "});
11720}
11721
11722#[gpui::test]
11723async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11724    init_test(cx, |_| {});
11725
11726    let language = Arc::new(Language::new(
11727        LanguageConfig {
11728            line_comments: vec!["// ".into()],
11729            ..Default::default()
11730        },
11731        Some(tree_sitter_rust::LANGUAGE.into()),
11732    ));
11733
11734    let mut cx = EditorTestContext::new(cx).await;
11735
11736    cx.language_registry().add(language.clone());
11737    cx.update_buffer(|buffer, cx| {
11738        buffer.set_language(Some(language), cx);
11739    });
11740
11741    let toggle_comments = &ToggleComments {
11742        advance_downwards: true,
11743        ignore_indent: false,
11744    };
11745
11746    // Single cursor on one line -> advance
11747    // Cursor moves horizontally 3 characters as well on non-blank line
11748    cx.set_state(indoc!(
11749        "fn a() {
11750             ˇdog();
11751             cat();
11752        }"
11753    ));
11754    cx.update_editor(|editor, window, cx| {
11755        editor.toggle_comments(toggle_comments, window, cx);
11756    });
11757    cx.assert_editor_state(indoc!(
11758        "fn a() {
11759             // dog();
11760             catˇ();
11761        }"
11762    ));
11763
11764    // Single selection on one line -> don't advance
11765    cx.set_state(indoc!(
11766        "fn a() {
11767             «dog()ˇ»;
11768             cat();
11769        }"
11770    ));
11771    cx.update_editor(|editor, window, cx| {
11772        editor.toggle_comments(toggle_comments, window, cx);
11773    });
11774    cx.assert_editor_state(indoc!(
11775        "fn a() {
11776             // «dog()ˇ»;
11777             cat();
11778        }"
11779    ));
11780
11781    // Multiple cursors on one line -> advance
11782    cx.set_state(indoc!(
11783        "fn a() {
11784             ˇdˇog();
11785             cat();
11786        }"
11787    ));
11788    cx.update_editor(|editor, window, cx| {
11789        editor.toggle_comments(toggle_comments, window, cx);
11790    });
11791    cx.assert_editor_state(indoc!(
11792        "fn a() {
11793             // dog();
11794             catˇ(ˇ);
11795        }"
11796    ));
11797
11798    // Multiple cursors on one line, with selection -> don't advance
11799    cx.set_state(indoc!(
11800        "fn a() {
11801             ˇdˇog«()ˇ»;
11802             cat();
11803        }"
11804    ));
11805    cx.update_editor(|editor, window, cx| {
11806        editor.toggle_comments(toggle_comments, window, cx);
11807    });
11808    cx.assert_editor_state(indoc!(
11809        "fn a() {
11810             // ˇdˇog«()ˇ»;
11811             cat();
11812        }"
11813    ));
11814
11815    // Single cursor on one line -> advance
11816    // Cursor moves to column 0 on blank line
11817    cx.set_state(indoc!(
11818        "fn a() {
11819             ˇdog();
11820
11821             cat();
11822        }"
11823    ));
11824    cx.update_editor(|editor, window, cx| {
11825        editor.toggle_comments(toggle_comments, window, cx);
11826    });
11827    cx.assert_editor_state(indoc!(
11828        "fn a() {
11829             // dog();
11830        ˇ
11831             cat();
11832        }"
11833    ));
11834
11835    // Single cursor on one line -> advance
11836    // Cursor starts and ends at column 0
11837    cx.set_state(indoc!(
11838        "fn a() {
11839         ˇ    dog();
11840             cat();
11841        }"
11842    ));
11843    cx.update_editor(|editor, window, cx| {
11844        editor.toggle_comments(toggle_comments, window, cx);
11845    });
11846    cx.assert_editor_state(indoc!(
11847        "fn a() {
11848             // dog();
11849         ˇ    cat();
11850        }"
11851    ));
11852}
11853
11854#[gpui::test]
11855async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11856    init_test(cx, |_| {});
11857
11858    let mut cx = EditorTestContext::new(cx).await;
11859
11860    let html_language = Arc::new(
11861        Language::new(
11862            LanguageConfig {
11863                name: "HTML".into(),
11864                block_comment: Some(("<!-- ".into(), " -->".into())),
11865                ..Default::default()
11866            },
11867            Some(tree_sitter_html::LANGUAGE.into()),
11868        )
11869        .with_injection_query(
11870            r#"
11871            (script_element
11872                (raw_text) @injection.content
11873                (#set! injection.language "javascript"))
11874            "#,
11875        )
11876        .unwrap(),
11877    );
11878
11879    let javascript_language = Arc::new(Language::new(
11880        LanguageConfig {
11881            name: "JavaScript".into(),
11882            line_comments: vec!["// ".into()],
11883            ..Default::default()
11884        },
11885        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11886    ));
11887
11888    cx.language_registry().add(html_language.clone());
11889    cx.language_registry().add(javascript_language.clone());
11890    cx.update_buffer(|buffer, cx| {
11891        buffer.set_language(Some(html_language), cx);
11892    });
11893
11894    // Toggle comments for empty selections
11895    cx.set_state(
11896        &r#"
11897            <p>A</p>ˇ
11898            <p>B</p>ˇ
11899            <p>C</p>ˇ
11900        "#
11901        .unindent(),
11902    );
11903    cx.update_editor(|editor, window, cx| {
11904        editor.toggle_comments(&ToggleComments::default(), window, cx)
11905    });
11906    cx.assert_editor_state(
11907        &r#"
11908            <!-- <p>A</p>ˇ -->
11909            <!-- <p>B</p>ˇ -->
11910            <!-- <p>C</p>ˇ -->
11911        "#
11912        .unindent(),
11913    );
11914    cx.update_editor(|editor, window, cx| {
11915        editor.toggle_comments(&ToggleComments::default(), window, cx)
11916    });
11917    cx.assert_editor_state(
11918        &r#"
11919            <p>A</p>ˇ
11920            <p>B</p>ˇ
11921            <p>C</p>ˇ
11922        "#
11923        .unindent(),
11924    );
11925
11926    // Toggle comments for mixture of empty and non-empty selections, where
11927    // multiple selections occupy a given line.
11928    cx.set_state(
11929        &r#"
11930            <p>A«</p>
11931            <p>ˇ»B</p>ˇ
11932            <p>C«</p>
11933            <p>ˇ»D</p>ˇ
11934        "#
11935        .unindent(),
11936    );
11937
11938    cx.update_editor(|editor, window, cx| {
11939        editor.toggle_comments(&ToggleComments::default(), window, cx)
11940    });
11941    cx.assert_editor_state(
11942        &r#"
11943            <!-- <p>A«</p>
11944            <p>ˇ»B</p>ˇ -->
11945            <!-- <p>C«</p>
11946            <p>ˇ»D</p>ˇ -->
11947        "#
11948        .unindent(),
11949    );
11950    cx.update_editor(|editor, window, cx| {
11951        editor.toggle_comments(&ToggleComments::default(), window, cx)
11952    });
11953    cx.assert_editor_state(
11954        &r#"
11955            <p>A«</p>
11956            <p>ˇ»B</p>ˇ
11957            <p>C«</p>
11958            <p>ˇ»D</p>ˇ
11959        "#
11960        .unindent(),
11961    );
11962
11963    // Toggle comments when different languages are active for different
11964    // selections.
11965    cx.set_state(
11966        &r#"
11967            ˇ<script>
11968                ˇvar x = new Y();
11969            ˇ</script>
11970        "#
11971        .unindent(),
11972    );
11973    cx.executor().run_until_parked();
11974    cx.update_editor(|editor, window, cx| {
11975        editor.toggle_comments(&ToggleComments::default(), window, cx)
11976    });
11977    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11978    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11979    cx.assert_editor_state(
11980        &r#"
11981            <!-- ˇ<script> -->
11982                // ˇvar x = new Y();
11983            <!-- ˇ</script> -->
11984        "#
11985        .unindent(),
11986    );
11987}
11988
11989#[gpui::test]
11990fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11991    init_test(cx, |_| {});
11992
11993    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11994    let multibuffer = cx.new(|cx| {
11995        let mut multibuffer = MultiBuffer::new(ReadWrite);
11996        multibuffer.push_excerpts(
11997            buffer.clone(),
11998            [
11999                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12000                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12001            ],
12002            cx,
12003        );
12004        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12005        multibuffer
12006    });
12007
12008    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12009    editor.update_in(cx, |editor, window, cx| {
12010        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12011        editor.change_selections(None, window, cx, |s| {
12012            s.select_ranges([
12013                Point::new(0, 0)..Point::new(0, 0),
12014                Point::new(1, 0)..Point::new(1, 0),
12015            ])
12016        });
12017
12018        editor.handle_input("X", window, cx);
12019        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12020        assert_eq!(
12021            editor.selections.ranges(cx),
12022            [
12023                Point::new(0, 1)..Point::new(0, 1),
12024                Point::new(1, 1)..Point::new(1, 1),
12025            ]
12026        );
12027
12028        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12029        editor.change_selections(None, window, cx, |s| {
12030            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12031        });
12032        editor.backspace(&Default::default(), window, cx);
12033        assert_eq!(editor.text(cx), "Xa\nbbb");
12034        assert_eq!(
12035            editor.selections.ranges(cx),
12036            [Point::new(1, 0)..Point::new(1, 0)]
12037        );
12038
12039        editor.change_selections(None, window, cx, |s| {
12040            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12041        });
12042        editor.backspace(&Default::default(), window, cx);
12043        assert_eq!(editor.text(cx), "X\nbb");
12044        assert_eq!(
12045            editor.selections.ranges(cx),
12046            [Point::new(0, 1)..Point::new(0, 1)]
12047        );
12048    });
12049}
12050
12051#[gpui::test]
12052fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12053    init_test(cx, |_| {});
12054
12055    let markers = vec![('[', ']').into(), ('(', ')').into()];
12056    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12057        indoc! {"
12058            [aaaa
12059            (bbbb]
12060            cccc)",
12061        },
12062        markers.clone(),
12063    );
12064    let excerpt_ranges = markers.into_iter().map(|marker| {
12065        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12066        ExcerptRange::new(context.clone())
12067    });
12068    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12069    let multibuffer = cx.new(|cx| {
12070        let mut multibuffer = MultiBuffer::new(ReadWrite);
12071        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12072        multibuffer
12073    });
12074
12075    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12076    editor.update_in(cx, |editor, window, cx| {
12077        let (expected_text, selection_ranges) = marked_text_ranges(
12078            indoc! {"
12079                aaaa
12080                bˇbbb
12081                bˇbbˇb
12082                cccc"
12083            },
12084            true,
12085        );
12086        assert_eq!(editor.text(cx), expected_text);
12087        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12088
12089        editor.handle_input("X", window, cx);
12090
12091        let (expected_text, expected_selections) = marked_text_ranges(
12092            indoc! {"
12093                aaaa
12094                bXˇbbXb
12095                bXˇbbXˇb
12096                cccc"
12097            },
12098            false,
12099        );
12100        assert_eq!(editor.text(cx), expected_text);
12101        assert_eq!(editor.selections.ranges(cx), expected_selections);
12102
12103        editor.newline(&Newline, window, cx);
12104        let (expected_text, expected_selections) = marked_text_ranges(
12105            indoc! {"
12106                aaaa
12107                bX
12108                ˇbbX
12109                b
12110                bX
12111                ˇbbX
12112                ˇb
12113                cccc"
12114            },
12115            false,
12116        );
12117        assert_eq!(editor.text(cx), expected_text);
12118        assert_eq!(editor.selections.ranges(cx), expected_selections);
12119    });
12120}
12121
12122#[gpui::test]
12123fn test_refresh_selections(cx: &mut TestAppContext) {
12124    init_test(cx, |_| {});
12125
12126    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12127    let mut excerpt1_id = None;
12128    let multibuffer = cx.new(|cx| {
12129        let mut multibuffer = MultiBuffer::new(ReadWrite);
12130        excerpt1_id = multibuffer
12131            .push_excerpts(
12132                buffer.clone(),
12133                [
12134                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12135                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12136                ],
12137                cx,
12138            )
12139            .into_iter()
12140            .next();
12141        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12142        multibuffer
12143    });
12144
12145    let editor = cx.add_window(|window, cx| {
12146        let mut editor = build_editor(multibuffer.clone(), window, cx);
12147        let snapshot = editor.snapshot(window, cx);
12148        editor.change_selections(None, window, cx, |s| {
12149            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12150        });
12151        editor.begin_selection(
12152            Point::new(2, 1).to_display_point(&snapshot),
12153            true,
12154            1,
12155            window,
12156            cx,
12157        );
12158        assert_eq!(
12159            editor.selections.ranges(cx),
12160            [
12161                Point::new(1, 3)..Point::new(1, 3),
12162                Point::new(2, 1)..Point::new(2, 1),
12163            ]
12164        );
12165        editor
12166    });
12167
12168    // Refreshing selections is a no-op when excerpts haven't changed.
12169    _ = editor.update(cx, |editor, window, cx| {
12170        editor.change_selections(None, window, cx, |s| s.refresh());
12171        assert_eq!(
12172            editor.selections.ranges(cx),
12173            [
12174                Point::new(1, 3)..Point::new(1, 3),
12175                Point::new(2, 1)..Point::new(2, 1),
12176            ]
12177        );
12178    });
12179
12180    multibuffer.update(cx, |multibuffer, cx| {
12181        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12182    });
12183    _ = editor.update(cx, |editor, window, cx| {
12184        // Removing an excerpt causes the first selection to become degenerate.
12185        assert_eq!(
12186            editor.selections.ranges(cx),
12187            [
12188                Point::new(0, 0)..Point::new(0, 0),
12189                Point::new(0, 1)..Point::new(0, 1)
12190            ]
12191        );
12192
12193        // Refreshing selections will relocate the first selection to the original buffer
12194        // location.
12195        editor.change_selections(None, window, cx, |s| s.refresh());
12196        assert_eq!(
12197            editor.selections.ranges(cx),
12198            [
12199                Point::new(0, 1)..Point::new(0, 1),
12200                Point::new(0, 3)..Point::new(0, 3)
12201            ]
12202        );
12203        assert!(editor.selections.pending_anchor().is_some());
12204    });
12205}
12206
12207#[gpui::test]
12208fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12209    init_test(cx, |_| {});
12210
12211    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12212    let mut excerpt1_id = None;
12213    let multibuffer = cx.new(|cx| {
12214        let mut multibuffer = MultiBuffer::new(ReadWrite);
12215        excerpt1_id = multibuffer
12216            .push_excerpts(
12217                buffer.clone(),
12218                [
12219                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12220                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12221                ],
12222                cx,
12223            )
12224            .into_iter()
12225            .next();
12226        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12227        multibuffer
12228    });
12229
12230    let editor = cx.add_window(|window, cx| {
12231        let mut editor = build_editor(multibuffer.clone(), window, cx);
12232        let snapshot = editor.snapshot(window, cx);
12233        editor.begin_selection(
12234            Point::new(1, 3).to_display_point(&snapshot),
12235            false,
12236            1,
12237            window,
12238            cx,
12239        );
12240        assert_eq!(
12241            editor.selections.ranges(cx),
12242            [Point::new(1, 3)..Point::new(1, 3)]
12243        );
12244        editor
12245    });
12246
12247    multibuffer.update(cx, |multibuffer, cx| {
12248        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12249    });
12250    _ = editor.update(cx, |editor, window, cx| {
12251        assert_eq!(
12252            editor.selections.ranges(cx),
12253            [Point::new(0, 0)..Point::new(0, 0)]
12254        );
12255
12256        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12257        editor.change_selections(None, window, cx, |s| s.refresh());
12258        assert_eq!(
12259            editor.selections.ranges(cx),
12260            [Point::new(0, 3)..Point::new(0, 3)]
12261        );
12262        assert!(editor.selections.pending_anchor().is_some());
12263    });
12264}
12265
12266#[gpui::test]
12267async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12268    init_test(cx, |_| {});
12269
12270    let language = Arc::new(
12271        Language::new(
12272            LanguageConfig {
12273                brackets: BracketPairConfig {
12274                    pairs: vec![
12275                        BracketPair {
12276                            start: "{".to_string(),
12277                            end: "}".to_string(),
12278                            close: true,
12279                            surround: true,
12280                            newline: true,
12281                        },
12282                        BracketPair {
12283                            start: "/* ".to_string(),
12284                            end: " */".to_string(),
12285                            close: true,
12286                            surround: true,
12287                            newline: true,
12288                        },
12289                    ],
12290                    ..Default::default()
12291                },
12292                ..Default::default()
12293            },
12294            Some(tree_sitter_rust::LANGUAGE.into()),
12295        )
12296        .with_indents_query("")
12297        .unwrap(),
12298    );
12299
12300    let text = concat!(
12301        "{   }\n",     //
12302        "  x\n",       //
12303        "  /*   */\n", //
12304        "x\n",         //
12305        "{{} }\n",     //
12306    );
12307
12308    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12309    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12310    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12311    editor
12312        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12313        .await;
12314
12315    editor.update_in(cx, |editor, window, cx| {
12316        editor.change_selections(None, window, cx, |s| {
12317            s.select_display_ranges([
12318                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12319                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12320                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12321            ])
12322        });
12323        editor.newline(&Newline, window, cx);
12324
12325        assert_eq!(
12326            editor.buffer().read(cx).read(cx).text(),
12327            concat!(
12328                "{ \n",    // Suppress rustfmt
12329                "\n",      //
12330                "}\n",     //
12331                "  x\n",   //
12332                "  /* \n", //
12333                "  \n",    //
12334                "  */\n",  //
12335                "x\n",     //
12336                "{{} \n",  //
12337                "}\n",     //
12338            )
12339        );
12340    });
12341}
12342
12343#[gpui::test]
12344fn test_highlighted_ranges(cx: &mut TestAppContext) {
12345    init_test(cx, |_| {});
12346
12347    let editor = cx.add_window(|window, cx| {
12348        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12349        build_editor(buffer.clone(), window, cx)
12350    });
12351
12352    _ = editor.update(cx, |editor, window, cx| {
12353        struct Type1;
12354        struct Type2;
12355
12356        let buffer = editor.buffer.read(cx).snapshot(cx);
12357
12358        let anchor_range =
12359            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12360
12361        editor.highlight_background::<Type1>(
12362            &[
12363                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12364                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12365                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12366                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12367            ],
12368            |_| Hsla::red(),
12369            cx,
12370        );
12371        editor.highlight_background::<Type2>(
12372            &[
12373                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12374                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12375                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12376                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12377            ],
12378            |_| Hsla::green(),
12379            cx,
12380        );
12381
12382        let snapshot = editor.snapshot(window, cx);
12383        let mut highlighted_ranges = editor.background_highlights_in_range(
12384            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12385            &snapshot,
12386            cx.theme().colors(),
12387        );
12388        // Enforce a consistent ordering based on color without relying on the ordering of the
12389        // highlight's `TypeId` which is non-executor.
12390        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12391        assert_eq!(
12392            highlighted_ranges,
12393            &[
12394                (
12395                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12396                    Hsla::red(),
12397                ),
12398                (
12399                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12400                    Hsla::red(),
12401                ),
12402                (
12403                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12404                    Hsla::green(),
12405                ),
12406                (
12407                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12408                    Hsla::green(),
12409                ),
12410            ]
12411        );
12412        assert_eq!(
12413            editor.background_highlights_in_range(
12414                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12415                &snapshot,
12416                cx.theme().colors(),
12417            ),
12418            &[(
12419                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12420                Hsla::red(),
12421            )]
12422        );
12423    });
12424}
12425
12426#[gpui::test]
12427async fn test_following(cx: &mut TestAppContext) {
12428    init_test(cx, |_| {});
12429
12430    let fs = FakeFs::new(cx.executor());
12431    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12432
12433    let buffer = project.update(cx, |project, cx| {
12434        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12435        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12436    });
12437    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12438    let follower = cx.update(|cx| {
12439        cx.open_window(
12440            WindowOptions {
12441                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12442                    gpui::Point::new(px(0.), px(0.)),
12443                    gpui::Point::new(px(10.), px(80.)),
12444                ))),
12445                ..Default::default()
12446            },
12447            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12448        )
12449        .unwrap()
12450    });
12451
12452    let is_still_following = Rc::new(RefCell::new(true));
12453    let follower_edit_event_count = Rc::new(RefCell::new(0));
12454    let pending_update = Rc::new(RefCell::new(None));
12455    let leader_entity = leader.root(cx).unwrap();
12456    let follower_entity = follower.root(cx).unwrap();
12457    _ = follower.update(cx, {
12458        let update = pending_update.clone();
12459        let is_still_following = is_still_following.clone();
12460        let follower_edit_event_count = follower_edit_event_count.clone();
12461        |_, window, cx| {
12462            cx.subscribe_in(
12463                &leader_entity,
12464                window,
12465                move |_, leader, event, window, cx| {
12466                    leader.read(cx).add_event_to_update_proto(
12467                        event,
12468                        &mut update.borrow_mut(),
12469                        window,
12470                        cx,
12471                    );
12472                },
12473            )
12474            .detach();
12475
12476            cx.subscribe_in(
12477                &follower_entity,
12478                window,
12479                move |_, _, event: &EditorEvent, _window, _cx| {
12480                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12481                        *is_still_following.borrow_mut() = false;
12482                    }
12483
12484                    if let EditorEvent::BufferEdited = event {
12485                        *follower_edit_event_count.borrow_mut() += 1;
12486                    }
12487                },
12488            )
12489            .detach();
12490        }
12491    });
12492
12493    // Update the selections only
12494    _ = leader.update(cx, |leader, window, cx| {
12495        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12496    });
12497    follower
12498        .update(cx, |follower, window, cx| {
12499            follower.apply_update_proto(
12500                &project,
12501                pending_update.borrow_mut().take().unwrap(),
12502                window,
12503                cx,
12504            )
12505        })
12506        .unwrap()
12507        .await
12508        .unwrap();
12509    _ = follower.update(cx, |follower, _, cx| {
12510        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12511    });
12512    assert!(*is_still_following.borrow());
12513    assert_eq!(*follower_edit_event_count.borrow(), 0);
12514
12515    // Update the scroll position only
12516    _ = leader.update(cx, |leader, window, cx| {
12517        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12518    });
12519    follower
12520        .update(cx, |follower, window, cx| {
12521            follower.apply_update_proto(
12522                &project,
12523                pending_update.borrow_mut().take().unwrap(),
12524                window,
12525                cx,
12526            )
12527        })
12528        .unwrap()
12529        .await
12530        .unwrap();
12531    assert_eq!(
12532        follower
12533            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12534            .unwrap(),
12535        gpui::Point::new(1.5, 3.5)
12536    );
12537    assert!(*is_still_following.borrow());
12538    assert_eq!(*follower_edit_event_count.borrow(), 0);
12539
12540    // Update the selections and scroll position. The follower's scroll position is updated
12541    // via autoscroll, not via the leader's exact scroll position.
12542    _ = leader.update(cx, |leader, window, cx| {
12543        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12544        leader.request_autoscroll(Autoscroll::newest(), cx);
12545        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12546    });
12547    follower
12548        .update(cx, |follower, window, cx| {
12549            follower.apply_update_proto(
12550                &project,
12551                pending_update.borrow_mut().take().unwrap(),
12552                window,
12553                cx,
12554            )
12555        })
12556        .unwrap()
12557        .await
12558        .unwrap();
12559    _ = follower.update(cx, |follower, _, cx| {
12560        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12561        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12562    });
12563    assert!(*is_still_following.borrow());
12564
12565    // Creating a pending selection that precedes another selection
12566    _ = leader.update(cx, |leader, window, cx| {
12567        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12568        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12569    });
12570    follower
12571        .update(cx, |follower, window, cx| {
12572            follower.apply_update_proto(
12573                &project,
12574                pending_update.borrow_mut().take().unwrap(),
12575                window,
12576                cx,
12577            )
12578        })
12579        .unwrap()
12580        .await
12581        .unwrap();
12582    _ = follower.update(cx, |follower, _, cx| {
12583        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12584    });
12585    assert!(*is_still_following.borrow());
12586
12587    // Extend the pending selection so that it surrounds another selection
12588    _ = leader.update(cx, |leader, window, cx| {
12589        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12590    });
12591    follower
12592        .update(cx, |follower, window, cx| {
12593            follower.apply_update_proto(
12594                &project,
12595                pending_update.borrow_mut().take().unwrap(),
12596                window,
12597                cx,
12598            )
12599        })
12600        .unwrap()
12601        .await
12602        .unwrap();
12603    _ = follower.update(cx, |follower, _, cx| {
12604        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12605    });
12606
12607    // Scrolling locally breaks the follow
12608    _ = follower.update(cx, |follower, window, cx| {
12609        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12610        follower.set_scroll_anchor(
12611            ScrollAnchor {
12612                anchor: top_anchor,
12613                offset: gpui::Point::new(0.0, 0.5),
12614            },
12615            window,
12616            cx,
12617        );
12618    });
12619    assert!(!(*is_still_following.borrow()));
12620}
12621
12622#[gpui::test]
12623async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12624    init_test(cx, |_| {});
12625
12626    let fs = FakeFs::new(cx.executor());
12627    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12628    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12629    let pane = workspace
12630        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12631        .unwrap();
12632
12633    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12634
12635    let leader = pane.update_in(cx, |_, window, cx| {
12636        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12637        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12638    });
12639
12640    // Start following the editor when it has no excerpts.
12641    let mut state_message =
12642        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12643    let workspace_entity = workspace.root(cx).unwrap();
12644    let follower_1 = cx
12645        .update_window(*workspace.deref(), |_, window, cx| {
12646            Editor::from_state_proto(
12647                workspace_entity,
12648                ViewId {
12649                    creator: CollaboratorId::PeerId(PeerId::default()),
12650                    id: 0,
12651                },
12652                &mut state_message,
12653                window,
12654                cx,
12655            )
12656        })
12657        .unwrap()
12658        .unwrap()
12659        .await
12660        .unwrap();
12661
12662    let update_message = Rc::new(RefCell::new(None));
12663    follower_1.update_in(cx, {
12664        let update = update_message.clone();
12665        |_, window, cx| {
12666            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12667                leader.read(cx).add_event_to_update_proto(
12668                    event,
12669                    &mut update.borrow_mut(),
12670                    window,
12671                    cx,
12672                );
12673            })
12674            .detach();
12675        }
12676    });
12677
12678    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12679        (
12680            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12681            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12682        )
12683    });
12684
12685    // Insert some excerpts.
12686    leader.update(cx, |leader, cx| {
12687        leader.buffer.update(cx, |multibuffer, cx| {
12688            multibuffer.set_excerpts_for_path(
12689                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12690                buffer_1.clone(),
12691                vec![
12692                    Point::row_range(0..3),
12693                    Point::row_range(1..6),
12694                    Point::row_range(12..15),
12695                ],
12696                0,
12697                cx,
12698            );
12699            multibuffer.set_excerpts_for_path(
12700                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12701                buffer_2.clone(),
12702                vec![Point::row_range(0..6), Point::row_range(8..12)],
12703                0,
12704                cx,
12705            );
12706        });
12707    });
12708
12709    // Apply the update of adding the excerpts.
12710    follower_1
12711        .update_in(cx, |follower, window, cx| {
12712            follower.apply_update_proto(
12713                &project,
12714                update_message.borrow().clone().unwrap(),
12715                window,
12716                cx,
12717            )
12718        })
12719        .await
12720        .unwrap();
12721    assert_eq!(
12722        follower_1.update(cx, |editor, cx| editor.text(cx)),
12723        leader.update(cx, |editor, cx| editor.text(cx))
12724    );
12725    update_message.borrow_mut().take();
12726
12727    // Start following separately after it already has excerpts.
12728    let mut state_message =
12729        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12730    let workspace_entity = workspace.root(cx).unwrap();
12731    let follower_2 = cx
12732        .update_window(*workspace.deref(), |_, window, cx| {
12733            Editor::from_state_proto(
12734                workspace_entity,
12735                ViewId {
12736                    creator: CollaboratorId::PeerId(PeerId::default()),
12737                    id: 0,
12738                },
12739                &mut state_message,
12740                window,
12741                cx,
12742            )
12743        })
12744        .unwrap()
12745        .unwrap()
12746        .await
12747        .unwrap();
12748    assert_eq!(
12749        follower_2.update(cx, |editor, cx| editor.text(cx)),
12750        leader.update(cx, |editor, cx| editor.text(cx))
12751    );
12752
12753    // Remove some excerpts.
12754    leader.update(cx, |leader, cx| {
12755        leader.buffer.update(cx, |multibuffer, cx| {
12756            let excerpt_ids = multibuffer.excerpt_ids();
12757            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12758            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12759        });
12760    });
12761
12762    // Apply the update of removing the excerpts.
12763    follower_1
12764        .update_in(cx, |follower, window, cx| {
12765            follower.apply_update_proto(
12766                &project,
12767                update_message.borrow().clone().unwrap(),
12768                window,
12769                cx,
12770            )
12771        })
12772        .await
12773        .unwrap();
12774    follower_2
12775        .update_in(cx, |follower, window, cx| {
12776            follower.apply_update_proto(
12777                &project,
12778                update_message.borrow().clone().unwrap(),
12779                window,
12780                cx,
12781            )
12782        })
12783        .await
12784        .unwrap();
12785    update_message.borrow_mut().take();
12786    assert_eq!(
12787        follower_1.update(cx, |editor, cx| editor.text(cx)),
12788        leader.update(cx, |editor, cx| editor.text(cx))
12789    );
12790}
12791
12792#[gpui::test]
12793async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12794    init_test(cx, |_| {});
12795
12796    let mut cx = EditorTestContext::new(cx).await;
12797    let lsp_store =
12798        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12799
12800    cx.set_state(indoc! {"
12801        ˇfn func(abc def: i32) -> u32 {
12802        }
12803    "});
12804
12805    cx.update(|_, cx| {
12806        lsp_store.update(cx, |lsp_store, cx| {
12807            lsp_store
12808                .update_diagnostics(
12809                    LanguageServerId(0),
12810                    lsp::PublishDiagnosticsParams {
12811                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12812                        version: None,
12813                        diagnostics: vec![
12814                            lsp::Diagnostic {
12815                                range: lsp::Range::new(
12816                                    lsp::Position::new(0, 11),
12817                                    lsp::Position::new(0, 12),
12818                                ),
12819                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12820                                ..Default::default()
12821                            },
12822                            lsp::Diagnostic {
12823                                range: lsp::Range::new(
12824                                    lsp::Position::new(0, 12),
12825                                    lsp::Position::new(0, 15),
12826                                ),
12827                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12828                                ..Default::default()
12829                            },
12830                            lsp::Diagnostic {
12831                                range: lsp::Range::new(
12832                                    lsp::Position::new(0, 25),
12833                                    lsp::Position::new(0, 28),
12834                                ),
12835                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12836                                ..Default::default()
12837                            },
12838                        ],
12839                    },
12840                    &[],
12841                    cx,
12842                )
12843                .unwrap()
12844        });
12845    });
12846
12847    executor.run_until_parked();
12848
12849    cx.update_editor(|editor, window, cx| {
12850        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12851    });
12852
12853    cx.assert_editor_state(indoc! {"
12854        fn func(abc def: i32) -> ˇu32 {
12855        }
12856    "});
12857
12858    cx.update_editor(|editor, window, cx| {
12859        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12860    });
12861
12862    cx.assert_editor_state(indoc! {"
12863        fn func(abc ˇdef: i32) -> u32 {
12864        }
12865    "});
12866
12867    cx.update_editor(|editor, window, cx| {
12868        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12869    });
12870
12871    cx.assert_editor_state(indoc! {"
12872        fn func(abcˇ def: i32) -> u32 {
12873        }
12874    "});
12875
12876    cx.update_editor(|editor, window, cx| {
12877        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12878    });
12879
12880    cx.assert_editor_state(indoc! {"
12881        fn func(abc def: i32) -> ˇu32 {
12882        }
12883    "});
12884}
12885
12886#[gpui::test]
12887async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12888    init_test(cx, |_| {});
12889
12890    let mut cx = EditorTestContext::new(cx).await;
12891
12892    let diff_base = r#"
12893        use some::mod;
12894
12895        const A: u32 = 42;
12896
12897        fn main() {
12898            println!("hello");
12899
12900            println!("world");
12901        }
12902        "#
12903    .unindent();
12904
12905    // Edits are modified, removed, modified, added
12906    cx.set_state(
12907        &r#"
12908        use some::modified;
12909
12910        ˇ
12911        fn main() {
12912            println!("hello there");
12913
12914            println!("around the");
12915            println!("world");
12916        }
12917        "#
12918        .unindent(),
12919    );
12920
12921    cx.set_head_text(&diff_base);
12922    executor.run_until_parked();
12923
12924    cx.update_editor(|editor, window, cx| {
12925        //Wrap around the bottom of the buffer
12926        for _ in 0..3 {
12927            editor.go_to_next_hunk(&GoToHunk, window, cx);
12928        }
12929    });
12930
12931    cx.assert_editor_state(
12932        &r#"
12933        ˇuse some::modified;
12934
12935
12936        fn main() {
12937            println!("hello there");
12938
12939            println!("around the");
12940            println!("world");
12941        }
12942        "#
12943        .unindent(),
12944    );
12945
12946    cx.update_editor(|editor, window, cx| {
12947        //Wrap around the top of the buffer
12948        for _ in 0..2 {
12949            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12950        }
12951    });
12952
12953    cx.assert_editor_state(
12954        &r#"
12955        use some::modified;
12956
12957
12958        fn main() {
12959        ˇ    println!("hello there");
12960
12961            println!("around the");
12962            println!("world");
12963        }
12964        "#
12965        .unindent(),
12966    );
12967
12968    cx.update_editor(|editor, window, cx| {
12969        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12970    });
12971
12972    cx.assert_editor_state(
12973        &r#"
12974        use some::modified;
12975
12976        ˇ
12977        fn main() {
12978            println!("hello there");
12979
12980            println!("around the");
12981            println!("world");
12982        }
12983        "#
12984        .unindent(),
12985    );
12986
12987    cx.update_editor(|editor, window, cx| {
12988        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12989    });
12990
12991    cx.assert_editor_state(
12992        &r#"
12993        ˇuse some::modified;
12994
12995
12996        fn main() {
12997            println!("hello there");
12998
12999            println!("around the");
13000            println!("world");
13001        }
13002        "#
13003        .unindent(),
13004    );
13005
13006    cx.update_editor(|editor, window, cx| {
13007        for _ in 0..2 {
13008            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13009        }
13010    });
13011
13012    cx.assert_editor_state(
13013        &r#"
13014        use some::modified;
13015
13016
13017        fn main() {
13018        ˇ    println!("hello there");
13019
13020            println!("around the");
13021            println!("world");
13022        }
13023        "#
13024        .unindent(),
13025    );
13026
13027    cx.update_editor(|editor, window, cx| {
13028        editor.fold(&Fold, window, cx);
13029    });
13030
13031    cx.update_editor(|editor, window, cx| {
13032        editor.go_to_next_hunk(&GoToHunk, window, cx);
13033    });
13034
13035    cx.assert_editor_state(
13036        &r#"
13037        ˇuse some::modified;
13038
13039
13040        fn main() {
13041            println!("hello there");
13042
13043            println!("around the");
13044            println!("world");
13045        }
13046        "#
13047        .unindent(),
13048    );
13049}
13050
13051#[test]
13052fn test_split_words() {
13053    fn split(text: &str) -> Vec<&str> {
13054        split_words(text).collect()
13055    }
13056
13057    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13058    assert_eq!(split("hello_world"), &["hello_", "world"]);
13059    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13060    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13061    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13062    assert_eq!(split("helloworld"), &["helloworld"]);
13063
13064    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13065}
13066
13067#[gpui::test]
13068async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13069    init_test(cx, |_| {});
13070
13071    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13072    let mut assert = |before, after| {
13073        let _state_context = cx.set_state(before);
13074        cx.run_until_parked();
13075        cx.update_editor(|editor, window, cx| {
13076            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13077        });
13078        cx.run_until_parked();
13079        cx.assert_editor_state(after);
13080    };
13081
13082    // Outside bracket jumps to outside of matching bracket
13083    assert("console.logˇ(var);", "console.log(var)ˇ;");
13084    assert("console.log(var)ˇ;", "console.logˇ(var);");
13085
13086    // Inside bracket jumps to inside of matching bracket
13087    assert("console.log(ˇvar);", "console.log(varˇ);");
13088    assert("console.log(varˇ);", "console.log(ˇvar);");
13089
13090    // When outside a bracket and inside, favor jumping to the inside bracket
13091    assert(
13092        "console.log('foo', [1, 2, 3]ˇ);",
13093        "console.log(ˇ'foo', [1, 2, 3]);",
13094    );
13095    assert(
13096        "console.log(ˇ'foo', [1, 2, 3]);",
13097        "console.log('foo', [1, 2, 3]ˇ);",
13098    );
13099
13100    // Bias forward if two options are equally likely
13101    assert(
13102        "let result = curried_fun()ˇ();",
13103        "let result = curried_fun()()ˇ;",
13104    );
13105
13106    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13107    assert(
13108        indoc! {"
13109            function test() {
13110                console.log('test')ˇ
13111            }"},
13112        indoc! {"
13113            function test() {
13114                console.logˇ('test')
13115            }"},
13116    );
13117}
13118
13119#[gpui::test]
13120async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13121    init_test(cx, |_| {});
13122
13123    let fs = FakeFs::new(cx.executor());
13124    fs.insert_tree(
13125        path!("/a"),
13126        json!({
13127            "main.rs": "fn main() { let a = 5; }",
13128            "other.rs": "// Test file",
13129        }),
13130    )
13131    .await;
13132    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13133
13134    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13135    language_registry.add(Arc::new(Language::new(
13136        LanguageConfig {
13137            name: "Rust".into(),
13138            matcher: LanguageMatcher {
13139                path_suffixes: vec!["rs".to_string()],
13140                ..Default::default()
13141            },
13142            brackets: BracketPairConfig {
13143                pairs: vec![BracketPair {
13144                    start: "{".to_string(),
13145                    end: "}".to_string(),
13146                    close: true,
13147                    surround: true,
13148                    newline: true,
13149                }],
13150                disabled_scopes_by_bracket_ix: Vec::new(),
13151            },
13152            ..Default::default()
13153        },
13154        Some(tree_sitter_rust::LANGUAGE.into()),
13155    )));
13156    let mut fake_servers = language_registry.register_fake_lsp(
13157        "Rust",
13158        FakeLspAdapter {
13159            capabilities: lsp::ServerCapabilities {
13160                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13161                    first_trigger_character: "{".to_string(),
13162                    more_trigger_character: None,
13163                }),
13164                ..Default::default()
13165            },
13166            ..Default::default()
13167        },
13168    );
13169
13170    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13171
13172    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13173
13174    let worktree_id = workspace
13175        .update(cx, |workspace, _, cx| {
13176            workspace.project().update(cx, |project, cx| {
13177                project.worktrees(cx).next().unwrap().read(cx).id()
13178            })
13179        })
13180        .unwrap();
13181
13182    let buffer = project
13183        .update(cx, |project, cx| {
13184            project.open_local_buffer(path!("/a/main.rs"), cx)
13185        })
13186        .await
13187        .unwrap();
13188    let editor_handle = workspace
13189        .update(cx, |workspace, window, cx| {
13190            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13191        })
13192        .unwrap()
13193        .await
13194        .unwrap()
13195        .downcast::<Editor>()
13196        .unwrap();
13197
13198    cx.executor().start_waiting();
13199    let fake_server = fake_servers.next().await.unwrap();
13200
13201    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13202        |params, _| async move {
13203            assert_eq!(
13204                params.text_document_position.text_document.uri,
13205                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13206            );
13207            assert_eq!(
13208                params.text_document_position.position,
13209                lsp::Position::new(0, 21),
13210            );
13211
13212            Ok(Some(vec![lsp::TextEdit {
13213                new_text: "]".to_string(),
13214                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13215            }]))
13216        },
13217    );
13218
13219    editor_handle.update_in(cx, |editor, window, cx| {
13220        window.focus(&editor.focus_handle(cx));
13221        editor.change_selections(None, window, cx, |s| {
13222            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13223        });
13224        editor.handle_input("{", window, cx);
13225    });
13226
13227    cx.executor().run_until_parked();
13228
13229    buffer.update(cx, |buffer, _| {
13230        assert_eq!(
13231            buffer.text(),
13232            "fn main() { let a = {5}; }",
13233            "No extra braces from on type formatting should appear in the buffer"
13234        )
13235    });
13236}
13237
13238#[gpui::test]
13239async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13240    init_test(cx, |_| {});
13241
13242    let fs = FakeFs::new(cx.executor());
13243    fs.insert_tree(
13244        path!("/a"),
13245        json!({
13246            "main.rs": "fn main() { let a = 5; }",
13247            "other.rs": "// Test file",
13248        }),
13249    )
13250    .await;
13251
13252    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13253
13254    let server_restarts = Arc::new(AtomicUsize::new(0));
13255    let closure_restarts = Arc::clone(&server_restarts);
13256    let language_server_name = "test language server";
13257    let language_name: LanguageName = "Rust".into();
13258
13259    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13260    language_registry.add(Arc::new(Language::new(
13261        LanguageConfig {
13262            name: language_name.clone(),
13263            matcher: LanguageMatcher {
13264                path_suffixes: vec!["rs".to_string()],
13265                ..Default::default()
13266            },
13267            ..Default::default()
13268        },
13269        Some(tree_sitter_rust::LANGUAGE.into()),
13270    )));
13271    let mut fake_servers = language_registry.register_fake_lsp(
13272        "Rust",
13273        FakeLspAdapter {
13274            name: language_server_name,
13275            initialization_options: Some(json!({
13276                "testOptionValue": true
13277            })),
13278            initializer: Some(Box::new(move |fake_server| {
13279                let task_restarts = Arc::clone(&closure_restarts);
13280                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13281                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13282                    futures::future::ready(Ok(()))
13283                });
13284            })),
13285            ..Default::default()
13286        },
13287    );
13288
13289    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13290    let _buffer = project
13291        .update(cx, |project, cx| {
13292            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13293        })
13294        .await
13295        .unwrap();
13296    let _fake_server = fake_servers.next().await.unwrap();
13297    update_test_language_settings(cx, |language_settings| {
13298        language_settings.languages.insert(
13299            language_name.clone(),
13300            LanguageSettingsContent {
13301                tab_size: NonZeroU32::new(8),
13302                ..Default::default()
13303            },
13304        );
13305    });
13306    cx.executor().run_until_parked();
13307    assert_eq!(
13308        server_restarts.load(atomic::Ordering::Acquire),
13309        0,
13310        "Should not restart LSP server on an unrelated change"
13311    );
13312
13313    update_test_project_settings(cx, |project_settings| {
13314        project_settings.lsp.insert(
13315            "Some other server name".into(),
13316            LspSettings {
13317                binary: None,
13318                settings: None,
13319                initialization_options: Some(json!({
13320                    "some other init value": false
13321                })),
13322                enable_lsp_tasks: false,
13323            },
13324        );
13325    });
13326    cx.executor().run_until_parked();
13327    assert_eq!(
13328        server_restarts.load(atomic::Ordering::Acquire),
13329        0,
13330        "Should not restart LSP server on an unrelated LSP settings change"
13331    );
13332
13333    update_test_project_settings(cx, |project_settings| {
13334        project_settings.lsp.insert(
13335            language_server_name.into(),
13336            LspSettings {
13337                binary: None,
13338                settings: None,
13339                initialization_options: Some(json!({
13340                    "anotherInitValue": false
13341                })),
13342                enable_lsp_tasks: false,
13343            },
13344        );
13345    });
13346    cx.executor().run_until_parked();
13347    assert_eq!(
13348        server_restarts.load(atomic::Ordering::Acquire),
13349        1,
13350        "Should restart LSP server on a related LSP settings change"
13351    );
13352
13353    update_test_project_settings(cx, |project_settings| {
13354        project_settings.lsp.insert(
13355            language_server_name.into(),
13356            LspSettings {
13357                binary: None,
13358                settings: None,
13359                initialization_options: Some(json!({
13360                    "anotherInitValue": false
13361                })),
13362                enable_lsp_tasks: false,
13363            },
13364        );
13365    });
13366    cx.executor().run_until_parked();
13367    assert_eq!(
13368        server_restarts.load(atomic::Ordering::Acquire),
13369        1,
13370        "Should not restart LSP server on a related LSP settings change that is the same"
13371    );
13372
13373    update_test_project_settings(cx, |project_settings| {
13374        project_settings.lsp.insert(
13375            language_server_name.into(),
13376            LspSettings {
13377                binary: None,
13378                settings: None,
13379                initialization_options: None,
13380                enable_lsp_tasks: false,
13381            },
13382        );
13383    });
13384    cx.executor().run_until_parked();
13385    assert_eq!(
13386        server_restarts.load(atomic::Ordering::Acquire),
13387        2,
13388        "Should restart LSP server on another related LSP settings change"
13389    );
13390}
13391
13392#[gpui::test]
13393async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13394    init_test(cx, |_| {});
13395
13396    let mut cx = EditorLspTestContext::new_rust(
13397        lsp::ServerCapabilities {
13398            completion_provider: Some(lsp::CompletionOptions {
13399                trigger_characters: Some(vec![".".to_string()]),
13400                resolve_provider: Some(true),
13401                ..Default::default()
13402            }),
13403            ..Default::default()
13404        },
13405        cx,
13406    )
13407    .await;
13408
13409    cx.set_state("fn main() { let a = 2ˇ; }");
13410    cx.simulate_keystroke(".");
13411    let completion_item = lsp::CompletionItem {
13412        label: "some".into(),
13413        kind: Some(lsp::CompletionItemKind::SNIPPET),
13414        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13415        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13416            kind: lsp::MarkupKind::Markdown,
13417            value: "```rust\nSome(2)\n```".to_string(),
13418        })),
13419        deprecated: Some(false),
13420        sort_text: Some("fffffff2".to_string()),
13421        filter_text: Some("some".to_string()),
13422        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13423        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13424            range: lsp::Range {
13425                start: lsp::Position {
13426                    line: 0,
13427                    character: 22,
13428                },
13429                end: lsp::Position {
13430                    line: 0,
13431                    character: 22,
13432                },
13433            },
13434            new_text: "Some(2)".to_string(),
13435        })),
13436        additional_text_edits: Some(vec![lsp::TextEdit {
13437            range: lsp::Range {
13438                start: lsp::Position {
13439                    line: 0,
13440                    character: 20,
13441                },
13442                end: lsp::Position {
13443                    line: 0,
13444                    character: 22,
13445                },
13446            },
13447            new_text: "".to_string(),
13448        }]),
13449        ..Default::default()
13450    };
13451
13452    let closure_completion_item = completion_item.clone();
13453    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13454        let task_completion_item = closure_completion_item.clone();
13455        async move {
13456            Ok(Some(lsp::CompletionResponse::Array(vec![
13457                task_completion_item,
13458            ])))
13459        }
13460    });
13461
13462    request.next().await;
13463
13464    cx.condition(|editor, _| editor.context_menu_visible())
13465        .await;
13466    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13467        editor
13468            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13469            .unwrap()
13470    });
13471    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13472
13473    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13474        let task_completion_item = completion_item.clone();
13475        async move { Ok(task_completion_item) }
13476    })
13477    .next()
13478    .await
13479    .unwrap();
13480    apply_additional_edits.await.unwrap();
13481    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13482}
13483
13484#[gpui::test]
13485async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13486    init_test(cx, |_| {});
13487
13488    let mut cx = EditorLspTestContext::new_rust(
13489        lsp::ServerCapabilities {
13490            completion_provider: Some(lsp::CompletionOptions {
13491                trigger_characters: Some(vec![".".to_string()]),
13492                resolve_provider: Some(true),
13493                ..Default::default()
13494            }),
13495            ..Default::default()
13496        },
13497        cx,
13498    )
13499    .await;
13500
13501    cx.set_state("fn main() { let a = 2ˇ; }");
13502    cx.simulate_keystroke(".");
13503
13504    let item1 = lsp::CompletionItem {
13505        label: "method id()".to_string(),
13506        filter_text: Some("id".to_string()),
13507        detail: None,
13508        documentation: None,
13509        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13510            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13511            new_text: ".id".to_string(),
13512        })),
13513        ..lsp::CompletionItem::default()
13514    };
13515
13516    let item2 = lsp::CompletionItem {
13517        label: "other".to_string(),
13518        filter_text: Some("other".to_string()),
13519        detail: None,
13520        documentation: None,
13521        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13522            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13523            new_text: ".other".to_string(),
13524        })),
13525        ..lsp::CompletionItem::default()
13526    };
13527
13528    let item1 = item1.clone();
13529    cx.set_request_handler::<lsp::request::Completion, _, _>({
13530        let item1 = item1.clone();
13531        move |_, _, _| {
13532            let item1 = item1.clone();
13533            let item2 = item2.clone();
13534            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13535        }
13536    })
13537    .next()
13538    .await;
13539
13540    cx.condition(|editor, _| editor.context_menu_visible())
13541        .await;
13542    cx.update_editor(|editor, _, _| {
13543        let context_menu = editor.context_menu.borrow_mut();
13544        let context_menu = context_menu
13545            .as_ref()
13546            .expect("Should have the context menu deployed");
13547        match context_menu {
13548            CodeContextMenu::Completions(completions_menu) => {
13549                let completions = completions_menu.completions.borrow_mut();
13550                assert_eq!(
13551                    completions
13552                        .iter()
13553                        .map(|completion| &completion.label.text)
13554                        .collect::<Vec<_>>(),
13555                    vec!["method id()", "other"]
13556                )
13557            }
13558            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13559        }
13560    });
13561
13562    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13563        let item1 = item1.clone();
13564        move |_, item_to_resolve, _| {
13565            let item1 = item1.clone();
13566            async move {
13567                if item1 == item_to_resolve {
13568                    Ok(lsp::CompletionItem {
13569                        label: "method id()".to_string(),
13570                        filter_text: Some("id".to_string()),
13571                        detail: Some("Now resolved!".to_string()),
13572                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13573                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13574                            range: lsp::Range::new(
13575                                lsp::Position::new(0, 22),
13576                                lsp::Position::new(0, 22),
13577                            ),
13578                            new_text: ".id".to_string(),
13579                        })),
13580                        ..lsp::CompletionItem::default()
13581                    })
13582                } else {
13583                    Ok(item_to_resolve)
13584                }
13585            }
13586        }
13587    })
13588    .next()
13589    .await
13590    .unwrap();
13591    cx.run_until_parked();
13592
13593    cx.update_editor(|editor, window, cx| {
13594        editor.context_menu_next(&Default::default(), window, cx);
13595    });
13596
13597    cx.update_editor(|editor, _, _| {
13598        let context_menu = editor.context_menu.borrow_mut();
13599        let context_menu = context_menu
13600            .as_ref()
13601            .expect("Should have the context menu deployed");
13602        match context_menu {
13603            CodeContextMenu::Completions(completions_menu) => {
13604                let completions = completions_menu.completions.borrow_mut();
13605                assert_eq!(
13606                    completions
13607                        .iter()
13608                        .map(|completion| &completion.label.text)
13609                        .collect::<Vec<_>>(),
13610                    vec!["method id() Now resolved!", "other"],
13611                    "Should update first completion label, but not second as the filter text did not match."
13612                );
13613            }
13614            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13615        }
13616    });
13617}
13618
13619#[gpui::test]
13620async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13621    init_test(cx, |_| {});
13622
13623    let mut cx = EditorLspTestContext::new_rust(
13624        lsp::ServerCapabilities {
13625            completion_provider: Some(lsp::CompletionOptions {
13626                trigger_characters: Some(vec![".".to_string()]),
13627                resolve_provider: Some(true),
13628                ..Default::default()
13629            }),
13630            ..Default::default()
13631        },
13632        cx,
13633    )
13634    .await;
13635
13636    cx.set_state("fn main() { let a = 2ˇ; }");
13637    cx.simulate_keystroke(".");
13638
13639    let unresolved_item_1 = lsp::CompletionItem {
13640        label: "id".to_string(),
13641        filter_text: Some("id".to_string()),
13642        detail: None,
13643        documentation: None,
13644        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13645            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13646            new_text: ".id".to_string(),
13647        })),
13648        ..lsp::CompletionItem::default()
13649    };
13650    let resolved_item_1 = lsp::CompletionItem {
13651        additional_text_edits: Some(vec![lsp::TextEdit {
13652            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13653            new_text: "!!".to_string(),
13654        }]),
13655        ..unresolved_item_1.clone()
13656    };
13657    let unresolved_item_2 = lsp::CompletionItem {
13658        label: "other".to_string(),
13659        filter_text: Some("other".to_string()),
13660        detail: None,
13661        documentation: None,
13662        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13663            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13664            new_text: ".other".to_string(),
13665        })),
13666        ..lsp::CompletionItem::default()
13667    };
13668    let resolved_item_2 = lsp::CompletionItem {
13669        additional_text_edits: Some(vec![lsp::TextEdit {
13670            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13671            new_text: "??".to_string(),
13672        }]),
13673        ..unresolved_item_2.clone()
13674    };
13675
13676    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13677    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13678    cx.lsp
13679        .server
13680        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13681            let unresolved_item_1 = unresolved_item_1.clone();
13682            let resolved_item_1 = resolved_item_1.clone();
13683            let unresolved_item_2 = unresolved_item_2.clone();
13684            let resolved_item_2 = resolved_item_2.clone();
13685            let resolve_requests_1 = resolve_requests_1.clone();
13686            let resolve_requests_2 = resolve_requests_2.clone();
13687            move |unresolved_request, _| {
13688                let unresolved_item_1 = unresolved_item_1.clone();
13689                let resolved_item_1 = resolved_item_1.clone();
13690                let unresolved_item_2 = unresolved_item_2.clone();
13691                let resolved_item_2 = resolved_item_2.clone();
13692                let resolve_requests_1 = resolve_requests_1.clone();
13693                let resolve_requests_2 = resolve_requests_2.clone();
13694                async move {
13695                    if unresolved_request == unresolved_item_1 {
13696                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13697                        Ok(resolved_item_1.clone())
13698                    } else if unresolved_request == unresolved_item_2 {
13699                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13700                        Ok(resolved_item_2.clone())
13701                    } else {
13702                        panic!("Unexpected completion item {unresolved_request:?}")
13703                    }
13704                }
13705            }
13706        })
13707        .detach();
13708
13709    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13710        let unresolved_item_1 = unresolved_item_1.clone();
13711        let unresolved_item_2 = unresolved_item_2.clone();
13712        async move {
13713            Ok(Some(lsp::CompletionResponse::Array(vec![
13714                unresolved_item_1,
13715                unresolved_item_2,
13716            ])))
13717        }
13718    })
13719    .next()
13720    .await;
13721
13722    cx.condition(|editor, _| editor.context_menu_visible())
13723        .await;
13724    cx.update_editor(|editor, _, _| {
13725        let context_menu = editor.context_menu.borrow_mut();
13726        let context_menu = context_menu
13727            .as_ref()
13728            .expect("Should have the context menu deployed");
13729        match context_menu {
13730            CodeContextMenu::Completions(completions_menu) => {
13731                let completions = completions_menu.completions.borrow_mut();
13732                assert_eq!(
13733                    completions
13734                        .iter()
13735                        .map(|completion| &completion.label.text)
13736                        .collect::<Vec<_>>(),
13737                    vec!["id", "other"]
13738                )
13739            }
13740            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13741        }
13742    });
13743    cx.run_until_parked();
13744
13745    cx.update_editor(|editor, window, cx| {
13746        editor.context_menu_next(&ContextMenuNext, window, cx);
13747    });
13748    cx.run_until_parked();
13749    cx.update_editor(|editor, window, cx| {
13750        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13751    });
13752    cx.run_until_parked();
13753    cx.update_editor(|editor, window, cx| {
13754        editor.context_menu_next(&ContextMenuNext, window, cx);
13755    });
13756    cx.run_until_parked();
13757    cx.update_editor(|editor, window, cx| {
13758        editor
13759            .compose_completion(&ComposeCompletion::default(), window, cx)
13760            .expect("No task returned")
13761    })
13762    .await
13763    .expect("Completion failed");
13764    cx.run_until_parked();
13765
13766    cx.update_editor(|editor, _, cx| {
13767        assert_eq!(
13768            resolve_requests_1.load(atomic::Ordering::Acquire),
13769            1,
13770            "Should always resolve once despite multiple selections"
13771        );
13772        assert_eq!(
13773            resolve_requests_2.load(atomic::Ordering::Acquire),
13774            1,
13775            "Should always resolve once after multiple selections and applying the completion"
13776        );
13777        assert_eq!(
13778            editor.text(cx),
13779            "fn main() { let a = ??.other; }",
13780            "Should use resolved data when applying the completion"
13781        );
13782    });
13783}
13784
13785#[gpui::test]
13786async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13787    init_test(cx, |_| {});
13788
13789    let item_0 = lsp::CompletionItem {
13790        label: "abs".into(),
13791        insert_text: Some("abs".into()),
13792        data: Some(json!({ "very": "special"})),
13793        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13794        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13795            lsp::InsertReplaceEdit {
13796                new_text: "abs".to_string(),
13797                insert: lsp::Range::default(),
13798                replace: lsp::Range::default(),
13799            },
13800        )),
13801        ..lsp::CompletionItem::default()
13802    };
13803    let items = iter::once(item_0.clone())
13804        .chain((11..51).map(|i| lsp::CompletionItem {
13805            label: format!("item_{}", i),
13806            insert_text: Some(format!("item_{}", i)),
13807            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13808            ..lsp::CompletionItem::default()
13809        }))
13810        .collect::<Vec<_>>();
13811
13812    let default_commit_characters = vec!["?".to_string()];
13813    let default_data = json!({ "default": "data"});
13814    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13815    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13816    let default_edit_range = lsp::Range {
13817        start: lsp::Position {
13818            line: 0,
13819            character: 5,
13820        },
13821        end: lsp::Position {
13822            line: 0,
13823            character: 5,
13824        },
13825    };
13826
13827    let mut cx = EditorLspTestContext::new_rust(
13828        lsp::ServerCapabilities {
13829            completion_provider: Some(lsp::CompletionOptions {
13830                trigger_characters: Some(vec![".".to_string()]),
13831                resolve_provider: Some(true),
13832                ..Default::default()
13833            }),
13834            ..Default::default()
13835        },
13836        cx,
13837    )
13838    .await;
13839
13840    cx.set_state("fn main() { let a = 2ˇ; }");
13841    cx.simulate_keystroke(".");
13842
13843    let completion_data = default_data.clone();
13844    let completion_characters = default_commit_characters.clone();
13845    let completion_items = items.clone();
13846    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13847        let default_data = completion_data.clone();
13848        let default_commit_characters = completion_characters.clone();
13849        let items = completion_items.clone();
13850        async move {
13851            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13852                items,
13853                item_defaults: Some(lsp::CompletionListItemDefaults {
13854                    data: Some(default_data.clone()),
13855                    commit_characters: Some(default_commit_characters.clone()),
13856                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13857                        default_edit_range,
13858                    )),
13859                    insert_text_format: Some(default_insert_text_format),
13860                    insert_text_mode: Some(default_insert_text_mode),
13861                }),
13862                ..lsp::CompletionList::default()
13863            })))
13864        }
13865    })
13866    .next()
13867    .await;
13868
13869    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13870    cx.lsp
13871        .server
13872        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13873            let closure_resolved_items = resolved_items.clone();
13874            move |item_to_resolve, _| {
13875                let closure_resolved_items = closure_resolved_items.clone();
13876                async move {
13877                    closure_resolved_items.lock().push(item_to_resolve.clone());
13878                    Ok(item_to_resolve)
13879                }
13880            }
13881        })
13882        .detach();
13883
13884    cx.condition(|editor, _| editor.context_menu_visible())
13885        .await;
13886    cx.run_until_parked();
13887    cx.update_editor(|editor, _, _| {
13888        let menu = editor.context_menu.borrow_mut();
13889        match menu.as_ref().expect("should have the completions menu") {
13890            CodeContextMenu::Completions(completions_menu) => {
13891                assert_eq!(
13892                    completions_menu
13893                        .entries
13894                        .borrow()
13895                        .iter()
13896                        .map(|mat| mat.string.clone())
13897                        .collect::<Vec<String>>(),
13898                    items
13899                        .iter()
13900                        .map(|completion| completion.label.clone())
13901                        .collect::<Vec<String>>()
13902                );
13903            }
13904            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13905        }
13906    });
13907    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13908    // with 4 from the end.
13909    assert_eq!(
13910        *resolved_items.lock(),
13911        [&items[0..16], &items[items.len() - 4..items.len()]]
13912            .concat()
13913            .iter()
13914            .cloned()
13915            .map(|mut item| {
13916                if item.data.is_none() {
13917                    item.data = Some(default_data.clone());
13918                }
13919                item
13920            })
13921            .collect::<Vec<lsp::CompletionItem>>(),
13922        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13923    );
13924    resolved_items.lock().clear();
13925
13926    cx.update_editor(|editor, window, cx| {
13927        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13928    });
13929    cx.run_until_parked();
13930    // Completions that have already been resolved are skipped.
13931    assert_eq!(
13932        *resolved_items.lock(),
13933        items[items.len() - 16..items.len() - 4]
13934            .iter()
13935            .cloned()
13936            .map(|mut item| {
13937                if item.data.is_none() {
13938                    item.data = Some(default_data.clone());
13939                }
13940                item
13941            })
13942            .collect::<Vec<lsp::CompletionItem>>()
13943    );
13944    resolved_items.lock().clear();
13945}
13946
13947#[gpui::test]
13948async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13949    init_test(cx, |_| {});
13950
13951    let mut cx = EditorLspTestContext::new(
13952        Language::new(
13953            LanguageConfig {
13954                matcher: LanguageMatcher {
13955                    path_suffixes: vec!["jsx".into()],
13956                    ..Default::default()
13957                },
13958                overrides: [(
13959                    "element".into(),
13960                    LanguageConfigOverride {
13961                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13962                        ..Default::default()
13963                    },
13964                )]
13965                .into_iter()
13966                .collect(),
13967                ..Default::default()
13968            },
13969            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13970        )
13971        .with_override_query("(jsx_self_closing_element) @element")
13972        .unwrap(),
13973        lsp::ServerCapabilities {
13974            completion_provider: Some(lsp::CompletionOptions {
13975                trigger_characters: Some(vec![":".to_string()]),
13976                ..Default::default()
13977            }),
13978            ..Default::default()
13979        },
13980        cx,
13981    )
13982    .await;
13983
13984    cx.lsp
13985        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13986            Ok(Some(lsp::CompletionResponse::Array(vec![
13987                lsp::CompletionItem {
13988                    label: "bg-blue".into(),
13989                    ..Default::default()
13990                },
13991                lsp::CompletionItem {
13992                    label: "bg-red".into(),
13993                    ..Default::default()
13994                },
13995                lsp::CompletionItem {
13996                    label: "bg-yellow".into(),
13997                    ..Default::default()
13998                },
13999            ])))
14000        });
14001
14002    cx.set_state(r#"<p class="bgˇ" />"#);
14003
14004    // Trigger completion when typing a dash, because the dash is an extra
14005    // word character in the 'element' scope, which contains the cursor.
14006    cx.simulate_keystroke("-");
14007    cx.executor().run_until_parked();
14008    cx.update_editor(|editor, _, _| {
14009        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14010        {
14011            assert_eq!(
14012                completion_menu_entries(&menu),
14013                &["bg-red", "bg-blue", "bg-yellow"]
14014            );
14015        } else {
14016            panic!("expected completion menu to be open");
14017        }
14018    });
14019
14020    cx.simulate_keystroke("l");
14021    cx.executor().run_until_parked();
14022    cx.update_editor(|editor, _, _| {
14023        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14024        {
14025            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14026        } else {
14027            panic!("expected completion menu to be open");
14028        }
14029    });
14030
14031    // When filtering completions, consider the character after the '-' to
14032    // be the start of a subword.
14033    cx.set_state(r#"<p class="yelˇ" />"#);
14034    cx.simulate_keystroke("l");
14035    cx.executor().run_until_parked();
14036    cx.update_editor(|editor, _, _| {
14037        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14038        {
14039            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14040        } else {
14041            panic!("expected completion menu to be open");
14042        }
14043    });
14044}
14045
14046fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14047    let entries = menu.entries.borrow();
14048    entries.iter().map(|mat| mat.string.clone()).collect()
14049}
14050
14051#[gpui::test]
14052async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14053    init_test(cx, |settings| {
14054        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14055            FormatterList(vec![Formatter::Prettier].into()),
14056        ))
14057    });
14058
14059    let fs = FakeFs::new(cx.executor());
14060    fs.insert_file(path!("/file.ts"), Default::default()).await;
14061
14062    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14063    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14064
14065    language_registry.add(Arc::new(Language::new(
14066        LanguageConfig {
14067            name: "TypeScript".into(),
14068            matcher: LanguageMatcher {
14069                path_suffixes: vec!["ts".to_string()],
14070                ..Default::default()
14071            },
14072            ..Default::default()
14073        },
14074        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14075    )));
14076    update_test_language_settings(cx, |settings| {
14077        settings.defaults.prettier = Some(PrettierSettings {
14078            allowed: true,
14079            ..PrettierSettings::default()
14080        });
14081    });
14082
14083    let test_plugin = "test_plugin";
14084    let _ = language_registry.register_fake_lsp(
14085        "TypeScript",
14086        FakeLspAdapter {
14087            prettier_plugins: vec![test_plugin],
14088            ..Default::default()
14089        },
14090    );
14091
14092    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14093    let buffer = project
14094        .update(cx, |project, cx| {
14095            project.open_local_buffer(path!("/file.ts"), cx)
14096        })
14097        .await
14098        .unwrap();
14099
14100    let buffer_text = "one\ntwo\nthree\n";
14101    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14102    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14103    editor.update_in(cx, |editor, window, cx| {
14104        editor.set_text(buffer_text, window, cx)
14105    });
14106
14107    editor
14108        .update_in(cx, |editor, window, cx| {
14109            editor.perform_format(
14110                project.clone(),
14111                FormatTrigger::Manual,
14112                FormatTarget::Buffers,
14113                window,
14114                cx,
14115            )
14116        })
14117        .unwrap()
14118        .await;
14119    assert_eq!(
14120        editor.update(cx, |editor, cx| editor.text(cx)),
14121        buffer_text.to_string() + prettier_format_suffix,
14122        "Test prettier formatting was not applied to the original buffer text",
14123    );
14124
14125    update_test_language_settings(cx, |settings| {
14126        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14127    });
14128    let format = editor.update_in(cx, |editor, window, cx| {
14129        editor.perform_format(
14130            project.clone(),
14131            FormatTrigger::Manual,
14132            FormatTarget::Buffers,
14133            window,
14134            cx,
14135        )
14136    });
14137    format.await.unwrap();
14138    assert_eq!(
14139        editor.update(cx, |editor, cx| editor.text(cx)),
14140        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14141        "Autoformatting (via test prettier) was not applied to the original buffer text",
14142    );
14143}
14144
14145#[gpui::test]
14146async fn test_addition_reverts(cx: &mut TestAppContext) {
14147    init_test(cx, |_| {});
14148    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14149    let base_text = indoc! {r#"
14150        struct Row;
14151        struct Row1;
14152        struct Row2;
14153
14154        struct Row4;
14155        struct Row5;
14156        struct Row6;
14157
14158        struct Row8;
14159        struct Row9;
14160        struct Row10;"#};
14161
14162    // When addition hunks are not adjacent to carets, no hunk revert is performed
14163    assert_hunk_revert(
14164        indoc! {r#"struct Row;
14165                   struct Row1;
14166                   struct Row1.1;
14167                   struct Row1.2;
14168                   struct Row2;ˇ
14169
14170                   struct Row4;
14171                   struct Row5;
14172                   struct Row6;
14173
14174                   struct Row8;
14175                   ˇstruct Row9;
14176                   struct Row9.1;
14177                   struct Row9.2;
14178                   struct Row9.3;
14179                   struct Row10;"#},
14180        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14181        indoc! {r#"struct Row;
14182                   struct Row1;
14183                   struct Row1.1;
14184                   struct Row1.2;
14185                   struct Row2;ˇ
14186
14187                   struct Row4;
14188                   struct Row5;
14189                   struct Row6;
14190
14191                   struct Row8;
14192                   ˇstruct Row9;
14193                   struct Row9.1;
14194                   struct Row9.2;
14195                   struct Row9.3;
14196                   struct Row10;"#},
14197        base_text,
14198        &mut cx,
14199    );
14200    // Same for selections
14201    assert_hunk_revert(
14202        indoc! {r#"struct Row;
14203                   struct Row1;
14204                   struct Row2;
14205                   struct Row2.1;
14206                   struct Row2.2;
14207                   «ˇ
14208                   struct Row4;
14209                   struct» Row5;
14210                   «struct Row6;
14211                   ˇ»
14212                   struct Row9.1;
14213                   struct Row9.2;
14214                   struct Row9.3;
14215                   struct Row8;
14216                   struct Row9;
14217                   struct Row10;"#},
14218        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14219        indoc! {r#"struct Row;
14220                   struct Row1;
14221                   struct Row2;
14222                   struct Row2.1;
14223                   struct Row2.2;
14224                   «ˇ
14225                   struct Row4;
14226                   struct» Row5;
14227                   «struct Row6;
14228                   ˇ»
14229                   struct Row9.1;
14230                   struct Row9.2;
14231                   struct Row9.3;
14232                   struct Row8;
14233                   struct Row9;
14234                   struct Row10;"#},
14235        base_text,
14236        &mut cx,
14237    );
14238
14239    // When carets and selections intersect the addition hunks, those are reverted.
14240    // Adjacent carets got merged.
14241    assert_hunk_revert(
14242        indoc! {r#"struct Row;
14243                   ˇ// something on the top
14244                   struct Row1;
14245                   struct Row2;
14246                   struct Roˇw3.1;
14247                   struct Row2.2;
14248                   struct Row2.3;ˇ
14249
14250                   struct Row4;
14251                   struct ˇRow5.1;
14252                   struct Row5.2;
14253                   struct «Rowˇ»5.3;
14254                   struct Row5;
14255                   struct Row6;
14256                   ˇ
14257                   struct Row9.1;
14258                   struct «Rowˇ»9.2;
14259                   struct «ˇRow»9.3;
14260                   struct Row8;
14261                   struct Row9;
14262                   «ˇ// something on bottom»
14263                   struct Row10;"#},
14264        vec![
14265            DiffHunkStatusKind::Added,
14266            DiffHunkStatusKind::Added,
14267            DiffHunkStatusKind::Added,
14268            DiffHunkStatusKind::Added,
14269            DiffHunkStatusKind::Added,
14270        ],
14271        indoc! {r#"struct Row;
14272                   ˇstruct Row1;
14273                   struct Row2;
14274                   ˇ
14275                   struct Row4;
14276                   ˇstruct Row5;
14277                   struct Row6;
14278                   ˇ
14279                   ˇstruct Row8;
14280                   struct Row9;
14281                   ˇstruct Row10;"#},
14282        base_text,
14283        &mut cx,
14284    );
14285}
14286
14287#[gpui::test]
14288async fn test_modification_reverts(cx: &mut TestAppContext) {
14289    init_test(cx, |_| {});
14290    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14291    let base_text = indoc! {r#"
14292        struct Row;
14293        struct Row1;
14294        struct Row2;
14295
14296        struct Row4;
14297        struct Row5;
14298        struct Row6;
14299
14300        struct Row8;
14301        struct Row9;
14302        struct Row10;"#};
14303
14304    // Modification hunks behave the same as the addition ones.
14305    assert_hunk_revert(
14306        indoc! {r#"struct Row;
14307                   struct Row1;
14308                   struct Row33;
14309                   ˇ
14310                   struct Row4;
14311                   struct Row5;
14312                   struct Row6;
14313                   ˇ
14314                   struct Row99;
14315                   struct Row9;
14316                   struct Row10;"#},
14317        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14318        indoc! {r#"struct Row;
14319                   struct Row1;
14320                   struct Row33;
14321                   ˇ
14322                   struct Row4;
14323                   struct Row5;
14324                   struct Row6;
14325                   ˇ
14326                   struct Row99;
14327                   struct Row9;
14328                   struct Row10;"#},
14329        base_text,
14330        &mut cx,
14331    );
14332    assert_hunk_revert(
14333        indoc! {r#"struct Row;
14334                   struct Row1;
14335                   struct Row33;
14336                   «ˇ
14337                   struct Row4;
14338                   struct» Row5;
14339                   «struct Row6;
14340                   ˇ»
14341                   struct Row99;
14342                   struct Row9;
14343                   struct Row10;"#},
14344        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14345        indoc! {r#"struct Row;
14346                   struct Row1;
14347                   struct Row33;
14348                   «ˇ
14349                   struct Row4;
14350                   struct» Row5;
14351                   «struct Row6;
14352                   ˇ»
14353                   struct Row99;
14354                   struct Row9;
14355                   struct Row10;"#},
14356        base_text,
14357        &mut cx,
14358    );
14359
14360    assert_hunk_revert(
14361        indoc! {r#"ˇstruct Row1.1;
14362                   struct Row1;
14363                   «ˇstr»uct Row22;
14364
14365                   struct ˇRow44;
14366                   struct Row5;
14367                   struct «Rˇ»ow66;ˇ
14368
14369                   «struˇ»ct Row88;
14370                   struct Row9;
14371                   struct Row1011;ˇ"#},
14372        vec![
14373            DiffHunkStatusKind::Modified,
14374            DiffHunkStatusKind::Modified,
14375            DiffHunkStatusKind::Modified,
14376            DiffHunkStatusKind::Modified,
14377            DiffHunkStatusKind::Modified,
14378            DiffHunkStatusKind::Modified,
14379        ],
14380        indoc! {r#"struct Row;
14381                   ˇstruct Row1;
14382                   struct Row2;
14383                   ˇ
14384                   struct Row4;
14385                   ˇstruct Row5;
14386                   struct Row6;
14387                   ˇ
14388                   struct Row8;
14389                   ˇstruct Row9;
14390                   struct Row10;ˇ"#},
14391        base_text,
14392        &mut cx,
14393    );
14394}
14395
14396#[gpui::test]
14397async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14398    init_test(cx, |_| {});
14399    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14400    let base_text = indoc! {r#"
14401        one
14402
14403        two
14404        three
14405        "#};
14406
14407    cx.set_head_text(base_text);
14408    cx.set_state("\nˇ\n");
14409    cx.executor().run_until_parked();
14410    cx.update_editor(|editor, _window, cx| {
14411        editor.expand_selected_diff_hunks(cx);
14412    });
14413    cx.executor().run_until_parked();
14414    cx.update_editor(|editor, window, cx| {
14415        editor.backspace(&Default::default(), window, cx);
14416    });
14417    cx.run_until_parked();
14418    cx.assert_state_with_diff(
14419        indoc! {r#"
14420
14421        - two
14422        - threeˇ
14423        +
14424        "#}
14425        .to_string(),
14426    );
14427}
14428
14429#[gpui::test]
14430async fn test_deletion_reverts(cx: &mut TestAppContext) {
14431    init_test(cx, |_| {});
14432    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14433    let base_text = indoc! {r#"struct Row;
14434struct Row1;
14435struct Row2;
14436
14437struct Row4;
14438struct Row5;
14439struct Row6;
14440
14441struct Row8;
14442struct Row9;
14443struct Row10;"#};
14444
14445    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14446    assert_hunk_revert(
14447        indoc! {r#"struct Row;
14448                   struct Row2;
14449
14450                   ˇstruct Row4;
14451                   struct Row5;
14452                   struct Row6;
14453                   ˇ
14454                   struct Row8;
14455                   struct Row10;"#},
14456        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14457        indoc! {r#"struct Row;
14458                   struct Row2;
14459
14460                   ˇstruct Row4;
14461                   struct Row5;
14462                   struct Row6;
14463                   ˇ
14464                   struct Row8;
14465                   struct Row10;"#},
14466        base_text,
14467        &mut cx,
14468    );
14469    assert_hunk_revert(
14470        indoc! {r#"struct Row;
14471                   struct Row2;
14472
14473                   «ˇstruct Row4;
14474                   struct» Row5;
14475                   «struct Row6;
14476                   ˇ»
14477                   struct Row8;
14478                   struct Row10;"#},
14479        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14480        indoc! {r#"struct Row;
14481                   struct Row2;
14482
14483                   «ˇstruct Row4;
14484                   struct» Row5;
14485                   «struct Row6;
14486                   ˇ»
14487                   struct Row8;
14488                   struct Row10;"#},
14489        base_text,
14490        &mut cx,
14491    );
14492
14493    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14494    assert_hunk_revert(
14495        indoc! {r#"struct Row;
14496                   ˇstruct Row2;
14497
14498                   struct Row4;
14499                   struct Row5;
14500                   struct Row6;
14501
14502                   struct Row8;ˇ
14503                   struct Row10;"#},
14504        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14505        indoc! {r#"struct Row;
14506                   struct Row1;
14507                   ˇstruct Row2;
14508
14509                   struct Row4;
14510                   struct Row5;
14511                   struct Row6;
14512
14513                   struct Row8;ˇ
14514                   struct Row9;
14515                   struct Row10;"#},
14516        base_text,
14517        &mut cx,
14518    );
14519    assert_hunk_revert(
14520        indoc! {r#"struct Row;
14521                   struct Row2«ˇ;
14522                   struct Row4;
14523                   struct» Row5;
14524                   «struct Row6;
14525
14526                   struct Row8;ˇ»
14527                   struct Row10;"#},
14528        vec![
14529            DiffHunkStatusKind::Deleted,
14530            DiffHunkStatusKind::Deleted,
14531            DiffHunkStatusKind::Deleted,
14532        ],
14533        indoc! {r#"struct Row;
14534                   struct Row1;
14535                   struct Row2«ˇ;
14536
14537                   struct Row4;
14538                   struct» Row5;
14539                   «struct Row6;
14540
14541                   struct Row8;ˇ»
14542                   struct Row9;
14543                   struct Row10;"#},
14544        base_text,
14545        &mut cx,
14546    );
14547}
14548
14549#[gpui::test]
14550async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14551    init_test(cx, |_| {});
14552
14553    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14554    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14555    let base_text_3 =
14556        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14557
14558    let text_1 = edit_first_char_of_every_line(base_text_1);
14559    let text_2 = edit_first_char_of_every_line(base_text_2);
14560    let text_3 = edit_first_char_of_every_line(base_text_3);
14561
14562    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14563    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14564    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14565
14566    let multibuffer = cx.new(|cx| {
14567        let mut multibuffer = MultiBuffer::new(ReadWrite);
14568        multibuffer.push_excerpts(
14569            buffer_1.clone(),
14570            [
14571                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14572                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14573                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14574            ],
14575            cx,
14576        );
14577        multibuffer.push_excerpts(
14578            buffer_2.clone(),
14579            [
14580                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14581                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14582                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14583            ],
14584            cx,
14585        );
14586        multibuffer.push_excerpts(
14587            buffer_3.clone(),
14588            [
14589                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14590                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14591                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14592            ],
14593            cx,
14594        );
14595        multibuffer
14596    });
14597
14598    let fs = FakeFs::new(cx.executor());
14599    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14600    let (editor, cx) = cx
14601        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14602    editor.update_in(cx, |editor, _window, cx| {
14603        for (buffer, diff_base) in [
14604            (buffer_1.clone(), base_text_1),
14605            (buffer_2.clone(), base_text_2),
14606            (buffer_3.clone(), base_text_3),
14607        ] {
14608            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14609            editor
14610                .buffer
14611                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14612        }
14613    });
14614    cx.executor().run_until_parked();
14615
14616    editor.update_in(cx, |editor, window, cx| {
14617        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}");
14618        editor.select_all(&SelectAll, window, cx);
14619        editor.git_restore(&Default::default(), window, cx);
14620    });
14621    cx.executor().run_until_parked();
14622
14623    // When all ranges are selected, all buffer hunks are reverted.
14624    editor.update(cx, |editor, cx| {
14625        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");
14626    });
14627    buffer_1.update(cx, |buffer, _| {
14628        assert_eq!(buffer.text(), base_text_1);
14629    });
14630    buffer_2.update(cx, |buffer, _| {
14631        assert_eq!(buffer.text(), base_text_2);
14632    });
14633    buffer_3.update(cx, |buffer, _| {
14634        assert_eq!(buffer.text(), base_text_3);
14635    });
14636
14637    editor.update_in(cx, |editor, window, cx| {
14638        editor.undo(&Default::default(), window, cx);
14639    });
14640
14641    editor.update_in(cx, |editor, window, cx| {
14642        editor.change_selections(None, window, cx, |s| {
14643            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14644        });
14645        editor.git_restore(&Default::default(), window, cx);
14646    });
14647
14648    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14649    // but not affect buffer_2 and its related excerpts.
14650    editor.update(cx, |editor, cx| {
14651        assert_eq!(
14652            editor.text(cx),
14653            "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}"
14654        );
14655    });
14656    buffer_1.update(cx, |buffer, _| {
14657        assert_eq!(buffer.text(), base_text_1);
14658    });
14659    buffer_2.update(cx, |buffer, _| {
14660        assert_eq!(
14661            buffer.text(),
14662            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14663        );
14664    });
14665    buffer_3.update(cx, |buffer, _| {
14666        assert_eq!(
14667            buffer.text(),
14668            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14669        );
14670    });
14671
14672    fn edit_first_char_of_every_line(text: &str) -> String {
14673        text.split('\n')
14674            .map(|line| format!("X{}", &line[1..]))
14675            .collect::<Vec<_>>()
14676            .join("\n")
14677    }
14678}
14679
14680#[gpui::test]
14681async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14682    init_test(cx, |_| {});
14683
14684    let cols = 4;
14685    let rows = 10;
14686    let sample_text_1 = sample_text(rows, cols, 'a');
14687    assert_eq!(
14688        sample_text_1,
14689        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14690    );
14691    let sample_text_2 = sample_text(rows, cols, 'l');
14692    assert_eq!(
14693        sample_text_2,
14694        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14695    );
14696    let sample_text_3 = sample_text(rows, cols, 'v');
14697    assert_eq!(
14698        sample_text_3,
14699        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14700    );
14701
14702    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14703    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14704    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14705
14706    let multi_buffer = cx.new(|cx| {
14707        let mut multibuffer = MultiBuffer::new(ReadWrite);
14708        multibuffer.push_excerpts(
14709            buffer_1.clone(),
14710            [
14711                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14712                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14713                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14714            ],
14715            cx,
14716        );
14717        multibuffer.push_excerpts(
14718            buffer_2.clone(),
14719            [
14720                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14721                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14722                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14723            ],
14724            cx,
14725        );
14726        multibuffer.push_excerpts(
14727            buffer_3.clone(),
14728            [
14729                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14730                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14731                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14732            ],
14733            cx,
14734        );
14735        multibuffer
14736    });
14737
14738    let fs = FakeFs::new(cx.executor());
14739    fs.insert_tree(
14740        "/a",
14741        json!({
14742            "main.rs": sample_text_1,
14743            "other.rs": sample_text_2,
14744            "lib.rs": sample_text_3,
14745        }),
14746    )
14747    .await;
14748    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14749    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14750    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14751    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14752        Editor::new(
14753            EditorMode::full(),
14754            multi_buffer,
14755            Some(project.clone()),
14756            window,
14757            cx,
14758        )
14759    });
14760    let multibuffer_item_id = workspace
14761        .update(cx, |workspace, window, cx| {
14762            assert!(
14763                workspace.active_item(cx).is_none(),
14764                "active item should be None before the first item is added"
14765            );
14766            workspace.add_item_to_active_pane(
14767                Box::new(multi_buffer_editor.clone()),
14768                None,
14769                true,
14770                window,
14771                cx,
14772            );
14773            let active_item = workspace
14774                .active_item(cx)
14775                .expect("should have an active item after adding the multi buffer");
14776            assert!(
14777                !active_item.is_singleton(cx),
14778                "A multi buffer was expected to active after adding"
14779            );
14780            active_item.item_id()
14781        })
14782        .unwrap();
14783    cx.executor().run_until_parked();
14784
14785    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14786        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14787            s.select_ranges(Some(1..2))
14788        });
14789        editor.open_excerpts(&OpenExcerpts, window, cx);
14790    });
14791    cx.executor().run_until_parked();
14792    let first_item_id = workspace
14793        .update(cx, |workspace, window, cx| {
14794            let active_item = workspace
14795                .active_item(cx)
14796                .expect("should have an active item after navigating into the 1st buffer");
14797            let first_item_id = active_item.item_id();
14798            assert_ne!(
14799                first_item_id, multibuffer_item_id,
14800                "Should navigate into the 1st buffer and activate it"
14801            );
14802            assert!(
14803                active_item.is_singleton(cx),
14804                "New active item should be a singleton buffer"
14805            );
14806            assert_eq!(
14807                active_item
14808                    .act_as::<Editor>(cx)
14809                    .expect("should have navigated into an editor for the 1st buffer")
14810                    .read(cx)
14811                    .text(cx),
14812                sample_text_1
14813            );
14814
14815            workspace
14816                .go_back(workspace.active_pane().downgrade(), window, cx)
14817                .detach_and_log_err(cx);
14818
14819            first_item_id
14820        })
14821        .unwrap();
14822    cx.executor().run_until_parked();
14823    workspace
14824        .update(cx, |workspace, _, cx| {
14825            let active_item = workspace
14826                .active_item(cx)
14827                .expect("should have an active item after navigating back");
14828            assert_eq!(
14829                active_item.item_id(),
14830                multibuffer_item_id,
14831                "Should navigate back to the multi buffer"
14832            );
14833            assert!(!active_item.is_singleton(cx));
14834        })
14835        .unwrap();
14836
14837    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14838        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14839            s.select_ranges(Some(39..40))
14840        });
14841        editor.open_excerpts(&OpenExcerpts, window, cx);
14842    });
14843    cx.executor().run_until_parked();
14844    let second_item_id = workspace
14845        .update(cx, |workspace, window, cx| {
14846            let active_item = workspace
14847                .active_item(cx)
14848                .expect("should have an active item after navigating into the 2nd buffer");
14849            let second_item_id = active_item.item_id();
14850            assert_ne!(
14851                second_item_id, multibuffer_item_id,
14852                "Should navigate away from the multibuffer"
14853            );
14854            assert_ne!(
14855                second_item_id, first_item_id,
14856                "Should navigate into the 2nd buffer and activate it"
14857            );
14858            assert!(
14859                active_item.is_singleton(cx),
14860                "New active item should be a singleton buffer"
14861            );
14862            assert_eq!(
14863                active_item
14864                    .act_as::<Editor>(cx)
14865                    .expect("should have navigated into an editor")
14866                    .read(cx)
14867                    .text(cx),
14868                sample_text_2
14869            );
14870
14871            workspace
14872                .go_back(workspace.active_pane().downgrade(), window, cx)
14873                .detach_and_log_err(cx);
14874
14875            second_item_id
14876        })
14877        .unwrap();
14878    cx.executor().run_until_parked();
14879    workspace
14880        .update(cx, |workspace, _, cx| {
14881            let active_item = workspace
14882                .active_item(cx)
14883                .expect("should have an active item after navigating back from the 2nd buffer");
14884            assert_eq!(
14885                active_item.item_id(),
14886                multibuffer_item_id,
14887                "Should navigate back from the 2nd buffer to the multi buffer"
14888            );
14889            assert!(!active_item.is_singleton(cx));
14890        })
14891        .unwrap();
14892
14893    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14894        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14895            s.select_ranges(Some(70..70))
14896        });
14897        editor.open_excerpts(&OpenExcerpts, window, cx);
14898    });
14899    cx.executor().run_until_parked();
14900    workspace
14901        .update(cx, |workspace, window, cx| {
14902            let active_item = workspace
14903                .active_item(cx)
14904                .expect("should have an active item after navigating into the 3rd buffer");
14905            let third_item_id = active_item.item_id();
14906            assert_ne!(
14907                third_item_id, multibuffer_item_id,
14908                "Should navigate into the 3rd buffer and activate it"
14909            );
14910            assert_ne!(third_item_id, first_item_id);
14911            assert_ne!(third_item_id, second_item_id);
14912            assert!(
14913                active_item.is_singleton(cx),
14914                "New active item should be a singleton buffer"
14915            );
14916            assert_eq!(
14917                active_item
14918                    .act_as::<Editor>(cx)
14919                    .expect("should have navigated into an editor")
14920                    .read(cx)
14921                    .text(cx),
14922                sample_text_3
14923            );
14924
14925            workspace
14926                .go_back(workspace.active_pane().downgrade(), window, cx)
14927                .detach_and_log_err(cx);
14928        })
14929        .unwrap();
14930    cx.executor().run_until_parked();
14931    workspace
14932        .update(cx, |workspace, _, cx| {
14933            let active_item = workspace
14934                .active_item(cx)
14935                .expect("should have an active item after navigating back from the 3rd buffer");
14936            assert_eq!(
14937                active_item.item_id(),
14938                multibuffer_item_id,
14939                "Should navigate back from the 3rd buffer to the multi buffer"
14940            );
14941            assert!(!active_item.is_singleton(cx));
14942        })
14943        .unwrap();
14944}
14945
14946#[gpui::test]
14947async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14948    init_test(cx, |_| {});
14949
14950    let mut cx = EditorTestContext::new(cx).await;
14951
14952    let diff_base = r#"
14953        use some::mod;
14954
14955        const A: u32 = 42;
14956
14957        fn main() {
14958            println!("hello");
14959
14960            println!("world");
14961        }
14962        "#
14963    .unindent();
14964
14965    cx.set_state(
14966        &r#"
14967        use some::modified;
14968
14969        ˇ
14970        fn main() {
14971            println!("hello there");
14972
14973            println!("around the");
14974            println!("world");
14975        }
14976        "#
14977        .unindent(),
14978    );
14979
14980    cx.set_head_text(&diff_base);
14981    executor.run_until_parked();
14982
14983    cx.update_editor(|editor, window, cx| {
14984        editor.go_to_next_hunk(&GoToHunk, window, cx);
14985        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14986    });
14987    executor.run_until_parked();
14988    cx.assert_state_with_diff(
14989        r#"
14990          use some::modified;
14991
14992
14993          fn main() {
14994        -     println!("hello");
14995        + ˇ    println!("hello there");
14996
14997              println!("around the");
14998              println!("world");
14999          }
15000        "#
15001        .unindent(),
15002    );
15003
15004    cx.update_editor(|editor, window, cx| {
15005        for _ in 0..2 {
15006            editor.go_to_next_hunk(&GoToHunk, window, cx);
15007            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15008        }
15009    });
15010    executor.run_until_parked();
15011    cx.assert_state_with_diff(
15012        r#"
15013        - use some::mod;
15014        + ˇuse some::modified;
15015
15016
15017          fn main() {
15018        -     println!("hello");
15019        +     println!("hello there");
15020
15021        +     println!("around the");
15022              println!("world");
15023          }
15024        "#
15025        .unindent(),
15026    );
15027
15028    cx.update_editor(|editor, window, cx| {
15029        editor.go_to_next_hunk(&GoToHunk, window, cx);
15030        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15031    });
15032    executor.run_until_parked();
15033    cx.assert_state_with_diff(
15034        r#"
15035        - use some::mod;
15036        + use some::modified;
15037
15038        - const A: u32 = 42;
15039          ˇ
15040          fn main() {
15041        -     println!("hello");
15042        +     println!("hello there");
15043
15044        +     println!("around the");
15045              println!("world");
15046          }
15047        "#
15048        .unindent(),
15049    );
15050
15051    cx.update_editor(|editor, window, cx| {
15052        editor.cancel(&Cancel, window, cx);
15053    });
15054
15055    cx.assert_state_with_diff(
15056        r#"
15057          use some::modified;
15058
15059          ˇ
15060          fn main() {
15061              println!("hello there");
15062
15063              println!("around the");
15064              println!("world");
15065          }
15066        "#
15067        .unindent(),
15068    );
15069}
15070
15071#[gpui::test]
15072async fn test_diff_base_change_with_expanded_diff_hunks(
15073    executor: BackgroundExecutor,
15074    cx: &mut TestAppContext,
15075) {
15076    init_test(cx, |_| {});
15077
15078    let mut cx = EditorTestContext::new(cx).await;
15079
15080    let diff_base = r#"
15081        use some::mod1;
15082        use some::mod2;
15083
15084        const A: u32 = 42;
15085        const B: u32 = 42;
15086        const C: u32 = 42;
15087
15088        fn main() {
15089            println!("hello");
15090
15091            println!("world");
15092        }
15093        "#
15094    .unindent();
15095
15096    cx.set_state(
15097        &r#"
15098        use some::mod2;
15099
15100        const A: u32 = 42;
15101        const C: u32 = 42;
15102
15103        fn main(ˇ) {
15104            //println!("hello");
15105
15106            println!("world");
15107            //
15108            //
15109        }
15110        "#
15111        .unindent(),
15112    );
15113
15114    cx.set_head_text(&diff_base);
15115    executor.run_until_parked();
15116
15117    cx.update_editor(|editor, window, cx| {
15118        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15119    });
15120    executor.run_until_parked();
15121    cx.assert_state_with_diff(
15122        r#"
15123        - use some::mod1;
15124          use some::mod2;
15125
15126          const A: u32 = 42;
15127        - const B: u32 = 42;
15128          const C: u32 = 42;
15129
15130          fn main(ˇ) {
15131        -     println!("hello");
15132        +     //println!("hello");
15133
15134              println!("world");
15135        +     //
15136        +     //
15137          }
15138        "#
15139        .unindent(),
15140    );
15141
15142    cx.set_head_text("new diff base!");
15143    executor.run_until_parked();
15144    cx.assert_state_with_diff(
15145        r#"
15146        - new diff base!
15147        + use some::mod2;
15148        +
15149        + const A: u32 = 42;
15150        + const C: u32 = 42;
15151        +
15152        + fn main(ˇ) {
15153        +     //println!("hello");
15154        +
15155        +     println!("world");
15156        +     //
15157        +     //
15158        + }
15159        "#
15160        .unindent(),
15161    );
15162}
15163
15164#[gpui::test]
15165async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15166    init_test(cx, |_| {});
15167
15168    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15169    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15170    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15171    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15172    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15173    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15174
15175    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15176    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15177    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15178
15179    let multi_buffer = cx.new(|cx| {
15180        let mut multibuffer = MultiBuffer::new(ReadWrite);
15181        multibuffer.push_excerpts(
15182            buffer_1.clone(),
15183            [
15184                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15185                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15186                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15187            ],
15188            cx,
15189        );
15190        multibuffer.push_excerpts(
15191            buffer_2.clone(),
15192            [
15193                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15194                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15195                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15196            ],
15197            cx,
15198        );
15199        multibuffer.push_excerpts(
15200            buffer_3.clone(),
15201            [
15202                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15203                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15204                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15205            ],
15206            cx,
15207        );
15208        multibuffer
15209    });
15210
15211    let editor =
15212        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15213    editor
15214        .update(cx, |editor, _window, cx| {
15215            for (buffer, diff_base) in [
15216                (buffer_1.clone(), file_1_old),
15217                (buffer_2.clone(), file_2_old),
15218                (buffer_3.clone(), file_3_old),
15219            ] {
15220                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15221                editor
15222                    .buffer
15223                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15224            }
15225        })
15226        .unwrap();
15227
15228    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15229    cx.run_until_parked();
15230
15231    cx.assert_editor_state(
15232        &"
15233            ˇaaa
15234            ccc
15235            ddd
15236
15237            ggg
15238            hhh
15239
15240
15241            lll
15242            mmm
15243            NNN
15244
15245            qqq
15246            rrr
15247
15248            uuu
15249            111
15250            222
15251            333
15252
15253            666
15254            777
15255
15256            000
15257            !!!"
15258        .unindent(),
15259    );
15260
15261    cx.update_editor(|editor, window, cx| {
15262        editor.select_all(&SelectAll, window, cx);
15263        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15264    });
15265    cx.executor().run_until_parked();
15266
15267    cx.assert_state_with_diff(
15268        "
15269            «aaa
15270          - bbb
15271            ccc
15272            ddd
15273
15274            ggg
15275            hhh
15276
15277
15278            lll
15279            mmm
15280          - nnn
15281          + NNN
15282
15283            qqq
15284            rrr
15285
15286            uuu
15287            111
15288            222
15289            333
15290
15291          + 666
15292            777
15293
15294            000
15295            !!!ˇ»"
15296            .unindent(),
15297    );
15298}
15299
15300#[gpui::test]
15301async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15302    init_test(cx, |_| {});
15303
15304    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15305    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15306
15307    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15308    let multi_buffer = cx.new(|cx| {
15309        let mut multibuffer = MultiBuffer::new(ReadWrite);
15310        multibuffer.push_excerpts(
15311            buffer.clone(),
15312            [
15313                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15314                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15315                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15316            ],
15317            cx,
15318        );
15319        multibuffer
15320    });
15321
15322    let editor =
15323        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15324    editor
15325        .update(cx, |editor, _window, cx| {
15326            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15327            editor
15328                .buffer
15329                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15330        })
15331        .unwrap();
15332
15333    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15334    cx.run_until_parked();
15335
15336    cx.update_editor(|editor, window, cx| {
15337        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15338    });
15339    cx.executor().run_until_parked();
15340
15341    // When the start of a hunk coincides with the start of its excerpt,
15342    // the hunk is expanded. When the start of a a hunk is earlier than
15343    // the start of its excerpt, the hunk is not expanded.
15344    cx.assert_state_with_diff(
15345        "
15346            ˇaaa
15347          - bbb
15348          + BBB
15349
15350          - ddd
15351          - eee
15352          + DDD
15353          + EEE
15354            fff
15355
15356            iii
15357        "
15358        .unindent(),
15359    );
15360}
15361
15362#[gpui::test]
15363async fn test_edits_around_expanded_insertion_hunks(
15364    executor: BackgroundExecutor,
15365    cx: &mut TestAppContext,
15366) {
15367    init_test(cx, |_| {});
15368
15369    let mut cx = EditorTestContext::new(cx).await;
15370
15371    let diff_base = r#"
15372        use some::mod1;
15373        use some::mod2;
15374
15375        const A: u32 = 42;
15376
15377        fn main() {
15378            println!("hello");
15379
15380            println!("world");
15381        }
15382        "#
15383    .unindent();
15384    executor.run_until_parked();
15385    cx.set_state(
15386        &r#"
15387        use some::mod1;
15388        use some::mod2;
15389
15390        const A: u32 = 42;
15391        const B: u32 = 42;
15392        const C: u32 = 42;
15393        ˇ
15394
15395        fn main() {
15396            println!("hello");
15397
15398            println!("world");
15399        }
15400        "#
15401        .unindent(),
15402    );
15403
15404    cx.set_head_text(&diff_base);
15405    executor.run_until_parked();
15406
15407    cx.update_editor(|editor, window, cx| {
15408        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15409    });
15410    executor.run_until_parked();
15411
15412    cx.assert_state_with_diff(
15413        r#"
15414        use some::mod1;
15415        use some::mod2;
15416
15417        const A: u32 = 42;
15418      + const B: u32 = 42;
15419      + const C: u32 = 42;
15420      + ˇ
15421
15422        fn main() {
15423            println!("hello");
15424
15425            println!("world");
15426        }
15427      "#
15428        .unindent(),
15429    );
15430
15431    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15432    executor.run_until_parked();
15433
15434    cx.assert_state_with_diff(
15435        r#"
15436        use some::mod1;
15437        use some::mod2;
15438
15439        const A: u32 = 42;
15440      + const B: u32 = 42;
15441      + const C: u32 = 42;
15442      + const D: u32 = 42;
15443      + ˇ
15444
15445        fn main() {
15446            println!("hello");
15447
15448            println!("world");
15449        }
15450      "#
15451        .unindent(),
15452    );
15453
15454    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15455    executor.run_until_parked();
15456
15457    cx.assert_state_with_diff(
15458        r#"
15459        use some::mod1;
15460        use some::mod2;
15461
15462        const A: u32 = 42;
15463      + const B: u32 = 42;
15464      + const C: u32 = 42;
15465      + const D: u32 = 42;
15466      + const E: u32 = 42;
15467      + ˇ
15468
15469        fn main() {
15470            println!("hello");
15471
15472            println!("world");
15473        }
15474      "#
15475        .unindent(),
15476    );
15477
15478    cx.update_editor(|editor, window, cx| {
15479        editor.delete_line(&DeleteLine, window, cx);
15480    });
15481    executor.run_until_parked();
15482
15483    cx.assert_state_with_diff(
15484        r#"
15485        use some::mod1;
15486        use some::mod2;
15487
15488        const A: u32 = 42;
15489      + const B: u32 = 42;
15490      + const C: u32 = 42;
15491      + const D: u32 = 42;
15492      + const E: u32 = 42;
15493        ˇ
15494        fn main() {
15495            println!("hello");
15496
15497            println!("world");
15498        }
15499      "#
15500        .unindent(),
15501    );
15502
15503    cx.update_editor(|editor, window, cx| {
15504        editor.move_up(&MoveUp, window, cx);
15505        editor.delete_line(&DeleteLine, window, cx);
15506        editor.move_up(&MoveUp, window, cx);
15507        editor.delete_line(&DeleteLine, window, cx);
15508        editor.move_up(&MoveUp, window, cx);
15509        editor.delete_line(&DeleteLine, window, cx);
15510    });
15511    executor.run_until_parked();
15512    cx.assert_state_with_diff(
15513        r#"
15514        use some::mod1;
15515        use some::mod2;
15516
15517        const A: u32 = 42;
15518      + const B: u32 = 42;
15519        ˇ
15520        fn main() {
15521            println!("hello");
15522
15523            println!("world");
15524        }
15525      "#
15526        .unindent(),
15527    );
15528
15529    cx.update_editor(|editor, window, cx| {
15530        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15531        editor.delete_line(&DeleteLine, window, cx);
15532    });
15533    executor.run_until_parked();
15534    cx.assert_state_with_diff(
15535        r#"
15536        ˇ
15537        fn main() {
15538            println!("hello");
15539
15540            println!("world");
15541        }
15542      "#
15543        .unindent(),
15544    );
15545}
15546
15547#[gpui::test]
15548async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15549    init_test(cx, |_| {});
15550
15551    let mut cx = EditorTestContext::new(cx).await;
15552    cx.set_head_text(indoc! { "
15553        one
15554        two
15555        three
15556        four
15557        five
15558        "
15559    });
15560    cx.set_state(indoc! { "
15561        one
15562        ˇthree
15563        five
15564    "});
15565    cx.run_until_parked();
15566    cx.update_editor(|editor, window, cx| {
15567        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15568    });
15569    cx.assert_state_with_diff(
15570        indoc! { "
15571        one
15572      - two
15573        ˇthree
15574      - four
15575        five
15576    "}
15577        .to_string(),
15578    );
15579    cx.update_editor(|editor, window, cx| {
15580        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15581    });
15582
15583    cx.assert_state_with_diff(
15584        indoc! { "
15585        one
15586        ˇthree
15587        five
15588    "}
15589        .to_string(),
15590    );
15591
15592    cx.set_state(indoc! { "
15593        one
15594        ˇTWO
15595        three
15596        four
15597        five
15598    "});
15599    cx.run_until_parked();
15600    cx.update_editor(|editor, window, cx| {
15601        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15602    });
15603
15604    cx.assert_state_with_diff(
15605        indoc! { "
15606            one
15607          - two
15608          + ˇTWO
15609            three
15610            four
15611            five
15612        "}
15613        .to_string(),
15614    );
15615    cx.update_editor(|editor, window, cx| {
15616        editor.move_up(&Default::default(), window, cx);
15617        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15618    });
15619    cx.assert_state_with_diff(
15620        indoc! { "
15621            one
15622            ˇTWO
15623            three
15624            four
15625            five
15626        "}
15627        .to_string(),
15628    );
15629}
15630
15631#[gpui::test]
15632async fn test_edits_around_expanded_deletion_hunks(
15633    executor: BackgroundExecutor,
15634    cx: &mut TestAppContext,
15635) {
15636    init_test(cx, |_| {});
15637
15638    let mut cx = EditorTestContext::new(cx).await;
15639
15640    let diff_base = r#"
15641        use some::mod1;
15642        use some::mod2;
15643
15644        const A: u32 = 42;
15645        const B: u32 = 42;
15646        const C: u32 = 42;
15647
15648
15649        fn main() {
15650            println!("hello");
15651
15652            println!("world");
15653        }
15654    "#
15655    .unindent();
15656    executor.run_until_parked();
15657    cx.set_state(
15658        &r#"
15659        use some::mod1;
15660        use some::mod2;
15661
15662        ˇconst B: u32 = 42;
15663        const C: u32 = 42;
15664
15665
15666        fn main() {
15667            println!("hello");
15668
15669            println!("world");
15670        }
15671        "#
15672        .unindent(),
15673    );
15674
15675    cx.set_head_text(&diff_base);
15676    executor.run_until_parked();
15677
15678    cx.update_editor(|editor, window, cx| {
15679        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15680    });
15681    executor.run_until_parked();
15682
15683    cx.assert_state_with_diff(
15684        r#"
15685        use some::mod1;
15686        use some::mod2;
15687
15688      - const A: u32 = 42;
15689        ˇconst B: u32 = 42;
15690        const C: u32 = 42;
15691
15692
15693        fn main() {
15694            println!("hello");
15695
15696            println!("world");
15697        }
15698      "#
15699        .unindent(),
15700    );
15701
15702    cx.update_editor(|editor, window, cx| {
15703        editor.delete_line(&DeleteLine, window, cx);
15704    });
15705    executor.run_until_parked();
15706    cx.assert_state_with_diff(
15707        r#"
15708        use some::mod1;
15709        use some::mod2;
15710
15711      - const A: u32 = 42;
15712      - const B: u32 = 42;
15713        ˇconst C: u32 = 42;
15714
15715
15716        fn main() {
15717            println!("hello");
15718
15719            println!("world");
15720        }
15721      "#
15722        .unindent(),
15723    );
15724
15725    cx.update_editor(|editor, window, cx| {
15726        editor.delete_line(&DeleteLine, window, cx);
15727    });
15728    executor.run_until_parked();
15729    cx.assert_state_with_diff(
15730        r#"
15731        use some::mod1;
15732        use some::mod2;
15733
15734      - const A: u32 = 42;
15735      - const B: u32 = 42;
15736      - const C: u32 = 42;
15737        ˇ
15738
15739        fn main() {
15740            println!("hello");
15741
15742            println!("world");
15743        }
15744      "#
15745        .unindent(),
15746    );
15747
15748    cx.update_editor(|editor, window, cx| {
15749        editor.handle_input("replacement", window, cx);
15750    });
15751    executor.run_until_parked();
15752    cx.assert_state_with_diff(
15753        r#"
15754        use some::mod1;
15755        use some::mod2;
15756
15757      - const A: u32 = 42;
15758      - const B: u32 = 42;
15759      - const C: u32 = 42;
15760      -
15761      + replacementˇ
15762
15763        fn main() {
15764            println!("hello");
15765
15766            println!("world");
15767        }
15768      "#
15769        .unindent(),
15770    );
15771}
15772
15773#[gpui::test]
15774async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15775    init_test(cx, |_| {});
15776
15777    let mut cx = EditorTestContext::new(cx).await;
15778
15779    let base_text = r#"
15780        one
15781        two
15782        three
15783        four
15784        five
15785    "#
15786    .unindent();
15787    executor.run_until_parked();
15788    cx.set_state(
15789        &r#"
15790        one
15791        two
15792        fˇour
15793        five
15794        "#
15795        .unindent(),
15796    );
15797
15798    cx.set_head_text(&base_text);
15799    executor.run_until_parked();
15800
15801    cx.update_editor(|editor, window, cx| {
15802        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15803    });
15804    executor.run_until_parked();
15805
15806    cx.assert_state_with_diff(
15807        r#"
15808          one
15809          two
15810        - three
15811          fˇour
15812          five
15813        "#
15814        .unindent(),
15815    );
15816
15817    cx.update_editor(|editor, window, cx| {
15818        editor.backspace(&Backspace, window, cx);
15819        editor.backspace(&Backspace, window, cx);
15820    });
15821    executor.run_until_parked();
15822    cx.assert_state_with_diff(
15823        r#"
15824          one
15825          two
15826        - threeˇ
15827        - four
15828        + our
15829          five
15830        "#
15831        .unindent(),
15832    );
15833}
15834
15835#[gpui::test]
15836async fn test_edit_after_expanded_modification_hunk(
15837    executor: BackgroundExecutor,
15838    cx: &mut TestAppContext,
15839) {
15840    init_test(cx, |_| {});
15841
15842    let mut cx = EditorTestContext::new(cx).await;
15843
15844    let diff_base = r#"
15845        use some::mod1;
15846        use some::mod2;
15847
15848        const A: u32 = 42;
15849        const B: u32 = 42;
15850        const C: u32 = 42;
15851        const D: u32 = 42;
15852
15853
15854        fn main() {
15855            println!("hello");
15856
15857            println!("world");
15858        }"#
15859    .unindent();
15860
15861    cx.set_state(
15862        &r#"
15863        use some::mod1;
15864        use some::mod2;
15865
15866        const A: u32 = 42;
15867        const B: u32 = 42;
15868        const C: u32 = 43ˇ
15869        const D: u32 = 42;
15870
15871
15872        fn main() {
15873            println!("hello");
15874
15875            println!("world");
15876        }"#
15877        .unindent(),
15878    );
15879
15880    cx.set_head_text(&diff_base);
15881    executor.run_until_parked();
15882    cx.update_editor(|editor, window, cx| {
15883        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15884    });
15885    executor.run_until_parked();
15886
15887    cx.assert_state_with_diff(
15888        r#"
15889        use some::mod1;
15890        use some::mod2;
15891
15892        const A: u32 = 42;
15893        const B: u32 = 42;
15894      - const C: u32 = 42;
15895      + const C: u32 = 43ˇ
15896        const D: u32 = 42;
15897
15898
15899        fn main() {
15900            println!("hello");
15901
15902            println!("world");
15903        }"#
15904        .unindent(),
15905    );
15906
15907    cx.update_editor(|editor, window, cx| {
15908        editor.handle_input("\nnew_line\n", window, cx);
15909    });
15910    executor.run_until_parked();
15911
15912    cx.assert_state_with_diff(
15913        r#"
15914        use some::mod1;
15915        use some::mod2;
15916
15917        const A: u32 = 42;
15918        const B: u32 = 42;
15919      - const C: u32 = 42;
15920      + const C: u32 = 43
15921      + new_line
15922      + ˇ
15923        const D: u32 = 42;
15924
15925
15926        fn main() {
15927            println!("hello");
15928
15929            println!("world");
15930        }"#
15931        .unindent(),
15932    );
15933}
15934
15935#[gpui::test]
15936async fn test_stage_and_unstage_added_file_hunk(
15937    executor: BackgroundExecutor,
15938    cx: &mut TestAppContext,
15939) {
15940    init_test(cx, |_| {});
15941
15942    let mut cx = EditorTestContext::new(cx).await;
15943    cx.update_editor(|editor, _, cx| {
15944        editor.set_expand_all_diff_hunks(cx);
15945    });
15946
15947    let working_copy = r#"
15948            ˇfn main() {
15949                println!("hello, world!");
15950            }
15951        "#
15952    .unindent();
15953
15954    cx.set_state(&working_copy);
15955    executor.run_until_parked();
15956
15957    cx.assert_state_with_diff(
15958        r#"
15959            + ˇfn main() {
15960            +     println!("hello, world!");
15961            + }
15962        "#
15963        .unindent(),
15964    );
15965    cx.assert_index_text(None);
15966
15967    cx.update_editor(|editor, window, cx| {
15968        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15969    });
15970    executor.run_until_parked();
15971    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15972    cx.assert_state_with_diff(
15973        r#"
15974            + ˇfn main() {
15975            +     println!("hello, world!");
15976            + }
15977        "#
15978        .unindent(),
15979    );
15980
15981    cx.update_editor(|editor, window, cx| {
15982        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15983    });
15984    executor.run_until_parked();
15985    cx.assert_index_text(None);
15986}
15987
15988async fn setup_indent_guides_editor(
15989    text: &str,
15990    cx: &mut TestAppContext,
15991) -> (BufferId, EditorTestContext) {
15992    init_test(cx, |_| {});
15993
15994    let mut cx = EditorTestContext::new(cx).await;
15995
15996    let buffer_id = cx.update_editor(|editor, window, cx| {
15997        editor.set_text(text, window, cx);
15998        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15999
16000        buffer_ids[0]
16001    });
16002
16003    (buffer_id, cx)
16004}
16005
16006fn assert_indent_guides(
16007    range: Range<u32>,
16008    expected: Vec<IndentGuide>,
16009    active_indices: Option<Vec<usize>>,
16010    cx: &mut EditorTestContext,
16011) {
16012    let indent_guides = cx.update_editor(|editor, window, cx| {
16013        let snapshot = editor.snapshot(window, cx).display_snapshot;
16014        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16015            editor,
16016            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16017            true,
16018            &snapshot,
16019            cx,
16020        );
16021
16022        indent_guides.sort_by(|a, b| {
16023            a.depth.cmp(&b.depth).then(
16024                a.start_row
16025                    .cmp(&b.start_row)
16026                    .then(a.end_row.cmp(&b.end_row)),
16027            )
16028        });
16029        indent_guides
16030    });
16031
16032    if let Some(expected) = active_indices {
16033        let active_indices = cx.update_editor(|editor, window, cx| {
16034            let snapshot = editor.snapshot(window, cx).display_snapshot;
16035            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16036        });
16037
16038        assert_eq!(
16039            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16040            expected,
16041            "Active indent guide indices do not match"
16042        );
16043    }
16044
16045    assert_eq!(indent_guides, expected, "Indent guides do not match");
16046}
16047
16048fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16049    IndentGuide {
16050        buffer_id,
16051        start_row: MultiBufferRow(start_row),
16052        end_row: MultiBufferRow(end_row),
16053        depth,
16054        tab_size: 4,
16055        settings: IndentGuideSettings {
16056            enabled: true,
16057            line_width: 1,
16058            active_line_width: 1,
16059            ..Default::default()
16060        },
16061    }
16062}
16063
16064#[gpui::test]
16065async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16066    let (buffer_id, mut cx) = setup_indent_guides_editor(
16067        &"
16068    fn main() {
16069        let a = 1;
16070    }"
16071        .unindent(),
16072        cx,
16073    )
16074    .await;
16075
16076    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16077}
16078
16079#[gpui::test]
16080async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16081    let (buffer_id, mut cx) = setup_indent_guides_editor(
16082        &"
16083    fn main() {
16084        let a = 1;
16085        let b = 2;
16086    }"
16087        .unindent(),
16088        cx,
16089    )
16090    .await;
16091
16092    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16093}
16094
16095#[gpui::test]
16096async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16097    let (buffer_id, mut cx) = setup_indent_guides_editor(
16098        &"
16099    fn main() {
16100        let a = 1;
16101        if a == 3 {
16102            let b = 2;
16103        } else {
16104            let c = 3;
16105        }
16106    }"
16107        .unindent(),
16108        cx,
16109    )
16110    .await;
16111
16112    assert_indent_guides(
16113        0..8,
16114        vec![
16115            indent_guide(buffer_id, 1, 6, 0),
16116            indent_guide(buffer_id, 3, 3, 1),
16117            indent_guide(buffer_id, 5, 5, 1),
16118        ],
16119        None,
16120        &mut cx,
16121    );
16122}
16123
16124#[gpui::test]
16125async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16126    let (buffer_id, mut cx) = setup_indent_guides_editor(
16127        &"
16128    fn main() {
16129        let a = 1;
16130            let b = 2;
16131        let c = 3;
16132    }"
16133        .unindent(),
16134        cx,
16135    )
16136    .await;
16137
16138    assert_indent_guides(
16139        0..5,
16140        vec![
16141            indent_guide(buffer_id, 1, 3, 0),
16142            indent_guide(buffer_id, 2, 2, 1),
16143        ],
16144        None,
16145        &mut cx,
16146    );
16147}
16148
16149#[gpui::test]
16150async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16151    let (buffer_id, mut cx) = setup_indent_guides_editor(
16152        &"
16153        fn main() {
16154            let a = 1;
16155
16156            let c = 3;
16157        }"
16158        .unindent(),
16159        cx,
16160    )
16161    .await;
16162
16163    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16164}
16165
16166#[gpui::test]
16167async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16168    let (buffer_id, mut cx) = setup_indent_guides_editor(
16169        &"
16170        fn main() {
16171            let a = 1;
16172
16173            let c = 3;
16174
16175            if a == 3 {
16176                let b = 2;
16177            } else {
16178                let c = 3;
16179            }
16180        }"
16181        .unindent(),
16182        cx,
16183    )
16184    .await;
16185
16186    assert_indent_guides(
16187        0..11,
16188        vec![
16189            indent_guide(buffer_id, 1, 9, 0),
16190            indent_guide(buffer_id, 6, 6, 1),
16191            indent_guide(buffer_id, 8, 8, 1),
16192        ],
16193        None,
16194        &mut cx,
16195    );
16196}
16197
16198#[gpui::test]
16199async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16200    let (buffer_id, mut cx) = setup_indent_guides_editor(
16201        &"
16202        fn main() {
16203            let a = 1;
16204
16205            let c = 3;
16206
16207            if a == 3 {
16208                let b = 2;
16209            } else {
16210                let c = 3;
16211            }
16212        }"
16213        .unindent(),
16214        cx,
16215    )
16216    .await;
16217
16218    assert_indent_guides(
16219        1..11,
16220        vec![
16221            indent_guide(buffer_id, 1, 9, 0),
16222            indent_guide(buffer_id, 6, 6, 1),
16223            indent_guide(buffer_id, 8, 8, 1),
16224        ],
16225        None,
16226        &mut cx,
16227    );
16228}
16229
16230#[gpui::test]
16231async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16232    let (buffer_id, mut cx) = setup_indent_guides_editor(
16233        &"
16234        fn main() {
16235            let a = 1;
16236
16237            let c = 3;
16238
16239            if a == 3 {
16240                let b = 2;
16241            } else {
16242                let c = 3;
16243            }
16244        }"
16245        .unindent(),
16246        cx,
16247    )
16248    .await;
16249
16250    assert_indent_guides(
16251        1..10,
16252        vec![
16253            indent_guide(buffer_id, 1, 9, 0),
16254            indent_guide(buffer_id, 6, 6, 1),
16255            indent_guide(buffer_id, 8, 8, 1),
16256        ],
16257        None,
16258        &mut cx,
16259    );
16260}
16261
16262#[gpui::test]
16263async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16264    let (buffer_id, mut cx) = setup_indent_guides_editor(
16265        &"
16266        block1
16267            block2
16268                block3
16269                    block4
16270            block2
16271        block1
16272        block1"
16273            .unindent(),
16274        cx,
16275    )
16276    .await;
16277
16278    assert_indent_guides(
16279        1..10,
16280        vec![
16281            indent_guide(buffer_id, 1, 4, 0),
16282            indent_guide(buffer_id, 2, 3, 1),
16283            indent_guide(buffer_id, 3, 3, 2),
16284        ],
16285        None,
16286        &mut cx,
16287    );
16288}
16289
16290#[gpui::test]
16291async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16292    let (buffer_id, mut cx) = setup_indent_guides_editor(
16293        &"
16294        block1
16295            block2
16296                block3
16297
16298        block1
16299        block1"
16300            .unindent(),
16301        cx,
16302    )
16303    .await;
16304
16305    assert_indent_guides(
16306        0..6,
16307        vec![
16308            indent_guide(buffer_id, 1, 2, 0),
16309            indent_guide(buffer_id, 2, 2, 1),
16310        ],
16311        None,
16312        &mut cx,
16313    );
16314}
16315
16316#[gpui::test]
16317async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16318    let (buffer_id, mut cx) = setup_indent_guides_editor(
16319        &"
16320        block1
16321
16322
16323
16324            block2
16325        "
16326        .unindent(),
16327        cx,
16328    )
16329    .await;
16330
16331    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16332}
16333
16334#[gpui::test]
16335async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16336    let (buffer_id, mut cx) = setup_indent_guides_editor(
16337        &"
16338        def a:
16339        \tb = 3
16340        \tif True:
16341        \t\tc = 4
16342        \t\td = 5
16343        \tprint(b)
16344        "
16345        .unindent(),
16346        cx,
16347    )
16348    .await;
16349
16350    assert_indent_guides(
16351        0..6,
16352        vec![
16353            indent_guide(buffer_id, 1, 6, 0),
16354            indent_guide(buffer_id, 3, 4, 1),
16355        ],
16356        None,
16357        &mut cx,
16358    );
16359}
16360
16361#[gpui::test]
16362async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16363    let (buffer_id, mut cx) = setup_indent_guides_editor(
16364        &"
16365    fn main() {
16366        let a = 1;
16367    }"
16368        .unindent(),
16369        cx,
16370    )
16371    .await;
16372
16373    cx.update_editor(|editor, window, cx| {
16374        editor.change_selections(None, window, cx, |s| {
16375            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16376        });
16377    });
16378
16379    assert_indent_guides(
16380        0..3,
16381        vec![indent_guide(buffer_id, 1, 1, 0)],
16382        Some(vec![0]),
16383        &mut cx,
16384    );
16385}
16386
16387#[gpui::test]
16388async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16389    let (buffer_id, mut cx) = setup_indent_guides_editor(
16390        &"
16391    fn main() {
16392        if 1 == 2 {
16393            let a = 1;
16394        }
16395    }"
16396        .unindent(),
16397        cx,
16398    )
16399    .await;
16400
16401    cx.update_editor(|editor, window, cx| {
16402        editor.change_selections(None, window, cx, |s| {
16403            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16404        });
16405    });
16406
16407    assert_indent_guides(
16408        0..4,
16409        vec![
16410            indent_guide(buffer_id, 1, 3, 0),
16411            indent_guide(buffer_id, 2, 2, 1),
16412        ],
16413        Some(vec![1]),
16414        &mut cx,
16415    );
16416
16417    cx.update_editor(|editor, window, cx| {
16418        editor.change_selections(None, window, cx, |s| {
16419            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16420        });
16421    });
16422
16423    assert_indent_guides(
16424        0..4,
16425        vec![
16426            indent_guide(buffer_id, 1, 3, 0),
16427            indent_guide(buffer_id, 2, 2, 1),
16428        ],
16429        Some(vec![1]),
16430        &mut cx,
16431    );
16432
16433    cx.update_editor(|editor, window, cx| {
16434        editor.change_selections(None, window, cx, |s| {
16435            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16436        });
16437    });
16438
16439    assert_indent_guides(
16440        0..4,
16441        vec![
16442            indent_guide(buffer_id, 1, 3, 0),
16443            indent_guide(buffer_id, 2, 2, 1),
16444        ],
16445        Some(vec![0]),
16446        &mut cx,
16447    );
16448}
16449
16450#[gpui::test]
16451async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16452    let (buffer_id, mut cx) = setup_indent_guides_editor(
16453        &"
16454    fn main() {
16455        let a = 1;
16456
16457        let b = 2;
16458    }"
16459        .unindent(),
16460        cx,
16461    )
16462    .await;
16463
16464    cx.update_editor(|editor, window, cx| {
16465        editor.change_selections(None, window, cx, |s| {
16466            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16467        });
16468    });
16469
16470    assert_indent_guides(
16471        0..5,
16472        vec![indent_guide(buffer_id, 1, 3, 0)],
16473        Some(vec![0]),
16474        &mut cx,
16475    );
16476}
16477
16478#[gpui::test]
16479async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16480    let (buffer_id, mut cx) = setup_indent_guides_editor(
16481        &"
16482    def m:
16483        a = 1
16484        pass"
16485            .unindent(),
16486        cx,
16487    )
16488    .await;
16489
16490    cx.update_editor(|editor, window, cx| {
16491        editor.change_selections(None, window, cx, |s| {
16492            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16493        });
16494    });
16495
16496    assert_indent_guides(
16497        0..3,
16498        vec![indent_guide(buffer_id, 1, 2, 0)],
16499        Some(vec![0]),
16500        &mut cx,
16501    );
16502}
16503
16504#[gpui::test]
16505async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16506    init_test(cx, |_| {});
16507    let mut cx = EditorTestContext::new(cx).await;
16508    let text = indoc! {
16509        "
16510        impl A {
16511            fn b() {
16512                0;
16513                3;
16514                5;
16515                6;
16516                7;
16517            }
16518        }
16519        "
16520    };
16521    let base_text = indoc! {
16522        "
16523        impl A {
16524            fn b() {
16525                0;
16526                1;
16527                2;
16528                3;
16529                4;
16530            }
16531            fn c() {
16532                5;
16533                6;
16534                7;
16535            }
16536        }
16537        "
16538    };
16539
16540    cx.update_editor(|editor, window, cx| {
16541        editor.set_text(text, window, cx);
16542
16543        editor.buffer().update(cx, |multibuffer, cx| {
16544            let buffer = multibuffer.as_singleton().unwrap();
16545            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16546
16547            multibuffer.set_all_diff_hunks_expanded(cx);
16548            multibuffer.add_diff(diff, cx);
16549
16550            buffer.read(cx).remote_id()
16551        })
16552    });
16553    cx.run_until_parked();
16554
16555    cx.assert_state_with_diff(
16556        indoc! { "
16557          impl A {
16558              fn b() {
16559                  0;
16560        -         1;
16561        -         2;
16562                  3;
16563        -         4;
16564        -     }
16565        -     fn c() {
16566                  5;
16567                  6;
16568                  7;
16569              }
16570          }
16571          ˇ"
16572        }
16573        .to_string(),
16574    );
16575
16576    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16577        editor
16578            .snapshot(window, cx)
16579            .buffer_snapshot
16580            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16581            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16582            .collect::<Vec<_>>()
16583    });
16584    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16585    assert_eq!(
16586        actual_guides,
16587        vec![
16588            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16589            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16590            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16591        ]
16592    );
16593}
16594
16595#[gpui::test]
16596async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16597    init_test(cx, |_| {});
16598    let mut cx = EditorTestContext::new(cx).await;
16599
16600    let diff_base = r#"
16601        a
16602        b
16603        c
16604        "#
16605    .unindent();
16606
16607    cx.set_state(
16608        &r#"
16609        ˇA
16610        b
16611        C
16612        "#
16613        .unindent(),
16614    );
16615    cx.set_head_text(&diff_base);
16616    cx.update_editor(|editor, window, cx| {
16617        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16618    });
16619    executor.run_until_parked();
16620
16621    let both_hunks_expanded = r#"
16622        - a
16623        + ˇA
16624          b
16625        - c
16626        + C
16627        "#
16628    .unindent();
16629
16630    cx.assert_state_with_diff(both_hunks_expanded.clone());
16631
16632    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16633        let snapshot = editor.snapshot(window, cx);
16634        let hunks = editor
16635            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16636            .collect::<Vec<_>>();
16637        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16638        let buffer_id = hunks[0].buffer_id;
16639        hunks
16640            .into_iter()
16641            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16642            .collect::<Vec<_>>()
16643    });
16644    assert_eq!(hunk_ranges.len(), 2);
16645
16646    cx.update_editor(|editor, _, cx| {
16647        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16648    });
16649    executor.run_until_parked();
16650
16651    let second_hunk_expanded = r#"
16652          ˇA
16653          b
16654        - c
16655        + C
16656        "#
16657    .unindent();
16658
16659    cx.assert_state_with_diff(second_hunk_expanded);
16660
16661    cx.update_editor(|editor, _, cx| {
16662        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16663    });
16664    executor.run_until_parked();
16665
16666    cx.assert_state_with_diff(both_hunks_expanded.clone());
16667
16668    cx.update_editor(|editor, _, cx| {
16669        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16670    });
16671    executor.run_until_parked();
16672
16673    let first_hunk_expanded = r#"
16674        - a
16675        + ˇA
16676          b
16677          C
16678        "#
16679    .unindent();
16680
16681    cx.assert_state_with_diff(first_hunk_expanded);
16682
16683    cx.update_editor(|editor, _, cx| {
16684        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16685    });
16686    executor.run_until_parked();
16687
16688    cx.assert_state_with_diff(both_hunks_expanded);
16689
16690    cx.set_state(
16691        &r#"
16692        ˇA
16693        b
16694        "#
16695        .unindent(),
16696    );
16697    cx.run_until_parked();
16698
16699    // TODO this cursor position seems bad
16700    cx.assert_state_with_diff(
16701        r#"
16702        - ˇa
16703        + A
16704          b
16705        "#
16706        .unindent(),
16707    );
16708
16709    cx.update_editor(|editor, window, cx| {
16710        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16711    });
16712
16713    cx.assert_state_with_diff(
16714        r#"
16715            - ˇa
16716            + A
16717              b
16718            - c
16719            "#
16720        .unindent(),
16721    );
16722
16723    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16724        let snapshot = editor.snapshot(window, cx);
16725        let hunks = editor
16726            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16727            .collect::<Vec<_>>();
16728        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16729        let buffer_id = hunks[0].buffer_id;
16730        hunks
16731            .into_iter()
16732            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16733            .collect::<Vec<_>>()
16734    });
16735    assert_eq!(hunk_ranges.len(), 2);
16736
16737    cx.update_editor(|editor, _, cx| {
16738        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16739    });
16740    executor.run_until_parked();
16741
16742    cx.assert_state_with_diff(
16743        r#"
16744        - ˇa
16745        + A
16746          b
16747        "#
16748        .unindent(),
16749    );
16750}
16751
16752#[gpui::test]
16753async fn test_toggle_deletion_hunk_at_start_of_file(
16754    executor: BackgroundExecutor,
16755    cx: &mut TestAppContext,
16756) {
16757    init_test(cx, |_| {});
16758    let mut cx = EditorTestContext::new(cx).await;
16759
16760    let diff_base = r#"
16761        a
16762        b
16763        c
16764        "#
16765    .unindent();
16766
16767    cx.set_state(
16768        &r#"
16769        ˇb
16770        c
16771        "#
16772        .unindent(),
16773    );
16774    cx.set_head_text(&diff_base);
16775    cx.update_editor(|editor, window, cx| {
16776        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16777    });
16778    executor.run_until_parked();
16779
16780    let hunk_expanded = r#"
16781        - a
16782          ˇb
16783          c
16784        "#
16785    .unindent();
16786
16787    cx.assert_state_with_diff(hunk_expanded.clone());
16788
16789    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16790        let snapshot = editor.snapshot(window, cx);
16791        let hunks = editor
16792            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16793            .collect::<Vec<_>>();
16794        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16795        let buffer_id = hunks[0].buffer_id;
16796        hunks
16797            .into_iter()
16798            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16799            .collect::<Vec<_>>()
16800    });
16801    assert_eq!(hunk_ranges.len(), 1);
16802
16803    cx.update_editor(|editor, _, cx| {
16804        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16805    });
16806    executor.run_until_parked();
16807
16808    let hunk_collapsed = r#"
16809          ˇb
16810          c
16811        "#
16812    .unindent();
16813
16814    cx.assert_state_with_diff(hunk_collapsed);
16815
16816    cx.update_editor(|editor, _, cx| {
16817        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16818    });
16819    executor.run_until_parked();
16820
16821    cx.assert_state_with_diff(hunk_expanded.clone());
16822}
16823
16824#[gpui::test]
16825async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16826    init_test(cx, |_| {});
16827
16828    let fs = FakeFs::new(cx.executor());
16829    fs.insert_tree(
16830        path!("/test"),
16831        json!({
16832            ".git": {},
16833            "file-1": "ONE\n",
16834            "file-2": "TWO\n",
16835            "file-3": "THREE\n",
16836        }),
16837    )
16838    .await;
16839
16840    fs.set_head_for_repo(
16841        path!("/test/.git").as_ref(),
16842        &[
16843            ("file-1".into(), "one\n".into()),
16844            ("file-2".into(), "two\n".into()),
16845            ("file-3".into(), "three\n".into()),
16846        ],
16847    );
16848
16849    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16850    let mut buffers = vec![];
16851    for i in 1..=3 {
16852        let buffer = project
16853            .update(cx, |project, cx| {
16854                let path = format!(path!("/test/file-{}"), i);
16855                project.open_local_buffer(path, cx)
16856            })
16857            .await
16858            .unwrap();
16859        buffers.push(buffer);
16860    }
16861
16862    let multibuffer = cx.new(|cx| {
16863        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16864        multibuffer.set_all_diff_hunks_expanded(cx);
16865        for buffer in &buffers {
16866            let snapshot = buffer.read(cx).snapshot();
16867            multibuffer.set_excerpts_for_path(
16868                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16869                buffer.clone(),
16870                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16871                DEFAULT_MULTIBUFFER_CONTEXT,
16872                cx,
16873            );
16874        }
16875        multibuffer
16876    });
16877
16878    let editor = cx.add_window(|window, cx| {
16879        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16880    });
16881    cx.run_until_parked();
16882
16883    let snapshot = editor
16884        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16885        .unwrap();
16886    let hunks = snapshot
16887        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16888        .map(|hunk| match hunk {
16889            DisplayDiffHunk::Unfolded {
16890                display_row_range, ..
16891            } => display_row_range,
16892            DisplayDiffHunk::Folded { .. } => unreachable!(),
16893        })
16894        .collect::<Vec<_>>();
16895    assert_eq!(
16896        hunks,
16897        [
16898            DisplayRow(2)..DisplayRow(4),
16899            DisplayRow(7)..DisplayRow(9),
16900            DisplayRow(12)..DisplayRow(14),
16901        ]
16902    );
16903}
16904
16905#[gpui::test]
16906async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16907    init_test(cx, |_| {});
16908
16909    let mut cx = EditorTestContext::new(cx).await;
16910    cx.set_head_text(indoc! { "
16911        one
16912        two
16913        three
16914        four
16915        five
16916        "
16917    });
16918    cx.set_index_text(indoc! { "
16919        one
16920        two
16921        three
16922        four
16923        five
16924        "
16925    });
16926    cx.set_state(indoc! {"
16927        one
16928        TWO
16929        ˇTHREE
16930        FOUR
16931        five
16932    "});
16933    cx.run_until_parked();
16934    cx.update_editor(|editor, window, cx| {
16935        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16936    });
16937    cx.run_until_parked();
16938    cx.assert_index_text(Some(indoc! {"
16939        one
16940        TWO
16941        THREE
16942        FOUR
16943        five
16944    "}));
16945    cx.set_state(indoc! { "
16946        one
16947        TWO
16948        ˇTHREE-HUNDRED
16949        FOUR
16950        five
16951    "});
16952    cx.run_until_parked();
16953    cx.update_editor(|editor, window, cx| {
16954        let snapshot = editor.snapshot(window, cx);
16955        let hunks = editor
16956            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16957            .collect::<Vec<_>>();
16958        assert_eq!(hunks.len(), 1);
16959        assert_eq!(
16960            hunks[0].status(),
16961            DiffHunkStatus {
16962                kind: DiffHunkStatusKind::Modified,
16963                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16964            }
16965        );
16966
16967        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16968    });
16969    cx.run_until_parked();
16970    cx.assert_index_text(Some(indoc! {"
16971        one
16972        TWO
16973        THREE-HUNDRED
16974        FOUR
16975        five
16976    "}));
16977}
16978
16979#[gpui::test]
16980fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16981    init_test(cx, |_| {});
16982
16983    let editor = cx.add_window(|window, cx| {
16984        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16985        build_editor(buffer, window, cx)
16986    });
16987
16988    let render_args = Arc::new(Mutex::new(None));
16989    let snapshot = editor
16990        .update(cx, |editor, window, cx| {
16991            let snapshot = editor.buffer().read(cx).snapshot(cx);
16992            let range =
16993                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16994
16995            struct RenderArgs {
16996                row: MultiBufferRow,
16997                folded: bool,
16998                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16999            }
17000
17001            let crease = Crease::inline(
17002                range,
17003                FoldPlaceholder::test(),
17004                {
17005                    let toggle_callback = render_args.clone();
17006                    move |row, folded, callback, _window, _cx| {
17007                        *toggle_callback.lock() = Some(RenderArgs {
17008                            row,
17009                            folded,
17010                            callback,
17011                        });
17012                        div()
17013                    }
17014                },
17015                |_row, _folded, _window, _cx| div(),
17016            );
17017
17018            editor.insert_creases(Some(crease), cx);
17019            let snapshot = editor.snapshot(window, cx);
17020            let _div = snapshot.render_crease_toggle(
17021                MultiBufferRow(1),
17022                false,
17023                cx.entity().clone(),
17024                window,
17025                cx,
17026            );
17027            snapshot
17028        })
17029        .unwrap();
17030
17031    let render_args = render_args.lock().take().unwrap();
17032    assert_eq!(render_args.row, MultiBufferRow(1));
17033    assert!(!render_args.folded);
17034    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17035
17036    cx.update_window(*editor, |_, window, cx| {
17037        (render_args.callback)(true, window, cx)
17038    })
17039    .unwrap();
17040    let snapshot = editor
17041        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17042        .unwrap();
17043    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17044
17045    cx.update_window(*editor, |_, window, cx| {
17046        (render_args.callback)(false, window, cx)
17047    })
17048    .unwrap();
17049    let snapshot = editor
17050        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17051        .unwrap();
17052    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17053}
17054
17055#[gpui::test]
17056async fn test_input_text(cx: &mut TestAppContext) {
17057    init_test(cx, |_| {});
17058    let mut cx = EditorTestContext::new(cx).await;
17059
17060    cx.set_state(
17061        &r#"ˇone
17062        two
17063
17064        three
17065        fourˇ
17066        five
17067
17068        siˇx"#
17069            .unindent(),
17070    );
17071
17072    cx.dispatch_action(HandleInput(String::new()));
17073    cx.assert_editor_state(
17074        &r#"ˇone
17075        two
17076
17077        three
17078        fourˇ
17079        five
17080
17081        siˇx"#
17082            .unindent(),
17083    );
17084
17085    cx.dispatch_action(HandleInput("AAAA".to_string()));
17086    cx.assert_editor_state(
17087        &r#"AAAAˇone
17088        two
17089
17090        three
17091        fourAAAAˇ
17092        five
17093
17094        siAAAAˇx"#
17095            .unindent(),
17096    );
17097}
17098
17099#[gpui::test]
17100async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17101    init_test(cx, |_| {});
17102
17103    let mut cx = EditorTestContext::new(cx).await;
17104    cx.set_state(
17105        r#"let foo = 1;
17106let foo = 2;
17107let foo = 3;
17108let fooˇ = 4;
17109let foo = 5;
17110let foo = 6;
17111let foo = 7;
17112let foo = 8;
17113let foo = 9;
17114let foo = 10;
17115let foo = 11;
17116let foo = 12;
17117let foo = 13;
17118let foo = 14;
17119let foo = 15;"#,
17120    );
17121
17122    cx.update_editor(|e, window, cx| {
17123        assert_eq!(
17124            e.next_scroll_position,
17125            NextScrollCursorCenterTopBottom::Center,
17126            "Default next scroll direction is center",
17127        );
17128
17129        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17130        assert_eq!(
17131            e.next_scroll_position,
17132            NextScrollCursorCenterTopBottom::Top,
17133            "After center, next scroll direction should be top",
17134        );
17135
17136        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17137        assert_eq!(
17138            e.next_scroll_position,
17139            NextScrollCursorCenterTopBottom::Bottom,
17140            "After top, next scroll direction should be bottom",
17141        );
17142
17143        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17144        assert_eq!(
17145            e.next_scroll_position,
17146            NextScrollCursorCenterTopBottom::Center,
17147            "After bottom, scrolling should start over",
17148        );
17149
17150        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17151        assert_eq!(
17152            e.next_scroll_position,
17153            NextScrollCursorCenterTopBottom::Top,
17154            "Scrolling continues if retriggered fast enough"
17155        );
17156    });
17157
17158    cx.executor()
17159        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17160    cx.executor().run_until_parked();
17161    cx.update_editor(|e, _, _| {
17162        assert_eq!(
17163            e.next_scroll_position,
17164            NextScrollCursorCenterTopBottom::Center,
17165            "If scrolling is not triggered fast enough, it should reset"
17166        );
17167    });
17168}
17169
17170#[gpui::test]
17171async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17172    init_test(cx, |_| {});
17173    let mut cx = EditorLspTestContext::new_rust(
17174        lsp::ServerCapabilities {
17175            definition_provider: Some(lsp::OneOf::Left(true)),
17176            references_provider: Some(lsp::OneOf::Left(true)),
17177            ..lsp::ServerCapabilities::default()
17178        },
17179        cx,
17180    )
17181    .await;
17182
17183    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17184        let go_to_definition = cx
17185            .lsp
17186            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17187                move |params, _| async move {
17188                    if empty_go_to_definition {
17189                        Ok(None)
17190                    } else {
17191                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17192                            uri: params.text_document_position_params.text_document.uri,
17193                            range: lsp::Range::new(
17194                                lsp::Position::new(4, 3),
17195                                lsp::Position::new(4, 6),
17196                            ),
17197                        })))
17198                    }
17199                },
17200            );
17201        let references = cx
17202            .lsp
17203            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17204                Ok(Some(vec![lsp::Location {
17205                    uri: params.text_document_position.text_document.uri,
17206                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17207                }]))
17208            });
17209        (go_to_definition, references)
17210    };
17211
17212    cx.set_state(
17213        &r#"fn one() {
17214            let mut a = ˇtwo();
17215        }
17216
17217        fn two() {}"#
17218            .unindent(),
17219    );
17220    set_up_lsp_handlers(false, &mut cx);
17221    let navigated = cx
17222        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17223        .await
17224        .expect("Failed to navigate to definition");
17225    assert_eq!(
17226        navigated,
17227        Navigated::Yes,
17228        "Should have navigated to definition from the GetDefinition response"
17229    );
17230    cx.assert_editor_state(
17231        &r#"fn one() {
17232            let mut a = two();
17233        }
17234
17235        fn «twoˇ»() {}"#
17236            .unindent(),
17237    );
17238
17239    let editors = cx.update_workspace(|workspace, _, cx| {
17240        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17241    });
17242    cx.update_editor(|_, _, test_editor_cx| {
17243        assert_eq!(
17244            editors.len(),
17245            1,
17246            "Initially, only one, test, editor should be open in the workspace"
17247        );
17248        assert_eq!(
17249            test_editor_cx.entity(),
17250            editors.last().expect("Asserted len is 1").clone()
17251        );
17252    });
17253
17254    set_up_lsp_handlers(true, &mut cx);
17255    let navigated = cx
17256        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17257        .await
17258        .expect("Failed to navigate to lookup references");
17259    assert_eq!(
17260        navigated,
17261        Navigated::Yes,
17262        "Should have navigated to references as a fallback after empty GoToDefinition response"
17263    );
17264    // We should not change the selections in the existing file,
17265    // if opening another milti buffer with the references
17266    cx.assert_editor_state(
17267        &r#"fn one() {
17268            let mut a = two();
17269        }
17270
17271        fn «twoˇ»() {}"#
17272            .unindent(),
17273    );
17274    let editors = cx.update_workspace(|workspace, _, cx| {
17275        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17276    });
17277    cx.update_editor(|_, _, test_editor_cx| {
17278        assert_eq!(
17279            editors.len(),
17280            2,
17281            "After falling back to references search, we open a new editor with the results"
17282        );
17283        let references_fallback_text = editors
17284            .into_iter()
17285            .find(|new_editor| *new_editor != test_editor_cx.entity())
17286            .expect("Should have one non-test editor now")
17287            .read(test_editor_cx)
17288            .text(test_editor_cx);
17289        assert_eq!(
17290            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17291            "Should use the range from the references response and not the GoToDefinition one"
17292        );
17293    });
17294}
17295
17296#[gpui::test]
17297async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17298    init_test(cx, |_| {});
17299    cx.update(|cx| {
17300        let mut editor_settings = EditorSettings::get_global(cx).clone();
17301        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17302        EditorSettings::override_global(editor_settings, cx);
17303    });
17304    let mut cx = EditorLspTestContext::new_rust(
17305        lsp::ServerCapabilities {
17306            definition_provider: Some(lsp::OneOf::Left(true)),
17307            references_provider: Some(lsp::OneOf::Left(true)),
17308            ..lsp::ServerCapabilities::default()
17309        },
17310        cx,
17311    )
17312    .await;
17313    let original_state = r#"fn one() {
17314        let mut a = ˇtwo();
17315    }
17316
17317    fn two() {}"#
17318        .unindent();
17319    cx.set_state(&original_state);
17320
17321    let mut go_to_definition = cx
17322        .lsp
17323        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17324            move |_, _| async move { Ok(None) },
17325        );
17326    let _references = cx
17327        .lsp
17328        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17329            panic!("Should not call for references with no go to definition fallback")
17330        });
17331
17332    let navigated = cx
17333        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17334        .await
17335        .expect("Failed to navigate to lookup references");
17336    go_to_definition
17337        .next()
17338        .await
17339        .expect("Should have called the go_to_definition handler");
17340
17341    assert_eq!(
17342        navigated,
17343        Navigated::No,
17344        "Should have navigated to references as a fallback after empty GoToDefinition response"
17345    );
17346    cx.assert_editor_state(&original_state);
17347    let editors = cx.update_workspace(|workspace, _, cx| {
17348        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17349    });
17350    cx.update_editor(|_, _, _| {
17351        assert_eq!(
17352            editors.len(),
17353            1,
17354            "After unsuccessful fallback, no other editor should have been opened"
17355        );
17356    });
17357}
17358
17359#[gpui::test]
17360async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17361    init_test(cx, |_| {});
17362
17363    let language = Arc::new(Language::new(
17364        LanguageConfig::default(),
17365        Some(tree_sitter_rust::LANGUAGE.into()),
17366    ));
17367
17368    let text = r#"
17369        #[cfg(test)]
17370        mod tests() {
17371            #[test]
17372            fn runnable_1() {
17373                let a = 1;
17374            }
17375
17376            #[test]
17377            fn runnable_2() {
17378                let a = 1;
17379                let b = 2;
17380            }
17381        }
17382    "#
17383    .unindent();
17384
17385    let fs = FakeFs::new(cx.executor());
17386    fs.insert_file("/file.rs", Default::default()).await;
17387
17388    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17389    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17390    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17391    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17392    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17393
17394    let editor = cx.new_window_entity(|window, cx| {
17395        Editor::new(
17396            EditorMode::full(),
17397            multi_buffer,
17398            Some(project.clone()),
17399            window,
17400            cx,
17401        )
17402    });
17403
17404    editor.update_in(cx, |editor, window, cx| {
17405        let snapshot = editor.buffer().read(cx).snapshot(cx);
17406        editor.tasks.insert(
17407            (buffer.read(cx).remote_id(), 3),
17408            RunnableTasks {
17409                templates: vec![],
17410                offset: snapshot.anchor_before(43),
17411                column: 0,
17412                extra_variables: HashMap::default(),
17413                context_range: BufferOffset(43)..BufferOffset(85),
17414            },
17415        );
17416        editor.tasks.insert(
17417            (buffer.read(cx).remote_id(), 8),
17418            RunnableTasks {
17419                templates: vec![],
17420                offset: snapshot.anchor_before(86),
17421                column: 0,
17422                extra_variables: HashMap::default(),
17423                context_range: BufferOffset(86)..BufferOffset(191),
17424            },
17425        );
17426
17427        // Test finding task when cursor is inside function body
17428        editor.change_selections(None, window, cx, |s| {
17429            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17430        });
17431        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17432        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17433
17434        // Test finding task when cursor is on function name
17435        editor.change_selections(None, window, cx, |s| {
17436            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17437        });
17438        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17439        assert_eq!(row, 8, "Should find task when cursor is on function name");
17440    });
17441}
17442
17443#[gpui::test]
17444async fn test_folding_buffers(cx: &mut TestAppContext) {
17445    init_test(cx, |_| {});
17446
17447    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17448    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17449    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17450
17451    let fs = FakeFs::new(cx.executor());
17452    fs.insert_tree(
17453        path!("/a"),
17454        json!({
17455            "first.rs": sample_text_1,
17456            "second.rs": sample_text_2,
17457            "third.rs": sample_text_3,
17458        }),
17459    )
17460    .await;
17461    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17462    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17463    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17464    let worktree = project.update(cx, |project, cx| {
17465        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17466        assert_eq!(worktrees.len(), 1);
17467        worktrees.pop().unwrap()
17468    });
17469    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17470
17471    let buffer_1 = project
17472        .update(cx, |project, cx| {
17473            project.open_buffer((worktree_id, "first.rs"), cx)
17474        })
17475        .await
17476        .unwrap();
17477    let buffer_2 = project
17478        .update(cx, |project, cx| {
17479            project.open_buffer((worktree_id, "second.rs"), cx)
17480        })
17481        .await
17482        .unwrap();
17483    let buffer_3 = project
17484        .update(cx, |project, cx| {
17485            project.open_buffer((worktree_id, "third.rs"), cx)
17486        })
17487        .await
17488        .unwrap();
17489
17490    let multi_buffer = cx.new(|cx| {
17491        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17492        multi_buffer.push_excerpts(
17493            buffer_1.clone(),
17494            [
17495                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17496                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17497                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17498            ],
17499            cx,
17500        );
17501        multi_buffer.push_excerpts(
17502            buffer_2.clone(),
17503            [
17504                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17505                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17506                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17507            ],
17508            cx,
17509        );
17510        multi_buffer.push_excerpts(
17511            buffer_3.clone(),
17512            [
17513                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17514                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17515                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17516            ],
17517            cx,
17518        );
17519        multi_buffer
17520    });
17521    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17522        Editor::new(
17523            EditorMode::full(),
17524            multi_buffer.clone(),
17525            Some(project.clone()),
17526            window,
17527            cx,
17528        )
17529    });
17530
17531    assert_eq!(
17532        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17533        "\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",
17534    );
17535
17536    multi_buffer_editor.update(cx, |editor, cx| {
17537        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17538    });
17539    assert_eq!(
17540        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17541        "\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",
17542        "After folding the first buffer, its text should not be displayed"
17543    );
17544
17545    multi_buffer_editor.update(cx, |editor, cx| {
17546        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17547    });
17548    assert_eq!(
17549        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17550        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17551        "After folding the second buffer, its text should not be displayed"
17552    );
17553
17554    multi_buffer_editor.update(cx, |editor, cx| {
17555        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17556    });
17557    assert_eq!(
17558        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17559        "\n\n\n\n\n",
17560        "After folding the third buffer, its text should not be displayed"
17561    );
17562
17563    // Emulate selection inside the fold logic, that should work
17564    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17565        editor
17566            .snapshot(window, cx)
17567            .next_line_boundary(Point::new(0, 4));
17568    });
17569
17570    multi_buffer_editor.update(cx, |editor, cx| {
17571        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17572    });
17573    assert_eq!(
17574        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17575        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17576        "After unfolding the second buffer, its text should be displayed"
17577    );
17578
17579    // Typing inside of buffer 1 causes that buffer to be unfolded.
17580    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17581        assert_eq!(
17582            multi_buffer
17583                .read(cx)
17584                .snapshot(cx)
17585                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17586                .collect::<String>(),
17587            "bbbb"
17588        );
17589        editor.change_selections(None, window, cx, |selections| {
17590            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17591        });
17592        editor.handle_input("B", window, cx);
17593    });
17594
17595    assert_eq!(
17596        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17597        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17598        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17599    );
17600
17601    multi_buffer_editor.update(cx, |editor, cx| {
17602        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17603    });
17604    assert_eq!(
17605        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17606        "\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",
17607        "After unfolding the all buffers, all original text should be displayed"
17608    );
17609}
17610
17611#[gpui::test]
17612async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17613    init_test(cx, |_| {});
17614
17615    let sample_text_1 = "1111\n2222\n3333".to_string();
17616    let sample_text_2 = "4444\n5555\n6666".to_string();
17617    let sample_text_3 = "7777\n8888\n9999".to_string();
17618
17619    let fs = FakeFs::new(cx.executor());
17620    fs.insert_tree(
17621        path!("/a"),
17622        json!({
17623            "first.rs": sample_text_1,
17624            "second.rs": sample_text_2,
17625            "third.rs": sample_text_3,
17626        }),
17627    )
17628    .await;
17629    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17630    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17631    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17632    let worktree = project.update(cx, |project, cx| {
17633        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17634        assert_eq!(worktrees.len(), 1);
17635        worktrees.pop().unwrap()
17636    });
17637    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17638
17639    let buffer_1 = project
17640        .update(cx, |project, cx| {
17641            project.open_buffer((worktree_id, "first.rs"), cx)
17642        })
17643        .await
17644        .unwrap();
17645    let buffer_2 = project
17646        .update(cx, |project, cx| {
17647            project.open_buffer((worktree_id, "second.rs"), cx)
17648        })
17649        .await
17650        .unwrap();
17651    let buffer_3 = project
17652        .update(cx, |project, cx| {
17653            project.open_buffer((worktree_id, "third.rs"), cx)
17654        })
17655        .await
17656        .unwrap();
17657
17658    let multi_buffer = cx.new(|cx| {
17659        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17660        multi_buffer.push_excerpts(
17661            buffer_1.clone(),
17662            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17663            cx,
17664        );
17665        multi_buffer.push_excerpts(
17666            buffer_2.clone(),
17667            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17668            cx,
17669        );
17670        multi_buffer.push_excerpts(
17671            buffer_3.clone(),
17672            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17673            cx,
17674        );
17675        multi_buffer
17676    });
17677
17678    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17679        Editor::new(
17680            EditorMode::full(),
17681            multi_buffer,
17682            Some(project.clone()),
17683            window,
17684            cx,
17685        )
17686    });
17687
17688    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17689    assert_eq!(
17690        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17691        full_text,
17692    );
17693
17694    multi_buffer_editor.update(cx, |editor, cx| {
17695        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17696    });
17697    assert_eq!(
17698        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17699        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17700        "After folding the first buffer, its text should not be displayed"
17701    );
17702
17703    multi_buffer_editor.update(cx, |editor, cx| {
17704        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17705    });
17706
17707    assert_eq!(
17708        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17709        "\n\n\n\n\n\n7777\n8888\n9999",
17710        "After folding the second buffer, its text should not be displayed"
17711    );
17712
17713    multi_buffer_editor.update(cx, |editor, cx| {
17714        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17715    });
17716    assert_eq!(
17717        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17718        "\n\n\n\n\n",
17719        "After folding the third buffer, its text should not be displayed"
17720    );
17721
17722    multi_buffer_editor.update(cx, |editor, cx| {
17723        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17724    });
17725    assert_eq!(
17726        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17727        "\n\n\n\n4444\n5555\n6666\n\n",
17728        "After unfolding the second buffer, its text should be displayed"
17729    );
17730
17731    multi_buffer_editor.update(cx, |editor, cx| {
17732        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17733    });
17734    assert_eq!(
17735        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17736        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17737        "After unfolding the first buffer, its text should be displayed"
17738    );
17739
17740    multi_buffer_editor.update(cx, |editor, cx| {
17741        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17742    });
17743    assert_eq!(
17744        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17745        full_text,
17746        "After unfolding all buffers, all original text should be displayed"
17747    );
17748}
17749
17750#[gpui::test]
17751async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17752    init_test(cx, |_| {});
17753
17754    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17755
17756    let fs = FakeFs::new(cx.executor());
17757    fs.insert_tree(
17758        path!("/a"),
17759        json!({
17760            "main.rs": sample_text,
17761        }),
17762    )
17763    .await;
17764    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17765    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17766    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17767    let worktree = project.update(cx, |project, cx| {
17768        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17769        assert_eq!(worktrees.len(), 1);
17770        worktrees.pop().unwrap()
17771    });
17772    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17773
17774    let buffer_1 = project
17775        .update(cx, |project, cx| {
17776            project.open_buffer((worktree_id, "main.rs"), cx)
17777        })
17778        .await
17779        .unwrap();
17780
17781    let multi_buffer = cx.new(|cx| {
17782        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17783        multi_buffer.push_excerpts(
17784            buffer_1.clone(),
17785            [ExcerptRange::new(
17786                Point::new(0, 0)
17787                    ..Point::new(
17788                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17789                        0,
17790                    ),
17791            )],
17792            cx,
17793        );
17794        multi_buffer
17795    });
17796    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17797        Editor::new(
17798            EditorMode::full(),
17799            multi_buffer,
17800            Some(project.clone()),
17801            window,
17802            cx,
17803        )
17804    });
17805
17806    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17807    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17808        enum TestHighlight {}
17809        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17810        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17811        editor.highlight_text::<TestHighlight>(
17812            vec![highlight_range.clone()],
17813            HighlightStyle::color(Hsla::green()),
17814            cx,
17815        );
17816        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17817    });
17818
17819    let full_text = format!("\n\n{sample_text}");
17820    assert_eq!(
17821        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17822        full_text,
17823    );
17824}
17825
17826#[gpui::test]
17827async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17828    init_test(cx, |_| {});
17829    cx.update(|cx| {
17830        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17831            "keymaps/default-linux.json",
17832            cx,
17833        )
17834        .unwrap();
17835        cx.bind_keys(default_key_bindings);
17836    });
17837
17838    let (editor, cx) = cx.add_window_view(|window, cx| {
17839        let multi_buffer = MultiBuffer::build_multi(
17840            [
17841                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17842                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17843                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17844                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17845            ],
17846            cx,
17847        );
17848        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17849
17850        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17851        // fold all but the second buffer, so that we test navigating between two
17852        // adjacent folded buffers, as well as folded buffers at the start and
17853        // end the multibuffer
17854        editor.fold_buffer(buffer_ids[0], cx);
17855        editor.fold_buffer(buffer_ids[2], cx);
17856        editor.fold_buffer(buffer_ids[3], cx);
17857
17858        editor
17859    });
17860    cx.simulate_resize(size(px(1000.), px(1000.)));
17861
17862    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17863    cx.assert_excerpts_with_selections(indoc! {"
17864        [EXCERPT]
17865        ˇ[FOLDED]
17866        [EXCERPT]
17867        a1
17868        b1
17869        [EXCERPT]
17870        [FOLDED]
17871        [EXCERPT]
17872        [FOLDED]
17873        "
17874    });
17875    cx.simulate_keystroke("down");
17876    cx.assert_excerpts_with_selections(indoc! {"
17877        [EXCERPT]
17878        [FOLDED]
17879        [EXCERPT]
17880        ˇa1
17881        b1
17882        [EXCERPT]
17883        [FOLDED]
17884        [EXCERPT]
17885        [FOLDED]
17886        "
17887    });
17888    cx.simulate_keystroke("down");
17889    cx.assert_excerpts_with_selections(indoc! {"
17890        [EXCERPT]
17891        [FOLDED]
17892        [EXCERPT]
17893        a1
17894        ˇb1
17895        [EXCERPT]
17896        [FOLDED]
17897        [EXCERPT]
17898        [FOLDED]
17899        "
17900    });
17901    cx.simulate_keystroke("down");
17902    cx.assert_excerpts_with_selections(indoc! {"
17903        [EXCERPT]
17904        [FOLDED]
17905        [EXCERPT]
17906        a1
17907        b1
17908        ˇ[EXCERPT]
17909        [FOLDED]
17910        [EXCERPT]
17911        [FOLDED]
17912        "
17913    });
17914    cx.simulate_keystroke("down");
17915    cx.assert_excerpts_with_selections(indoc! {"
17916        [EXCERPT]
17917        [FOLDED]
17918        [EXCERPT]
17919        a1
17920        b1
17921        [EXCERPT]
17922        ˇ[FOLDED]
17923        [EXCERPT]
17924        [FOLDED]
17925        "
17926    });
17927    for _ in 0..5 {
17928        cx.simulate_keystroke("down");
17929        cx.assert_excerpts_with_selections(indoc! {"
17930            [EXCERPT]
17931            [FOLDED]
17932            [EXCERPT]
17933            a1
17934            b1
17935            [EXCERPT]
17936            [FOLDED]
17937            [EXCERPT]
17938            ˇ[FOLDED]
17939            "
17940        });
17941    }
17942
17943    cx.simulate_keystroke("up");
17944    cx.assert_excerpts_with_selections(indoc! {"
17945        [EXCERPT]
17946        [FOLDED]
17947        [EXCERPT]
17948        a1
17949        b1
17950        [EXCERPT]
17951        ˇ[FOLDED]
17952        [EXCERPT]
17953        [FOLDED]
17954        "
17955    });
17956    cx.simulate_keystroke("up");
17957    cx.assert_excerpts_with_selections(indoc! {"
17958        [EXCERPT]
17959        [FOLDED]
17960        [EXCERPT]
17961        a1
17962        b1
17963        ˇ[EXCERPT]
17964        [FOLDED]
17965        [EXCERPT]
17966        [FOLDED]
17967        "
17968    });
17969    cx.simulate_keystroke("up");
17970    cx.assert_excerpts_with_selections(indoc! {"
17971        [EXCERPT]
17972        [FOLDED]
17973        [EXCERPT]
17974        a1
17975        ˇb1
17976        [EXCERPT]
17977        [FOLDED]
17978        [EXCERPT]
17979        [FOLDED]
17980        "
17981    });
17982    cx.simulate_keystroke("up");
17983    cx.assert_excerpts_with_selections(indoc! {"
17984        [EXCERPT]
17985        [FOLDED]
17986        [EXCERPT]
17987        ˇa1
17988        b1
17989        [EXCERPT]
17990        [FOLDED]
17991        [EXCERPT]
17992        [FOLDED]
17993        "
17994    });
17995    for _ in 0..5 {
17996        cx.simulate_keystroke("up");
17997        cx.assert_excerpts_with_selections(indoc! {"
17998            [EXCERPT]
17999            ˇ[FOLDED]
18000            [EXCERPT]
18001            a1
18002            b1
18003            [EXCERPT]
18004            [FOLDED]
18005            [EXCERPT]
18006            [FOLDED]
18007            "
18008        });
18009    }
18010}
18011
18012#[gpui::test]
18013async fn test_inline_completion_text(cx: &mut TestAppContext) {
18014    init_test(cx, |_| {});
18015
18016    // Simple insertion
18017    assert_highlighted_edits(
18018        "Hello, world!",
18019        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18020        true,
18021        cx,
18022        |highlighted_edits, cx| {
18023            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18024            assert_eq!(highlighted_edits.highlights.len(), 1);
18025            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18026            assert_eq!(
18027                highlighted_edits.highlights[0].1.background_color,
18028                Some(cx.theme().status().created_background)
18029            );
18030        },
18031    )
18032    .await;
18033
18034    // Replacement
18035    assert_highlighted_edits(
18036        "This is a test.",
18037        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18038        false,
18039        cx,
18040        |highlighted_edits, cx| {
18041            assert_eq!(highlighted_edits.text, "That is a test.");
18042            assert_eq!(highlighted_edits.highlights.len(), 1);
18043            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18044            assert_eq!(
18045                highlighted_edits.highlights[0].1.background_color,
18046                Some(cx.theme().status().created_background)
18047            );
18048        },
18049    )
18050    .await;
18051
18052    // Multiple edits
18053    assert_highlighted_edits(
18054        "Hello, world!",
18055        vec![
18056            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18057            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18058        ],
18059        false,
18060        cx,
18061        |highlighted_edits, cx| {
18062            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18063            assert_eq!(highlighted_edits.highlights.len(), 2);
18064            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18065            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18066            assert_eq!(
18067                highlighted_edits.highlights[0].1.background_color,
18068                Some(cx.theme().status().created_background)
18069            );
18070            assert_eq!(
18071                highlighted_edits.highlights[1].1.background_color,
18072                Some(cx.theme().status().created_background)
18073            );
18074        },
18075    )
18076    .await;
18077
18078    // Multiple lines with edits
18079    assert_highlighted_edits(
18080        "First line\nSecond line\nThird line\nFourth line",
18081        vec![
18082            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18083            (
18084                Point::new(2, 0)..Point::new(2, 10),
18085                "New third line".to_string(),
18086            ),
18087            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18088        ],
18089        false,
18090        cx,
18091        |highlighted_edits, cx| {
18092            assert_eq!(
18093                highlighted_edits.text,
18094                "Second modified\nNew third line\nFourth updated line"
18095            );
18096            assert_eq!(highlighted_edits.highlights.len(), 3);
18097            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18098            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18099            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18100            for highlight in &highlighted_edits.highlights {
18101                assert_eq!(
18102                    highlight.1.background_color,
18103                    Some(cx.theme().status().created_background)
18104                );
18105            }
18106        },
18107    )
18108    .await;
18109}
18110
18111#[gpui::test]
18112async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18113    init_test(cx, |_| {});
18114
18115    // Deletion
18116    assert_highlighted_edits(
18117        "Hello, world!",
18118        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18119        true,
18120        cx,
18121        |highlighted_edits, cx| {
18122            assert_eq!(highlighted_edits.text, "Hello, world!");
18123            assert_eq!(highlighted_edits.highlights.len(), 1);
18124            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18125            assert_eq!(
18126                highlighted_edits.highlights[0].1.background_color,
18127                Some(cx.theme().status().deleted_background)
18128            );
18129        },
18130    )
18131    .await;
18132
18133    // Insertion
18134    assert_highlighted_edits(
18135        "Hello, world!",
18136        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18137        true,
18138        cx,
18139        |highlighted_edits, cx| {
18140            assert_eq!(highlighted_edits.highlights.len(), 1);
18141            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18142            assert_eq!(
18143                highlighted_edits.highlights[0].1.background_color,
18144                Some(cx.theme().status().created_background)
18145            );
18146        },
18147    )
18148    .await;
18149}
18150
18151async fn assert_highlighted_edits(
18152    text: &str,
18153    edits: Vec<(Range<Point>, String)>,
18154    include_deletions: bool,
18155    cx: &mut TestAppContext,
18156    assertion_fn: impl Fn(HighlightedText, &App),
18157) {
18158    let window = cx.add_window(|window, cx| {
18159        let buffer = MultiBuffer::build_simple(text, cx);
18160        Editor::new(EditorMode::full(), buffer, None, window, cx)
18161    });
18162    let cx = &mut VisualTestContext::from_window(*window, cx);
18163
18164    let (buffer, snapshot) = window
18165        .update(cx, |editor, _window, cx| {
18166            (
18167                editor.buffer().clone(),
18168                editor.buffer().read(cx).snapshot(cx),
18169            )
18170        })
18171        .unwrap();
18172
18173    let edits = edits
18174        .into_iter()
18175        .map(|(range, edit)| {
18176            (
18177                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18178                edit,
18179            )
18180        })
18181        .collect::<Vec<_>>();
18182
18183    let text_anchor_edits = edits
18184        .clone()
18185        .into_iter()
18186        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18187        .collect::<Vec<_>>();
18188
18189    let edit_preview = window
18190        .update(cx, |_, _window, cx| {
18191            buffer
18192                .read(cx)
18193                .as_singleton()
18194                .unwrap()
18195                .read(cx)
18196                .preview_edits(text_anchor_edits.into(), cx)
18197        })
18198        .unwrap()
18199        .await;
18200
18201    cx.update(|_window, cx| {
18202        let highlighted_edits = inline_completion_edit_text(
18203            &snapshot.as_singleton().unwrap().2,
18204            &edits,
18205            &edit_preview,
18206            include_deletions,
18207            cx,
18208        );
18209        assertion_fn(highlighted_edits, cx)
18210    });
18211}
18212
18213#[track_caller]
18214fn assert_breakpoint(
18215    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18216    path: &Arc<Path>,
18217    expected: Vec<(u32, Breakpoint)>,
18218) {
18219    if expected.len() == 0usize {
18220        assert!(!breakpoints.contains_key(path), "{}", path.display());
18221    } else {
18222        let mut breakpoint = breakpoints
18223            .get(path)
18224            .unwrap()
18225            .into_iter()
18226            .map(|breakpoint| {
18227                (
18228                    breakpoint.row,
18229                    Breakpoint {
18230                        message: breakpoint.message.clone(),
18231                        state: breakpoint.state,
18232                        condition: breakpoint.condition.clone(),
18233                        hit_condition: breakpoint.hit_condition.clone(),
18234                    },
18235                )
18236            })
18237            .collect::<Vec<_>>();
18238
18239        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18240
18241        assert_eq!(expected, breakpoint);
18242    }
18243}
18244
18245fn add_log_breakpoint_at_cursor(
18246    editor: &mut Editor,
18247    log_message: &str,
18248    window: &mut Window,
18249    cx: &mut Context<Editor>,
18250) {
18251    let (anchor, bp) = editor
18252        .breakpoints_at_cursors(window, cx)
18253        .first()
18254        .and_then(|(anchor, bp)| {
18255            if let Some(bp) = bp {
18256                Some((*anchor, bp.clone()))
18257            } else {
18258                None
18259            }
18260        })
18261        .unwrap_or_else(|| {
18262            let cursor_position: Point = editor.selections.newest(cx).head();
18263
18264            let breakpoint_position = editor
18265                .snapshot(window, cx)
18266                .display_snapshot
18267                .buffer_snapshot
18268                .anchor_before(Point::new(cursor_position.row, 0));
18269
18270            (breakpoint_position, Breakpoint::new_log(&log_message))
18271        });
18272
18273    editor.edit_breakpoint_at_anchor(
18274        anchor,
18275        bp,
18276        BreakpointEditAction::EditLogMessage(log_message.into()),
18277        cx,
18278    );
18279}
18280
18281#[gpui::test]
18282async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18283    init_test(cx, |_| {});
18284
18285    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18286    let fs = FakeFs::new(cx.executor());
18287    fs.insert_tree(
18288        path!("/a"),
18289        json!({
18290            "main.rs": sample_text,
18291        }),
18292    )
18293    .await;
18294    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18295    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18296    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18297
18298    let fs = FakeFs::new(cx.executor());
18299    fs.insert_tree(
18300        path!("/a"),
18301        json!({
18302            "main.rs": sample_text,
18303        }),
18304    )
18305    .await;
18306    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18307    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18308    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18309    let worktree_id = workspace
18310        .update(cx, |workspace, _window, cx| {
18311            workspace.project().update(cx, |project, cx| {
18312                project.worktrees(cx).next().unwrap().read(cx).id()
18313            })
18314        })
18315        .unwrap();
18316
18317    let buffer = project
18318        .update(cx, |project, cx| {
18319            project.open_buffer((worktree_id, "main.rs"), cx)
18320        })
18321        .await
18322        .unwrap();
18323
18324    let (editor, cx) = cx.add_window_view(|window, cx| {
18325        Editor::new(
18326            EditorMode::full(),
18327            MultiBuffer::build_from_buffer(buffer, cx),
18328            Some(project.clone()),
18329            window,
18330            cx,
18331        )
18332    });
18333
18334    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18335    let abs_path = project.read_with(cx, |project, cx| {
18336        project
18337            .absolute_path(&project_path, cx)
18338            .map(|path_buf| Arc::from(path_buf.to_owned()))
18339            .unwrap()
18340    });
18341
18342    // assert we can add breakpoint on the first line
18343    editor.update_in(cx, |editor, window, cx| {
18344        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18345        editor.move_to_end(&MoveToEnd, window, cx);
18346        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18347    });
18348
18349    let breakpoints = editor.update(cx, |editor, cx| {
18350        editor
18351            .breakpoint_store()
18352            .as_ref()
18353            .unwrap()
18354            .read(cx)
18355            .all_breakpoints(cx)
18356            .clone()
18357    });
18358
18359    assert_eq!(1, breakpoints.len());
18360    assert_breakpoint(
18361        &breakpoints,
18362        &abs_path,
18363        vec![
18364            (0, Breakpoint::new_standard()),
18365            (3, Breakpoint::new_standard()),
18366        ],
18367    );
18368
18369    editor.update_in(cx, |editor, window, cx| {
18370        editor.move_to_beginning(&MoveToBeginning, window, cx);
18371        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18372    });
18373
18374    let breakpoints = editor.update(cx, |editor, cx| {
18375        editor
18376            .breakpoint_store()
18377            .as_ref()
18378            .unwrap()
18379            .read(cx)
18380            .all_breakpoints(cx)
18381            .clone()
18382    });
18383
18384    assert_eq!(1, breakpoints.len());
18385    assert_breakpoint(
18386        &breakpoints,
18387        &abs_path,
18388        vec![(3, Breakpoint::new_standard())],
18389    );
18390
18391    editor.update_in(cx, |editor, window, cx| {
18392        editor.move_to_end(&MoveToEnd, window, cx);
18393        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18394    });
18395
18396    let breakpoints = editor.update(cx, |editor, cx| {
18397        editor
18398            .breakpoint_store()
18399            .as_ref()
18400            .unwrap()
18401            .read(cx)
18402            .all_breakpoints(cx)
18403            .clone()
18404    });
18405
18406    assert_eq!(0, breakpoints.len());
18407    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18408}
18409
18410#[gpui::test]
18411async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18412    init_test(cx, |_| {});
18413
18414    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18415
18416    let fs = FakeFs::new(cx.executor());
18417    fs.insert_tree(
18418        path!("/a"),
18419        json!({
18420            "main.rs": sample_text,
18421        }),
18422    )
18423    .await;
18424    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18425    let (workspace, cx) =
18426        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18427
18428    let worktree_id = workspace.update(cx, |workspace, cx| {
18429        workspace.project().update(cx, |project, cx| {
18430            project.worktrees(cx).next().unwrap().read(cx).id()
18431        })
18432    });
18433
18434    let buffer = project
18435        .update(cx, |project, cx| {
18436            project.open_buffer((worktree_id, "main.rs"), cx)
18437        })
18438        .await
18439        .unwrap();
18440
18441    let (editor, cx) = cx.add_window_view(|window, cx| {
18442        Editor::new(
18443            EditorMode::full(),
18444            MultiBuffer::build_from_buffer(buffer, cx),
18445            Some(project.clone()),
18446            window,
18447            cx,
18448        )
18449    });
18450
18451    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18452    let abs_path = project.read_with(cx, |project, cx| {
18453        project
18454            .absolute_path(&project_path, cx)
18455            .map(|path_buf| Arc::from(path_buf.to_owned()))
18456            .unwrap()
18457    });
18458
18459    editor.update_in(cx, |editor, window, cx| {
18460        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18461    });
18462
18463    let breakpoints = editor.update(cx, |editor, cx| {
18464        editor
18465            .breakpoint_store()
18466            .as_ref()
18467            .unwrap()
18468            .read(cx)
18469            .all_breakpoints(cx)
18470            .clone()
18471    });
18472
18473    assert_breakpoint(
18474        &breakpoints,
18475        &abs_path,
18476        vec![(0, Breakpoint::new_log("hello world"))],
18477    );
18478
18479    // Removing a log message from a log breakpoint should remove it
18480    editor.update_in(cx, |editor, window, cx| {
18481        add_log_breakpoint_at_cursor(editor, "", window, cx);
18482    });
18483
18484    let breakpoints = editor.update(cx, |editor, cx| {
18485        editor
18486            .breakpoint_store()
18487            .as_ref()
18488            .unwrap()
18489            .read(cx)
18490            .all_breakpoints(cx)
18491            .clone()
18492    });
18493
18494    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18495
18496    editor.update_in(cx, |editor, window, cx| {
18497        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18498        editor.move_to_end(&MoveToEnd, window, cx);
18499        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18500        // Not adding a log message to a standard breakpoint shouldn't remove it
18501        add_log_breakpoint_at_cursor(editor, "", window, cx);
18502    });
18503
18504    let breakpoints = editor.update(cx, |editor, cx| {
18505        editor
18506            .breakpoint_store()
18507            .as_ref()
18508            .unwrap()
18509            .read(cx)
18510            .all_breakpoints(cx)
18511            .clone()
18512    });
18513
18514    assert_breakpoint(
18515        &breakpoints,
18516        &abs_path,
18517        vec![
18518            (0, Breakpoint::new_standard()),
18519            (3, Breakpoint::new_standard()),
18520        ],
18521    );
18522
18523    editor.update_in(cx, |editor, window, cx| {
18524        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18525    });
18526
18527    let breakpoints = editor.update(cx, |editor, cx| {
18528        editor
18529            .breakpoint_store()
18530            .as_ref()
18531            .unwrap()
18532            .read(cx)
18533            .all_breakpoints(cx)
18534            .clone()
18535    });
18536
18537    assert_breakpoint(
18538        &breakpoints,
18539        &abs_path,
18540        vec![
18541            (0, Breakpoint::new_standard()),
18542            (3, Breakpoint::new_log("hello world")),
18543        ],
18544    );
18545
18546    editor.update_in(cx, |editor, window, cx| {
18547        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18548    });
18549
18550    let breakpoints = editor.update(cx, |editor, cx| {
18551        editor
18552            .breakpoint_store()
18553            .as_ref()
18554            .unwrap()
18555            .read(cx)
18556            .all_breakpoints(cx)
18557            .clone()
18558    });
18559
18560    assert_breakpoint(
18561        &breakpoints,
18562        &abs_path,
18563        vec![
18564            (0, Breakpoint::new_standard()),
18565            (3, Breakpoint::new_log("hello Earth!!")),
18566        ],
18567    );
18568}
18569
18570/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18571/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18572/// or when breakpoints were placed out of order. This tests for a regression too
18573#[gpui::test]
18574async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18575    init_test(cx, |_| {});
18576
18577    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18578    let fs = FakeFs::new(cx.executor());
18579    fs.insert_tree(
18580        path!("/a"),
18581        json!({
18582            "main.rs": sample_text,
18583        }),
18584    )
18585    .await;
18586    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18587    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18588    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18589
18590    let fs = FakeFs::new(cx.executor());
18591    fs.insert_tree(
18592        path!("/a"),
18593        json!({
18594            "main.rs": sample_text,
18595        }),
18596    )
18597    .await;
18598    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18599    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18600    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18601    let worktree_id = workspace
18602        .update(cx, |workspace, _window, cx| {
18603            workspace.project().update(cx, |project, cx| {
18604                project.worktrees(cx).next().unwrap().read(cx).id()
18605            })
18606        })
18607        .unwrap();
18608
18609    let buffer = project
18610        .update(cx, |project, cx| {
18611            project.open_buffer((worktree_id, "main.rs"), cx)
18612        })
18613        .await
18614        .unwrap();
18615
18616    let (editor, cx) = cx.add_window_view(|window, cx| {
18617        Editor::new(
18618            EditorMode::full(),
18619            MultiBuffer::build_from_buffer(buffer, cx),
18620            Some(project.clone()),
18621            window,
18622            cx,
18623        )
18624    });
18625
18626    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18627    let abs_path = project.read_with(cx, |project, cx| {
18628        project
18629            .absolute_path(&project_path, cx)
18630            .map(|path_buf| Arc::from(path_buf.to_owned()))
18631            .unwrap()
18632    });
18633
18634    // assert we can add breakpoint on the first line
18635    editor.update_in(cx, |editor, window, cx| {
18636        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18637        editor.move_to_end(&MoveToEnd, window, cx);
18638        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18639        editor.move_up(&MoveUp, window, cx);
18640        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18641    });
18642
18643    let breakpoints = editor.update(cx, |editor, cx| {
18644        editor
18645            .breakpoint_store()
18646            .as_ref()
18647            .unwrap()
18648            .read(cx)
18649            .all_breakpoints(cx)
18650            .clone()
18651    });
18652
18653    assert_eq!(1, breakpoints.len());
18654    assert_breakpoint(
18655        &breakpoints,
18656        &abs_path,
18657        vec![
18658            (0, Breakpoint::new_standard()),
18659            (2, Breakpoint::new_standard()),
18660            (3, Breakpoint::new_standard()),
18661        ],
18662    );
18663
18664    editor.update_in(cx, |editor, window, cx| {
18665        editor.move_to_beginning(&MoveToBeginning, window, cx);
18666        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18667        editor.move_to_end(&MoveToEnd, window, cx);
18668        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18669        // Disabling a breakpoint that doesn't exist should do nothing
18670        editor.move_up(&MoveUp, window, cx);
18671        editor.move_up(&MoveUp, window, cx);
18672        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18673    });
18674
18675    let breakpoints = editor.update(cx, |editor, cx| {
18676        editor
18677            .breakpoint_store()
18678            .as_ref()
18679            .unwrap()
18680            .read(cx)
18681            .all_breakpoints(cx)
18682            .clone()
18683    });
18684
18685    let disable_breakpoint = {
18686        let mut bp = Breakpoint::new_standard();
18687        bp.state = BreakpointState::Disabled;
18688        bp
18689    };
18690
18691    assert_eq!(1, breakpoints.len());
18692    assert_breakpoint(
18693        &breakpoints,
18694        &abs_path,
18695        vec![
18696            (0, disable_breakpoint.clone()),
18697            (2, Breakpoint::new_standard()),
18698            (3, disable_breakpoint.clone()),
18699        ],
18700    );
18701
18702    editor.update_in(cx, |editor, window, cx| {
18703        editor.move_to_beginning(&MoveToBeginning, window, cx);
18704        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18705        editor.move_to_end(&MoveToEnd, window, cx);
18706        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18707        editor.move_up(&MoveUp, window, cx);
18708        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18709    });
18710
18711    let breakpoints = editor.update(cx, |editor, cx| {
18712        editor
18713            .breakpoint_store()
18714            .as_ref()
18715            .unwrap()
18716            .read(cx)
18717            .all_breakpoints(cx)
18718            .clone()
18719    });
18720
18721    assert_eq!(1, breakpoints.len());
18722    assert_breakpoint(
18723        &breakpoints,
18724        &abs_path,
18725        vec![
18726            (0, Breakpoint::new_standard()),
18727            (2, disable_breakpoint),
18728            (3, Breakpoint::new_standard()),
18729        ],
18730    );
18731}
18732
18733#[gpui::test]
18734async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18735    init_test(cx, |_| {});
18736    let capabilities = lsp::ServerCapabilities {
18737        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18738            prepare_provider: Some(true),
18739            work_done_progress_options: Default::default(),
18740        })),
18741        ..Default::default()
18742    };
18743    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18744
18745    cx.set_state(indoc! {"
18746        struct Fˇoo {}
18747    "});
18748
18749    cx.update_editor(|editor, _, cx| {
18750        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18751        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18752        editor.highlight_background::<DocumentHighlightRead>(
18753            &[highlight_range],
18754            |c| c.editor_document_highlight_read_background,
18755            cx,
18756        );
18757    });
18758
18759    let mut prepare_rename_handler = cx
18760        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18761            move |_, _, _| async move {
18762                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18763                    start: lsp::Position {
18764                        line: 0,
18765                        character: 7,
18766                    },
18767                    end: lsp::Position {
18768                        line: 0,
18769                        character: 10,
18770                    },
18771                })))
18772            },
18773        );
18774    let prepare_rename_task = cx
18775        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18776        .expect("Prepare rename was not started");
18777    prepare_rename_handler.next().await.unwrap();
18778    prepare_rename_task.await.expect("Prepare rename failed");
18779
18780    let mut rename_handler =
18781        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18782            let edit = lsp::TextEdit {
18783                range: lsp::Range {
18784                    start: lsp::Position {
18785                        line: 0,
18786                        character: 7,
18787                    },
18788                    end: lsp::Position {
18789                        line: 0,
18790                        character: 10,
18791                    },
18792                },
18793                new_text: "FooRenamed".to_string(),
18794            };
18795            Ok(Some(lsp::WorkspaceEdit::new(
18796                // Specify the same edit twice
18797                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18798            )))
18799        });
18800    let rename_task = cx
18801        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18802        .expect("Confirm rename was not started");
18803    rename_handler.next().await.unwrap();
18804    rename_task.await.expect("Confirm rename failed");
18805    cx.run_until_parked();
18806
18807    // Despite two edits, only one is actually applied as those are identical
18808    cx.assert_editor_state(indoc! {"
18809        struct FooRenamedˇ {}
18810    "});
18811}
18812
18813#[gpui::test]
18814async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18815    init_test(cx, |_| {});
18816    // These capabilities indicate that the server does not support prepare rename.
18817    let capabilities = lsp::ServerCapabilities {
18818        rename_provider: Some(lsp::OneOf::Left(true)),
18819        ..Default::default()
18820    };
18821    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18822
18823    cx.set_state(indoc! {"
18824        struct Fˇoo {}
18825    "});
18826
18827    cx.update_editor(|editor, _window, cx| {
18828        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18829        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18830        editor.highlight_background::<DocumentHighlightRead>(
18831            &[highlight_range],
18832            |c| c.editor_document_highlight_read_background,
18833            cx,
18834        );
18835    });
18836
18837    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18838        .expect("Prepare rename was not started")
18839        .await
18840        .expect("Prepare rename failed");
18841
18842    let mut rename_handler =
18843        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18844            let edit = lsp::TextEdit {
18845                range: lsp::Range {
18846                    start: lsp::Position {
18847                        line: 0,
18848                        character: 7,
18849                    },
18850                    end: lsp::Position {
18851                        line: 0,
18852                        character: 10,
18853                    },
18854                },
18855                new_text: "FooRenamed".to_string(),
18856            };
18857            Ok(Some(lsp::WorkspaceEdit::new(
18858                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18859            )))
18860        });
18861    let rename_task = cx
18862        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18863        .expect("Confirm rename was not started");
18864    rename_handler.next().await.unwrap();
18865    rename_task.await.expect("Confirm rename failed");
18866    cx.run_until_parked();
18867
18868    // Correct range is renamed, as `surrounding_word` is used to find it.
18869    cx.assert_editor_state(indoc! {"
18870        struct FooRenamedˇ {}
18871    "});
18872}
18873
18874#[gpui::test]
18875async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18876    init_test(cx, |_| {});
18877    let mut cx = EditorTestContext::new(cx).await;
18878
18879    let language = Arc::new(
18880        Language::new(
18881            LanguageConfig::default(),
18882            Some(tree_sitter_html::LANGUAGE.into()),
18883        )
18884        .with_brackets_query(
18885            r#"
18886            ("<" @open "/>" @close)
18887            ("</" @open ">" @close)
18888            ("<" @open ">" @close)
18889            ("\"" @open "\"" @close)
18890            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18891        "#,
18892        )
18893        .unwrap(),
18894    );
18895    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18896
18897    cx.set_state(indoc! {"
18898        <span>ˇ</span>
18899    "});
18900    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18901    cx.assert_editor_state(indoc! {"
18902        <span>
18903        ˇ
18904        </span>
18905    "});
18906
18907    cx.set_state(indoc! {"
18908        <span><span></span>ˇ</span>
18909    "});
18910    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18911    cx.assert_editor_state(indoc! {"
18912        <span><span></span>
18913        ˇ</span>
18914    "});
18915
18916    cx.set_state(indoc! {"
18917        <span>ˇ
18918        </span>
18919    "});
18920    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18921    cx.assert_editor_state(indoc! {"
18922        <span>
18923        ˇ
18924        </span>
18925    "});
18926}
18927
18928#[gpui::test(iterations = 10)]
18929async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18930    init_test(cx, |_| {});
18931
18932    let fs = FakeFs::new(cx.executor());
18933    fs.insert_tree(
18934        path!("/dir"),
18935        json!({
18936            "a.ts": "a",
18937        }),
18938    )
18939    .await;
18940
18941    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18942    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18943    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18944
18945    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18946    language_registry.add(Arc::new(Language::new(
18947        LanguageConfig {
18948            name: "TypeScript".into(),
18949            matcher: LanguageMatcher {
18950                path_suffixes: vec!["ts".to_string()],
18951                ..Default::default()
18952            },
18953            ..Default::default()
18954        },
18955        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18956    )));
18957    let mut fake_language_servers = language_registry.register_fake_lsp(
18958        "TypeScript",
18959        FakeLspAdapter {
18960            capabilities: lsp::ServerCapabilities {
18961                code_lens_provider: Some(lsp::CodeLensOptions {
18962                    resolve_provider: Some(true),
18963                }),
18964                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18965                    commands: vec!["_the/command".to_string()],
18966                    ..lsp::ExecuteCommandOptions::default()
18967                }),
18968                ..lsp::ServerCapabilities::default()
18969            },
18970            ..FakeLspAdapter::default()
18971        },
18972    );
18973
18974    let (buffer, _handle) = project
18975        .update(cx, |p, cx| {
18976            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18977        })
18978        .await
18979        .unwrap();
18980    cx.executor().run_until_parked();
18981
18982    let fake_server = fake_language_servers.next().await.unwrap();
18983
18984    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18985    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18986    drop(buffer_snapshot);
18987    let actions = cx
18988        .update_window(*workspace, |_, window, cx| {
18989            project.code_actions(&buffer, anchor..anchor, window, cx)
18990        })
18991        .unwrap();
18992
18993    fake_server
18994        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18995            Ok(Some(vec![
18996                lsp::CodeLens {
18997                    range: lsp::Range::default(),
18998                    command: Some(lsp::Command {
18999                        title: "Code lens command".to_owned(),
19000                        command: "_the/command".to_owned(),
19001                        arguments: None,
19002                    }),
19003                    data: None,
19004                },
19005                lsp::CodeLens {
19006                    range: lsp::Range::default(),
19007                    command: Some(lsp::Command {
19008                        title: "Command not in capabilities".to_owned(),
19009                        command: "not in capabilities".to_owned(),
19010                        arguments: None,
19011                    }),
19012                    data: None,
19013                },
19014                lsp::CodeLens {
19015                    range: lsp::Range {
19016                        start: lsp::Position {
19017                            line: 1,
19018                            character: 1,
19019                        },
19020                        end: lsp::Position {
19021                            line: 1,
19022                            character: 1,
19023                        },
19024                    },
19025                    command: Some(lsp::Command {
19026                        title: "Command not in range".to_owned(),
19027                        command: "_the/command".to_owned(),
19028                        arguments: None,
19029                    }),
19030                    data: None,
19031                },
19032            ]))
19033        })
19034        .next()
19035        .await;
19036
19037    let actions = actions.await.unwrap();
19038    assert_eq!(
19039        actions.len(),
19040        1,
19041        "Should have only one valid action for the 0..0 range"
19042    );
19043    let action = actions[0].clone();
19044    let apply = project.update(cx, |project, cx| {
19045        project.apply_code_action(buffer.clone(), action, true, cx)
19046    });
19047
19048    // Resolving the code action does not populate its edits. In absence of
19049    // edits, we must execute the given command.
19050    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19051        |mut lens, _| async move {
19052            let lens_command = lens.command.as_mut().expect("should have a command");
19053            assert_eq!(lens_command.title, "Code lens command");
19054            lens_command.arguments = Some(vec![json!("the-argument")]);
19055            Ok(lens)
19056        },
19057    );
19058
19059    // While executing the command, the language server sends the editor
19060    // a `workspaceEdit` request.
19061    fake_server
19062        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19063            let fake = fake_server.clone();
19064            move |params, _| {
19065                assert_eq!(params.command, "_the/command");
19066                let fake = fake.clone();
19067                async move {
19068                    fake.server
19069                        .request::<lsp::request::ApplyWorkspaceEdit>(
19070                            lsp::ApplyWorkspaceEditParams {
19071                                label: None,
19072                                edit: lsp::WorkspaceEdit {
19073                                    changes: Some(
19074                                        [(
19075                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19076                                            vec![lsp::TextEdit {
19077                                                range: lsp::Range::new(
19078                                                    lsp::Position::new(0, 0),
19079                                                    lsp::Position::new(0, 0),
19080                                                ),
19081                                                new_text: "X".into(),
19082                                            }],
19083                                        )]
19084                                        .into_iter()
19085                                        .collect(),
19086                                    ),
19087                                    ..Default::default()
19088                                },
19089                            },
19090                        )
19091                        .await
19092                        .unwrap();
19093                    Ok(Some(json!(null)))
19094                }
19095            }
19096        })
19097        .next()
19098        .await;
19099
19100    // Applying the code lens command returns a project transaction containing the edits
19101    // sent by the language server in its `workspaceEdit` request.
19102    let transaction = apply.await.unwrap();
19103    assert!(transaction.0.contains_key(&buffer));
19104    buffer.update(cx, |buffer, cx| {
19105        assert_eq!(buffer.text(), "Xa");
19106        buffer.undo(cx);
19107        assert_eq!(buffer.text(), "a");
19108    });
19109}
19110
19111#[gpui::test]
19112async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19113    init_test(cx, |_| {});
19114
19115    let fs = FakeFs::new(cx.executor());
19116    let main_text = r#"fn main() {
19117println!("1");
19118println!("2");
19119println!("3");
19120println!("4");
19121println!("5");
19122}"#;
19123    let lib_text = "mod foo {}";
19124    fs.insert_tree(
19125        path!("/a"),
19126        json!({
19127            "lib.rs": lib_text,
19128            "main.rs": main_text,
19129        }),
19130    )
19131    .await;
19132
19133    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19134    let (workspace, cx) =
19135        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19136    let worktree_id = workspace.update(cx, |workspace, cx| {
19137        workspace.project().update(cx, |project, cx| {
19138            project.worktrees(cx).next().unwrap().read(cx).id()
19139        })
19140    });
19141
19142    let expected_ranges = vec![
19143        Point::new(0, 0)..Point::new(0, 0),
19144        Point::new(1, 0)..Point::new(1, 1),
19145        Point::new(2, 0)..Point::new(2, 2),
19146        Point::new(3, 0)..Point::new(3, 3),
19147    ];
19148
19149    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19150    let editor_1 = workspace
19151        .update_in(cx, |workspace, window, cx| {
19152            workspace.open_path(
19153                (worktree_id, "main.rs"),
19154                Some(pane_1.downgrade()),
19155                true,
19156                window,
19157                cx,
19158            )
19159        })
19160        .unwrap()
19161        .await
19162        .downcast::<Editor>()
19163        .unwrap();
19164    pane_1.update(cx, |pane, cx| {
19165        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19166        open_editor.update(cx, |editor, cx| {
19167            assert_eq!(
19168                editor.display_text(cx),
19169                main_text,
19170                "Original main.rs text on initial open",
19171            );
19172            assert_eq!(
19173                editor
19174                    .selections
19175                    .all::<Point>(cx)
19176                    .into_iter()
19177                    .map(|s| s.range())
19178                    .collect::<Vec<_>>(),
19179                vec![Point::zero()..Point::zero()],
19180                "Default selections on initial open",
19181            );
19182        })
19183    });
19184    editor_1.update_in(cx, |editor, window, cx| {
19185        editor.change_selections(None, window, cx, |s| {
19186            s.select_ranges(expected_ranges.clone());
19187        });
19188    });
19189
19190    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19191        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19192    });
19193    let editor_2 = workspace
19194        .update_in(cx, |workspace, window, cx| {
19195            workspace.open_path(
19196                (worktree_id, "main.rs"),
19197                Some(pane_2.downgrade()),
19198                true,
19199                window,
19200                cx,
19201            )
19202        })
19203        .unwrap()
19204        .await
19205        .downcast::<Editor>()
19206        .unwrap();
19207    pane_2.update(cx, |pane, cx| {
19208        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19209        open_editor.update(cx, |editor, cx| {
19210            assert_eq!(
19211                editor.display_text(cx),
19212                main_text,
19213                "Original main.rs text on initial open in another panel",
19214            );
19215            assert_eq!(
19216                editor
19217                    .selections
19218                    .all::<Point>(cx)
19219                    .into_iter()
19220                    .map(|s| s.range())
19221                    .collect::<Vec<_>>(),
19222                vec![Point::zero()..Point::zero()],
19223                "Default selections on initial open in another panel",
19224            );
19225        })
19226    });
19227
19228    editor_2.update_in(cx, |editor, window, cx| {
19229        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19230    });
19231
19232    let _other_editor_1 = workspace
19233        .update_in(cx, |workspace, window, cx| {
19234            workspace.open_path(
19235                (worktree_id, "lib.rs"),
19236                Some(pane_1.downgrade()),
19237                true,
19238                window,
19239                cx,
19240            )
19241        })
19242        .unwrap()
19243        .await
19244        .downcast::<Editor>()
19245        .unwrap();
19246    pane_1
19247        .update_in(cx, |pane, window, cx| {
19248            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19249                .unwrap()
19250        })
19251        .await
19252        .unwrap();
19253    drop(editor_1);
19254    pane_1.update(cx, |pane, cx| {
19255        pane.active_item()
19256            .unwrap()
19257            .downcast::<Editor>()
19258            .unwrap()
19259            .update(cx, |editor, cx| {
19260                assert_eq!(
19261                    editor.display_text(cx),
19262                    lib_text,
19263                    "Other file should be open and active",
19264                );
19265            });
19266        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19267    });
19268
19269    let _other_editor_2 = workspace
19270        .update_in(cx, |workspace, window, cx| {
19271            workspace.open_path(
19272                (worktree_id, "lib.rs"),
19273                Some(pane_2.downgrade()),
19274                true,
19275                window,
19276                cx,
19277            )
19278        })
19279        .unwrap()
19280        .await
19281        .downcast::<Editor>()
19282        .unwrap();
19283    pane_2
19284        .update_in(cx, |pane, window, cx| {
19285            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19286                .unwrap()
19287        })
19288        .await
19289        .unwrap();
19290    drop(editor_2);
19291    pane_2.update(cx, |pane, cx| {
19292        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19293        open_editor.update(cx, |editor, cx| {
19294            assert_eq!(
19295                editor.display_text(cx),
19296                lib_text,
19297                "Other file should be open and active in another panel too",
19298            );
19299        });
19300        assert_eq!(
19301            pane.items().count(),
19302            1,
19303            "No other editors should be open in another pane",
19304        );
19305    });
19306
19307    let _editor_1_reopened = workspace
19308        .update_in(cx, |workspace, window, cx| {
19309            workspace.open_path(
19310                (worktree_id, "main.rs"),
19311                Some(pane_1.downgrade()),
19312                true,
19313                window,
19314                cx,
19315            )
19316        })
19317        .unwrap()
19318        .await
19319        .downcast::<Editor>()
19320        .unwrap();
19321    let _editor_2_reopened = workspace
19322        .update_in(cx, |workspace, window, cx| {
19323            workspace.open_path(
19324                (worktree_id, "main.rs"),
19325                Some(pane_2.downgrade()),
19326                true,
19327                window,
19328                cx,
19329            )
19330        })
19331        .unwrap()
19332        .await
19333        .downcast::<Editor>()
19334        .unwrap();
19335    pane_1.update(cx, |pane, cx| {
19336        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19337        open_editor.update(cx, |editor, cx| {
19338            assert_eq!(
19339                editor.display_text(cx),
19340                main_text,
19341                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19342            );
19343            assert_eq!(
19344                editor
19345                    .selections
19346                    .all::<Point>(cx)
19347                    .into_iter()
19348                    .map(|s| s.range())
19349                    .collect::<Vec<_>>(),
19350                expected_ranges,
19351                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19352            );
19353        })
19354    });
19355    pane_2.update(cx, |pane, cx| {
19356        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19357        open_editor.update(cx, |editor, cx| {
19358            assert_eq!(
19359                editor.display_text(cx),
19360                r#"fn main() {
19361⋯rintln!("1");
19362⋯intln!("2");
19363⋯ntln!("3");
19364println!("4");
19365println!("5");
19366}"#,
19367                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19368            );
19369            assert_eq!(
19370                editor
19371                    .selections
19372                    .all::<Point>(cx)
19373                    .into_iter()
19374                    .map(|s| s.range())
19375                    .collect::<Vec<_>>(),
19376                vec![Point::zero()..Point::zero()],
19377                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19378            );
19379        })
19380    });
19381}
19382
19383#[gpui::test]
19384async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19385    init_test(cx, |_| {});
19386
19387    let fs = FakeFs::new(cx.executor());
19388    let main_text = r#"fn main() {
19389println!("1");
19390println!("2");
19391println!("3");
19392println!("4");
19393println!("5");
19394}"#;
19395    let lib_text = "mod foo {}";
19396    fs.insert_tree(
19397        path!("/a"),
19398        json!({
19399            "lib.rs": lib_text,
19400            "main.rs": main_text,
19401        }),
19402    )
19403    .await;
19404
19405    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19406    let (workspace, cx) =
19407        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19408    let worktree_id = workspace.update(cx, |workspace, cx| {
19409        workspace.project().update(cx, |project, cx| {
19410            project.worktrees(cx).next().unwrap().read(cx).id()
19411        })
19412    });
19413
19414    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19415    let editor = workspace
19416        .update_in(cx, |workspace, window, cx| {
19417            workspace.open_path(
19418                (worktree_id, "main.rs"),
19419                Some(pane.downgrade()),
19420                true,
19421                window,
19422                cx,
19423            )
19424        })
19425        .unwrap()
19426        .await
19427        .downcast::<Editor>()
19428        .unwrap();
19429    pane.update(cx, |pane, cx| {
19430        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19431        open_editor.update(cx, |editor, cx| {
19432            assert_eq!(
19433                editor.display_text(cx),
19434                main_text,
19435                "Original main.rs text on initial open",
19436            );
19437        })
19438    });
19439    editor.update_in(cx, |editor, window, cx| {
19440        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19441    });
19442
19443    cx.update_global(|store: &mut SettingsStore, cx| {
19444        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19445            s.restore_on_file_reopen = Some(false);
19446        });
19447    });
19448    editor.update_in(cx, |editor, window, cx| {
19449        editor.fold_ranges(
19450            vec![
19451                Point::new(1, 0)..Point::new(1, 1),
19452                Point::new(2, 0)..Point::new(2, 2),
19453                Point::new(3, 0)..Point::new(3, 3),
19454            ],
19455            false,
19456            window,
19457            cx,
19458        );
19459    });
19460    pane.update_in(cx, |pane, window, cx| {
19461        pane.close_all_items(&CloseAllItems::default(), window, cx)
19462            .unwrap()
19463    })
19464    .await
19465    .unwrap();
19466    pane.update(cx, |pane, _| {
19467        assert!(pane.active_item().is_none());
19468    });
19469    cx.update_global(|store: &mut SettingsStore, cx| {
19470        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19471            s.restore_on_file_reopen = Some(true);
19472        });
19473    });
19474
19475    let _editor_reopened = workspace
19476        .update_in(cx, |workspace, window, cx| {
19477            workspace.open_path(
19478                (worktree_id, "main.rs"),
19479                Some(pane.downgrade()),
19480                true,
19481                window,
19482                cx,
19483            )
19484        })
19485        .unwrap()
19486        .await
19487        .downcast::<Editor>()
19488        .unwrap();
19489    pane.update(cx, |pane, cx| {
19490        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19491        open_editor.update(cx, |editor, cx| {
19492            assert_eq!(
19493                editor.display_text(cx),
19494                main_text,
19495                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19496            );
19497        })
19498    });
19499}
19500
19501#[gpui::test]
19502async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19503    struct EmptyModalView {
19504        focus_handle: gpui::FocusHandle,
19505    }
19506    impl EventEmitter<DismissEvent> for EmptyModalView {}
19507    impl Render for EmptyModalView {
19508        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19509            div()
19510        }
19511    }
19512    impl Focusable for EmptyModalView {
19513        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19514            self.focus_handle.clone()
19515        }
19516    }
19517    impl workspace::ModalView for EmptyModalView {}
19518    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19519        EmptyModalView {
19520            focus_handle: cx.focus_handle(),
19521        }
19522    }
19523
19524    init_test(cx, |_| {});
19525
19526    let fs = FakeFs::new(cx.executor());
19527    let project = Project::test(fs, [], cx).await;
19528    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19529    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19530    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19531    let editor = cx.new_window_entity(|window, cx| {
19532        Editor::new(
19533            EditorMode::full(),
19534            buffer,
19535            Some(project.clone()),
19536            window,
19537            cx,
19538        )
19539    });
19540    workspace
19541        .update(cx, |workspace, window, cx| {
19542            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19543        })
19544        .unwrap();
19545    editor.update_in(cx, |editor, window, cx| {
19546        editor.open_context_menu(&OpenContextMenu, window, cx);
19547        assert!(editor.mouse_context_menu.is_some());
19548    });
19549    workspace
19550        .update(cx, |workspace, window, cx| {
19551            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19552        })
19553        .unwrap();
19554    cx.read(|cx| {
19555        assert!(editor.read(cx).mouse_context_menu.is_none());
19556    });
19557}
19558
19559#[gpui::test]
19560async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19561    init_test(cx, |_| {});
19562
19563    let fs = FakeFs::new(cx.executor());
19564    fs.insert_file(path!("/file.html"), Default::default())
19565        .await;
19566
19567    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19568
19569    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19570    let html_language = Arc::new(Language::new(
19571        LanguageConfig {
19572            name: "HTML".into(),
19573            matcher: LanguageMatcher {
19574                path_suffixes: vec!["html".to_string()],
19575                ..LanguageMatcher::default()
19576            },
19577            brackets: BracketPairConfig {
19578                pairs: vec![BracketPair {
19579                    start: "<".into(),
19580                    end: ">".into(),
19581                    close: true,
19582                    ..Default::default()
19583                }],
19584                ..Default::default()
19585            },
19586            ..Default::default()
19587        },
19588        Some(tree_sitter_html::LANGUAGE.into()),
19589    ));
19590    language_registry.add(html_language);
19591    let mut fake_servers = language_registry.register_fake_lsp(
19592        "HTML",
19593        FakeLspAdapter {
19594            capabilities: lsp::ServerCapabilities {
19595                completion_provider: Some(lsp::CompletionOptions {
19596                    resolve_provider: Some(true),
19597                    ..Default::default()
19598                }),
19599                ..Default::default()
19600            },
19601            ..Default::default()
19602        },
19603    );
19604
19605    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19606    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19607
19608    let worktree_id = workspace
19609        .update(cx, |workspace, _window, cx| {
19610            workspace.project().update(cx, |project, cx| {
19611                project.worktrees(cx).next().unwrap().read(cx).id()
19612            })
19613        })
19614        .unwrap();
19615    project
19616        .update(cx, |project, cx| {
19617            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19618        })
19619        .await
19620        .unwrap();
19621    let editor = workspace
19622        .update(cx, |workspace, window, cx| {
19623            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19624        })
19625        .unwrap()
19626        .await
19627        .unwrap()
19628        .downcast::<Editor>()
19629        .unwrap();
19630
19631    let fake_server = fake_servers.next().await.unwrap();
19632    editor.update_in(cx, |editor, window, cx| {
19633        editor.set_text("<ad></ad>", window, cx);
19634        editor.change_selections(None, window, cx, |selections| {
19635            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19636        });
19637        let Some((buffer, _)) = editor
19638            .buffer
19639            .read(cx)
19640            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19641        else {
19642            panic!("Failed to get buffer for selection position");
19643        };
19644        let buffer = buffer.read(cx);
19645        let buffer_id = buffer.remote_id();
19646        let opening_range =
19647            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19648        let closing_range =
19649            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19650        let mut linked_ranges = HashMap::default();
19651        linked_ranges.insert(
19652            buffer_id,
19653            vec![(opening_range.clone(), vec![closing_range.clone()])],
19654        );
19655        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19656    });
19657    let mut completion_handle =
19658        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19659            Ok(Some(lsp::CompletionResponse::Array(vec![
19660                lsp::CompletionItem {
19661                    label: "head".to_string(),
19662                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19663                        lsp::InsertReplaceEdit {
19664                            new_text: "head".to_string(),
19665                            insert: lsp::Range::new(
19666                                lsp::Position::new(0, 1),
19667                                lsp::Position::new(0, 3),
19668                            ),
19669                            replace: lsp::Range::new(
19670                                lsp::Position::new(0, 1),
19671                                lsp::Position::new(0, 3),
19672                            ),
19673                        },
19674                    )),
19675                    ..Default::default()
19676                },
19677            ])))
19678        });
19679    editor.update_in(cx, |editor, window, cx| {
19680        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19681    });
19682    cx.run_until_parked();
19683    completion_handle.next().await.unwrap();
19684    editor.update(cx, |editor, _| {
19685        assert!(
19686            editor.context_menu_visible(),
19687            "Completion menu should be visible"
19688        );
19689    });
19690    editor.update_in(cx, |editor, window, cx| {
19691        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19692    });
19693    cx.executor().run_until_parked();
19694    editor.update(cx, |editor, cx| {
19695        assert_eq!(editor.text(cx), "<head></head>");
19696    });
19697}
19698
19699fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19700    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19701    point..point
19702}
19703
19704fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19705    let (text, ranges) = marked_text_ranges(marked_text, true);
19706    assert_eq!(editor.text(cx), text);
19707    assert_eq!(
19708        editor.selections.ranges(cx),
19709        ranges,
19710        "Assert selections are {}",
19711        marked_text
19712    );
19713}
19714
19715pub fn handle_signature_help_request(
19716    cx: &mut EditorLspTestContext,
19717    mocked_response: lsp::SignatureHelp,
19718) -> impl Future<Output = ()> + use<> {
19719    let mut request =
19720        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19721            let mocked_response = mocked_response.clone();
19722            async move { Ok(Some(mocked_response)) }
19723        });
19724
19725    async move {
19726        request.next().await;
19727    }
19728}
19729
19730/// Handle completion request passing a marked string specifying where the completion
19731/// should be triggered from using '|' character, what range should be replaced, and what completions
19732/// should be returned using '<' and '>' to delimit the range.
19733///
19734/// Also see `handle_completion_request_with_insert_and_replace`.
19735#[track_caller]
19736pub fn handle_completion_request(
19737    cx: &mut EditorLspTestContext,
19738    marked_string: &str,
19739    completions: Vec<&'static str>,
19740    counter: Arc<AtomicUsize>,
19741) -> impl Future<Output = ()> {
19742    let complete_from_marker: TextRangeMarker = '|'.into();
19743    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19744    let (_, mut marked_ranges) = marked_text_ranges_by(
19745        marked_string,
19746        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19747    );
19748
19749    let complete_from_position =
19750        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19751    let replace_range =
19752        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19753
19754    let mut request =
19755        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19756            let completions = completions.clone();
19757            counter.fetch_add(1, atomic::Ordering::Release);
19758            async move {
19759                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19760                assert_eq!(
19761                    params.text_document_position.position,
19762                    complete_from_position
19763                );
19764                Ok(Some(lsp::CompletionResponse::Array(
19765                    completions
19766                        .iter()
19767                        .map(|completion_text| lsp::CompletionItem {
19768                            label: completion_text.to_string(),
19769                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19770                                range: replace_range,
19771                                new_text: completion_text.to_string(),
19772                            })),
19773                            ..Default::default()
19774                        })
19775                        .collect(),
19776                )))
19777            }
19778        });
19779
19780    async move {
19781        request.next().await;
19782    }
19783}
19784
19785/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19786/// given instead, which also contains an `insert` range.
19787///
19788/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19789/// that is, `replace_range.start..cursor_pos`.
19790pub fn handle_completion_request_with_insert_and_replace(
19791    cx: &mut EditorLspTestContext,
19792    marked_string: &str,
19793    completions: Vec<&'static str>,
19794    counter: Arc<AtomicUsize>,
19795) -> impl Future<Output = ()> {
19796    let complete_from_marker: TextRangeMarker = '|'.into();
19797    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19798    let (_, mut marked_ranges) = marked_text_ranges_by(
19799        marked_string,
19800        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19801    );
19802
19803    let complete_from_position =
19804        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19805    let replace_range =
19806        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19807
19808    let mut request =
19809        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19810            let completions = completions.clone();
19811            counter.fetch_add(1, atomic::Ordering::Release);
19812            async move {
19813                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19814                assert_eq!(
19815                    params.text_document_position.position, complete_from_position,
19816                    "marker `|` position doesn't match",
19817                );
19818                Ok(Some(lsp::CompletionResponse::Array(
19819                    completions
19820                        .iter()
19821                        .map(|completion_text| lsp::CompletionItem {
19822                            label: completion_text.to_string(),
19823                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19824                                lsp::InsertReplaceEdit {
19825                                    insert: lsp::Range {
19826                                        start: replace_range.start,
19827                                        end: complete_from_position,
19828                                    },
19829                                    replace: replace_range,
19830                                    new_text: completion_text.to_string(),
19831                                },
19832                            )),
19833                            ..Default::default()
19834                        })
19835                        .collect(),
19836                )))
19837            }
19838        });
19839
19840    async move {
19841        request.next().await;
19842    }
19843}
19844
19845fn handle_resolve_completion_request(
19846    cx: &mut EditorLspTestContext,
19847    edits: Option<Vec<(&'static str, &'static str)>>,
19848) -> impl Future<Output = ()> {
19849    let edits = edits.map(|edits| {
19850        edits
19851            .iter()
19852            .map(|(marked_string, new_text)| {
19853                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19854                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19855                lsp::TextEdit::new(replace_range, new_text.to_string())
19856            })
19857            .collect::<Vec<_>>()
19858    });
19859
19860    let mut request =
19861        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19862            let edits = edits.clone();
19863            async move {
19864                Ok(lsp::CompletionItem {
19865                    additional_text_edits: edits,
19866                    ..Default::default()
19867                })
19868            }
19869        });
19870
19871    async move {
19872        request.next().await;
19873    }
19874}
19875
19876pub(crate) fn update_test_language_settings(
19877    cx: &mut TestAppContext,
19878    f: impl Fn(&mut AllLanguageSettingsContent),
19879) {
19880    cx.update(|cx| {
19881        SettingsStore::update_global(cx, |store, cx| {
19882            store.update_user_settings::<AllLanguageSettings>(cx, f);
19883        });
19884    });
19885}
19886
19887pub(crate) fn update_test_project_settings(
19888    cx: &mut TestAppContext,
19889    f: impl Fn(&mut ProjectSettings),
19890) {
19891    cx.update(|cx| {
19892        SettingsStore::update_global(cx, |store, cx| {
19893            store.update_user_settings::<ProjectSettings>(cx, f);
19894        });
19895    });
19896}
19897
19898pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19899    cx.update(|cx| {
19900        assets::Assets.load_test_fonts(cx);
19901        let store = SettingsStore::test(cx);
19902        cx.set_global(store);
19903        theme::init(theme::LoadThemes::JustBase, cx);
19904        release_channel::init(SemanticVersion::default(), cx);
19905        client::init_settings(cx);
19906        language::init(cx);
19907        Project::init_settings(cx);
19908        workspace::init_settings(cx);
19909        crate::init(cx);
19910    });
19911
19912    update_test_language_settings(cx, f);
19913}
19914
19915#[track_caller]
19916fn assert_hunk_revert(
19917    not_reverted_text_with_selections: &str,
19918    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19919    expected_reverted_text_with_selections: &str,
19920    base_text: &str,
19921    cx: &mut EditorLspTestContext,
19922) {
19923    cx.set_state(not_reverted_text_with_selections);
19924    cx.set_head_text(base_text);
19925    cx.executor().run_until_parked();
19926
19927    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19928        let snapshot = editor.snapshot(window, cx);
19929        let reverted_hunk_statuses = snapshot
19930            .buffer_snapshot
19931            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19932            .map(|hunk| hunk.status().kind)
19933            .collect::<Vec<_>>();
19934
19935        editor.git_restore(&Default::default(), window, cx);
19936        reverted_hunk_statuses
19937    });
19938    cx.executor().run_until_parked();
19939    cx.assert_editor_state(expected_reverted_text_with_selections);
19940    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19941}