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\ndef«abcˇ»\n«abcˇ»");
 6134
 6135    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6136        .unwrap();
 6137    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6138}
 6139
 6140#[gpui::test]
 6141async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6142    init_test(cx, |_| {});
 6143
 6144    let mut cx = EditorTestContext::new(cx).await;
 6145    cx.set_state("");
 6146
 6147    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6148        .unwrap();
 6149    cx.assert_editor_state("«aˇ»");
 6150    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6151        .unwrap();
 6152    cx.assert_editor_state("«aˇ»");
 6153}
 6154
 6155#[gpui::test]
 6156async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6157    init_test(cx, |_| {});
 6158
 6159    let mut cx = EditorTestContext::new(cx).await;
 6160    cx.set_state(
 6161        r#"let foo = 2;
 6162lˇet foo = 2;
 6163let fooˇ = 2;
 6164let foo = 2;
 6165let foo = ˇ2;"#,
 6166    );
 6167
 6168    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6169        .unwrap();
 6170    cx.assert_editor_state(
 6171        r#"let foo = 2;
 6172«letˇ» foo = 2;
 6173let «fooˇ» = 2;
 6174let foo = 2;
 6175let foo = «2ˇ»;"#,
 6176    );
 6177
 6178    // noop for multiple selections with different contents
 6179    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6180        .unwrap();
 6181    cx.assert_editor_state(
 6182        r#"let foo = 2;
 6183«letˇ» foo = 2;
 6184let «fooˇ» = 2;
 6185let foo = 2;
 6186let foo = «2ˇ»;"#,
 6187    );
 6188}
 6189
 6190#[gpui::test]
 6191async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6192    init_test(cx, |_| {});
 6193
 6194    let mut cx = EditorTestContext::new(cx).await;
 6195    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6196
 6197    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6198        .unwrap();
 6199    // selection direction is preserved
 6200    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6201
 6202    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6203        .unwrap();
 6204    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6205
 6206    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6207    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6208
 6209    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6210    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\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    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6217        .unwrap();
 6218    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6219}
 6220
 6221#[gpui::test]
 6222async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6223    init_test(cx, |_| {});
 6224
 6225    let language = Arc::new(Language::new(
 6226        LanguageConfig::default(),
 6227        Some(tree_sitter_rust::LANGUAGE.into()),
 6228    ));
 6229
 6230    let text = r#"
 6231        use mod1::mod2::{mod3, mod4};
 6232
 6233        fn fn_1(param1: bool, param2: &str) {
 6234            let var1 = "text";
 6235        }
 6236    "#
 6237    .unindent();
 6238
 6239    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6240    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6241    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6242
 6243    editor
 6244        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6245        .await;
 6246
 6247    editor.update_in(cx, |editor, window, cx| {
 6248        editor.change_selections(None, window, cx, |s| {
 6249            s.select_display_ranges([
 6250                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6251                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6252                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6253            ]);
 6254        });
 6255        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6256    });
 6257    editor.update(cx, |editor, cx| {
 6258        assert_text_with_selections(
 6259            editor,
 6260            indoc! {r#"
 6261                use mod1::mod2::{mod3, «mod4ˇ»};
 6262
 6263                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6264                    let var1 = "«ˇtext»";
 6265                }
 6266            "#},
 6267            cx,
 6268        );
 6269    });
 6270
 6271    editor.update_in(cx, |editor, window, cx| {
 6272        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6273    });
 6274    editor.update(cx, |editor, cx| {
 6275        assert_text_with_selections(
 6276            editor,
 6277            indoc! {r#"
 6278                use mod1::mod2::«{mod3, mod4}ˇ»;
 6279
 6280                «ˇfn fn_1(param1: bool, param2: &str) {
 6281                    let var1 = "text";
 6282 6283            "#},
 6284            cx,
 6285        );
 6286    });
 6287
 6288    editor.update_in(cx, |editor, window, cx| {
 6289        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6290    });
 6291    assert_eq!(
 6292        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6293        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6294    );
 6295
 6296    // Trying to expand the selected syntax node one more time has no effect.
 6297    editor.update_in(cx, |editor, window, cx| {
 6298        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6299    });
 6300    assert_eq!(
 6301        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6302        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6303    );
 6304
 6305    editor.update_in(cx, |editor, window, cx| {
 6306        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6307    });
 6308    editor.update(cx, |editor, cx| {
 6309        assert_text_with_selections(
 6310            editor,
 6311            indoc! {r#"
 6312                use mod1::mod2::«{mod3, mod4}ˇ»;
 6313
 6314                «ˇfn fn_1(param1: bool, param2: &str) {
 6315                    let var1 = "text";
 6316 6317            "#},
 6318            cx,
 6319        );
 6320    });
 6321
 6322    editor.update_in(cx, |editor, window, cx| {
 6323        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6324    });
 6325    editor.update(cx, |editor, cx| {
 6326        assert_text_with_selections(
 6327            editor,
 6328            indoc! {r#"
 6329                use mod1::mod2::{mod3, «mod4ˇ»};
 6330
 6331                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6332                    let var1 = "«ˇtext»";
 6333                }
 6334            "#},
 6335            cx,
 6336        );
 6337    });
 6338
 6339    editor.update_in(cx, |editor, window, cx| {
 6340        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6341    });
 6342    editor.update(cx, |editor, cx| {
 6343        assert_text_with_selections(
 6344            editor,
 6345            indoc! {r#"
 6346                use mod1::mod2::{mod3, mo«ˇ»d4};
 6347
 6348                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6349                    let var1 = "te«ˇ»xt";
 6350                }
 6351            "#},
 6352            cx,
 6353        );
 6354    });
 6355
 6356    // Trying to shrink the selected syntax node one more time has no effect.
 6357    editor.update_in(cx, |editor, window, cx| {
 6358        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6359    });
 6360    editor.update_in(cx, |editor, _, cx| {
 6361        assert_text_with_selections(
 6362            editor,
 6363            indoc! {r#"
 6364                use mod1::mod2::{mod3, mo«ˇ»d4};
 6365
 6366                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6367                    let var1 = "te«ˇ»xt";
 6368                }
 6369            "#},
 6370            cx,
 6371        );
 6372    });
 6373
 6374    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6375    // a fold.
 6376    editor.update_in(cx, |editor, window, cx| {
 6377        editor.fold_creases(
 6378            vec![
 6379                Crease::simple(
 6380                    Point::new(0, 21)..Point::new(0, 24),
 6381                    FoldPlaceholder::test(),
 6382                ),
 6383                Crease::simple(
 6384                    Point::new(3, 20)..Point::new(3, 22),
 6385                    FoldPlaceholder::test(),
 6386                ),
 6387            ],
 6388            true,
 6389            window,
 6390            cx,
 6391        );
 6392        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6393    });
 6394    editor.update(cx, |editor, cx| {
 6395        assert_text_with_selections(
 6396            editor,
 6397            indoc! {r#"
 6398                use mod1::mod2::«{mod3, mod4}ˇ»;
 6399
 6400                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6401                    let var1 = "«ˇtext»";
 6402                }
 6403            "#},
 6404            cx,
 6405        );
 6406    });
 6407}
 6408
 6409#[gpui::test]
 6410async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6411    init_test(cx, |_| {});
 6412
 6413    let language = Arc::new(Language::new(
 6414        LanguageConfig::default(),
 6415        Some(tree_sitter_rust::LANGUAGE.into()),
 6416    ));
 6417
 6418    let text = r#"
 6419        use mod1::mod2::{mod3, mod4};
 6420
 6421        fn fn_1(param1: bool, param2: &str) {
 6422            let var1 = "hello world";
 6423        }
 6424    "#
 6425    .unindent();
 6426
 6427    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6428    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6429    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6430
 6431    editor
 6432        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6433        .await;
 6434
 6435    // Test 1: Cursor on a letter of a string word
 6436    editor.update_in(cx, |editor, window, cx| {
 6437        editor.change_selections(None, window, cx, |s| {
 6438            s.select_display_ranges([
 6439                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6440            ]);
 6441        });
 6442    });
 6443    editor.update_in(cx, |editor, window, cx| {
 6444        assert_text_with_selections(
 6445            editor,
 6446            indoc! {r#"
 6447                use mod1::mod2::{mod3, mod4};
 6448
 6449                fn fn_1(param1: bool, param2: &str) {
 6450                    let var1 = "hˇello world";
 6451                }
 6452            "#},
 6453            cx,
 6454        );
 6455        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6456        assert_text_with_selections(
 6457            editor,
 6458            indoc! {r#"
 6459                use mod1::mod2::{mod3, mod4};
 6460
 6461                fn fn_1(param1: bool, param2: &str) {
 6462                    let var1 = "«ˇhello» world";
 6463                }
 6464            "#},
 6465            cx,
 6466        );
 6467    });
 6468
 6469    // Test 2: Partial selection within a word
 6470    editor.update_in(cx, |editor, window, cx| {
 6471        editor.change_selections(None, window, cx, |s| {
 6472            s.select_display_ranges([
 6473                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6474            ]);
 6475        });
 6476    });
 6477    editor.update_in(cx, |editor, window, cx| {
 6478        assert_text_with_selections(
 6479            editor,
 6480            indoc! {r#"
 6481                use mod1::mod2::{mod3, mod4};
 6482
 6483                fn fn_1(param1: bool, param2: &str) {
 6484                    let var1 = "h«elˇ»lo world";
 6485                }
 6486            "#},
 6487            cx,
 6488        );
 6489        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6490        assert_text_with_selections(
 6491            editor,
 6492            indoc! {r#"
 6493                use mod1::mod2::{mod3, mod4};
 6494
 6495                fn fn_1(param1: bool, param2: &str) {
 6496                    let var1 = "«ˇhello» world";
 6497                }
 6498            "#},
 6499            cx,
 6500        );
 6501    });
 6502
 6503    // Test 3: Complete word already selected
 6504    editor.update_in(cx, |editor, window, cx| {
 6505        editor.change_selections(None, window, cx, |s| {
 6506            s.select_display_ranges([
 6507                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6508            ]);
 6509        });
 6510    });
 6511    editor.update_in(cx, |editor, window, cx| {
 6512        assert_text_with_selections(
 6513            editor,
 6514            indoc! {r#"
 6515                use mod1::mod2::{mod3, mod4};
 6516
 6517                fn fn_1(param1: bool, param2: &str) {
 6518                    let var1 = "«helloˇ» world";
 6519                }
 6520            "#},
 6521            cx,
 6522        );
 6523        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6524        assert_text_with_selections(
 6525            editor,
 6526            indoc! {r#"
 6527                use mod1::mod2::{mod3, mod4};
 6528
 6529                fn fn_1(param1: bool, param2: &str) {
 6530                    let var1 = "«hello worldˇ»";
 6531                }
 6532            "#},
 6533            cx,
 6534        );
 6535    });
 6536
 6537    // Test 4: Selection spanning across words
 6538    editor.update_in(cx, |editor, window, cx| {
 6539        editor.change_selections(None, window, cx, |s| {
 6540            s.select_display_ranges([
 6541                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6542            ]);
 6543        });
 6544    });
 6545    editor.update_in(cx, |editor, window, cx| {
 6546        assert_text_with_selections(
 6547            editor,
 6548            indoc! {r#"
 6549                use mod1::mod2::{mod3, mod4};
 6550
 6551                fn fn_1(param1: bool, param2: &str) {
 6552                    let var1 = "hel«lo woˇ»rld";
 6553                }
 6554            "#},
 6555            cx,
 6556        );
 6557        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6558        assert_text_with_selections(
 6559            editor,
 6560            indoc! {r#"
 6561                use mod1::mod2::{mod3, mod4};
 6562
 6563                fn fn_1(param1: bool, param2: &str) {
 6564                    let var1 = "«ˇhello world»";
 6565                }
 6566            "#},
 6567            cx,
 6568        );
 6569    });
 6570
 6571    // Test 5: Expansion beyond string
 6572    editor.update_in(cx, |editor, window, cx| {
 6573        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6574        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6575        assert_text_with_selections(
 6576            editor,
 6577            indoc! {r#"
 6578                use mod1::mod2::{mod3, mod4};
 6579
 6580                fn fn_1(param1: bool, param2: &str) {
 6581                    «ˇlet var1 = "hello world";»
 6582                }
 6583            "#},
 6584            cx,
 6585        );
 6586    });
 6587}
 6588
 6589#[gpui::test]
 6590async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6591    init_test(cx, |_| {});
 6592
 6593    let base_text = r#"
 6594        impl A {
 6595            // this is an uncommitted comment
 6596
 6597            fn b() {
 6598                c();
 6599            }
 6600
 6601            // this is another uncommitted comment
 6602
 6603            fn d() {
 6604                // e
 6605                // f
 6606            }
 6607        }
 6608
 6609        fn g() {
 6610            // h
 6611        }
 6612    "#
 6613    .unindent();
 6614
 6615    let text = r#"
 6616        ˇimpl A {
 6617
 6618            fn b() {
 6619                c();
 6620            }
 6621
 6622            fn d() {
 6623                // e
 6624                // f
 6625            }
 6626        }
 6627
 6628        fn g() {
 6629            // h
 6630        }
 6631    "#
 6632    .unindent();
 6633
 6634    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6635    cx.set_state(&text);
 6636    cx.set_head_text(&base_text);
 6637    cx.update_editor(|editor, window, cx| {
 6638        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6639    });
 6640
 6641    cx.assert_state_with_diff(
 6642        "
 6643        ˇimpl A {
 6644      -     // this is an uncommitted comment
 6645
 6646            fn b() {
 6647                c();
 6648            }
 6649
 6650      -     // this is another uncommitted comment
 6651      -
 6652            fn d() {
 6653                // e
 6654                // f
 6655            }
 6656        }
 6657
 6658        fn g() {
 6659            // h
 6660        }
 6661    "
 6662        .unindent(),
 6663    );
 6664
 6665    let expected_display_text = "
 6666        impl A {
 6667            // this is an uncommitted comment
 6668
 6669            fn b() {
 6670 6671            }
 6672
 6673            // this is another uncommitted comment
 6674
 6675            fn d() {
 6676 6677            }
 6678        }
 6679
 6680        fn g() {
 6681 6682        }
 6683        "
 6684    .unindent();
 6685
 6686    cx.update_editor(|editor, window, cx| {
 6687        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6688        assert_eq!(editor.display_text(cx), expected_display_text);
 6689    });
 6690}
 6691
 6692#[gpui::test]
 6693async fn test_autoindent(cx: &mut TestAppContext) {
 6694    init_test(cx, |_| {});
 6695
 6696    let language = Arc::new(
 6697        Language::new(
 6698            LanguageConfig {
 6699                brackets: BracketPairConfig {
 6700                    pairs: vec![
 6701                        BracketPair {
 6702                            start: "{".to_string(),
 6703                            end: "}".to_string(),
 6704                            close: false,
 6705                            surround: false,
 6706                            newline: true,
 6707                        },
 6708                        BracketPair {
 6709                            start: "(".to_string(),
 6710                            end: ")".to_string(),
 6711                            close: false,
 6712                            surround: false,
 6713                            newline: true,
 6714                        },
 6715                    ],
 6716                    ..Default::default()
 6717                },
 6718                ..Default::default()
 6719            },
 6720            Some(tree_sitter_rust::LANGUAGE.into()),
 6721        )
 6722        .with_indents_query(
 6723            r#"
 6724                (_ "(" ")" @end) @indent
 6725                (_ "{" "}" @end) @indent
 6726            "#,
 6727        )
 6728        .unwrap(),
 6729    );
 6730
 6731    let text = "fn a() {}";
 6732
 6733    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6734    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6735    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6736    editor
 6737        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6738        .await;
 6739
 6740    editor.update_in(cx, |editor, window, cx| {
 6741        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6742        editor.newline(&Newline, window, cx);
 6743        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6744        assert_eq!(
 6745            editor.selections.ranges(cx),
 6746            &[
 6747                Point::new(1, 4)..Point::new(1, 4),
 6748                Point::new(3, 4)..Point::new(3, 4),
 6749                Point::new(5, 0)..Point::new(5, 0)
 6750            ]
 6751        );
 6752    });
 6753}
 6754
 6755#[gpui::test]
 6756async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6757    init_test(cx, |_| {});
 6758
 6759    {
 6760        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6761        cx.set_state(indoc! {"
 6762            impl A {
 6763
 6764                fn b() {}
 6765
 6766            «fn c() {
 6767
 6768            }ˇ»
 6769            }
 6770        "});
 6771
 6772        cx.update_editor(|editor, window, cx| {
 6773            editor.autoindent(&Default::default(), window, cx);
 6774        });
 6775
 6776        cx.assert_editor_state(indoc! {"
 6777            impl A {
 6778
 6779                fn b() {}
 6780
 6781                «fn c() {
 6782
 6783                }ˇ»
 6784            }
 6785        "});
 6786    }
 6787
 6788    {
 6789        let mut cx = EditorTestContext::new_multibuffer(
 6790            cx,
 6791            [indoc! { "
 6792                impl A {
 6793                «
 6794                // a
 6795                fn b(){}
 6796                »
 6797                «
 6798                    }
 6799                    fn c(){}
 6800                »
 6801            "}],
 6802        );
 6803
 6804        let buffer = cx.update_editor(|editor, _, cx| {
 6805            let buffer = editor.buffer().update(cx, |buffer, _| {
 6806                buffer.all_buffers().iter().next().unwrap().clone()
 6807            });
 6808            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6809            buffer
 6810        });
 6811
 6812        cx.run_until_parked();
 6813        cx.update_editor(|editor, window, cx| {
 6814            editor.select_all(&Default::default(), window, cx);
 6815            editor.autoindent(&Default::default(), window, cx)
 6816        });
 6817        cx.run_until_parked();
 6818
 6819        cx.update(|_, cx| {
 6820            assert_eq!(
 6821                buffer.read(cx).text(),
 6822                indoc! { "
 6823                    impl A {
 6824
 6825                        // a
 6826                        fn b(){}
 6827
 6828
 6829                    }
 6830                    fn c(){}
 6831
 6832                " }
 6833            )
 6834        });
 6835    }
 6836}
 6837
 6838#[gpui::test]
 6839async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6840    init_test(cx, |_| {});
 6841
 6842    let mut cx = EditorTestContext::new(cx).await;
 6843
 6844    let language = Arc::new(Language::new(
 6845        LanguageConfig {
 6846            brackets: BracketPairConfig {
 6847                pairs: vec![
 6848                    BracketPair {
 6849                        start: "{".to_string(),
 6850                        end: "}".to_string(),
 6851                        close: true,
 6852                        surround: true,
 6853                        newline: true,
 6854                    },
 6855                    BracketPair {
 6856                        start: "(".to_string(),
 6857                        end: ")".to_string(),
 6858                        close: true,
 6859                        surround: true,
 6860                        newline: true,
 6861                    },
 6862                    BracketPair {
 6863                        start: "/*".to_string(),
 6864                        end: " */".to_string(),
 6865                        close: true,
 6866                        surround: true,
 6867                        newline: true,
 6868                    },
 6869                    BracketPair {
 6870                        start: "[".to_string(),
 6871                        end: "]".to_string(),
 6872                        close: false,
 6873                        surround: false,
 6874                        newline: true,
 6875                    },
 6876                    BracketPair {
 6877                        start: "\"".to_string(),
 6878                        end: "\"".to_string(),
 6879                        close: true,
 6880                        surround: true,
 6881                        newline: false,
 6882                    },
 6883                    BracketPair {
 6884                        start: "<".to_string(),
 6885                        end: ">".to_string(),
 6886                        close: false,
 6887                        surround: true,
 6888                        newline: true,
 6889                    },
 6890                ],
 6891                ..Default::default()
 6892            },
 6893            autoclose_before: "})]".to_string(),
 6894            ..Default::default()
 6895        },
 6896        Some(tree_sitter_rust::LANGUAGE.into()),
 6897    ));
 6898
 6899    cx.language_registry().add(language.clone());
 6900    cx.update_buffer(|buffer, cx| {
 6901        buffer.set_language(Some(language), cx);
 6902    });
 6903
 6904    cx.set_state(
 6905        &r#"
 6906            🏀ˇ
 6907            εˇ
 6908            ❤️ˇ
 6909        "#
 6910        .unindent(),
 6911    );
 6912
 6913    // autoclose multiple nested brackets at multiple cursors
 6914    cx.update_editor(|editor, window, cx| {
 6915        editor.handle_input("{", window, cx);
 6916        editor.handle_input("{", window, cx);
 6917        editor.handle_input("{", window, cx);
 6918    });
 6919    cx.assert_editor_state(
 6920        &"
 6921            🏀{{{ˇ}}}
 6922            ε{{{ˇ}}}
 6923            ❤️{{{ˇ}}}
 6924        "
 6925        .unindent(),
 6926    );
 6927
 6928    // insert a different closing bracket
 6929    cx.update_editor(|editor, window, cx| {
 6930        editor.handle_input(")", window, cx);
 6931    });
 6932    cx.assert_editor_state(
 6933        &"
 6934            🏀{{{)ˇ}}}
 6935            ε{{{)ˇ}}}
 6936            ❤️{{{)ˇ}}}
 6937        "
 6938        .unindent(),
 6939    );
 6940
 6941    // skip over the auto-closed brackets when typing a closing bracket
 6942    cx.update_editor(|editor, window, cx| {
 6943        editor.move_right(&MoveRight, window, cx);
 6944        editor.handle_input("}", window, cx);
 6945        editor.handle_input("}", window, cx);
 6946        editor.handle_input("}", window, cx);
 6947    });
 6948    cx.assert_editor_state(
 6949        &"
 6950            🏀{{{)}}}}ˇ
 6951            ε{{{)}}}}ˇ
 6952            ❤️{{{)}}}}ˇ
 6953        "
 6954        .unindent(),
 6955    );
 6956
 6957    // autoclose multi-character pairs
 6958    cx.set_state(
 6959        &"
 6960            ˇ
 6961            ˇ
 6962        "
 6963        .unindent(),
 6964    );
 6965    cx.update_editor(|editor, window, cx| {
 6966        editor.handle_input("/", window, cx);
 6967        editor.handle_input("*", window, cx);
 6968    });
 6969    cx.assert_editor_state(
 6970        &"
 6971            /*ˇ */
 6972            /*ˇ */
 6973        "
 6974        .unindent(),
 6975    );
 6976
 6977    // one cursor autocloses a multi-character pair, one cursor
 6978    // does not autoclose.
 6979    cx.set_state(
 6980        &"
 6981 6982            ˇ
 6983        "
 6984        .unindent(),
 6985    );
 6986    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6987    cx.assert_editor_state(
 6988        &"
 6989            /*ˇ */
 6990 6991        "
 6992        .unindent(),
 6993    );
 6994
 6995    // Don't autoclose if the next character isn't whitespace and isn't
 6996    // listed in the language's "autoclose_before" section.
 6997    cx.set_state("ˇa b");
 6998    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6999    cx.assert_editor_state("{ˇa b");
 7000
 7001    // Don't autoclose if `close` is false for the bracket pair
 7002    cx.set_state("ˇ");
 7003    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7004    cx.assert_editor_state("");
 7005
 7006    // Surround with brackets if text is selected
 7007    cx.set_state("«aˇ» b");
 7008    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7009    cx.assert_editor_state("{«aˇ»} b");
 7010
 7011    // Autoclose when not immediately after a word character
 7012    cx.set_state("a ˇ");
 7013    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7014    cx.assert_editor_state("a \"ˇ\"");
 7015
 7016    // Autoclose pair where the start and end characters are the same
 7017    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7018    cx.assert_editor_state("a \"\"ˇ");
 7019
 7020    // Don't autoclose when immediately after a word character
 7021    cx.set_state("");
 7022    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7023    cx.assert_editor_state("a\"ˇ");
 7024
 7025    // Do autoclose when after a non-word character
 7026    cx.set_state("");
 7027    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7028    cx.assert_editor_state("{\"ˇ\"");
 7029
 7030    // Non identical pairs autoclose regardless of preceding character
 7031    cx.set_state("");
 7032    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7033    cx.assert_editor_state("a{ˇ}");
 7034
 7035    // Don't autoclose pair if autoclose is disabled
 7036    cx.set_state("ˇ");
 7037    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7038    cx.assert_editor_state("");
 7039
 7040    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7041    cx.set_state("«aˇ» b");
 7042    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7043    cx.assert_editor_state("<«aˇ»> b");
 7044}
 7045
 7046#[gpui::test]
 7047async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7048    init_test(cx, |settings| {
 7049        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7050    });
 7051
 7052    let mut cx = EditorTestContext::new(cx).await;
 7053
 7054    let language = Arc::new(Language::new(
 7055        LanguageConfig {
 7056            brackets: BracketPairConfig {
 7057                pairs: vec![
 7058                    BracketPair {
 7059                        start: "{".to_string(),
 7060                        end: "}".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        newline: true,
 7064                    },
 7065                    BracketPair {
 7066                        start: "(".to_string(),
 7067                        end: ")".to_string(),
 7068                        close: true,
 7069                        surround: true,
 7070                        newline: true,
 7071                    },
 7072                    BracketPair {
 7073                        start: "[".to_string(),
 7074                        end: "]".to_string(),
 7075                        close: false,
 7076                        surround: false,
 7077                        newline: true,
 7078                    },
 7079                ],
 7080                ..Default::default()
 7081            },
 7082            autoclose_before: "})]".to_string(),
 7083            ..Default::default()
 7084        },
 7085        Some(tree_sitter_rust::LANGUAGE.into()),
 7086    ));
 7087
 7088    cx.language_registry().add(language.clone());
 7089    cx.update_buffer(|buffer, cx| {
 7090        buffer.set_language(Some(language), cx);
 7091    });
 7092
 7093    cx.set_state(
 7094        &"
 7095            ˇ
 7096            ˇ
 7097            ˇ
 7098        "
 7099        .unindent(),
 7100    );
 7101
 7102    // ensure only matching closing brackets are skipped over
 7103    cx.update_editor(|editor, window, cx| {
 7104        editor.handle_input("}", window, cx);
 7105        editor.move_left(&MoveLeft, window, cx);
 7106        editor.handle_input(")", window, cx);
 7107        editor.move_left(&MoveLeft, window, cx);
 7108    });
 7109    cx.assert_editor_state(
 7110        &"
 7111            ˇ)}
 7112            ˇ)}
 7113            ˇ)}
 7114        "
 7115        .unindent(),
 7116    );
 7117
 7118    // skip-over closing brackets at multiple cursors
 7119    cx.update_editor(|editor, window, cx| {
 7120        editor.handle_input(")", window, cx);
 7121        editor.handle_input("}", window, cx);
 7122    });
 7123    cx.assert_editor_state(
 7124        &"
 7125            )}ˇ
 7126            )}ˇ
 7127            )}ˇ
 7128        "
 7129        .unindent(),
 7130    );
 7131
 7132    // ignore non-close brackets
 7133    cx.update_editor(|editor, window, cx| {
 7134        editor.handle_input("]", window, cx);
 7135        editor.move_left(&MoveLeft, window, cx);
 7136        editor.handle_input("]", window, cx);
 7137    });
 7138    cx.assert_editor_state(
 7139        &"
 7140            )}]ˇ]
 7141            )}]ˇ]
 7142            )}]ˇ]
 7143        "
 7144        .unindent(),
 7145    );
 7146}
 7147
 7148#[gpui::test]
 7149async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7150    init_test(cx, |_| {});
 7151
 7152    let mut cx = EditorTestContext::new(cx).await;
 7153
 7154    let html_language = Arc::new(
 7155        Language::new(
 7156            LanguageConfig {
 7157                name: "HTML".into(),
 7158                brackets: BracketPairConfig {
 7159                    pairs: vec![
 7160                        BracketPair {
 7161                            start: "<".into(),
 7162                            end: ">".into(),
 7163                            close: true,
 7164                            ..Default::default()
 7165                        },
 7166                        BracketPair {
 7167                            start: "{".into(),
 7168                            end: "}".into(),
 7169                            close: true,
 7170                            ..Default::default()
 7171                        },
 7172                        BracketPair {
 7173                            start: "(".into(),
 7174                            end: ")".into(),
 7175                            close: true,
 7176                            ..Default::default()
 7177                        },
 7178                    ],
 7179                    ..Default::default()
 7180                },
 7181                autoclose_before: "})]>".into(),
 7182                ..Default::default()
 7183            },
 7184            Some(tree_sitter_html::LANGUAGE.into()),
 7185        )
 7186        .with_injection_query(
 7187            r#"
 7188            (script_element
 7189                (raw_text) @injection.content
 7190                (#set! injection.language "javascript"))
 7191            "#,
 7192        )
 7193        .unwrap(),
 7194    );
 7195
 7196    let javascript_language = Arc::new(Language::new(
 7197        LanguageConfig {
 7198            name: "JavaScript".into(),
 7199            brackets: BracketPairConfig {
 7200                pairs: vec![
 7201                    BracketPair {
 7202                        start: "/*".into(),
 7203                        end: " */".into(),
 7204                        close: true,
 7205                        ..Default::default()
 7206                    },
 7207                    BracketPair {
 7208                        start: "{".into(),
 7209                        end: "}".into(),
 7210                        close: true,
 7211                        ..Default::default()
 7212                    },
 7213                    BracketPair {
 7214                        start: "(".into(),
 7215                        end: ")".into(),
 7216                        close: true,
 7217                        ..Default::default()
 7218                    },
 7219                ],
 7220                ..Default::default()
 7221            },
 7222            autoclose_before: "})]>".into(),
 7223            ..Default::default()
 7224        },
 7225        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7226    ));
 7227
 7228    cx.language_registry().add(html_language.clone());
 7229    cx.language_registry().add(javascript_language.clone());
 7230
 7231    cx.update_buffer(|buffer, cx| {
 7232        buffer.set_language(Some(html_language), cx);
 7233    });
 7234
 7235    cx.set_state(
 7236        &r#"
 7237            <body>ˇ
 7238                <script>
 7239                    var x = 1;ˇ
 7240                </script>
 7241            </body>ˇ
 7242        "#
 7243        .unindent(),
 7244    );
 7245
 7246    // Precondition: different languages are active at different locations.
 7247    cx.update_editor(|editor, window, cx| {
 7248        let snapshot = editor.snapshot(window, cx);
 7249        let cursors = editor.selections.ranges::<usize>(cx);
 7250        let languages = cursors
 7251            .iter()
 7252            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7253            .collect::<Vec<_>>();
 7254        assert_eq!(
 7255            languages,
 7256            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7257        );
 7258    });
 7259
 7260    // Angle brackets autoclose in HTML, but not JavaScript.
 7261    cx.update_editor(|editor, window, cx| {
 7262        editor.handle_input("<", window, cx);
 7263        editor.handle_input("a", window, cx);
 7264    });
 7265    cx.assert_editor_state(
 7266        &r#"
 7267            <body><aˇ>
 7268                <script>
 7269                    var x = 1;<aˇ
 7270                </script>
 7271            </body><aˇ>
 7272        "#
 7273        .unindent(),
 7274    );
 7275
 7276    // Curly braces and parens autoclose in both HTML and JavaScript.
 7277    cx.update_editor(|editor, window, cx| {
 7278        editor.handle_input(" b=", window, cx);
 7279        editor.handle_input("{", window, cx);
 7280        editor.handle_input("c", window, cx);
 7281        editor.handle_input("(", window, cx);
 7282    });
 7283    cx.assert_editor_state(
 7284        &r#"
 7285            <body><a b={c(ˇ)}>
 7286                <script>
 7287                    var x = 1;<a b={c(ˇ)}
 7288                </script>
 7289            </body><a b={c(ˇ)}>
 7290        "#
 7291        .unindent(),
 7292    );
 7293
 7294    // Brackets that were already autoclosed are skipped.
 7295    cx.update_editor(|editor, window, cx| {
 7296        editor.handle_input(")", window, cx);
 7297        editor.handle_input("d", window, cx);
 7298        editor.handle_input("}", window, cx);
 7299    });
 7300    cx.assert_editor_state(
 7301        &r#"
 7302            <body><a b={c()d}ˇ>
 7303                <script>
 7304                    var x = 1;<a b={c()d}ˇ
 7305                </script>
 7306            </body><a b={c()d}ˇ>
 7307        "#
 7308        .unindent(),
 7309    );
 7310    cx.update_editor(|editor, window, cx| {
 7311        editor.handle_input(">", window, cx);
 7312    });
 7313    cx.assert_editor_state(
 7314        &r#"
 7315            <body><a b={c()d}>ˇ
 7316                <script>
 7317                    var x = 1;<a b={c()d}>ˇ
 7318                </script>
 7319            </body><a b={c()d}>ˇ
 7320        "#
 7321        .unindent(),
 7322    );
 7323
 7324    // Reset
 7325    cx.set_state(
 7326        &r#"
 7327            <body>ˇ
 7328                <script>
 7329                    var x = 1;ˇ
 7330                </script>
 7331            </body>ˇ
 7332        "#
 7333        .unindent(),
 7334    );
 7335
 7336    cx.update_editor(|editor, window, cx| {
 7337        editor.handle_input("<", window, cx);
 7338    });
 7339    cx.assert_editor_state(
 7340        &r#"
 7341            <body><ˇ>
 7342                <script>
 7343                    var x = 1;<ˇ
 7344                </script>
 7345            </body><ˇ>
 7346        "#
 7347        .unindent(),
 7348    );
 7349
 7350    // When backspacing, the closing angle brackets are removed.
 7351    cx.update_editor(|editor, window, cx| {
 7352        editor.backspace(&Backspace, window, cx);
 7353    });
 7354    cx.assert_editor_state(
 7355        &r#"
 7356            <body>ˇ
 7357                <script>
 7358                    var x = 1;ˇ
 7359                </script>
 7360            </body>ˇ
 7361        "#
 7362        .unindent(),
 7363    );
 7364
 7365    // Block comments autoclose in JavaScript, but not HTML.
 7366    cx.update_editor(|editor, window, cx| {
 7367        editor.handle_input("/", window, cx);
 7368        editor.handle_input("*", window, cx);
 7369    });
 7370    cx.assert_editor_state(
 7371        &r#"
 7372            <body>/*ˇ
 7373                <script>
 7374                    var x = 1;/*ˇ */
 7375                </script>
 7376            </body>/*ˇ
 7377        "#
 7378        .unindent(),
 7379    );
 7380}
 7381
 7382#[gpui::test]
 7383async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7384    init_test(cx, |_| {});
 7385
 7386    let mut cx = EditorTestContext::new(cx).await;
 7387
 7388    let rust_language = Arc::new(
 7389        Language::new(
 7390            LanguageConfig {
 7391                name: "Rust".into(),
 7392                brackets: serde_json::from_value(json!([
 7393                    { "start": "{", "end": "}", "close": true, "newline": true },
 7394                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7395                ]))
 7396                .unwrap(),
 7397                autoclose_before: "})]>".into(),
 7398                ..Default::default()
 7399            },
 7400            Some(tree_sitter_rust::LANGUAGE.into()),
 7401        )
 7402        .with_override_query("(string_literal) @string")
 7403        .unwrap(),
 7404    );
 7405
 7406    cx.language_registry().add(rust_language.clone());
 7407    cx.update_buffer(|buffer, cx| {
 7408        buffer.set_language(Some(rust_language), cx);
 7409    });
 7410
 7411    cx.set_state(
 7412        &r#"
 7413            let x = ˇ
 7414        "#
 7415        .unindent(),
 7416    );
 7417
 7418    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7419    cx.update_editor(|editor, window, cx| {
 7420        editor.handle_input("\"", window, cx);
 7421    });
 7422    cx.assert_editor_state(
 7423        &r#"
 7424            let x = "ˇ"
 7425        "#
 7426        .unindent(),
 7427    );
 7428
 7429    // Inserting another quotation mark. The cursor moves across the existing
 7430    // automatically-inserted quotation mark.
 7431    cx.update_editor(|editor, window, cx| {
 7432        editor.handle_input("\"", window, cx);
 7433    });
 7434    cx.assert_editor_state(
 7435        &r#"
 7436            let x = ""ˇ
 7437        "#
 7438        .unindent(),
 7439    );
 7440
 7441    // Reset
 7442    cx.set_state(
 7443        &r#"
 7444            let x = ˇ
 7445        "#
 7446        .unindent(),
 7447    );
 7448
 7449    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7450    cx.update_editor(|editor, window, cx| {
 7451        editor.handle_input("\"", window, cx);
 7452        editor.handle_input(" ", window, cx);
 7453        editor.move_left(&Default::default(), window, cx);
 7454        editor.handle_input("\\", window, cx);
 7455        editor.handle_input("\"", window, cx);
 7456    });
 7457    cx.assert_editor_state(
 7458        &r#"
 7459            let x = "\"ˇ "
 7460        "#
 7461        .unindent(),
 7462    );
 7463
 7464    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7465    // mark. Nothing is inserted.
 7466    cx.update_editor(|editor, window, cx| {
 7467        editor.move_right(&Default::default(), window, cx);
 7468        editor.handle_input("\"", window, cx);
 7469    });
 7470    cx.assert_editor_state(
 7471        &r#"
 7472            let x = "\" "ˇ
 7473        "#
 7474        .unindent(),
 7475    );
 7476}
 7477
 7478#[gpui::test]
 7479async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7480    init_test(cx, |_| {});
 7481
 7482    let language = Arc::new(Language::new(
 7483        LanguageConfig {
 7484            brackets: BracketPairConfig {
 7485                pairs: vec![
 7486                    BracketPair {
 7487                        start: "{".to_string(),
 7488                        end: "}".to_string(),
 7489                        close: true,
 7490                        surround: true,
 7491                        newline: true,
 7492                    },
 7493                    BracketPair {
 7494                        start: "/* ".to_string(),
 7495                        end: "*/".to_string(),
 7496                        close: true,
 7497                        surround: true,
 7498                        ..Default::default()
 7499                    },
 7500                ],
 7501                ..Default::default()
 7502            },
 7503            ..Default::default()
 7504        },
 7505        Some(tree_sitter_rust::LANGUAGE.into()),
 7506    ));
 7507
 7508    let text = r#"
 7509        a
 7510        b
 7511        c
 7512    "#
 7513    .unindent();
 7514
 7515    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7516    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7517    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7518    editor
 7519        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7520        .await;
 7521
 7522    editor.update_in(cx, |editor, window, cx| {
 7523        editor.change_selections(None, window, cx, |s| {
 7524            s.select_display_ranges([
 7525                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7526                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7527                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7528            ])
 7529        });
 7530
 7531        editor.handle_input("{", window, cx);
 7532        editor.handle_input("{", window, cx);
 7533        editor.handle_input("{", window, cx);
 7534        assert_eq!(
 7535            editor.text(cx),
 7536            "
 7537                {{{a}}}
 7538                {{{b}}}
 7539                {{{c}}}
 7540            "
 7541            .unindent()
 7542        );
 7543        assert_eq!(
 7544            editor.selections.display_ranges(cx),
 7545            [
 7546                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7547                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7548                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7549            ]
 7550        );
 7551
 7552        editor.undo(&Undo, window, cx);
 7553        editor.undo(&Undo, window, cx);
 7554        editor.undo(&Undo, window, cx);
 7555        assert_eq!(
 7556            editor.text(cx),
 7557            "
 7558                a
 7559                b
 7560                c
 7561            "
 7562            .unindent()
 7563        );
 7564        assert_eq!(
 7565            editor.selections.display_ranges(cx),
 7566            [
 7567                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7568                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7569                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7570            ]
 7571        );
 7572
 7573        // Ensure inserting the first character of a multi-byte bracket pair
 7574        // doesn't surround the selections with the bracket.
 7575        editor.handle_input("/", window, cx);
 7576        assert_eq!(
 7577            editor.text(cx),
 7578            "
 7579                /
 7580                /
 7581                /
 7582            "
 7583            .unindent()
 7584        );
 7585        assert_eq!(
 7586            editor.selections.display_ranges(cx),
 7587            [
 7588                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7589                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7590                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7591            ]
 7592        );
 7593
 7594        editor.undo(&Undo, window, cx);
 7595        assert_eq!(
 7596            editor.text(cx),
 7597            "
 7598                a
 7599                b
 7600                c
 7601            "
 7602            .unindent()
 7603        );
 7604        assert_eq!(
 7605            editor.selections.display_ranges(cx),
 7606            [
 7607                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7608                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7609                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7610            ]
 7611        );
 7612
 7613        // Ensure inserting the last character of a multi-byte bracket pair
 7614        // doesn't surround the selections with the bracket.
 7615        editor.handle_input("*", window, cx);
 7616        assert_eq!(
 7617            editor.text(cx),
 7618            "
 7619                *
 7620                *
 7621                *
 7622            "
 7623            .unindent()
 7624        );
 7625        assert_eq!(
 7626            editor.selections.display_ranges(cx),
 7627            [
 7628                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7629                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7630                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7631            ]
 7632        );
 7633    });
 7634}
 7635
 7636#[gpui::test]
 7637async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7638    init_test(cx, |_| {});
 7639
 7640    let language = Arc::new(Language::new(
 7641        LanguageConfig {
 7642            brackets: BracketPairConfig {
 7643                pairs: vec![BracketPair {
 7644                    start: "{".to_string(),
 7645                    end: "}".to_string(),
 7646                    close: true,
 7647                    surround: true,
 7648                    newline: true,
 7649                }],
 7650                ..Default::default()
 7651            },
 7652            autoclose_before: "}".to_string(),
 7653            ..Default::default()
 7654        },
 7655        Some(tree_sitter_rust::LANGUAGE.into()),
 7656    ));
 7657
 7658    let text = r#"
 7659        a
 7660        b
 7661        c
 7662    "#
 7663    .unindent();
 7664
 7665    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7666    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7667    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7668    editor
 7669        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7670        .await;
 7671
 7672    editor.update_in(cx, |editor, window, cx| {
 7673        editor.change_selections(None, window, cx, |s| {
 7674            s.select_ranges([
 7675                Point::new(0, 1)..Point::new(0, 1),
 7676                Point::new(1, 1)..Point::new(1, 1),
 7677                Point::new(2, 1)..Point::new(2, 1),
 7678            ])
 7679        });
 7680
 7681        editor.handle_input("{", window, cx);
 7682        editor.handle_input("{", window, cx);
 7683        editor.handle_input("_", window, cx);
 7684        assert_eq!(
 7685            editor.text(cx),
 7686            "
 7687                a{{_}}
 7688                b{{_}}
 7689                c{{_}}
 7690            "
 7691            .unindent()
 7692        );
 7693        assert_eq!(
 7694            editor.selections.ranges::<Point>(cx),
 7695            [
 7696                Point::new(0, 4)..Point::new(0, 4),
 7697                Point::new(1, 4)..Point::new(1, 4),
 7698                Point::new(2, 4)..Point::new(2, 4)
 7699            ]
 7700        );
 7701
 7702        editor.backspace(&Default::default(), window, cx);
 7703        editor.backspace(&Default::default(), window, cx);
 7704        assert_eq!(
 7705            editor.text(cx),
 7706            "
 7707                a{}
 7708                b{}
 7709                c{}
 7710            "
 7711            .unindent()
 7712        );
 7713        assert_eq!(
 7714            editor.selections.ranges::<Point>(cx),
 7715            [
 7716                Point::new(0, 2)..Point::new(0, 2),
 7717                Point::new(1, 2)..Point::new(1, 2),
 7718                Point::new(2, 2)..Point::new(2, 2)
 7719            ]
 7720        );
 7721
 7722        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7723        assert_eq!(
 7724            editor.text(cx),
 7725            "
 7726                a
 7727                b
 7728                c
 7729            "
 7730            .unindent()
 7731        );
 7732        assert_eq!(
 7733            editor.selections.ranges::<Point>(cx),
 7734            [
 7735                Point::new(0, 1)..Point::new(0, 1),
 7736                Point::new(1, 1)..Point::new(1, 1),
 7737                Point::new(2, 1)..Point::new(2, 1)
 7738            ]
 7739        );
 7740    });
 7741}
 7742
 7743#[gpui::test]
 7744async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7745    init_test(cx, |settings| {
 7746        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7747    });
 7748
 7749    let mut cx = EditorTestContext::new(cx).await;
 7750
 7751    let language = Arc::new(Language::new(
 7752        LanguageConfig {
 7753            brackets: BracketPairConfig {
 7754                pairs: vec![
 7755                    BracketPair {
 7756                        start: "{".to_string(),
 7757                        end: "}".to_string(),
 7758                        close: true,
 7759                        surround: true,
 7760                        newline: true,
 7761                    },
 7762                    BracketPair {
 7763                        start: "(".to_string(),
 7764                        end: ")".to_string(),
 7765                        close: true,
 7766                        surround: true,
 7767                        newline: true,
 7768                    },
 7769                    BracketPair {
 7770                        start: "[".to_string(),
 7771                        end: "]".to_string(),
 7772                        close: false,
 7773                        surround: true,
 7774                        newline: true,
 7775                    },
 7776                ],
 7777                ..Default::default()
 7778            },
 7779            autoclose_before: "})]".to_string(),
 7780            ..Default::default()
 7781        },
 7782        Some(tree_sitter_rust::LANGUAGE.into()),
 7783    ));
 7784
 7785    cx.language_registry().add(language.clone());
 7786    cx.update_buffer(|buffer, cx| {
 7787        buffer.set_language(Some(language), cx);
 7788    });
 7789
 7790    cx.set_state(
 7791        &"
 7792            {(ˇ)}
 7793            [[ˇ]]
 7794            {(ˇ)}
 7795        "
 7796        .unindent(),
 7797    );
 7798
 7799    cx.update_editor(|editor, window, cx| {
 7800        editor.backspace(&Default::default(), window, cx);
 7801        editor.backspace(&Default::default(), window, cx);
 7802    });
 7803
 7804    cx.assert_editor_state(
 7805        &"
 7806            ˇ
 7807            ˇ]]
 7808            ˇ
 7809        "
 7810        .unindent(),
 7811    );
 7812
 7813    cx.update_editor(|editor, window, cx| {
 7814        editor.handle_input("{", window, cx);
 7815        editor.handle_input("{", window, cx);
 7816        editor.move_right(&MoveRight, window, cx);
 7817        editor.move_right(&MoveRight, window, cx);
 7818        editor.move_left(&MoveLeft, window, cx);
 7819        editor.move_left(&MoveLeft, window, cx);
 7820        editor.backspace(&Default::default(), window, cx);
 7821    });
 7822
 7823    cx.assert_editor_state(
 7824        &"
 7825            {ˇ}
 7826            {ˇ}]]
 7827            {ˇ}
 7828        "
 7829        .unindent(),
 7830    );
 7831
 7832    cx.update_editor(|editor, window, cx| {
 7833        editor.backspace(&Default::default(), window, cx);
 7834    });
 7835
 7836    cx.assert_editor_state(
 7837        &"
 7838            ˇ
 7839            ˇ]]
 7840            ˇ
 7841        "
 7842        .unindent(),
 7843    );
 7844}
 7845
 7846#[gpui::test]
 7847async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7848    init_test(cx, |_| {});
 7849
 7850    let language = Arc::new(Language::new(
 7851        LanguageConfig::default(),
 7852        Some(tree_sitter_rust::LANGUAGE.into()),
 7853    ));
 7854
 7855    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7856    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7857    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7858    editor
 7859        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7860        .await;
 7861
 7862    editor.update_in(cx, |editor, window, cx| {
 7863        editor.set_auto_replace_emoji_shortcode(true);
 7864
 7865        editor.handle_input("Hello ", window, cx);
 7866        editor.handle_input(":wave", window, cx);
 7867        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7868
 7869        editor.handle_input(":", window, cx);
 7870        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7871
 7872        editor.handle_input(" :smile", window, cx);
 7873        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7874
 7875        editor.handle_input(":", window, cx);
 7876        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7877
 7878        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7879        editor.handle_input(":wave", window, cx);
 7880        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7881
 7882        editor.handle_input(":", window, cx);
 7883        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7884
 7885        editor.handle_input(":1", window, cx);
 7886        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7887
 7888        editor.handle_input(":", window, cx);
 7889        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7890
 7891        // Ensure shortcode does not get replaced when it is part of a word
 7892        editor.handle_input(" Test:wave", window, cx);
 7893        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7894
 7895        editor.handle_input(":", window, cx);
 7896        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7897
 7898        editor.set_auto_replace_emoji_shortcode(false);
 7899
 7900        // Ensure shortcode does not get replaced when auto replace is off
 7901        editor.handle_input(" :wave", window, cx);
 7902        assert_eq!(
 7903            editor.text(cx),
 7904            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7905        );
 7906
 7907        editor.handle_input(":", window, cx);
 7908        assert_eq!(
 7909            editor.text(cx),
 7910            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7911        );
 7912    });
 7913}
 7914
 7915#[gpui::test]
 7916async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7917    init_test(cx, |_| {});
 7918
 7919    let (text, insertion_ranges) = marked_text_ranges(
 7920        indoc! {"
 7921            ˇ
 7922        "},
 7923        false,
 7924    );
 7925
 7926    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7927    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7928
 7929    _ = editor.update_in(cx, |editor, window, cx| {
 7930        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7931
 7932        editor
 7933            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7934            .unwrap();
 7935
 7936        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7937            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7938            assert_eq!(editor.text(cx), expected_text);
 7939            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7940        }
 7941
 7942        assert(
 7943            editor,
 7944            cx,
 7945            indoc! {"
 7946            type «» =•
 7947            "},
 7948        );
 7949
 7950        assert!(editor.context_menu_visible(), "There should be a matches");
 7951    });
 7952}
 7953
 7954#[gpui::test]
 7955async fn test_snippets(cx: &mut TestAppContext) {
 7956    init_test(cx, |_| {});
 7957
 7958    let (text, insertion_ranges) = marked_text_ranges(
 7959        indoc! {"
 7960            a.ˇ b
 7961            a.ˇ b
 7962            a.ˇ b
 7963        "},
 7964        false,
 7965    );
 7966
 7967    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7968    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7969
 7970    editor.update_in(cx, |editor, window, cx| {
 7971        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7972
 7973        editor
 7974            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7975            .unwrap();
 7976
 7977        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7978            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7979            assert_eq!(editor.text(cx), expected_text);
 7980            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7981        }
 7982
 7983        assert(
 7984            editor,
 7985            cx,
 7986            indoc! {"
 7987                a.f(«one», two, «three») b
 7988                a.f(«one», two, «three») b
 7989                a.f(«one», two, «three») b
 7990            "},
 7991        );
 7992
 7993        // Can't move earlier than the first tab stop
 7994        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7995        assert(
 7996            editor,
 7997            cx,
 7998            indoc! {"
 7999                a.f(«one», two, «three») b
 8000                a.f(«one», two, «three») b
 8001                a.f(«one», two, «three») b
 8002            "},
 8003        );
 8004
 8005        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8006        assert(
 8007            editor,
 8008            cx,
 8009            indoc! {"
 8010                a.f(one, «two», three) b
 8011                a.f(one, «two», three) b
 8012                a.f(one, «two», three) b
 8013            "},
 8014        );
 8015
 8016        editor.move_to_prev_snippet_tabstop(window, cx);
 8017        assert(
 8018            editor,
 8019            cx,
 8020            indoc! {"
 8021                a.f(«one», two, «three») b
 8022                a.f(«one», two, «three») b
 8023                a.f(«one», two, «three») b
 8024            "},
 8025        );
 8026
 8027        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8028        assert(
 8029            editor,
 8030            cx,
 8031            indoc! {"
 8032                a.f(one, «two», three) b
 8033                a.f(one, «two», three) b
 8034                a.f(one, «two», three) b
 8035            "},
 8036        );
 8037        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8038        assert(
 8039            editor,
 8040            cx,
 8041            indoc! {"
 8042                a.f(one, two, three)ˇ b
 8043                a.f(one, two, three)ˇ b
 8044                a.f(one, two, three)ˇ b
 8045            "},
 8046        );
 8047
 8048        // As soon as the last tab stop is reached, snippet state is gone
 8049        editor.move_to_prev_snippet_tabstop(window, cx);
 8050        assert(
 8051            editor,
 8052            cx,
 8053            indoc! {"
 8054                a.f(one, two, three)ˇ b
 8055                a.f(one, two, three)ˇ b
 8056                a.f(one, two, three)ˇ b
 8057            "},
 8058        );
 8059    });
 8060}
 8061
 8062#[gpui::test]
 8063async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8064    init_test(cx, |_| {});
 8065
 8066    let fs = FakeFs::new(cx.executor());
 8067    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8068
 8069    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8070
 8071    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8072    language_registry.add(rust_lang());
 8073    let mut fake_servers = language_registry.register_fake_lsp(
 8074        "Rust",
 8075        FakeLspAdapter {
 8076            capabilities: lsp::ServerCapabilities {
 8077                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8078                ..Default::default()
 8079            },
 8080            ..Default::default()
 8081        },
 8082    );
 8083
 8084    let buffer = project
 8085        .update(cx, |project, cx| {
 8086            project.open_local_buffer(path!("/file.rs"), cx)
 8087        })
 8088        .await
 8089        .unwrap();
 8090
 8091    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8092    let (editor, cx) = cx.add_window_view(|window, cx| {
 8093        build_editor_with_project(project.clone(), buffer, window, cx)
 8094    });
 8095    editor.update_in(cx, |editor, window, cx| {
 8096        editor.set_text("one\ntwo\nthree\n", window, cx)
 8097    });
 8098    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8099
 8100    cx.executor().start_waiting();
 8101    let fake_server = fake_servers.next().await.unwrap();
 8102
 8103    {
 8104        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8105            move |params, _| async move {
 8106                assert_eq!(
 8107                    params.text_document.uri,
 8108                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8109                );
 8110                assert_eq!(params.options.tab_size, 4);
 8111                Ok(Some(vec![lsp::TextEdit::new(
 8112                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8113                    ", ".to_string(),
 8114                )]))
 8115            },
 8116        );
 8117        let save = editor
 8118            .update_in(cx, |editor, window, cx| {
 8119                editor.save(true, project.clone(), window, cx)
 8120            })
 8121            .unwrap();
 8122        cx.executor().start_waiting();
 8123        save.await;
 8124
 8125        assert_eq!(
 8126            editor.update(cx, |editor, cx| editor.text(cx)),
 8127            "one, two\nthree\n"
 8128        );
 8129        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8130    }
 8131
 8132    {
 8133        editor.update_in(cx, |editor, window, cx| {
 8134            editor.set_text("one\ntwo\nthree\n", window, cx)
 8135        });
 8136        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8137
 8138        // Ensure we can still save even if formatting hangs.
 8139        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8140            move |params, _| async move {
 8141                assert_eq!(
 8142                    params.text_document.uri,
 8143                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8144                );
 8145                futures::future::pending::<()>().await;
 8146                unreachable!()
 8147            },
 8148        );
 8149        let save = editor
 8150            .update_in(cx, |editor, window, cx| {
 8151                editor.save(true, project.clone(), window, cx)
 8152            })
 8153            .unwrap();
 8154        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8155        cx.executor().start_waiting();
 8156        save.await;
 8157        assert_eq!(
 8158            editor.update(cx, |editor, cx| editor.text(cx)),
 8159            "one\ntwo\nthree\n"
 8160        );
 8161    }
 8162
 8163    // For non-dirty buffer, no formatting request should be sent
 8164    {
 8165        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8166
 8167        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8168            panic!("Should not be invoked on non-dirty buffer");
 8169        });
 8170        let save = editor
 8171            .update_in(cx, |editor, window, cx| {
 8172                editor.save(true, project.clone(), window, cx)
 8173            })
 8174            .unwrap();
 8175        cx.executor().start_waiting();
 8176        save.await;
 8177    }
 8178
 8179    // Set rust language override and assert overridden tabsize is sent to language server
 8180    update_test_language_settings(cx, |settings| {
 8181        settings.languages.insert(
 8182            "Rust".into(),
 8183            LanguageSettingsContent {
 8184                tab_size: NonZeroU32::new(8),
 8185                ..Default::default()
 8186            },
 8187        );
 8188    });
 8189
 8190    {
 8191        editor.update_in(cx, |editor, window, cx| {
 8192            editor.set_text("somehting_new\n", window, cx)
 8193        });
 8194        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8195        let _formatting_request_signal = fake_server
 8196            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8197                assert_eq!(
 8198                    params.text_document.uri,
 8199                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8200                );
 8201                assert_eq!(params.options.tab_size, 8);
 8202                Ok(Some(vec![]))
 8203            });
 8204        let save = editor
 8205            .update_in(cx, |editor, window, cx| {
 8206                editor.save(true, project.clone(), window, cx)
 8207            })
 8208            .unwrap();
 8209        cx.executor().start_waiting();
 8210        save.await;
 8211    }
 8212}
 8213
 8214#[gpui::test]
 8215async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8216    init_test(cx, |_| {});
 8217
 8218    let cols = 4;
 8219    let rows = 10;
 8220    let sample_text_1 = sample_text(rows, cols, 'a');
 8221    assert_eq!(
 8222        sample_text_1,
 8223        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8224    );
 8225    let sample_text_2 = sample_text(rows, cols, 'l');
 8226    assert_eq!(
 8227        sample_text_2,
 8228        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8229    );
 8230    let sample_text_3 = sample_text(rows, cols, 'v');
 8231    assert_eq!(
 8232        sample_text_3,
 8233        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8234    );
 8235
 8236    let fs = FakeFs::new(cx.executor());
 8237    fs.insert_tree(
 8238        path!("/a"),
 8239        json!({
 8240            "main.rs": sample_text_1,
 8241            "other.rs": sample_text_2,
 8242            "lib.rs": sample_text_3,
 8243        }),
 8244    )
 8245    .await;
 8246
 8247    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8248    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8249    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8250
 8251    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8252    language_registry.add(rust_lang());
 8253    let mut fake_servers = language_registry.register_fake_lsp(
 8254        "Rust",
 8255        FakeLspAdapter {
 8256            capabilities: lsp::ServerCapabilities {
 8257                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8258                ..Default::default()
 8259            },
 8260            ..Default::default()
 8261        },
 8262    );
 8263
 8264    let worktree = project.update(cx, |project, cx| {
 8265        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8266        assert_eq!(worktrees.len(), 1);
 8267        worktrees.pop().unwrap()
 8268    });
 8269    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8270
 8271    let buffer_1 = project
 8272        .update(cx, |project, cx| {
 8273            project.open_buffer((worktree_id, "main.rs"), cx)
 8274        })
 8275        .await
 8276        .unwrap();
 8277    let buffer_2 = project
 8278        .update(cx, |project, cx| {
 8279            project.open_buffer((worktree_id, "other.rs"), cx)
 8280        })
 8281        .await
 8282        .unwrap();
 8283    let buffer_3 = project
 8284        .update(cx, |project, cx| {
 8285            project.open_buffer((worktree_id, "lib.rs"), cx)
 8286        })
 8287        .await
 8288        .unwrap();
 8289
 8290    let multi_buffer = cx.new(|cx| {
 8291        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8292        multi_buffer.push_excerpts(
 8293            buffer_1.clone(),
 8294            [
 8295                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8296                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8297                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8298            ],
 8299            cx,
 8300        );
 8301        multi_buffer.push_excerpts(
 8302            buffer_2.clone(),
 8303            [
 8304                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8305                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8306                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8307            ],
 8308            cx,
 8309        );
 8310        multi_buffer.push_excerpts(
 8311            buffer_3.clone(),
 8312            [
 8313                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8314                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8315                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8316            ],
 8317            cx,
 8318        );
 8319        multi_buffer
 8320    });
 8321    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8322        Editor::new(
 8323            EditorMode::full(),
 8324            multi_buffer,
 8325            Some(project.clone()),
 8326            window,
 8327            cx,
 8328        )
 8329    });
 8330
 8331    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8332        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8333            s.select_ranges(Some(1..2))
 8334        });
 8335        editor.insert("|one|two|three|", window, cx);
 8336    });
 8337    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8338    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8339        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8340            s.select_ranges(Some(60..70))
 8341        });
 8342        editor.insert("|four|five|six|", window, cx);
 8343    });
 8344    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8345
 8346    // First two buffers should be edited, but not the third one.
 8347    assert_eq!(
 8348        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8349        "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}",
 8350    );
 8351    buffer_1.update(cx, |buffer, _| {
 8352        assert!(buffer.is_dirty());
 8353        assert_eq!(
 8354            buffer.text(),
 8355            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8356        )
 8357    });
 8358    buffer_2.update(cx, |buffer, _| {
 8359        assert!(buffer.is_dirty());
 8360        assert_eq!(
 8361            buffer.text(),
 8362            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8363        )
 8364    });
 8365    buffer_3.update(cx, |buffer, _| {
 8366        assert!(!buffer.is_dirty());
 8367        assert_eq!(buffer.text(), sample_text_3,)
 8368    });
 8369    cx.executor().run_until_parked();
 8370
 8371    cx.executor().start_waiting();
 8372    let save = multi_buffer_editor
 8373        .update_in(cx, |editor, window, cx| {
 8374            editor.save(true, project.clone(), window, cx)
 8375        })
 8376        .unwrap();
 8377
 8378    let fake_server = fake_servers.next().await.unwrap();
 8379    fake_server
 8380        .server
 8381        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8382            Ok(Some(vec![lsp::TextEdit::new(
 8383                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8384                format!("[{} formatted]", params.text_document.uri),
 8385            )]))
 8386        })
 8387        .detach();
 8388    save.await;
 8389
 8390    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8391    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8392    assert_eq!(
 8393        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8394        uri!(
 8395            "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}"
 8396        ),
 8397    );
 8398    buffer_1.update(cx, |buffer, _| {
 8399        assert!(!buffer.is_dirty());
 8400        assert_eq!(
 8401            buffer.text(),
 8402            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8403        )
 8404    });
 8405    buffer_2.update(cx, |buffer, _| {
 8406        assert!(!buffer.is_dirty());
 8407        assert_eq!(
 8408            buffer.text(),
 8409            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8410        )
 8411    });
 8412    buffer_3.update(cx, |buffer, _| {
 8413        assert!(!buffer.is_dirty());
 8414        assert_eq!(buffer.text(), sample_text_3,)
 8415    });
 8416}
 8417
 8418#[gpui::test]
 8419async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8420    init_test(cx, |_| {});
 8421
 8422    let fs = FakeFs::new(cx.executor());
 8423    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8424
 8425    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8426
 8427    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8428    language_registry.add(rust_lang());
 8429    let mut fake_servers = language_registry.register_fake_lsp(
 8430        "Rust",
 8431        FakeLspAdapter {
 8432            capabilities: lsp::ServerCapabilities {
 8433                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8434                ..Default::default()
 8435            },
 8436            ..Default::default()
 8437        },
 8438    );
 8439
 8440    let buffer = project
 8441        .update(cx, |project, cx| {
 8442            project.open_local_buffer(path!("/file.rs"), cx)
 8443        })
 8444        .await
 8445        .unwrap();
 8446
 8447    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8448    let (editor, cx) = cx.add_window_view(|window, cx| {
 8449        build_editor_with_project(project.clone(), buffer, window, cx)
 8450    });
 8451    editor.update_in(cx, |editor, window, cx| {
 8452        editor.set_text("one\ntwo\nthree\n", window, cx)
 8453    });
 8454    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8455
 8456    cx.executor().start_waiting();
 8457    let fake_server = fake_servers.next().await.unwrap();
 8458
 8459    let save = editor
 8460        .update_in(cx, |editor, window, cx| {
 8461            editor.save(true, project.clone(), window, cx)
 8462        })
 8463        .unwrap();
 8464    fake_server
 8465        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8466            assert_eq!(
 8467                params.text_document.uri,
 8468                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8469            );
 8470            assert_eq!(params.options.tab_size, 4);
 8471            Ok(Some(vec![lsp::TextEdit::new(
 8472                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8473                ", ".to_string(),
 8474            )]))
 8475        })
 8476        .next()
 8477        .await;
 8478    cx.executor().start_waiting();
 8479    save.await;
 8480    assert_eq!(
 8481        editor.update(cx, |editor, cx| editor.text(cx)),
 8482        "one, two\nthree\n"
 8483    );
 8484    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8485
 8486    editor.update_in(cx, |editor, window, cx| {
 8487        editor.set_text("one\ntwo\nthree\n", window, cx)
 8488    });
 8489    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8490
 8491    // Ensure we can still save even if formatting hangs.
 8492    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8493        move |params, _| async move {
 8494            assert_eq!(
 8495                params.text_document.uri,
 8496                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8497            );
 8498            futures::future::pending::<()>().await;
 8499            unreachable!()
 8500        },
 8501    );
 8502    let save = editor
 8503        .update_in(cx, |editor, window, cx| {
 8504            editor.save(true, project.clone(), window, cx)
 8505        })
 8506        .unwrap();
 8507    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8508    cx.executor().start_waiting();
 8509    save.await;
 8510    assert_eq!(
 8511        editor.update(cx, |editor, cx| editor.text(cx)),
 8512        "one\ntwo\nthree\n"
 8513    );
 8514    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8515
 8516    // For non-dirty buffer, no formatting request should be sent
 8517    let save = editor
 8518        .update_in(cx, |editor, window, cx| {
 8519            editor.save(true, project.clone(), window, cx)
 8520        })
 8521        .unwrap();
 8522    let _pending_format_request = fake_server
 8523        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8524            panic!("Should not be invoked on non-dirty buffer");
 8525        })
 8526        .next();
 8527    cx.executor().start_waiting();
 8528    save.await;
 8529
 8530    // Set Rust language override and assert overridden tabsize is sent to language server
 8531    update_test_language_settings(cx, |settings| {
 8532        settings.languages.insert(
 8533            "Rust".into(),
 8534            LanguageSettingsContent {
 8535                tab_size: NonZeroU32::new(8),
 8536                ..Default::default()
 8537            },
 8538        );
 8539    });
 8540
 8541    editor.update_in(cx, |editor, window, cx| {
 8542        editor.set_text("somehting_new\n", window, cx)
 8543    });
 8544    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8545    let save = editor
 8546        .update_in(cx, |editor, window, cx| {
 8547            editor.save(true, project.clone(), window, cx)
 8548        })
 8549        .unwrap();
 8550    fake_server
 8551        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8552            assert_eq!(
 8553                params.text_document.uri,
 8554                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8555            );
 8556            assert_eq!(params.options.tab_size, 8);
 8557            Ok(Some(vec![]))
 8558        })
 8559        .next()
 8560        .await;
 8561    cx.executor().start_waiting();
 8562    save.await;
 8563}
 8564
 8565#[gpui::test]
 8566async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8567    init_test(cx, |settings| {
 8568        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8569            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8570        ))
 8571    });
 8572
 8573    let fs = FakeFs::new(cx.executor());
 8574    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8575
 8576    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8577
 8578    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8579    language_registry.add(Arc::new(Language::new(
 8580        LanguageConfig {
 8581            name: "Rust".into(),
 8582            matcher: LanguageMatcher {
 8583                path_suffixes: vec!["rs".to_string()],
 8584                ..Default::default()
 8585            },
 8586            ..LanguageConfig::default()
 8587        },
 8588        Some(tree_sitter_rust::LANGUAGE.into()),
 8589    )));
 8590    update_test_language_settings(cx, |settings| {
 8591        // Enable Prettier formatting for the same buffer, and ensure
 8592        // LSP is called instead of Prettier.
 8593        settings.defaults.prettier = Some(PrettierSettings {
 8594            allowed: true,
 8595            ..PrettierSettings::default()
 8596        });
 8597    });
 8598    let mut fake_servers = language_registry.register_fake_lsp(
 8599        "Rust",
 8600        FakeLspAdapter {
 8601            capabilities: lsp::ServerCapabilities {
 8602                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8603                ..Default::default()
 8604            },
 8605            ..Default::default()
 8606        },
 8607    );
 8608
 8609    let buffer = project
 8610        .update(cx, |project, cx| {
 8611            project.open_local_buffer(path!("/file.rs"), cx)
 8612        })
 8613        .await
 8614        .unwrap();
 8615
 8616    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8617    let (editor, cx) = cx.add_window_view(|window, cx| {
 8618        build_editor_with_project(project.clone(), buffer, window, cx)
 8619    });
 8620    editor.update_in(cx, |editor, window, cx| {
 8621        editor.set_text("one\ntwo\nthree\n", window, cx)
 8622    });
 8623
 8624    cx.executor().start_waiting();
 8625    let fake_server = fake_servers.next().await.unwrap();
 8626
 8627    let format = editor
 8628        .update_in(cx, |editor, window, cx| {
 8629            editor.perform_format(
 8630                project.clone(),
 8631                FormatTrigger::Manual,
 8632                FormatTarget::Buffers,
 8633                window,
 8634                cx,
 8635            )
 8636        })
 8637        .unwrap();
 8638    fake_server
 8639        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8640            assert_eq!(
 8641                params.text_document.uri,
 8642                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8643            );
 8644            assert_eq!(params.options.tab_size, 4);
 8645            Ok(Some(vec![lsp::TextEdit::new(
 8646                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8647                ", ".to_string(),
 8648            )]))
 8649        })
 8650        .next()
 8651        .await;
 8652    cx.executor().start_waiting();
 8653    format.await;
 8654    assert_eq!(
 8655        editor.update(cx, |editor, cx| editor.text(cx)),
 8656        "one, two\nthree\n"
 8657    );
 8658
 8659    editor.update_in(cx, |editor, window, cx| {
 8660        editor.set_text("one\ntwo\nthree\n", window, cx)
 8661    });
 8662    // Ensure we don't lock if formatting hangs.
 8663    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8664        move |params, _| async move {
 8665            assert_eq!(
 8666                params.text_document.uri,
 8667                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8668            );
 8669            futures::future::pending::<()>().await;
 8670            unreachable!()
 8671        },
 8672    );
 8673    let format = editor
 8674        .update_in(cx, |editor, window, cx| {
 8675            editor.perform_format(
 8676                project,
 8677                FormatTrigger::Manual,
 8678                FormatTarget::Buffers,
 8679                window,
 8680                cx,
 8681            )
 8682        })
 8683        .unwrap();
 8684    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8685    cx.executor().start_waiting();
 8686    format.await;
 8687    assert_eq!(
 8688        editor.update(cx, |editor, cx| editor.text(cx)),
 8689        "one\ntwo\nthree\n"
 8690    );
 8691}
 8692
 8693#[gpui::test]
 8694async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8695    init_test(cx, |settings| {
 8696        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8697        settings.defaults.formatter =
 8698            Some(language_settings::SelectedFormatter::List(FormatterList(
 8699                vec![
 8700                    Formatter::LanguageServer { name: None },
 8701                    Formatter::CodeActions(
 8702                        [
 8703                            ("code-action-1".into(), true),
 8704                            ("code-action-2".into(), true),
 8705                        ]
 8706                        .into_iter()
 8707                        .collect(),
 8708                    ),
 8709                ]
 8710                .into(),
 8711            )))
 8712    });
 8713
 8714    let fs = FakeFs::new(cx.executor());
 8715    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8716        .await;
 8717
 8718    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8719    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8720    language_registry.add(rust_lang());
 8721
 8722    let mut fake_servers = language_registry.register_fake_lsp(
 8723        "Rust",
 8724        FakeLspAdapter {
 8725            capabilities: lsp::ServerCapabilities {
 8726                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8727                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8728                    commands: vec!["the-command-for-code-action-1".into()],
 8729                    ..Default::default()
 8730                }),
 8731                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8732                ..Default::default()
 8733            },
 8734            ..Default::default()
 8735        },
 8736    );
 8737
 8738    let buffer = project
 8739        .update(cx, |project, cx| {
 8740            project.open_local_buffer(path!("/file.rs"), cx)
 8741        })
 8742        .await
 8743        .unwrap();
 8744
 8745    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8746    let (editor, cx) = cx.add_window_view(|window, cx| {
 8747        build_editor_with_project(project.clone(), buffer, window, cx)
 8748    });
 8749
 8750    cx.executor().start_waiting();
 8751
 8752    let fake_server = fake_servers.next().await.unwrap();
 8753    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8754        move |_params, _| async move {
 8755            Ok(Some(vec![lsp::TextEdit::new(
 8756                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8757                "applied-formatting\n".to_string(),
 8758            )]))
 8759        },
 8760    );
 8761    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8762        move |params, _| async move {
 8763            assert_eq!(
 8764                params.context.only,
 8765                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8766            );
 8767            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8768            Ok(Some(vec![
 8769                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8770                    kind: Some("code-action-1".into()),
 8771                    edit: Some(lsp::WorkspaceEdit::new(
 8772                        [(
 8773                            uri.clone(),
 8774                            vec![lsp::TextEdit::new(
 8775                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8776                                "applied-code-action-1-edit\n".to_string(),
 8777                            )],
 8778                        )]
 8779                        .into_iter()
 8780                        .collect(),
 8781                    )),
 8782                    command: Some(lsp::Command {
 8783                        command: "the-command-for-code-action-1".into(),
 8784                        ..Default::default()
 8785                    }),
 8786                    ..Default::default()
 8787                }),
 8788                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8789                    kind: Some("code-action-2".into()),
 8790                    edit: Some(lsp::WorkspaceEdit::new(
 8791                        [(
 8792                            uri.clone(),
 8793                            vec![lsp::TextEdit::new(
 8794                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8795                                "applied-code-action-2-edit\n".to_string(),
 8796                            )],
 8797                        )]
 8798                        .into_iter()
 8799                        .collect(),
 8800                    )),
 8801                    ..Default::default()
 8802                }),
 8803            ]))
 8804        },
 8805    );
 8806
 8807    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8808        move |params, _| async move { Ok(params) }
 8809    });
 8810
 8811    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8812    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8813        let fake = fake_server.clone();
 8814        let lock = command_lock.clone();
 8815        move |params, _| {
 8816            assert_eq!(params.command, "the-command-for-code-action-1");
 8817            let fake = fake.clone();
 8818            let lock = lock.clone();
 8819            async move {
 8820                lock.lock().await;
 8821                fake.server
 8822                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8823                        label: None,
 8824                        edit: lsp::WorkspaceEdit {
 8825                            changes: Some(
 8826                                [(
 8827                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8828                                    vec![lsp::TextEdit {
 8829                                        range: lsp::Range::new(
 8830                                            lsp::Position::new(0, 0),
 8831                                            lsp::Position::new(0, 0),
 8832                                        ),
 8833                                        new_text: "applied-code-action-1-command\n".into(),
 8834                                    }],
 8835                                )]
 8836                                .into_iter()
 8837                                .collect(),
 8838                            ),
 8839                            ..Default::default()
 8840                        },
 8841                    })
 8842                    .await
 8843                    .unwrap();
 8844                Ok(Some(json!(null)))
 8845            }
 8846        }
 8847    });
 8848
 8849    cx.executor().start_waiting();
 8850    editor
 8851        .update_in(cx, |editor, window, cx| {
 8852            editor.perform_format(
 8853                project.clone(),
 8854                FormatTrigger::Manual,
 8855                FormatTarget::Buffers,
 8856                window,
 8857                cx,
 8858            )
 8859        })
 8860        .unwrap()
 8861        .await;
 8862    editor.update(cx, |editor, cx| {
 8863        assert_eq!(
 8864            editor.text(cx),
 8865            r#"
 8866                applied-code-action-2-edit
 8867                applied-code-action-1-command
 8868                applied-code-action-1-edit
 8869                applied-formatting
 8870                one
 8871                two
 8872                three
 8873            "#
 8874            .unindent()
 8875        );
 8876    });
 8877
 8878    editor.update_in(cx, |editor, window, cx| {
 8879        editor.undo(&Default::default(), window, cx);
 8880        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8881    });
 8882
 8883    // Perform a manual edit while waiting for an LSP command
 8884    // that's being run as part of a formatting code action.
 8885    let lock_guard = command_lock.lock().await;
 8886    let format = editor
 8887        .update_in(cx, |editor, window, cx| {
 8888            editor.perform_format(
 8889                project.clone(),
 8890                FormatTrigger::Manual,
 8891                FormatTarget::Buffers,
 8892                window,
 8893                cx,
 8894            )
 8895        })
 8896        .unwrap();
 8897    cx.run_until_parked();
 8898    editor.update(cx, |editor, cx| {
 8899        assert_eq!(
 8900            editor.text(cx),
 8901            r#"
 8902                applied-code-action-1-edit
 8903                applied-formatting
 8904                one
 8905                two
 8906                three
 8907            "#
 8908            .unindent()
 8909        );
 8910
 8911        editor.buffer.update(cx, |buffer, cx| {
 8912            let ix = buffer.len(cx);
 8913            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8914        });
 8915    });
 8916
 8917    // Allow the LSP command to proceed. Because the buffer was edited,
 8918    // the second code action will not be run.
 8919    drop(lock_guard);
 8920    format.await;
 8921    editor.update_in(cx, |editor, window, cx| {
 8922        assert_eq!(
 8923            editor.text(cx),
 8924            r#"
 8925                applied-code-action-1-command
 8926                applied-code-action-1-edit
 8927                applied-formatting
 8928                one
 8929                two
 8930                three
 8931                edited
 8932            "#
 8933            .unindent()
 8934        );
 8935
 8936        // The manual edit is undone first, because it is the last thing the user did
 8937        // (even though the command completed afterwards).
 8938        editor.undo(&Default::default(), window, cx);
 8939        assert_eq!(
 8940            editor.text(cx),
 8941            r#"
 8942                applied-code-action-1-command
 8943                applied-code-action-1-edit
 8944                applied-formatting
 8945                one
 8946                two
 8947                three
 8948            "#
 8949            .unindent()
 8950        );
 8951
 8952        // All the formatting (including the command, which completed after the manual edit)
 8953        // is undone together.
 8954        editor.undo(&Default::default(), window, cx);
 8955        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8956    });
 8957}
 8958
 8959#[gpui::test]
 8960async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8961    init_test(cx, |settings| {
 8962        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8963            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8964        ))
 8965    });
 8966
 8967    let fs = FakeFs::new(cx.executor());
 8968    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8969
 8970    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8971
 8972    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8973    language_registry.add(Arc::new(Language::new(
 8974        LanguageConfig {
 8975            name: "TypeScript".into(),
 8976            matcher: LanguageMatcher {
 8977                path_suffixes: vec!["ts".to_string()],
 8978                ..Default::default()
 8979            },
 8980            ..LanguageConfig::default()
 8981        },
 8982        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8983    )));
 8984    update_test_language_settings(cx, |settings| {
 8985        settings.defaults.prettier = Some(PrettierSettings {
 8986            allowed: true,
 8987            ..PrettierSettings::default()
 8988        });
 8989    });
 8990    let mut fake_servers = language_registry.register_fake_lsp(
 8991        "TypeScript",
 8992        FakeLspAdapter {
 8993            capabilities: lsp::ServerCapabilities {
 8994                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8995                ..Default::default()
 8996            },
 8997            ..Default::default()
 8998        },
 8999    );
 9000
 9001    let buffer = project
 9002        .update(cx, |project, cx| {
 9003            project.open_local_buffer(path!("/file.ts"), cx)
 9004        })
 9005        .await
 9006        .unwrap();
 9007
 9008    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9009    let (editor, cx) = cx.add_window_view(|window, cx| {
 9010        build_editor_with_project(project.clone(), buffer, window, cx)
 9011    });
 9012    editor.update_in(cx, |editor, window, cx| {
 9013        editor.set_text(
 9014            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9015            window,
 9016            cx,
 9017        )
 9018    });
 9019
 9020    cx.executor().start_waiting();
 9021    let fake_server = fake_servers.next().await.unwrap();
 9022
 9023    let format = editor
 9024        .update_in(cx, |editor, window, cx| {
 9025            editor.perform_code_action_kind(
 9026                project.clone(),
 9027                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9028                window,
 9029                cx,
 9030            )
 9031        })
 9032        .unwrap();
 9033    fake_server
 9034        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9035            assert_eq!(
 9036                params.text_document.uri,
 9037                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9038            );
 9039            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9040                lsp::CodeAction {
 9041                    title: "Organize Imports".to_string(),
 9042                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9043                    edit: Some(lsp::WorkspaceEdit {
 9044                        changes: Some(
 9045                            [(
 9046                                params.text_document.uri.clone(),
 9047                                vec![lsp::TextEdit::new(
 9048                                    lsp::Range::new(
 9049                                        lsp::Position::new(1, 0),
 9050                                        lsp::Position::new(2, 0),
 9051                                    ),
 9052                                    "".to_string(),
 9053                                )],
 9054                            )]
 9055                            .into_iter()
 9056                            .collect(),
 9057                        ),
 9058                        ..Default::default()
 9059                    }),
 9060                    ..Default::default()
 9061                },
 9062            )]))
 9063        })
 9064        .next()
 9065        .await;
 9066    cx.executor().start_waiting();
 9067    format.await;
 9068    assert_eq!(
 9069        editor.update(cx, |editor, cx| editor.text(cx)),
 9070        "import { a } from 'module';\n\nconst x = a;\n"
 9071    );
 9072
 9073    editor.update_in(cx, |editor, window, cx| {
 9074        editor.set_text(
 9075            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9076            window,
 9077            cx,
 9078        )
 9079    });
 9080    // Ensure we don't lock if code action hangs.
 9081    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9082        move |params, _| async move {
 9083            assert_eq!(
 9084                params.text_document.uri,
 9085                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9086            );
 9087            futures::future::pending::<()>().await;
 9088            unreachable!()
 9089        },
 9090    );
 9091    let format = editor
 9092        .update_in(cx, |editor, window, cx| {
 9093            editor.perform_code_action_kind(
 9094                project,
 9095                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9096                window,
 9097                cx,
 9098            )
 9099        })
 9100        .unwrap();
 9101    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9102    cx.executor().start_waiting();
 9103    format.await;
 9104    assert_eq!(
 9105        editor.update(cx, |editor, cx| editor.text(cx)),
 9106        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9107    );
 9108}
 9109
 9110#[gpui::test]
 9111async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9112    init_test(cx, |_| {});
 9113
 9114    let mut cx = EditorLspTestContext::new_rust(
 9115        lsp::ServerCapabilities {
 9116            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9117            ..Default::default()
 9118        },
 9119        cx,
 9120    )
 9121    .await;
 9122
 9123    cx.set_state(indoc! {"
 9124        one.twoˇ
 9125    "});
 9126
 9127    // The format request takes a long time. When it completes, it inserts
 9128    // a newline and an indent before the `.`
 9129    cx.lsp
 9130        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9131            let executor = cx.background_executor().clone();
 9132            async move {
 9133                executor.timer(Duration::from_millis(100)).await;
 9134                Ok(Some(vec![lsp::TextEdit {
 9135                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9136                    new_text: "\n    ".into(),
 9137                }]))
 9138            }
 9139        });
 9140
 9141    // Submit a format request.
 9142    let format_1 = cx
 9143        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9144        .unwrap();
 9145    cx.executor().run_until_parked();
 9146
 9147    // Submit a second format request.
 9148    let format_2 = cx
 9149        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9150        .unwrap();
 9151    cx.executor().run_until_parked();
 9152
 9153    // Wait for both format requests to complete
 9154    cx.executor().advance_clock(Duration::from_millis(200));
 9155    cx.executor().start_waiting();
 9156    format_1.await.unwrap();
 9157    cx.executor().start_waiting();
 9158    format_2.await.unwrap();
 9159
 9160    // The formatting edits only happens once.
 9161    cx.assert_editor_state(indoc! {"
 9162        one
 9163            .twoˇ
 9164    "});
 9165}
 9166
 9167#[gpui::test]
 9168async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9169    init_test(cx, |settings| {
 9170        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9171    });
 9172
 9173    let mut cx = EditorLspTestContext::new_rust(
 9174        lsp::ServerCapabilities {
 9175            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9176            ..Default::default()
 9177        },
 9178        cx,
 9179    )
 9180    .await;
 9181
 9182    // Set up a buffer white some trailing whitespace and no trailing newline.
 9183    cx.set_state(
 9184        &[
 9185            "one ",   //
 9186            "twoˇ",   //
 9187            "three ", //
 9188            "four",   //
 9189        ]
 9190        .join("\n"),
 9191    );
 9192
 9193    // Submit a format request.
 9194    let format = cx
 9195        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9196        .unwrap();
 9197
 9198    // Record which buffer changes have been sent to the language server
 9199    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9200    cx.lsp
 9201        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9202            let buffer_changes = buffer_changes.clone();
 9203            move |params, _| {
 9204                buffer_changes.lock().extend(
 9205                    params
 9206                        .content_changes
 9207                        .into_iter()
 9208                        .map(|e| (e.range.unwrap(), e.text)),
 9209                );
 9210            }
 9211        });
 9212
 9213    // Handle formatting requests to the language server.
 9214    cx.lsp
 9215        .set_request_handler::<lsp::request::Formatting, _, _>({
 9216            let buffer_changes = buffer_changes.clone();
 9217            move |_, _| {
 9218                // When formatting is requested, trailing whitespace has already been stripped,
 9219                // and the trailing newline has already been added.
 9220                assert_eq!(
 9221                    &buffer_changes.lock()[1..],
 9222                    &[
 9223                        (
 9224                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9225                            "".into()
 9226                        ),
 9227                        (
 9228                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9229                            "".into()
 9230                        ),
 9231                        (
 9232                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9233                            "\n".into()
 9234                        ),
 9235                    ]
 9236                );
 9237
 9238                // Insert blank lines between each line of the buffer.
 9239                async move {
 9240                    Ok(Some(vec![
 9241                        lsp::TextEdit {
 9242                            range: lsp::Range::new(
 9243                                lsp::Position::new(1, 0),
 9244                                lsp::Position::new(1, 0),
 9245                            ),
 9246                            new_text: "\n".into(),
 9247                        },
 9248                        lsp::TextEdit {
 9249                            range: lsp::Range::new(
 9250                                lsp::Position::new(2, 0),
 9251                                lsp::Position::new(2, 0),
 9252                            ),
 9253                            new_text: "\n".into(),
 9254                        },
 9255                    ]))
 9256                }
 9257            }
 9258        });
 9259
 9260    // After formatting the buffer, the trailing whitespace is stripped,
 9261    // a newline is appended, and the edits provided by the language server
 9262    // have been applied.
 9263    format.await.unwrap();
 9264    cx.assert_editor_state(
 9265        &[
 9266            "one",   //
 9267            "",      //
 9268            "twoˇ",  //
 9269            "",      //
 9270            "three", //
 9271            "four",  //
 9272            "",      //
 9273        ]
 9274        .join("\n"),
 9275    );
 9276
 9277    // Undoing the formatting undoes the trailing whitespace removal, the
 9278    // trailing newline, and the LSP edits.
 9279    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9280    cx.assert_editor_state(
 9281        &[
 9282            "one ",   //
 9283            "twoˇ",   //
 9284            "three ", //
 9285            "four",   //
 9286        ]
 9287        .join("\n"),
 9288    );
 9289}
 9290
 9291#[gpui::test]
 9292async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9293    cx: &mut TestAppContext,
 9294) {
 9295    init_test(cx, |_| {});
 9296
 9297    cx.update(|cx| {
 9298        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9299            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9300                settings.auto_signature_help = Some(true);
 9301            });
 9302        });
 9303    });
 9304
 9305    let mut cx = EditorLspTestContext::new_rust(
 9306        lsp::ServerCapabilities {
 9307            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9308                ..Default::default()
 9309            }),
 9310            ..Default::default()
 9311        },
 9312        cx,
 9313    )
 9314    .await;
 9315
 9316    let language = Language::new(
 9317        LanguageConfig {
 9318            name: "Rust".into(),
 9319            brackets: BracketPairConfig {
 9320                pairs: vec![
 9321                    BracketPair {
 9322                        start: "{".to_string(),
 9323                        end: "}".to_string(),
 9324                        close: true,
 9325                        surround: true,
 9326                        newline: true,
 9327                    },
 9328                    BracketPair {
 9329                        start: "(".to_string(),
 9330                        end: ")".to_string(),
 9331                        close: true,
 9332                        surround: true,
 9333                        newline: true,
 9334                    },
 9335                    BracketPair {
 9336                        start: "/*".to_string(),
 9337                        end: " */".to_string(),
 9338                        close: true,
 9339                        surround: true,
 9340                        newline: true,
 9341                    },
 9342                    BracketPair {
 9343                        start: "[".to_string(),
 9344                        end: "]".to_string(),
 9345                        close: false,
 9346                        surround: false,
 9347                        newline: true,
 9348                    },
 9349                    BracketPair {
 9350                        start: "\"".to_string(),
 9351                        end: "\"".to_string(),
 9352                        close: true,
 9353                        surround: true,
 9354                        newline: false,
 9355                    },
 9356                    BracketPair {
 9357                        start: "<".to_string(),
 9358                        end: ">".to_string(),
 9359                        close: false,
 9360                        surround: true,
 9361                        newline: true,
 9362                    },
 9363                ],
 9364                ..Default::default()
 9365            },
 9366            autoclose_before: "})]".to_string(),
 9367            ..Default::default()
 9368        },
 9369        Some(tree_sitter_rust::LANGUAGE.into()),
 9370    );
 9371    let language = Arc::new(language);
 9372
 9373    cx.language_registry().add(language.clone());
 9374    cx.update_buffer(|buffer, cx| {
 9375        buffer.set_language(Some(language), cx);
 9376    });
 9377
 9378    cx.set_state(
 9379        &r#"
 9380            fn main() {
 9381                sampleˇ
 9382            }
 9383        "#
 9384        .unindent(),
 9385    );
 9386
 9387    cx.update_editor(|editor, window, cx| {
 9388        editor.handle_input("(", window, cx);
 9389    });
 9390    cx.assert_editor_state(
 9391        &"
 9392            fn main() {
 9393                sample(ˇ)
 9394            }
 9395        "
 9396        .unindent(),
 9397    );
 9398
 9399    let mocked_response = lsp::SignatureHelp {
 9400        signatures: vec![lsp::SignatureInformation {
 9401            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9402            documentation: None,
 9403            parameters: Some(vec![
 9404                lsp::ParameterInformation {
 9405                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9406                    documentation: None,
 9407                },
 9408                lsp::ParameterInformation {
 9409                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9410                    documentation: None,
 9411                },
 9412            ]),
 9413            active_parameter: None,
 9414        }],
 9415        active_signature: Some(0),
 9416        active_parameter: Some(0),
 9417    };
 9418    handle_signature_help_request(&mut cx, mocked_response).await;
 9419
 9420    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9421        .await;
 9422
 9423    cx.editor(|editor, _, _| {
 9424        let signature_help_state = editor.signature_help_state.popover().cloned();
 9425        assert_eq!(
 9426            signature_help_state.unwrap().label,
 9427            "param1: u8, param2: u8"
 9428        );
 9429    });
 9430}
 9431
 9432#[gpui::test]
 9433async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9434    init_test(cx, |_| {});
 9435
 9436    cx.update(|cx| {
 9437        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9438            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9439                settings.auto_signature_help = Some(false);
 9440                settings.show_signature_help_after_edits = Some(false);
 9441            });
 9442        });
 9443    });
 9444
 9445    let mut cx = EditorLspTestContext::new_rust(
 9446        lsp::ServerCapabilities {
 9447            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9448                ..Default::default()
 9449            }),
 9450            ..Default::default()
 9451        },
 9452        cx,
 9453    )
 9454    .await;
 9455
 9456    let language = Language::new(
 9457        LanguageConfig {
 9458            name: "Rust".into(),
 9459            brackets: BracketPairConfig {
 9460                pairs: vec![
 9461                    BracketPair {
 9462                        start: "{".to_string(),
 9463                        end: "}".to_string(),
 9464                        close: true,
 9465                        surround: true,
 9466                        newline: true,
 9467                    },
 9468                    BracketPair {
 9469                        start: "(".to_string(),
 9470                        end: ")".to_string(),
 9471                        close: true,
 9472                        surround: true,
 9473                        newline: true,
 9474                    },
 9475                    BracketPair {
 9476                        start: "/*".to_string(),
 9477                        end: " */".to_string(),
 9478                        close: true,
 9479                        surround: true,
 9480                        newline: true,
 9481                    },
 9482                    BracketPair {
 9483                        start: "[".to_string(),
 9484                        end: "]".to_string(),
 9485                        close: false,
 9486                        surround: false,
 9487                        newline: true,
 9488                    },
 9489                    BracketPair {
 9490                        start: "\"".to_string(),
 9491                        end: "\"".to_string(),
 9492                        close: true,
 9493                        surround: true,
 9494                        newline: false,
 9495                    },
 9496                    BracketPair {
 9497                        start: "<".to_string(),
 9498                        end: ">".to_string(),
 9499                        close: false,
 9500                        surround: true,
 9501                        newline: true,
 9502                    },
 9503                ],
 9504                ..Default::default()
 9505            },
 9506            autoclose_before: "})]".to_string(),
 9507            ..Default::default()
 9508        },
 9509        Some(tree_sitter_rust::LANGUAGE.into()),
 9510    );
 9511    let language = Arc::new(language);
 9512
 9513    cx.language_registry().add(language.clone());
 9514    cx.update_buffer(|buffer, cx| {
 9515        buffer.set_language(Some(language), cx);
 9516    });
 9517
 9518    // Ensure that signature_help is not called when no signature help is enabled.
 9519    cx.set_state(
 9520        &r#"
 9521            fn main() {
 9522                sampleˇ
 9523            }
 9524        "#
 9525        .unindent(),
 9526    );
 9527    cx.update_editor(|editor, window, cx| {
 9528        editor.handle_input("(", window, cx);
 9529    });
 9530    cx.assert_editor_state(
 9531        &"
 9532            fn main() {
 9533                sample(ˇ)
 9534            }
 9535        "
 9536        .unindent(),
 9537    );
 9538    cx.editor(|editor, _, _| {
 9539        assert!(editor.signature_help_state.task().is_none());
 9540    });
 9541
 9542    let mocked_response = lsp::SignatureHelp {
 9543        signatures: vec![lsp::SignatureInformation {
 9544            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9545            documentation: None,
 9546            parameters: Some(vec![
 9547                lsp::ParameterInformation {
 9548                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9549                    documentation: None,
 9550                },
 9551                lsp::ParameterInformation {
 9552                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9553                    documentation: None,
 9554                },
 9555            ]),
 9556            active_parameter: None,
 9557        }],
 9558        active_signature: Some(0),
 9559        active_parameter: Some(0),
 9560    };
 9561
 9562    // Ensure that signature_help is called when enabled afte edits
 9563    cx.update(|_, cx| {
 9564        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9565            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9566                settings.auto_signature_help = Some(false);
 9567                settings.show_signature_help_after_edits = Some(true);
 9568            });
 9569        });
 9570    });
 9571    cx.set_state(
 9572        &r#"
 9573            fn main() {
 9574                sampleˇ
 9575            }
 9576        "#
 9577        .unindent(),
 9578    );
 9579    cx.update_editor(|editor, window, cx| {
 9580        editor.handle_input("(", window, cx);
 9581    });
 9582    cx.assert_editor_state(
 9583        &"
 9584            fn main() {
 9585                sample(ˇ)
 9586            }
 9587        "
 9588        .unindent(),
 9589    );
 9590    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9591    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9592        .await;
 9593    cx.update_editor(|editor, _, _| {
 9594        let signature_help_state = editor.signature_help_state.popover().cloned();
 9595        assert!(signature_help_state.is_some());
 9596        assert_eq!(
 9597            signature_help_state.unwrap().label,
 9598            "param1: u8, param2: u8"
 9599        );
 9600        editor.signature_help_state = SignatureHelpState::default();
 9601    });
 9602
 9603    // Ensure that signature_help is called when auto signature help override is enabled
 9604    cx.update(|_, cx| {
 9605        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9606            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9607                settings.auto_signature_help = Some(true);
 9608                settings.show_signature_help_after_edits = Some(false);
 9609            });
 9610        });
 9611    });
 9612    cx.set_state(
 9613        &r#"
 9614            fn main() {
 9615                sampleˇ
 9616            }
 9617        "#
 9618        .unindent(),
 9619    );
 9620    cx.update_editor(|editor, window, cx| {
 9621        editor.handle_input("(", window, cx);
 9622    });
 9623    cx.assert_editor_state(
 9624        &"
 9625            fn main() {
 9626                sample(ˇ)
 9627            }
 9628        "
 9629        .unindent(),
 9630    );
 9631    handle_signature_help_request(&mut cx, mocked_response).await;
 9632    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9633        .await;
 9634    cx.editor(|editor, _, _| {
 9635        let signature_help_state = editor.signature_help_state.popover().cloned();
 9636        assert!(signature_help_state.is_some());
 9637        assert_eq!(
 9638            signature_help_state.unwrap().label,
 9639            "param1: u8, param2: u8"
 9640        );
 9641    });
 9642}
 9643
 9644#[gpui::test]
 9645async fn test_signature_help(cx: &mut TestAppContext) {
 9646    init_test(cx, |_| {});
 9647    cx.update(|cx| {
 9648        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9649            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9650                settings.auto_signature_help = Some(true);
 9651            });
 9652        });
 9653    });
 9654
 9655    let mut cx = EditorLspTestContext::new_rust(
 9656        lsp::ServerCapabilities {
 9657            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9658                ..Default::default()
 9659            }),
 9660            ..Default::default()
 9661        },
 9662        cx,
 9663    )
 9664    .await;
 9665
 9666    // A test that directly calls `show_signature_help`
 9667    cx.update_editor(|editor, window, cx| {
 9668        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9669    });
 9670
 9671    let mocked_response = lsp::SignatureHelp {
 9672        signatures: vec![lsp::SignatureInformation {
 9673            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9674            documentation: None,
 9675            parameters: Some(vec![
 9676                lsp::ParameterInformation {
 9677                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9678                    documentation: None,
 9679                },
 9680                lsp::ParameterInformation {
 9681                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9682                    documentation: None,
 9683                },
 9684            ]),
 9685            active_parameter: None,
 9686        }],
 9687        active_signature: Some(0),
 9688        active_parameter: Some(0),
 9689    };
 9690    handle_signature_help_request(&mut cx, mocked_response).await;
 9691
 9692    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9693        .await;
 9694
 9695    cx.editor(|editor, _, _| {
 9696        let signature_help_state = editor.signature_help_state.popover().cloned();
 9697        assert!(signature_help_state.is_some());
 9698        assert_eq!(
 9699            signature_help_state.unwrap().label,
 9700            "param1: u8, param2: u8"
 9701        );
 9702    });
 9703
 9704    // When exiting outside from inside the brackets, `signature_help` is closed.
 9705    cx.set_state(indoc! {"
 9706        fn main() {
 9707            sample(ˇ);
 9708        }
 9709
 9710        fn sample(param1: u8, param2: u8) {}
 9711    "});
 9712
 9713    cx.update_editor(|editor, window, cx| {
 9714        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9715    });
 9716
 9717    let mocked_response = lsp::SignatureHelp {
 9718        signatures: Vec::new(),
 9719        active_signature: None,
 9720        active_parameter: None,
 9721    };
 9722    handle_signature_help_request(&mut cx, mocked_response).await;
 9723
 9724    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9725        .await;
 9726
 9727    cx.editor(|editor, _, _| {
 9728        assert!(!editor.signature_help_state.is_shown());
 9729    });
 9730
 9731    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9732    cx.set_state(indoc! {"
 9733        fn main() {
 9734            sample(ˇ);
 9735        }
 9736
 9737        fn sample(param1: u8, param2: u8) {}
 9738    "});
 9739
 9740    let mocked_response = lsp::SignatureHelp {
 9741        signatures: vec![lsp::SignatureInformation {
 9742            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9743            documentation: None,
 9744            parameters: Some(vec![
 9745                lsp::ParameterInformation {
 9746                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9747                    documentation: None,
 9748                },
 9749                lsp::ParameterInformation {
 9750                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9751                    documentation: None,
 9752                },
 9753            ]),
 9754            active_parameter: None,
 9755        }],
 9756        active_signature: Some(0),
 9757        active_parameter: Some(0),
 9758    };
 9759    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9760    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9761        .await;
 9762    cx.editor(|editor, _, _| {
 9763        assert!(editor.signature_help_state.is_shown());
 9764    });
 9765
 9766    // Restore the popover with more parameter input
 9767    cx.set_state(indoc! {"
 9768        fn main() {
 9769            sample(param1, param2ˇ);
 9770        }
 9771
 9772        fn sample(param1: u8, param2: u8) {}
 9773    "});
 9774
 9775    let mocked_response = lsp::SignatureHelp {
 9776        signatures: vec![lsp::SignatureInformation {
 9777            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9778            documentation: None,
 9779            parameters: Some(vec![
 9780                lsp::ParameterInformation {
 9781                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9782                    documentation: None,
 9783                },
 9784                lsp::ParameterInformation {
 9785                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9786                    documentation: None,
 9787                },
 9788            ]),
 9789            active_parameter: None,
 9790        }],
 9791        active_signature: Some(0),
 9792        active_parameter: Some(1),
 9793    };
 9794    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9795    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9796        .await;
 9797
 9798    // When selecting a range, the popover is gone.
 9799    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9800    cx.update_editor(|editor, window, cx| {
 9801        editor.change_selections(None, window, cx, |s| {
 9802            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9803        })
 9804    });
 9805    cx.assert_editor_state(indoc! {"
 9806        fn main() {
 9807            sample(param1, «ˇparam2»);
 9808        }
 9809
 9810        fn sample(param1: u8, param2: u8) {}
 9811    "});
 9812    cx.editor(|editor, _, _| {
 9813        assert!(!editor.signature_help_state.is_shown());
 9814    });
 9815
 9816    // When unselecting again, the popover is back if within the brackets.
 9817    cx.update_editor(|editor, window, cx| {
 9818        editor.change_selections(None, window, cx, |s| {
 9819            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9820        })
 9821    });
 9822    cx.assert_editor_state(indoc! {"
 9823        fn main() {
 9824            sample(param1, ˇparam2);
 9825        }
 9826
 9827        fn sample(param1: u8, param2: u8) {}
 9828    "});
 9829    handle_signature_help_request(&mut cx, mocked_response).await;
 9830    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9831        .await;
 9832    cx.editor(|editor, _, _| {
 9833        assert!(editor.signature_help_state.is_shown());
 9834    });
 9835
 9836    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9837    cx.update_editor(|editor, window, cx| {
 9838        editor.change_selections(None, window, cx, |s| {
 9839            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9840            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9841        })
 9842    });
 9843    cx.assert_editor_state(indoc! {"
 9844        fn main() {
 9845            sample(param1, ˇparam2);
 9846        }
 9847
 9848        fn sample(param1: u8, param2: u8) {}
 9849    "});
 9850
 9851    let mocked_response = lsp::SignatureHelp {
 9852        signatures: vec![lsp::SignatureInformation {
 9853            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9854            documentation: None,
 9855            parameters: Some(vec![
 9856                lsp::ParameterInformation {
 9857                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9858                    documentation: None,
 9859                },
 9860                lsp::ParameterInformation {
 9861                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9862                    documentation: None,
 9863                },
 9864            ]),
 9865            active_parameter: None,
 9866        }],
 9867        active_signature: Some(0),
 9868        active_parameter: Some(1),
 9869    };
 9870    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9871    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9872        .await;
 9873    cx.update_editor(|editor, _, cx| {
 9874        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9875    });
 9876    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9877        .await;
 9878    cx.update_editor(|editor, window, cx| {
 9879        editor.change_selections(None, window, cx, |s| {
 9880            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9881        })
 9882    });
 9883    cx.assert_editor_state(indoc! {"
 9884        fn main() {
 9885            sample(param1, «ˇparam2»);
 9886        }
 9887
 9888        fn sample(param1: u8, param2: u8) {}
 9889    "});
 9890    cx.update_editor(|editor, window, cx| {
 9891        editor.change_selections(None, window, cx, |s| {
 9892            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9893        })
 9894    });
 9895    cx.assert_editor_state(indoc! {"
 9896        fn main() {
 9897            sample(param1, ˇparam2);
 9898        }
 9899
 9900        fn sample(param1: u8, param2: u8) {}
 9901    "});
 9902    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9903        .await;
 9904}
 9905
 9906#[gpui::test]
 9907async fn test_completion_mode(cx: &mut TestAppContext) {
 9908    init_test(cx, |_| {});
 9909    let mut cx = EditorLspTestContext::new_rust(
 9910        lsp::ServerCapabilities {
 9911            completion_provider: Some(lsp::CompletionOptions {
 9912                resolve_provider: Some(true),
 9913                ..Default::default()
 9914            }),
 9915            ..Default::default()
 9916        },
 9917        cx,
 9918    )
 9919    .await;
 9920
 9921    struct Run {
 9922        run_description: &'static str,
 9923        initial_state: String,
 9924        buffer_marked_text: String,
 9925        completion_text: &'static str,
 9926        expected_with_insert_mode: String,
 9927        expected_with_replace_mode: String,
 9928        expected_with_replace_subsequence_mode: String,
 9929        expected_with_replace_suffix_mode: String,
 9930    }
 9931
 9932    let runs = [
 9933        Run {
 9934            run_description: "Start of word matches completion text",
 9935            initial_state: "before ediˇ after".into(),
 9936            buffer_marked_text: "before <edi|> after".into(),
 9937            completion_text: "editor",
 9938            expected_with_insert_mode: "before editorˇ after".into(),
 9939            expected_with_replace_mode: "before editorˇ after".into(),
 9940            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9941            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9942        },
 9943        Run {
 9944            run_description: "Accept same text at the middle of the word",
 9945            initial_state: "before ediˇtor after".into(),
 9946            buffer_marked_text: "before <edi|tor> after".into(),
 9947            completion_text: "editor",
 9948            expected_with_insert_mode: "before editorˇtor after".into(),
 9949            expected_with_replace_mode: "before editorˇ after".into(),
 9950            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9951            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9952        },
 9953        Run {
 9954            run_description: "End of word matches completion text -- cursor at end",
 9955            initial_state: "before torˇ after".into(),
 9956            buffer_marked_text: "before <tor|> after".into(),
 9957            completion_text: "editor",
 9958            expected_with_insert_mode: "before editorˇ after".into(),
 9959            expected_with_replace_mode: "before editorˇ after".into(),
 9960            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9961            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9962        },
 9963        Run {
 9964            run_description: "End of word matches completion text -- cursor at start",
 9965            initial_state: "before ˇtor after".into(),
 9966            buffer_marked_text: "before <|tor> after".into(),
 9967            completion_text: "editor",
 9968            expected_with_insert_mode: "before editorˇtor after".into(),
 9969            expected_with_replace_mode: "before editorˇ after".into(),
 9970            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9971            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9972        },
 9973        Run {
 9974            run_description: "Prepend text containing whitespace",
 9975            initial_state: "pˇfield: bool".into(),
 9976            buffer_marked_text: "<p|field>: bool".into(),
 9977            completion_text: "pub ",
 9978            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9979            expected_with_replace_mode: "pub ˇ: bool".into(),
 9980            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9981            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9982        },
 9983        Run {
 9984            run_description: "Add element to start of list",
 9985            initial_state: "[element_ˇelement_2]".into(),
 9986            buffer_marked_text: "[<element_|element_2>]".into(),
 9987            completion_text: "element_1",
 9988            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9989            expected_with_replace_mode: "[element_1ˇ]".into(),
 9990            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9991            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9992        },
 9993        Run {
 9994            run_description: "Add element to start of list -- first and second elements are equal",
 9995            initial_state: "[elˇelement]".into(),
 9996            buffer_marked_text: "[<el|element>]".into(),
 9997            completion_text: "element",
 9998            expected_with_insert_mode: "[elementˇelement]".into(),
 9999            expected_with_replace_mode: "[elementˇ]".into(),
10000            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10001            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10002        },
10003        Run {
10004            run_description: "Ends with matching suffix",
10005            initial_state: "SubˇError".into(),
10006            buffer_marked_text: "<Sub|Error>".into(),
10007            completion_text: "SubscriptionError",
10008            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10009            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10010            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10011            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10012        },
10013        Run {
10014            run_description: "Suffix is a subsequence -- contiguous",
10015            initial_state: "SubˇErr".into(),
10016            buffer_marked_text: "<Sub|Err>".into(),
10017            completion_text: "SubscriptionError",
10018            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10019            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10020            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10021            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10022        },
10023        Run {
10024            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10025            initial_state: "Suˇscrirr".into(),
10026            buffer_marked_text: "<Su|scrirr>".into(),
10027            completion_text: "SubscriptionError",
10028            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10029            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10030            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10031            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10032        },
10033        Run {
10034            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10035            initial_state: "foo(indˇix)".into(),
10036            buffer_marked_text: "foo(<ind|ix>)".into(),
10037            completion_text: "node_index",
10038            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10039            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10040            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10041            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10042        },
10043    ];
10044
10045    for run in runs {
10046        let run_variations = [
10047            (LspInsertMode::Insert, run.expected_with_insert_mode),
10048            (LspInsertMode::Replace, run.expected_with_replace_mode),
10049            (
10050                LspInsertMode::ReplaceSubsequence,
10051                run.expected_with_replace_subsequence_mode,
10052            ),
10053            (
10054                LspInsertMode::ReplaceSuffix,
10055                run.expected_with_replace_suffix_mode,
10056            ),
10057        ];
10058
10059        for (lsp_insert_mode, expected_text) in run_variations {
10060            eprintln!(
10061                "run = {:?}, mode = {lsp_insert_mode:.?}",
10062                run.run_description,
10063            );
10064
10065            update_test_language_settings(&mut cx, |settings| {
10066                settings.defaults.completions = Some(CompletionSettings {
10067                    lsp_insert_mode,
10068                    words: WordsCompletionMode::Disabled,
10069                    lsp: true,
10070                    lsp_fetch_timeout_ms: 0,
10071                });
10072            });
10073
10074            cx.set_state(&run.initial_state);
10075            cx.update_editor(|editor, window, cx| {
10076                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10077            });
10078
10079            let counter = Arc::new(AtomicUsize::new(0));
10080            handle_completion_request_with_insert_and_replace(
10081                &mut cx,
10082                &run.buffer_marked_text,
10083                vec![run.completion_text],
10084                counter.clone(),
10085            )
10086            .await;
10087            cx.condition(|editor, _| editor.context_menu_visible())
10088                .await;
10089            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10090
10091            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10092                editor
10093                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10094                    .unwrap()
10095            });
10096            cx.assert_editor_state(&expected_text);
10097            handle_resolve_completion_request(&mut cx, None).await;
10098            apply_additional_edits.await.unwrap();
10099        }
10100    }
10101}
10102
10103#[gpui::test]
10104async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10105    init_test(cx, |_| {});
10106    let mut cx = EditorLspTestContext::new_rust(
10107        lsp::ServerCapabilities {
10108            completion_provider: Some(lsp::CompletionOptions {
10109                resolve_provider: Some(true),
10110                ..Default::default()
10111            }),
10112            ..Default::default()
10113        },
10114        cx,
10115    )
10116    .await;
10117
10118    let initial_state = "SubˇError";
10119    let buffer_marked_text = "<Sub|Error>";
10120    let completion_text = "SubscriptionError";
10121    let expected_with_insert_mode = "SubscriptionErrorˇError";
10122    let expected_with_replace_mode = "SubscriptionErrorˇ";
10123
10124    update_test_language_settings(&mut cx, |settings| {
10125        settings.defaults.completions = Some(CompletionSettings {
10126            words: WordsCompletionMode::Disabled,
10127            // set the opposite here to ensure that the action is overriding the default behavior
10128            lsp_insert_mode: LspInsertMode::Insert,
10129            lsp: true,
10130            lsp_fetch_timeout_ms: 0,
10131        });
10132    });
10133
10134    cx.set_state(initial_state);
10135    cx.update_editor(|editor, window, cx| {
10136        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10137    });
10138
10139    let counter = Arc::new(AtomicUsize::new(0));
10140    handle_completion_request_with_insert_and_replace(
10141        &mut cx,
10142        &buffer_marked_text,
10143        vec![completion_text],
10144        counter.clone(),
10145    )
10146    .await;
10147    cx.condition(|editor, _| editor.context_menu_visible())
10148        .await;
10149    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10150
10151    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10152        editor
10153            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10154            .unwrap()
10155    });
10156    cx.assert_editor_state(&expected_with_replace_mode);
10157    handle_resolve_completion_request(&mut cx, None).await;
10158    apply_additional_edits.await.unwrap();
10159
10160    update_test_language_settings(&mut cx, |settings| {
10161        settings.defaults.completions = Some(CompletionSettings {
10162            words: WordsCompletionMode::Disabled,
10163            // set the opposite here to ensure that the action is overriding the default behavior
10164            lsp_insert_mode: LspInsertMode::Replace,
10165            lsp: true,
10166            lsp_fetch_timeout_ms: 0,
10167        });
10168    });
10169
10170    cx.set_state(initial_state);
10171    cx.update_editor(|editor, window, cx| {
10172        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10173    });
10174    handle_completion_request_with_insert_and_replace(
10175        &mut cx,
10176        &buffer_marked_text,
10177        vec![completion_text],
10178        counter.clone(),
10179    )
10180    .await;
10181    cx.condition(|editor, _| editor.context_menu_visible())
10182        .await;
10183    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10184
10185    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10186        editor
10187            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10188            .unwrap()
10189    });
10190    cx.assert_editor_state(&expected_with_insert_mode);
10191    handle_resolve_completion_request(&mut cx, None).await;
10192    apply_additional_edits.await.unwrap();
10193}
10194
10195#[gpui::test]
10196async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10197    init_test(cx, |_| {});
10198    let mut cx = EditorLspTestContext::new_rust(
10199        lsp::ServerCapabilities {
10200            completion_provider: Some(lsp::CompletionOptions {
10201                resolve_provider: Some(true),
10202                ..Default::default()
10203            }),
10204            ..Default::default()
10205        },
10206        cx,
10207    )
10208    .await;
10209
10210    // scenario: surrounding text matches completion text
10211    let completion_text = "to_offset";
10212    let initial_state = indoc! {"
10213        1. buf.to_offˇsuffix
10214        2. buf.to_offˇsuf
10215        3. buf.to_offˇfix
10216        4. buf.to_offˇ
10217        5. into_offˇensive
10218        6. ˇsuffix
10219        7. let ˇ //
10220        8. aaˇzz
10221        9. buf.to_off«zzzzzˇ»suffix
10222        10. buf.«ˇzzzzz»suffix
10223        11. to_off«ˇzzzzz»
10224
10225        buf.to_offˇsuffix  // newest cursor
10226    "};
10227    let completion_marked_buffer = indoc! {"
10228        1. buf.to_offsuffix
10229        2. buf.to_offsuf
10230        3. buf.to_offfix
10231        4. buf.to_off
10232        5. into_offensive
10233        6. suffix
10234        7. let  //
10235        8. aazz
10236        9. buf.to_offzzzzzsuffix
10237        10. buf.zzzzzsuffix
10238        11. to_offzzzzz
10239
10240        buf.<to_off|suffix>  // newest cursor
10241    "};
10242    let expected = indoc! {"
10243        1. buf.to_offsetˇ
10244        2. buf.to_offsetˇsuf
10245        3. buf.to_offsetˇfix
10246        4. buf.to_offsetˇ
10247        5. into_offsetˇensive
10248        6. to_offsetˇsuffix
10249        7. let to_offsetˇ //
10250        8. aato_offsetˇzz
10251        9. buf.to_offsetˇ
10252        10. buf.to_offsetˇsuffix
10253        11. to_offsetˇ
10254
10255        buf.to_offsetˇ  // newest cursor
10256    "};
10257    cx.set_state(initial_state);
10258    cx.update_editor(|editor, window, cx| {
10259        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10260    });
10261    handle_completion_request_with_insert_and_replace(
10262        &mut cx,
10263        completion_marked_buffer,
10264        vec![completion_text],
10265        Arc::new(AtomicUsize::new(0)),
10266    )
10267    .await;
10268    cx.condition(|editor, _| editor.context_menu_visible())
10269        .await;
10270    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10271        editor
10272            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10273            .unwrap()
10274    });
10275    cx.assert_editor_state(expected);
10276    handle_resolve_completion_request(&mut cx, None).await;
10277    apply_additional_edits.await.unwrap();
10278
10279    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10280    let completion_text = "foo_and_bar";
10281    let initial_state = indoc! {"
10282        1. ooanbˇ
10283        2. zooanbˇ
10284        3. ooanbˇz
10285        4. zooanbˇz
10286        5. ooanˇ
10287        6. oanbˇ
10288
10289        ooanbˇ
10290    "};
10291    let completion_marked_buffer = indoc! {"
10292        1. ooanb
10293        2. zooanb
10294        3. ooanbz
10295        4. zooanbz
10296        5. ooan
10297        6. oanb
10298
10299        <ooanb|>
10300    "};
10301    let expected = indoc! {"
10302        1. foo_and_barˇ
10303        2. zfoo_and_barˇ
10304        3. foo_and_barˇz
10305        4. zfoo_and_barˇz
10306        5. ooanfoo_and_barˇ
10307        6. oanbfoo_and_barˇ
10308
10309        foo_and_barˇ
10310    "};
10311    cx.set_state(initial_state);
10312    cx.update_editor(|editor, window, cx| {
10313        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10314    });
10315    handle_completion_request_with_insert_and_replace(
10316        &mut cx,
10317        completion_marked_buffer,
10318        vec![completion_text],
10319        Arc::new(AtomicUsize::new(0)),
10320    )
10321    .await;
10322    cx.condition(|editor, _| editor.context_menu_visible())
10323        .await;
10324    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10325        editor
10326            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10327            .unwrap()
10328    });
10329    cx.assert_editor_state(expected);
10330    handle_resolve_completion_request(&mut cx, None).await;
10331    apply_additional_edits.await.unwrap();
10332
10333    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10334    // (expects the same as if it was inserted at the end)
10335    let completion_text = "foo_and_bar";
10336    let initial_state = indoc! {"
10337        1. ooˇanb
10338        2. zooˇanb
10339        3. ooˇanbz
10340        4. zooˇanbz
10341
10342        ooˇanb
10343    "};
10344    let completion_marked_buffer = indoc! {"
10345        1. ooanb
10346        2. zooanb
10347        3. ooanbz
10348        4. zooanbz
10349
10350        <oo|anb>
10351    "};
10352    let expected = indoc! {"
10353        1. foo_and_barˇ
10354        2. zfoo_and_barˇ
10355        3. foo_and_barˇz
10356        4. zfoo_and_barˇz
10357
10358        foo_and_barˇ
10359    "};
10360    cx.set_state(initial_state);
10361    cx.update_editor(|editor, window, cx| {
10362        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10363    });
10364    handle_completion_request_with_insert_and_replace(
10365        &mut cx,
10366        completion_marked_buffer,
10367        vec![completion_text],
10368        Arc::new(AtomicUsize::new(0)),
10369    )
10370    .await;
10371    cx.condition(|editor, _| editor.context_menu_visible())
10372        .await;
10373    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10374        editor
10375            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10376            .unwrap()
10377    });
10378    cx.assert_editor_state(expected);
10379    handle_resolve_completion_request(&mut cx, None).await;
10380    apply_additional_edits.await.unwrap();
10381}
10382
10383// This used to crash
10384#[gpui::test]
10385async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10386    init_test(cx, |_| {});
10387
10388    let buffer_text = indoc! {"
10389        fn main() {
10390            10.satu;
10391
10392            //
10393            // separate cursors so they open in different excerpts (manually reproducible)
10394            //
10395
10396            10.satu20;
10397        }
10398    "};
10399    let multibuffer_text_with_selections = indoc! {"
10400        fn main() {
10401            10.satuˇ;
10402
10403            //
10404
10405            //
10406
10407            10.satuˇ20;
10408        }
10409    "};
10410    let expected_multibuffer = indoc! {"
10411        fn main() {
10412            10.saturating_sub()ˇ;
10413
10414            //
10415
10416            //
10417
10418            10.saturating_sub()ˇ;
10419        }
10420    "};
10421
10422    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10423    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10424
10425    let fs = FakeFs::new(cx.executor());
10426    fs.insert_tree(
10427        path!("/a"),
10428        json!({
10429            "main.rs": buffer_text,
10430        }),
10431    )
10432    .await;
10433
10434    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10435    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10436    language_registry.add(rust_lang());
10437    let mut fake_servers = language_registry.register_fake_lsp(
10438        "Rust",
10439        FakeLspAdapter {
10440            capabilities: lsp::ServerCapabilities {
10441                completion_provider: Some(lsp::CompletionOptions {
10442                    resolve_provider: None,
10443                    ..lsp::CompletionOptions::default()
10444                }),
10445                ..lsp::ServerCapabilities::default()
10446            },
10447            ..FakeLspAdapter::default()
10448        },
10449    );
10450    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10451    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10452    let buffer = project
10453        .update(cx, |project, cx| {
10454            project.open_local_buffer(path!("/a/main.rs"), cx)
10455        })
10456        .await
10457        .unwrap();
10458
10459    let multi_buffer = cx.new(|cx| {
10460        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10461        multi_buffer.push_excerpts(
10462            buffer.clone(),
10463            [ExcerptRange::new(0..first_excerpt_end)],
10464            cx,
10465        );
10466        multi_buffer.push_excerpts(
10467            buffer.clone(),
10468            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10469            cx,
10470        );
10471        multi_buffer
10472    });
10473
10474    let editor = workspace
10475        .update(cx, |_, window, cx| {
10476            cx.new(|cx| {
10477                Editor::new(
10478                    EditorMode::Full {
10479                        scale_ui_elements_with_buffer_font_size: false,
10480                        show_active_line_background: false,
10481                        sized_by_content: false,
10482                    },
10483                    multi_buffer.clone(),
10484                    Some(project.clone()),
10485                    window,
10486                    cx,
10487                )
10488            })
10489        })
10490        .unwrap();
10491
10492    let pane = workspace
10493        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10494        .unwrap();
10495    pane.update_in(cx, |pane, window, cx| {
10496        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10497    });
10498
10499    let fake_server = fake_servers.next().await.unwrap();
10500
10501    editor.update_in(cx, |editor, window, cx| {
10502        editor.change_selections(None, window, cx, |s| {
10503            s.select_ranges([
10504                Point::new(1, 11)..Point::new(1, 11),
10505                Point::new(7, 11)..Point::new(7, 11),
10506            ])
10507        });
10508
10509        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10510    });
10511
10512    editor.update_in(cx, |editor, window, cx| {
10513        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10514    });
10515
10516    fake_server
10517        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10518            let completion_item = lsp::CompletionItem {
10519                label: "saturating_sub()".into(),
10520                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10521                    lsp::InsertReplaceEdit {
10522                        new_text: "saturating_sub()".to_owned(),
10523                        insert: lsp::Range::new(
10524                            lsp::Position::new(7, 7),
10525                            lsp::Position::new(7, 11),
10526                        ),
10527                        replace: lsp::Range::new(
10528                            lsp::Position::new(7, 7),
10529                            lsp::Position::new(7, 13),
10530                        ),
10531                    },
10532                )),
10533                ..lsp::CompletionItem::default()
10534            };
10535
10536            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10537        })
10538        .next()
10539        .await
10540        .unwrap();
10541
10542    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10543        .await;
10544
10545    editor
10546        .update_in(cx, |editor, window, cx| {
10547            editor
10548                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10549                .unwrap()
10550        })
10551        .await
10552        .unwrap();
10553
10554    editor.update(cx, |editor, cx| {
10555        assert_text_with_selections(editor, expected_multibuffer, cx);
10556    })
10557}
10558
10559#[gpui::test]
10560async fn test_completion(cx: &mut TestAppContext) {
10561    init_test(cx, |_| {});
10562
10563    let mut cx = EditorLspTestContext::new_rust(
10564        lsp::ServerCapabilities {
10565            completion_provider: Some(lsp::CompletionOptions {
10566                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10567                resolve_provider: Some(true),
10568                ..Default::default()
10569            }),
10570            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10571            ..Default::default()
10572        },
10573        cx,
10574    )
10575    .await;
10576    let counter = Arc::new(AtomicUsize::new(0));
10577
10578    cx.set_state(indoc! {"
10579        oneˇ
10580        two
10581        three
10582    "});
10583    cx.simulate_keystroke(".");
10584    handle_completion_request(
10585        &mut cx,
10586        indoc! {"
10587            one.|<>
10588            two
10589            three
10590        "},
10591        vec!["first_completion", "second_completion"],
10592        counter.clone(),
10593    )
10594    .await;
10595    cx.condition(|editor, _| editor.context_menu_visible())
10596        .await;
10597    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10598
10599    let _handler = handle_signature_help_request(
10600        &mut cx,
10601        lsp::SignatureHelp {
10602            signatures: vec![lsp::SignatureInformation {
10603                label: "test signature".to_string(),
10604                documentation: None,
10605                parameters: Some(vec![lsp::ParameterInformation {
10606                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10607                    documentation: None,
10608                }]),
10609                active_parameter: None,
10610            }],
10611            active_signature: None,
10612            active_parameter: None,
10613        },
10614    );
10615    cx.update_editor(|editor, window, cx| {
10616        assert!(
10617            !editor.signature_help_state.is_shown(),
10618            "No signature help was called for"
10619        );
10620        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10621    });
10622    cx.run_until_parked();
10623    cx.update_editor(|editor, _, _| {
10624        assert!(
10625            !editor.signature_help_state.is_shown(),
10626            "No signature help should be shown when completions menu is open"
10627        );
10628    });
10629
10630    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10631        editor.context_menu_next(&Default::default(), window, cx);
10632        editor
10633            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10634            .unwrap()
10635    });
10636    cx.assert_editor_state(indoc! {"
10637        one.second_completionˇ
10638        two
10639        three
10640    "});
10641
10642    handle_resolve_completion_request(
10643        &mut cx,
10644        Some(vec![
10645            (
10646                //This overlaps with the primary completion edit which is
10647                //misbehavior from the LSP spec, test that we filter it out
10648                indoc! {"
10649                    one.second_ˇcompletion
10650                    two
10651                    threeˇ
10652                "},
10653                "overlapping additional edit",
10654            ),
10655            (
10656                indoc! {"
10657                    one.second_completion
10658                    two
10659                    threeˇ
10660                "},
10661                "\nadditional edit",
10662            ),
10663        ]),
10664    )
10665    .await;
10666    apply_additional_edits.await.unwrap();
10667    cx.assert_editor_state(indoc! {"
10668        one.second_completionˇ
10669        two
10670        three
10671        additional edit
10672    "});
10673
10674    cx.set_state(indoc! {"
10675        one.second_completion
10676        twoˇ
10677        threeˇ
10678        additional edit
10679    "});
10680    cx.simulate_keystroke(" ");
10681    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10682    cx.simulate_keystroke("s");
10683    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10684
10685    cx.assert_editor_state(indoc! {"
10686        one.second_completion
10687        two sˇ
10688        three sˇ
10689        additional edit
10690    "});
10691    handle_completion_request(
10692        &mut cx,
10693        indoc! {"
10694            one.second_completion
10695            two s
10696            three <s|>
10697            additional edit
10698        "},
10699        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10700        counter.clone(),
10701    )
10702    .await;
10703    cx.condition(|editor, _| editor.context_menu_visible())
10704        .await;
10705    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10706
10707    cx.simulate_keystroke("i");
10708
10709    handle_completion_request(
10710        &mut cx,
10711        indoc! {"
10712            one.second_completion
10713            two si
10714            three <si|>
10715            additional edit
10716        "},
10717        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10718        counter.clone(),
10719    )
10720    .await;
10721    cx.condition(|editor, _| editor.context_menu_visible())
10722        .await;
10723    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10724
10725    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10726        editor
10727            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10728            .unwrap()
10729    });
10730    cx.assert_editor_state(indoc! {"
10731        one.second_completion
10732        two sixth_completionˇ
10733        three sixth_completionˇ
10734        additional edit
10735    "});
10736
10737    apply_additional_edits.await.unwrap();
10738
10739    update_test_language_settings(&mut cx, |settings| {
10740        settings.defaults.show_completions_on_input = Some(false);
10741    });
10742    cx.set_state("editorˇ");
10743    cx.simulate_keystroke(".");
10744    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10745    cx.simulate_keystrokes("c l o");
10746    cx.assert_editor_state("editor.cloˇ");
10747    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10748    cx.update_editor(|editor, window, cx| {
10749        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10750    });
10751    handle_completion_request(
10752        &mut cx,
10753        "editor.<clo|>",
10754        vec!["close", "clobber"],
10755        counter.clone(),
10756    )
10757    .await;
10758    cx.condition(|editor, _| editor.context_menu_visible())
10759        .await;
10760    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10761
10762    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10763        editor
10764            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10765            .unwrap()
10766    });
10767    cx.assert_editor_state("editor.closeˇ");
10768    handle_resolve_completion_request(&mut cx, None).await;
10769    apply_additional_edits.await.unwrap();
10770}
10771
10772#[gpui::test]
10773async fn test_word_completion(cx: &mut TestAppContext) {
10774    let lsp_fetch_timeout_ms = 10;
10775    init_test(cx, |language_settings| {
10776        language_settings.defaults.completions = Some(CompletionSettings {
10777            words: WordsCompletionMode::Fallback,
10778            lsp: true,
10779            lsp_fetch_timeout_ms: 10,
10780            lsp_insert_mode: LspInsertMode::Insert,
10781        });
10782    });
10783
10784    let mut cx = EditorLspTestContext::new_rust(
10785        lsp::ServerCapabilities {
10786            completion_provider: Some(lsp::CompletionOptions {
10787                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10788                ..lsp::CompletionOptions::default()
10789            }),
10790            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10791            ..lsp::ServerCapabilities::default()
10792        },
10793        cx,
10794    )
10795    .await;
10796
10797    let throttle_completions = Arc::new(AtomicBool::new(false));
10798
10799    let lsp_throttle_completions = throttle_completions.clone();
10800    let _completion_requests_handler =
10801        cx.lsp
10802            .server
10803            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10804                let lsp_throttle_completions = lsp_throttle_completions.clone();
10805                let cx = cx.clone();
10806                async move {
10807                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10808                        cx.background_executor()
10809                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10810                            .await;
10811                    }
10812                    Ok(Some(lsp::CompletionResponse::Array(vec![
10813                        lsp::CompletionItem {
10814                            label: "first".into(),
10815                            ..lsp::CompletionItem::default()
10816                        },
10817                        lsp::CompletionItem {
10818                            label: "last".into(),
10819                            ..lsp::CompletionItem::default()
10820                        },
10821                    ])))
10822                }
10823            });
10824
10825    cx.set_state(indoc! {"
10826        oneˇ
10827        two
10828        three
10829    "});
10830    cx.simulate_keystroke(".");
10831    cx.executor().run_until_parked();
10832    cx.condition(|editor, _| editor.context_menu_visible())
10833        .await;
10834    cx.update_editor(|editor, window, cx| {
10835        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10836        {
10837            assert_eq!(
10838                completion_menu_entries(&menu),
10839                &["first", "last"],
10840                "When LSP server is fast to reply, no fallback word completions are used"
10841            );
10842        } else {
10843            panic!("expected completion menu to be open");
10844        }
10845        editor.cancel(&Cancel, window, cx);
10846    });
10847    cx.executor().run_until_parked();
10848    cx.condition(|editor, _| !editor.context_menu_visible())
10849        .await;
10850
10851    throttle_completions.store(true, atomic::Ordering::Release);
10852    cx.simulate_keystroke(".");
10853    cx.executor()
10854        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10855    cx.executor().run_until_parked();
10856    cx.condition(|editor, _| editor.context_menu_visible())
10857        .await;
10858    cx.update_editor(|editor, _, _| {
10859        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10860        {
10861            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10862                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10863        } else {
10864            panic!("expected completion menu to be open");
10865        }
10866    });
10867}
10868
10869#[gpui::test]
10870async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10871    init_test(cx, |language_settings| {
10872        language_settings.defaults.completions = Some(CompletionSettings {
10873            words: WordsCompletionMode::Enabled,
10874            lsp: true,
10875            lsp_fetch_timeout_ms: 0,
10876            lsp_insert_mode: LspInsertMode::Insert,
10877        });
10878    });
10879
10880    let mut cx = EditorLspTestContext::new_rust(
10881        lsp::ServerCapabilities {
10882            completion_provider: Some(lsp::CompletionOptions {
10883                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10884                ..lsp::CompletionOptions::default()
10885            }),
10886            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10887            ..lsp::ServerCapabilities::default()
10888        },
10889        cx,
10890    )
10891    .await;
10892
10893    let _completion_requests_handler =
10894        cx.lsp
10895            .server
10896            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10897                Ok(Some(lsp::CompletionResponse::Array(vec![
10898                    lsp::CompletionItem {
10899                        label: "first".into(),
10900                        ..lsp::CompletionItem::default()
10901                    },
10902                    lsp::CompletionItem {
10903                        label: "last".into(),
10904                        ..lsp::CompletionItem::default()
10905                    },
10906                ])))
10907            });
10908
10909    cx.set_state(indoc! {"ˇ
10910        first
10911        last
10912        second
10913    "});
10914    cx.simulate_keystroke(".");
10915    cx.executor().run_until_parked();
10916    cx.condition(|editor, _| editor.context_menu_visible())
10917        .await;
10918    cx.update_editor(|editor, _, _| {
10919        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10920        {
10921            assert_eq!(
10922                completion_menu_entries(&menu),
10923                &["first", "last", "second"],
10924                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10925            );
10926        } else {
10927            panic!("expected completion menu to be open");
10928        }
10929    });
10930}
10931
10932#[gpui::test]
10933async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10934    init_test(cx, |language_settings| {
10935        language_settings.defaults.completions = Some(CompletionSettings {
10936            words: WordsCompletionMode::Disabled,
10937            lsp: true,
10938            lsp_fetch_timeout_ms: 0,
10939            lsp_insert_mode: LspInsertMode::Insert,
10940        });
10941    });
10942
10943    let mut cx = EditorLspTestContext::new_rust(
10944        lsp::ServerCapabilities {
10945            completion_provider: Some(lsp::CompletionOptions {
10946                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10947                ..lsp::CompletionOptions::default()
10948            }),
10949            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10950            ..lsp::ServerCapabilities::default()
10951        },
10952        cx,
10953    )
10954    .await;
10955
10956    let _completion_requests_handler =
10957        cx.lsp
10958            .server
10959            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10960                panic!("LSP completions should not be queried when dealing with word completions")
10961            });
10962
10963    cx.set_state(indoc! {"ˇ
10964        first
10965        last
10966        second
10967    "});
10968    cx.update_editor(|editor, window, cx| {
10969        editor.show_word_completions(&ShowWordCompletions, window, cx);
10970    });
10971    cx.executor().run_until_parked();
10972    cx.condition(|editor, _| editor.context_menu_visible())
10973        .await;
10974    cx.update_editor(|editor, _, _| {
10975        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10976        {
10977            assert_eq!(
10978                completion_menu_entries(&menu),
10979                &["first", "last", "second"],
10980                "`ShowWordCompletions` action should show word completions"
10981            );
10982        } else {
10983            panic!("expected completion menu to be open");
10984        }
10985    });
10986
10987    cx.simulate_keystroke("l");
10988    cx.executor().run_until_parked();
10989    cx.condition(|editor, _| editor.context_menu_visible())
10990        .await;
10991    cx.update_editor(|editor, _, _| {
10992        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10993        {
10994            assert_eq!(
10995                completion_menu_entries(&menu),
10996                &["last"],
10997                "After showing word completions, further editing should filter them and not query the LSP"
10998            );
10999        } else {
11000            panic!("expected completion menu to be open");
11001        }
11002    });
11003}
11004
11005#[gpui::test]
11006async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11007    init_test(cx, |language_settings| {
11008        language_settings.defaults.completions = Some(CompletionSettings {
11009            words: WordsCompletionMode::Fallback,
11010            lsp: false,
11011            lsp_fetch_timeout_ms: 0,
11012            lsp_insert_mode: LspInsertMode::Insert,
11013        });
11014    });
11015
11016    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11017
11018    cx.set_state(indoc! {"ˇ
11019        0_usize
11020        let
11021        33
11022        4.5f32
11023    "});
11024    cx.update_editor(|editor, window, cx| {
11025        editor.show_completions(&ShowCompletions::default(), window, cx);
11026    });
11027    cx.executor().run_until_parked();
11028    cx.condition(|editor, _| editor.context_menu_visible())
11029        .await;
11030    cx.update_editor(|editor, window, cx| {
11031        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11032        {
11033            assert_eq!(
11034                completion_menu_entries(&menu),
11035                &["let"],
11036                "With no digits in the completion query, no digits should be in the word completions"
11037            );
11038        } else {
11039            panic!("expected completion menu to be open");
11040        }
11041        editor.cancel(&Cancel, window, cx);
11042    });
11043
11044    cx.set_state(indoc! {"11045        0_usize
11046        let
11047        3
11048        33.35f32
11049    "});
11050    cx.update_editor(|editor, window, cx| {
11051        editor.show_completions(&ShowCompletions::default(), window, cx);
11052    });
11053    cx.executor().run_until_parked();
11054    cx.condition(|editor, _| editor.context_menu_visible())
11055        .await;
11056    cx.update_editor(|editor, _, _| {
11057        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11058        {
11059            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11060                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11061        } else {
11062            panic!("expected completion menu to be open");
11063        }
11064    });
11065}
11066
11067fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11068    let position = || lsp::Position {
11069        line: params.text_document_position.position.line,
11070        character: params.text_document_position.position.character,
11071    };
11072    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11073        range: lsp::Range {
11074            start: position(),
11075            end: position(),
11076        },
11077        new_text: text.to_string(),
11078    }))
11079}
11080
11081#[gpui::test]
11082async fn test_multiline_completion(cx: &mut TestAppContext) {
11083    init_test(cx, |_| {});
11084
11085    let fs = FakeFs::new(cx.executor());
11086    fs.insert_tree(
11087        path!("/a"),
11088        json!({
11089            "main.ts": "a",
11090        }),
11091    )
11092    .await;
11093
11094    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11095    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11096    let typescript_language = Arc::new(Language::new(
11097        LanguageConfig {
11098            name: "TypeScript".into(),
11099            matcher: LanguageMatcher {
11100                path_suffixes: vec!["ts".to_string()],
11101                ..LanguageMatcher::default()
11102            },
11103            line_comments: vec!["// ".into()],
11104            ..LanguageConfig::default()
11105        },
11106        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11107    ));
11108    language_registry.add(typescript_language.clone());
11109    let mut fake_servers = language_registry.register_fake_lsp(
11110        "TypeScript",
11111        FakeLspAdapter {
11112            capabilities: lsp::ServerCapabilities {
11113                completion_provider: Some(lsp::CompletionOptions {
11114                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11115                    ..lsp::CompletionOptions::default()
11116                }),
11117                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11118                ..lsp::ServerCapabilities::default()
11119            },
11120            // Emulate vtsls label generation
11121            label_for_completion: Some(Box::new(|item, _| {
11122                let text = if let Some(description) = item
11123                    .label_details
11124                    .as_ref()
11125                    .and_then(|label_details| label_details.description.as_ref())
11126                {
11127                    format!("{} {}", item.label, description)
11128                } else if let Some(detail) = &item.detail {
11129                    format!("{} {}", item.label, detail)
11130                } else {
11131                    item.label.clone()
11132                };
11133                let len = text.len();
11134                Some(language::CodeLabel {
11135                    text,
11136                    runs: Vec::new(),
11137                    filter_range: 0..len,
11138                })
11139            })),
11140            ..FakeLspAdapter::default()
11141        },
11142    );
11143    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11144    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11145    let worktree_id = workspace
11146        .update(cx, |workspace, _window, cx| {
11147            workspace.project().update(cx, |project, cx| {
11148                project.worktrees(cx).next().unwrap().read(cx).id()
11149            })
11150        })
11151        .unwrap();
11152    let _buffer = project
11153        .update(cx, |project, cx| {
11154            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11155        })
11156        .await
11157        .unwrap();
11158    let editor = workspace
11159        .update(cx, |workspace, window, cx| {
11160            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11161        })
11162        .unwrap()
11163        .await
11164        .unwrap()
11165        .downcast::<Editor>()
11166        .unwrap();
11167    let fake_server = fake_servers.next().await.unwrap();
11168
11169    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11170    let multiline_label_2 = "a\nb\nc\n";
11171    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11172    let multiline_description = "d\ne\nf\n";
11173    let multiline_detail_2 = "g\nh\ni\n";
11174
11175    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11176        move |params, _| async move {
11177            Ok(Some(lsp::CompletionResponse::Array(vec![
11178                lsp::CompletionItem {
11179                    label: multiline_label.to_string(),
11180                    text_edit: gen_text_edit(&params, "new_text_1"),
11181                    ..lsp::CompletionItem::default()
11182                },
11183                lsp::CompletionItem {
11184                    label: "single line label 1".to_string(),
11185                    detail: Some(multiline_detail.to_string()),
11186                    text_edit: gen_text_edit(&params, "new_text_2"),
11187                    ..lsp::CompletionItem::default()
11188                },
11189                lsp::CompletionItem {
11190                    label: "single line label 2".to_string(),
11191                    label_details: Some(lsp::CompletionItemLabelDetails {
11192                        description: Some(multiline_description.to_string()),
11193                        detail: None,
11194                    }),
11195                    text_edit: gen_text_edit(&params, "new_text_2"),
11196                    ..lsp::CompletionItem::default()
11197                },
11198                lsp::CompletionItem {
11199                    label: multiline_label_2.to_string(),
11200                    detail: Some(multiline_detail_2.to_string()),
11201                    text_edit: gen_text_edit(&params, "new_text_3"),
11202                    ..lsp::CompletionItem::default()
11203                },
11204                lsp::CompletionItem {
11205                    label: "Label with many     spaces and \t but without newlines".to_string(),
11206                    detail: Some(
11207                        "Details with many     spaces and \t but without newlines".to_string(),
11208                    ),
11209                    text_edit: gen_text_edit(&params, "new_text_4"),
11210                    ..lsp::CompletionItem::default()
11211                },
11212            ])))
11213        },
11214    );
11215
11216    editor.update_in(cx, |editor, window, cx| {
11217        cx.focus_self(window);
11218        editor.move_to_end(&MoveToEnd, window, cx);
11219        editor.handle_input(".", window, cx);
11220    });
11221    cx.run_until_parked();
11222    completion_handle.next().await.unwrap();
11223
11224    editor.update(cx, |editor, _| {
11225        assert!(editor.context_menu_visible());
11226        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11227        {
11228            let completion_labels = menu
11229                .completions
11230                .borrow()
11231                .iter()
11232                .map(|c| c.label.text.clone())
11233                .collect::<Vec<_>>();
11234            assert_eq!(
11235                completion_labels,
11236                &[
11237                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11238                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11239                    "single line label 2 d e f ",
11240                    "a b c g h i ",
11241                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11242                ],
11243                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11244            );
11245
11246            for completion in menu
11247                .completions
11248                .borrow()
11249                .iter() {
11250                    assert_eq!(
11251                        completion.label.filter_range,
11252                        0..completion.label.text.len(),
11253                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11254                    );
11255                }
11256        } else {
11257            panic!("expected completion menu to be open");
11258        }
11259    });
11260}
11261
11262#[gpui::test]
11263async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11264    init_test(cx, |_| {});
11265    let mut cx = EditorLspTestContext::new_rust(
11266        lsp::ServerCapabilities {
11267            completion_provider: Some(lsp::CompletionOptions {
11268                trigger_characters: Some(vec![".".to_string()]),
11269                ..Default::default()
11270            }),
11271            ..Default::default()
11272        },
11273        cx,
11274    )
11275    .await;
11276    cx.lsp
11277        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11278            Ok(Some(lsp::CompletionResponse::Array(vec![
11279                lsp::CompletionItem {
11280                    label: "first".into(),
11281                    ..Default::default()
11282                },
11283                lsp::CompletionItem {
11284                    label: "last".into(),
11285                    ..Default::default()
11286                },
11287            ])))
11288        });
11289    cx.set_state("variableˇ");
11290    cx.simulate_keystroke(".");
11291    cx.executor().run_until_parked();
11292
11293    cx.update_editor(|editor, _, _| {
11294        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11295        {
11296            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11297        } else {
11298            panic!("expected completion menu to be open");
11299        }
11300    });
11301
11302    cx.update_editor(|editor, window, cx| {
11303        editor.move_page_down(&MovePageDown::default(), window, cx);
11304        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11305        {
11306            assert!(
11307                menu.selected_item == 1,
11308                "expected PageDown to select the last item from the context menu"
11309            );
11310        } else {
11311            panic!("expected completion menu to stay open after PageDown");
11312        }
11313    });
11314
11315    cx.update_editor(|editor, window, cx| {
11316        editor.move_page_up(&MovePageUp::default(), window, cx);
11317        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11318        {
11319            assert!(
11320                menu.selected_item == 0,
11321                "expected PageUp to select the first item from the context menu"
11322            );
11323        } else {
11324            panic!("expected completion menu to stay open after PageUp");
11325        }
11326    });
11327}
11328
11329#[gpui::test]
11330async fn test_as_is_completions(cx: &mut TestAppContext) {
11331    init_test(cx, |_| {});
11332    let mut cx = EditorLspTestContext::new_rust(
11333        lsp::ServerCapabilities {
11334            completion_provider: Some(lsp::CompletionOptions {
11335                ..Default::default()
11336            }),
11337            ..Default::default()
11338        },
11339        cx,
11340    )
11341    .await;
11342    cx.lsp
11343        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11344            Ok(Some(lsp::CompletionResponse::Array(vec![
11345                lsp::CompletionItem {
11346                    label: "unsafe".into(),
11347                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11348                        range: lsp::Range {
11349                            start: lsp::Position {
11350                                line: 1,
11351                                character: 2,
11352                            },
11353                            end: lsp::Position {
11354                                line: 1,
11355                                character: 3,
11356                            },
11357                        },
11358                        new_text: "unsafe".to_string(),
11359                    })),
11360                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11361                    ..Default::default()
11362                },
11363            ])))
11364        });
11365    cx.set_state("fn a() {}\n");
11366    cx.executor().run_until_parked();
11367    cx.update_editor(|editor, window, cx| {
11368        editor.show_completions(
11369            &ShowCompletions {
11370                trigger: Some("\n".into()),
11371            },
11372            window,
11373            cx,
11374        );
11375    });
11376    cx.executor().run_until_parked();
11377
11378    cx.update_editor(|editor, window, cx| {
11379        editor.confirm_completion(&Default::default(), window, cx)
11380    });
11381    cx.executor().run_until_parked();
11382    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11383}
11384
11385#[gpui::test]
11386async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11387    init_test(cx, |_| {});
11388
11389    let mut cx = EditorLspTestContext::new_rust(
11390        lsp::ServerCapabilities {
11391            completion_provider: Some(lsp::CompletionOptions {
11392                trigger_characters: Some(vec![".".to_string()]),
11393                resolve_provider: Some(true),
11394                ..Default::default()
11395            }),
11396            ..Default::default()
11397        },
11398        cx,
11399    )
11400    .await;
11401
11402    cx.set_state("fn main() { let a = 2ˇ; }");
11403    cx.simulate_keystroke(".");
11404    let completion_item = lsp::CompletionItem {
11405        label: "Some".into(),
11406        kind: Some(lsp::CompletionItemKind::SNIPPET),
11407        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11408        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11409            kind: lsp::MarkupKind::Markdown,
11410            value: "```rust\nSome(2)\n```".to_string(),
11411        })),
11412        deprecated: Some(false),
11413        sort_text: Some("Some".to_string()),
11414        filter_text: Some("Some".to_string()),
11415        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11416        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11417            range: lsp::Range {
11418                start: lsp::Position {
11419                    line: 0,
11420                    character: 22,
11421                },
11422                end: lsp::Position {
11423                    line: 0,
11424                    character: 22,
11425                },
11426            },
11427            new_text: "Some(2)".to_string(),
11428        })),
11429        additional_text_edits: Some(vec![lsp::TextEdit {
11430            range: lsp::Range {
11431                start: lsp::Position {
11432                    line: 0,
11433                    character: 20,
11434                },
11435                end: lsp::Position {
11436                    line: 0,
11437                    character: 22,
11438                },
11439            },
11440            new_text: "".to_string(),
11441        }]),
11442        ..Default::default()
11443    };
11444
11445    let closure_completion_item = completion_item.clone();
11446    let counter = Arc::new(AtomicUsize::new(0));
11447    let counter_clone = counter.clone();
11448    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11449        let task_completion_item = closure_completion_item.clone();
11450        counter_clone.fetch_add(1, atomic::Ordering::Release);
11451        async move {
11452            Ok(Some(lsp::CompletionResponse::Array(vec![
11453                task_completion_item,
11454            ])))
11455        }
11456    });
11457
11458    cx.condition(|editor, _| editor.context_menu_visible())
11459        .await;
11460    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11461    assert!(request.next().await.is_some());
11462    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11463
11464    cx.simulate_keystrokes("S o m");
11465    cx.condition(|editor, _| editor.context_menu_visible())
11466        .await;
11467    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11468    assert!(request.next().await.is_some());
11469    assert!(request.next().await.is_some());
11470    assert!(request.next().await.is_some());
11471    request.close();
11472    assert!(request.next().await.is_none());
11473    assert_eq!(
11474        counter.load(atomic::Ordering::Acquire),
11475        4,
11476        "With the completions menu open, only one LSP request should happen per input"
11477    );
11478}
11479
11480#[gpui::test]
11481async fn test_toggle_comment(cx: &mut TestAppContext) {
11482    init_test(cx, |_| {});
11483    let mut cx = EditorTestContext::new(cx).await;
11484    let language = Arc::new(Language::new(
11485        LanguageConfig {
11486            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11487            ..Default::default()
11488        },
11489        Some(tree_sitter_rust::LANGUAGE.into()),
11490    ));
11491    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11492
11493    // If multiple selections intersect a line, the line is only toggled once.
11494    cx.set_state(indoc! {"
11495        fn a() {
11496            «//b();
11497            ˇ»// «c();
11498            //ˇ»  d();
11499        }
11500    "});
11501
11502    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11503
11504    cx.assert_editor_state(indoc! {"
11505        fn a() {
11506            «b();
11507            c();
11508            ˇ» d();
11509        }
11510    "});
11511
11512    // The comment prefix is inserted at the same column for every line in a
11513    // selection.
11514    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11515
11516    cx.assert_editor_state(indoc! {"
11517        fn a() {
11518            // «b();
11519            // c();
11520            ˇ»//  d();
11521        }
11522    "});
11523
11524    // If a selection ends at the beginning of a line, that line is not toggled.
11525    cx.set_selections_state(indoc! {"
11526        fn a() {
11527            // b();
11528            «// c();
11529        ˇ»    //  d();
11530        }
11531    "});
11532
11533    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11534
11535    cx.assert_editor_state(indoc! {"
11536        fn a() {
11537            // b();
11538            «c();
11539        ˇ»    //  d();
11540        }
11541    "});
11542
11543    // If a selection span a single line and is empty, the line is toggled.
11544    cx.set_state(indoc! {"
11545        fn a() {
11546            a();
11547            b();
11548        ˇ
11549        }
11550    "});
11551
11552    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11553
11554    cx.assert_editor_state(indoc! {"
11555        fn a() {
11556            a();
11557            b();
11558        //•ˇ
11559        }
11560    "});
11561
11562    // If a selection span multiple lines, empty lines are not toggled.
11563    cx.set_state(indoc! {"
11564        fn a() {
11565            «a();
11566
11567            c();ˇ»
11568        }
11569    "});
11570
11571    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11572
11573    cx.assert_editor_state(indoc! {"
11574        fn a() {
11575            // «a();
11576
11577            // c();ˇ»
11578        }
11579    "});
11580
11581    // If a selection includes multiple comment prefixes, all lines are uncommented.
11582    cx.set_state(indoc! {"
11583        fn a() {
11584            «// a();
11585            /// b();
11586            //! c();ˇ»
11587        }
11588    "});
11589
11590    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11591
11592    cx.assert_editor_state(indoc! {"
11593        fn a() {
11594            «a();
11595            b();
11596            c();ˇ»
11597        }
11598    "});
11599}
11600
11601#[gpui::test]
11602async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11603    init_test(cx, |_| {});
11604    let mut cx = EditorTestContext::new(cx).await;
11605    let language = Arc::new(Language::new(
11606        LanguageConfig {
11607            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11608            ..Default::default()
11609        },
11610        Some(tree_sitter_rust::LANGUAGE.into()),
11611    ));
11612    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11613
11614    let toggle_comments = &ToggleComments {
11615        advance_downwards: false,
11616        ignore_indent: true,
11617    };
11618
11619    // If multiple selections intersect a line, the line is only toggled once.
11620    cx.set_state(indoc! {"
11621        fn a() {
11622        //    «b();
11623        //    c();
11624        //    ˇ» d();
11625        }
11626    "});
11627
11628    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11629
11630    cx.assert_editor_state(indoc! {"
11631        fn a() {
11632            «b();
11633            c();
11634            ˇ» d();
11635        }
11636    "});
11637
11638    // The comment prefix is inserted at the beginning of each line
11639    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11640
11641    cx.assert_editor_state(indoc! {"
11642        fn a() {
11643        //    «b();
11644        //    c();
11645        //    ˇ» d();
11646        }
11647    "});
11648
11649    // If a selection ends at the beginning of a line, that line is not toggled.
11650    cx.set_selections_state(indoc! {"
11651        fn a() {
11652        //    b();
11653        //    «c();
11654        ˇ»//     d();
11655        }
11656    "});
11657
11658    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11659
11660    cx.assert_editor_state(indoc! {"
11661        fn a() {
11662        //    b();
11663            «c();
11664        ˇ»//     d();
11665        }
11666    "});
11667
11668    // If a selection span a single line and is empty, the line is toggled.
11669    cx.set_state(indoc! {"
11670        fn a() {
11671            a();
11672            b();
11673        ˇ
11674        }
11675    "});
11676
11677    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11678
11679    cx.assert_editor_state(indoc! {"
11680        fn a() {
11681            a();
11682            b();
11683        //ˇ
11684        }
11685    "});
11686
11687    // If a selection span multiple lines, empty lines are not toggled.
11688    cx.set_state(indoc! {"
11689        fn a() {
11690            «a();
11691
11692            c();ˇ»
11693        }
11694    "});
11695
11696    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11697
11698    cx.assert_editor_state(indoc! {"
11699        fn a() {
11700        //    «a();
11701
11702        //    c();ˇ»
11703        }
11704    "});
11705
11706    // If a selection includes multiple comment prefixes, all lines are uncommented.
11707    cx.set_state(indoc! {"
11708        fn a() {
11709        //    «a();
11710        ///    b();
11711        //!    c();ˇ»
11712        }
11713    "});
11714
11715    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11716
11717    cx.assert_editor_state(indoc! {"
11718        fn a() {
11719            «a();
11720            b();
11721            c();ˇ»
11722        }
11723    "});
11724}
11725
11726#[gpui::test]
11727async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11728    init_test(cx, |_| {});
11729
11730    let language = Arc::new(Language::new(
11731        LanguageConfig {
11732            line_comments: vec!["// ".into()],
11733            ..Default::default()
11734        },
11735        Some(tree_sitter_rust::LANGUAGE.into()),
11736    ));
11737
11738    let mut cx = EditorTestContext::new(cx).await;
11739
11740    cx.language_registry().add(language.clone());
11741    cx.update_buffer(|buffer, cx| {
11742        buffer.set_language(Some(language), cx);
11743    });
11744
11745    let toggle_comments = &ToggleComments {
11746        advance_downwards: true,
11747        ignore_indent: false,
11748    };
11749
11750    // Single cursor on one line -> advance
11751    // Cursor moves horizontally 3 characters as well on non-blank line
11752    cx.set_state(indoc!(
11753        "fn a() {
11754             ˇdog();
11755             cat();
11756        }"
11757    ));
11758    cx.update_editor(|editor, window, cx| {
11759        editor.toggle_comments(toggle_comments, window, cx);
11760    });
11761    cx.assert_editor_state(indoc!(
11762        "fn a() {
11763             // dog();
11764             catˇ();
11765        }"
11766    ));
11767
11768    // Single selection on one line -> don't advance
11769    cx.set_state(indoc!(
11770        "fn a() {
11771             «dog()ˇ»;
11772             cat();
11773        }"
11774    ));
11775    cx.update_editor(|editor, window, cx| {
11776        editor.toggle_comments(toggle_comments, window, cx);
11777    });
11778    cx.assert_editor_state(indoc!(
11779        "fn a() {
11780             // «dog()ˇ»;
11781             cat();
11782        }"
11783    ));
11784
11785    // Multiple cursors on one line -> advance
11786    cx.set_state(indoc!(
11787        "fn a() {
11788             ˇdˇog();
11789             cat();
11790        }"
11791    ));
11792    cx.update_editor(|editor, window, cx| {
11793        editor.toggle_comments(toggle_comments, window, cx);
11794    });
11795    cx.assert_editor_state(indoc!(
11796        "fn a() {
11797             // dog();
11798             catˇ(ˇ);
11799        }"
11800    ));
11801
11802    // Multiple cursors on one line, with selection -> don't advance
11803    cx.set_state(indoc!(
11804        "fn a() {
11805             ˇdˇog«()ˇ»;
11806             cat();
11807        }"
11808    ));
11809    cx.update_editor(|editor, window, cx| {
11810        editor.toggle_comments(toggle_comments, window, cx);
11811    });
11812    cx.assert_editor_state(indoc!(
11813        "fn a() {
11814             // ˇdˇog«()ˇ»;
11815             cat();
11816        }"
11817    ));
11818
11819    // Single cursor on one line -> advance
11820    // Cursor moves to column 0 on blank line
11821    cx.set_state(indoc!(
11822        "fn a() {
11823             ˇdog();
11824
11825             cat();
11826        }"
11827    ));
11828    cx.update_editor(|editor, window, cx| {
11829        editor.toggle_comments(toggle_comments, window, cx);
11830    });
11831    cx.assert_editor_state(indoc!(
11832        "fn a() {
11833             // dog();
11834        ˇ
11835             cat();
11836        }"
11837    ));
11838
11839    // Single cursor on one line -> advance
11840    // Cursor starts and ends at column 0
11841    cx.set_state(indoc!(
11842        "fn a() {
11843         ˇ    dog();
11844             cat();
11845        }"
11846    ));
11847    cx.update_editor(|editor, window, cx| {
11848        editor.toggle_comments(toggle_comments, window, cx);
11849    });
11850    cx.assert_editor_state(indoc!(
11851        "fn a() {
11852             // dog();
11853         ˇ    cat();
11854        }"
11855    ));
11856}
11857
11858#[gpui::test]
11859async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11860    init_test(cx, |_| {});
11861
11862    let mut cx = EditorTestContext::new(cx).await;
11863
11864    let html_language = Arc::new(
11865        Language::new(
11866            LanguageConfig {
11867                name: "HTML".into(),
11868                block_comment: Some(("<!-- ".into(), " -->".into())),
11869                ..Default::default()
11870            },
11871            Some(tree_sitter_html::LANGUAGE.into()),
11872        )
11873        .with_injection_query(
11874            r#"
11875            (script_element
11876                (raw_text) @injection.content
11877                (#set! injection.language "javascript"))
11878            "#,
11879        )
11880        .unwrap(),
11881    );
11882
11883    let javascript_language = Arc::new(Language::new(
11884        LanguageConfig {
11885            name: "JavaScript".into(),
11886            line_comments: vec!["// ".into()],
11887            ..Default::default()
11888        },
11889        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11890    ));
11891
11892    cx.language_registry().add(html_language.clone());
11893    cx.language_registry().add(javascript_language.clone());
11894    cx.update_buffer(|buffer, cx| {
11895        buffer.set_language(Some(html_language), cx);
11896    });
11897
11898    // Toggle comments for empty selections
11899    cx.set_state(
11900        &r#"
11901            <p>A</p>ˇ
11902            <p>B</p>ˇ
11903            <p>C</p>ˇ
11904        "#
11905        .unindent(),
11906    );
11907    cx.update_editor(|editor, window, cx| {
11908        editor.toggle_comments(&ToggleComments::default(), window, cx)
11909    });
11910    cx.assert_editor_state(
11911        &r#"
11912            <!-- <p>A</p>ˇ -->
11913            <!-- <p>B</p>ˇ -->
11914            <!-- <p>C</p>ˇ -->
11915        "#
11916        .unindent(),
11917    );
11918    cx.update_editor(|editor, window, cx| {
11919        editor.toggle_comments(&ToggleComments::default(), window, cx)
11920    });
11921    cx.assert_editor_state(
11922        &r#"
11923            <p>A</p>ˇ
11924            <p>B</p>ˇ
11925            <p>C</p>ˇ
11926        "#
11927        .unindent(),
11928    );
11929
11930    // Toggle comments for mixture of empty and non-empty selections, where
11931    // multiple selections occupy a given line.
11932    cx.set_state(
11933        &r#"
11934            <p>A«</p>
11935            <p>ˇ»B</p>ˇ
11936            <p>C«</p>
11937            <p>ˇ»D</p>ˇ
11938        "#
11939        .unindent(),
11940    );
11941
11942    cx.update_editor(|editor, window, cx| {
11943        editor.toggle_comments(&ToggleComments::default(), window, cx)
11944    });
11945    cx.assert_editor_state(
11946        &r#"
11947            <!-- <p>A«</p>
11948            <p>ˇ»B</p>ˇ -->
11949            <!-- <p>C«</p>
11950            <p>ˇ»D</p>ˇ -->
11951        "#
11952        .unindent(),
11953    );
11954    cx.update_editor(|editor, window, cx| {
11955        editor.toggle_comments(&ToggleComments::default(), window, cx)
11956    });
11957    cx.assert_editor_state(
11958        &r#"
11959            <p>A«</p>
11960            <p>ˇ»B</p>ˇ
11961            <p>C«</p>
11962            <p>ˇ»D</p>ˇ
11963        "#
11964        .unindent(),
11965    );
11966
11967    // Toggle comments when different languages are active for different
11968    // selections.
11969    cx.set_state(
11970        &r#"
11971            ˇ<script>
11972                ˇvar x = new Y();
11973            ˇ</script>
11974        "#
11975        .unindent(),
11976    );
11977    cx.executor().run_until_parked();
11978    cx.update_editor(|editor, window, cx| {
11979        editor.toggle_comments(&ToggleComments::default(), window, cx)
11980    });
11981    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11982    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11983    cx.assert_editor_state(
11984        &r#"
11985            <!-- ˇ<script> -->
11986                // ˇvar x = new Y();
11987            <!-- ˇ</script> -->
11988        "#
11989        .unindent(),
11990    );
11991}
11992
11993#[gpui::test]
11994fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11995    init_test(cx, |_| {});
11996
11997    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11998    let multibuffer = cx.new(|cx| {
11999        let mut multibuffer = MultiBuffer::new(ReadWrite);
12000        multibuffer.push_excerpts(
12001            buffer.clone(),
12002            [
12003                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12004                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12005            ],
12006            cx,
12007        );
12008        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12009        multibuffer
12010    });
12011
12012    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12013    editor.update_in(cx, |editor, window, cx| {
12014        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12015        editor.change_selections(None, window, cx, |s| {
12016            s.select_ranges([
12017                Point::new(0, 0)..Point::new(0, 0),
12018                Point::new(1, 0)..Point::new(1, 0),
12019            ])
12020        });
12021
12022        editor.handle_input("X", window, cx);
12023        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12024        assert_eq!(
12025            editor.selections.ranges(cx),
12026            [
12027                Point::new(0, 1)..Point::new(0, 1),
12028                Point::new(1, 1)..Point::new(1, 1),
12029            ]
12030        );
12031
12032        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12033        editor.change_selections(None, window, cx, |s| {
12034            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12035        });
12036        editor.backspace(&Default::default(), window, cx);
12037        assert_eq!(editor.text(cx), "Xa\nbbb");
12038        assert_eq!(
12039            editor.selections.ranges(cx),
12040            [Point::new(1, 0)..Point::new(1, 0)]
12041        );
12042
12043        editor.change_selections(None, window, cx, |s| {
12044            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12045        });
12046        editor.backspace(&Default::default(), window, cx);
12047        assert_eq!(editor.text(cx), "X\nbb");
12048        assert_eq!(
12049            editor.selections.ranges(cx),
12050            [Point::new(0, 1)..Point::new(0, 1)]
12051        );
12052    });
12053}
12054
12055#[gpui::test]
12056fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12057    init_test(cx, |_| {});
12058
12059    let markers = vec![('[', ']').into(), ('(', ')').into()];
12060    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12061        indoc! {"
12062            [aaaa
12063            (bbbb]
12064            cccc)",
12065        },
12066        markers.clone(),
12067    );
12068    let excerpt_ranges = markers.into_iter().map(|marker| {
12069        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12070        ExcerptRange::new(context.clone())
12071    });
12072    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12073    let multibuffer = cx.new(|cx| {
12074        let mut multibuffer = MultiBuffer::new(ReadWrite);
12075        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12076        multibuffer
12077    });
12078
12079    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12080    editor.update_in(cx, |editor, window, cx| {
12081        let (expected_text, selection_ranges) = marked_text_ranges(
12082            indoc! {"
12083                aaaa
12084                bˇbbb
12085                bˇbbˇb
12086                cccc"
12087            },
12088            true,
12089        );
12090        assert_eq!(editor.text(cx), expected_text);
12091        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12092
12093        editor.handle_input("X", window, cx);
12094
12095        let (expected_text, expected_selections) = marked_text_ranges(
12096            indoc! {"
12097                aaaa
12098                bXˇbbXb
12099                bXˇbbXˇb
12100                cccc"
12101            },
12102            false,
12103        );
12104        assert_eq!(editor.text(cx), expected_text);
12105        assert_eq!(editor.selections.ranges(cx), expected_selections);
12106
12107        editor.newline(&Newline, window, cx);
12108        let (expected_text, expected_selections) = marked_text_ranges(
12109            indoc! {"
12110                aaaa
12111                bX
12112                ˇbbX
12113                b
12114                bX
12115                ˇbbX
12116                ˇb
12117                cccc"
12118            },
12119            false,
12120        );
12121        assert_eq!(editor.text(cx), expected_text);
12122        assert_eq!(editor.selections.ranges(cx), expected_selections);
12123    });
12124}
12125
12126#[gpui::test]
12127fn test_refresh_selections(cx: &mut TestAppContext) {
12128    init_test(cx, |_| {});
12129
12130    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12131    let mut excerpt1_id = None;
12132    let multibuffer = cx.new(|cx| {
12133        let mut multibuffer = MultiBuffer::new(ReadWrite);
12134        excerpt1_id = multibuffer
12135            .push_excerpts(
12136                buffer.clone(),
12137                [
12138                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12139                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12140                ],
12141                cx,
12142            )
12143            .into_iter()
12144            .next();
12145        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12146        multibuffer
12147    });
12148
12149    let editor = cx.add_window(|window, cx| {
12150        let mut editor = build_editor(multibuffer.clone(), window, cx);
12151        let snapshot = editor.snapshot(window, cx);
12152        editor.change_selections(None, window, cx, |s| {
12153            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12154        });
12155        editor.begin_selection(
12156            Point::new(2, 1).to_display_point(&snapshot),
12157            true,
12158            1,
12159            window,
12160            cx,
12161        );
12162        assert_eq!(
12163            editor.selections.ranges(cx),
12164            [
12165                Point::new(1, 3)..Point::new(1, 3),
12166                Point::new(2, 1)..Point::new(2, 1),
12167            ]
12168        );
12169        editor
12170    });
12171
12172    // Refreshing selections is a no-op when excerpts haven't changed.
12173    _ = editor.update(cx, |editor, window, cx| {
12174        editor.change_selections(None, window, cx, |s| s.refresh());
12175        assert_eq!(
12176            editor.selections.ranges(cx),
12177            [
12178                Point::new(1, 3)..Point::new(1, 3),
12179                Point::new(2, 1)..Point::new(2, 1),
12180            ]
12181        );
12182    });
12183
12184    multibuffer.update(cx, |multibuffer, cx| {
12185        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12186    });
12187    _ = editor.update(cx, |editor, window, cx| {
12188        // Removing an excerpt causes the first selection to become degenerate.
12189        assert_eq!(
12190            editor.selections.ranges(cx),
12191            [
12192                Point::new(0, 0)..Point::new(0, 0),
12193                Point::new(0, 1)..Point::new(0, 1)
12194            ]
12195        );
12196
12197        // Refreshing selections will relocate the first selection to the original buffer
12198        // location.
12199        editor.change_selections(None, window, cx, |s| s.refresh());
12200        assert_eq!(
12201            editor.selections.ranges(cx),
12202            [
12203                Point::new(0, 1)..Point::new(0, 1),
12204                Point::new(0, 3)..Point::new(0, 3)
12205            ]
12206        );
12207        assert!(editor.selections.pending_anchor().is_some());
12208    });
12209}
12210
12211#[gpui::test]
12212fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12213    init_test(cx, |_| {});
12214
12215    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12216    let mut excerpt1_id = None;
12217    let multibuffer = cx.new(|cx| {
12218        let mut multibuffer = MultiBuffer::new(ReadWrite);
12219        excerpt1_id = multibuffer
12220            .push_excerpts(
12221                buffer.clone(),
12222                [
12223                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12224                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12225                ],
12226                cx,
12227            )
12228            .into_iter()
12229            .next();
12230        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12231        multibuffer
12232    });
12233
12234    let editor = cx.add_window(|window, cx| {
12235        let mut editor = build_editor(multibuffer.clone(), window, cx);
12236        let snapshot = editor.snapshot(window, cx);
12237        editor.begin_selection(
12238            Point::new(1, 3).to_display_point(&snapshot),
12239            false,
12240            1,
12241            window,
12242            cx,
12243        );
12244        assert_eq!(
12245            editor.selections.ranges(cx),
12246            [Point::new(1, 3)..Point::new(1, 3)]
12247        );
12248        editor
12249    });
12250
12251    multibuffer.update(cx, |multibuffer, cx| {
12252        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12253    });
12254    _ = editor.update(cx, |editor, window, cx| {
12255        assert_eq!(
12256            editor.selections.ranges(cx),
12257            [Point::new(0, 0)..Point::new(0, 0)]
12258        );
12259
12260        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12261        editor.change_selections(None, window, cx, |s| s.refresh());
12262        assert_eq!(
12263            editor.selections.ranges(cx),
12264            [Point::new(0, 3)..Point::new(0, 3)]
12265        );
12266        assert!(editor.selections.pending_anchor().is_some());
12267    });
12268}
12269
12270#[gpui::test]
12271async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12272    init_test(cx, |_| {});
12273
12274    let language = Arc::new(
12275        Language::new(
12276            LanguageConfig {
12277                brackets: BracketPairConfig {
12278                    pairs: vec![
12279                        BracketPair {
12280                            start: "{".to_string(),
12281                            end: "}".to_string(),
12282                            close: true,
12283                            surround: true,
12284                            newline: true,
12285                        },
12286                        BracketPair {
12287                            start: "/* ".to_string(),
12288                            end: " */".to_string(),
12289                            close: true,
12290                            surround: true,
12291                            newline: true,
12292                        },
12293                    ],
12294                    ..Default::default()
12295                },
12296                ..Default::default()
12297            },
12298            Some(tree_sitter_rust::LANGUAGE.into()),
12299        )
12300        .with_indents_query("")
12301        .unwrap(),
12302    );
12303
12304    let text = concat!(
12305        "{   }\n",     //
12306        "  x\n",       //
12307        "  /*   */\n", //
12308        "x\n",         //
12309        "{{} }\n",     //
12310    );
12311
12312    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12313    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12314    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12315    editor
12316        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12317        .await;
12318
12319    editor.update_in(cx, |editor, window, cx| {
12320        editor.change_selections(None, window, cx, |s| {
12321            s.select_display_ranges([
12322                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12323                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12324                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12325            ])
12326        });
12327        editor.newline(&Newline, window, cx);
12328
12329        assert_eq!(
12330            editor.buffer().read(cx).read(cx).text(),
12331            concat!(
12332                "{ \n",    // Suppress rustfmt
12333                "\n",      //
12334                "}\n",     //
12335                "  x\n",   //
12336                "  /* \n", //
12337                "  \n",    //
12338                "  */\n",  //
12339                "x\n",     //
12340                "{{} \n",  //
12341                "}\n",     //
12342            )
12343        );
12344    });
12345}
12346
12347#[gpui::test]
12348fn test_highlighted_ranges(cx: &mut TestAppContext) {
12349    init_test(cx, |_| {});
12350
12351    let editor = cx.add_window(|window, cx| {
12352        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12353        build_editor(buffer.clone(), window, cx)
12354    });
12355
12356    _ = editor.update(cx, |editor, window, cx| {
12357        struct Type1;
12358        struct Type2;
12359
12360        let buffer = editor.buffer.read(cx).snapshot(cx);
12361
12362        let anchor_range =
12363            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12364
12365        editor.highlight_background::<Type1>(
12366            &[
12367                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12368                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12369                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12370                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12371            ],
12372            |_| Hsla::red(),
12373            cx,
12374        );
12375        editor.highlight_background::<Type2>(
12376            &[
12377                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12378                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12379                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12380                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12381            ],
12382            |_| Hsla::green(),
12383            cx,
12384        );
12385
12386        let snapshot = editor.snapshot(window, cx);
12387        let mut highlighted_ranges = editor.background_highlights_in_range(
12388            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12389            &snapshot,
12390            cx.theme().colors(),
12391        );
12392        // Enforce a consistent ordering based on color without relying on the ordering of the
12393        // highlight's `TypeId` which is non-executor.
12394        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12395        assert_eq!(
12396            highlighted_ranges,
12397            &[
12398                (
12399                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12400                    Hsla::red(),
12401                ),
12402                (
12403                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12404                    Hsla::red(),
12405                ),
12406                (
12407                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12408                    Hsla::green(),
12409                ),
12410                (
12411                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12412                    Hsla::green(),
12413                ),
12414            ]
12415        );
12416        assert_eq!(
12417            editor.background_highlights_in_range(
12418                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12419                &snapshot,
12420                cx.theme().colors(),
12421            ),
12422            &[(
12423                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12424                Hsla::red(),
12425            )]
12426        );
12427    });
12428}
12429
12430#[gpui::test]
12431async fn test_following(cx: &mut TestAppContext) {
12432    init_test(cx, |_| {});
12433
12434    let fs = FakeFs::new(cx.executor());
12435    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12436
12437    let buffer = project.update(cx, |project, cx| {
12438        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12439        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12440    });
12441    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12442    let follower = cx.update(|cx| {
12443        cx.open_window(
12444            WindowOptions {
12445                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12446                    gpui::Point::new(px(0.), px(0.)),
12447                    gpui::Point::new(px(10.), px(80.)),
12448                ))),
12449                ..Default::default()
12450            },
12451            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12452        )
12453        .unwrap()
12454    });
12455
12456    let is_still_following = Rc::new(RefCell::new(true));
12457    let follower_edit_event_count = Rc::new(RefCell::new(0));
12458    let pending_update = Rc::new(RefCell::new(None));
12459    let leader_entity = leader.root(cx).unwrap();
12460    let follower_entity = follower.root(cx).unwrap();
12461    _ = follower.update(cx, {
12462        let update = pending_update.clone();
12463        let is_still_following = is_still_following.clone();
12464        let follower_edit_event_count = follower_edit_event_count.clone();
12465        |_, window, cx| {
12466            cx.subscribe_in(
12467                &leader_entity,
12468                window,
12469                move |_, leader, event, window, cx| {
12470                    leader.read(cx).add_event_to_update_proto(
12471                        event,
12472                        &mut update.borrow_mut(),
12473                        window,
12474                        cx,
12475                    );
12476                },
12477            )
12478            .detach();
12479
12480            cx.subscribe_in(
12481                &follower_entity,
12482                window,
12483                move |_, _, event: &EditorEvent, _window, _cx| {
12484                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12485                        *is_still_following.borrow_mut() = false;
12486                    }
12487
12488                    if let EditorEvent::BufferEdited = event {
12489                        *follower_edit_event_count.borrow_mut() += 1;
12490                    }
12491                },
12492            )
12493            .detach();
12494        }
12495    });
12496
12497    // Update the selections only
12498    _ = leader.update(cx, |leader, window, cx| {
12499        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12500    });
12501    follower
12502        .update(cx, |follower, window, cx| {
12503            follower.apply_update_proto(
12504                &project,
12505                pending_update.borrow_mut().take().unwrap(),
12506                window,
12507                cx,
12508            )
12509        })
12510        .unwrap()
12511        .await
12512        .unwrap();
12513    _ = follower.update(cx, |follower, _, cx| {
12514        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12515    });
12516    assert!(*is_still_following.borrow());
12517    assert_eq!(*follower_edit_event_count.borrow(), 0);
12518
12519    // Update the scroll position only
12520    _ = leader.update(cx, |leader, window, cx| {
12521        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12522    });
12523    follower
12524        .update(cx, |follower, window, cx| {
12525            follower.apply_update_proto(
12526                &project,
12527                pending_update.borrow_mut().take().unwrap(),
12528                window,
12529                cx,
12530            )
12531        })
12532        .unwrap()
12533        .await
12534        .unwrap();
12535    assert_eq!(
12536        follower
12537            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12538            .unwrap(),
12539        gpui::Point::new(1.5, 3.5)
12540    );
12541    assert!(*is_still_following.borrow());
12542    assert_eq!(*follower_edit_event_count.borrow(), 0);
12543
12544    // Update the selections and scroll position. The follower's scroll position is updated
12545    // via autoscroll, not via the leader's exact scroll position.
12546    _ = leader.update(cx, |leader, window, cx| {
12547        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12548        leader.request_autoscroll(Autoscroll::newest(), cx);
12549        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12550    });
12551    follower
12552        .update(cx, |follower, window, cx| {
12553            follower.apply_update_proto(
12554                &project,
12555                pending_update.borrow_mut().take().unwrap(),
12556                window,
12557                cx,
12558            )
12559        })
12560        .unwrap()
12561        .await
12562        .unwrap();
12563    _ = follower.update(cx, |follower, _, cx| {
12564        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12565        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12566    });
12567    assert!(*is_still_following.borrow());
12568
12569    // Creating a pending selection that precedes another selection
12570    _ = leader.update(cx, |leader, window, cx| {
12571        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12572        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12573    });
12574    follower
12575        .update(cx, |follower, window, cx| {
12576            follower.apply_update_proto(
12577                &project,
12578                pending_update.borrow_mut().take().unwrap(),
12579                window,
12580                cx,
12581            )
12582        })
12583        .unwrap()
12584        .await
12585        .unwrap();
12586    _ = follower.update(cx, |follower, _, cx| {
12587        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12588    });
12589    assert!(*is_still_following.borrow());
12590
12591    // Extend the pending selection so that it surrounds another selection
12592    _ = leader.update(cx, |leader, window, cx| {
12593        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12594    });
12595    follower
12596        .update(cx, |follower, window, cx| {
12597            follower.apply_update_proto(
12598                &project,
12599                pending_update.borrow_mut().take().unwrap(),
12600                window,
12601                cx,
12602            )
12603        })
12604        .unwrap()
12605        .await
12606        .unwrap();
12607    _ = follower.update(cx, |follower, _, cx| {
12608        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12609    });
12610
12611    // Scrolling locally breaks the follow
12612    _ = follower.update(cx, |follower, window, cx| {
12613        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12614        follower.set_scroll_anchor(
12615            ScrollAnchor {
12616                anchor: top_anchor,
12617                offset: gpui::Point::new(0.0, 0.5),
12618            },
12619            window,
12620            cx,
12621        );
12622    });
12623    assert!(!(*is_still_following.borrow()));
12624}
12625
12626#[gpui::test]
12627async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12628    init_test(cx, |_| {});
12629
12630    let fs = FakeFs::new(cx.executor());
12631    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12632    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12633    let pane = workspace
12634        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12635        .unwrap();
12636
12637    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12638
12639    let leader = pane.update_in(cx, |_, window, cx| {
12640        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12641        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12642    });
12643
12644    // Start following the editor when it has no excerpts.
12645    let mut state_message =
12646        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12647    let workspace_entity = workspace.root(cx).unwrap();
12648    let follower_1 = cx
12649        .update_window(*workspace.deref(), |_, window, cx| {
12650            Editor::from_state_proto(
12651                workspace_entity,
12652                ViewId {
12653                    creator: CollaboratorId::PeerId(PeerId::default()),
12654                    id: 0,
12655                },
12656                &mut state_message,
12657                window,
12658                cx,
12659            )
12660        })
12661        .unwrap()
12662        .unwrap()
12663        .await
12664        .unwrap();
12665
12666    let update_message = Rc::new(RefCell::new(None));
12667    follower_1.update_in(cx, {
12668        let update = update_message.clone();
12669        |_, window, cx| {
12670            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12671                leader.read(cx).add_event_to_update_proto(
12672                    event,
12673                    &mut update.borrow_mut(),
12674                    window,
12675                    cx,
12676                );
12677            })
12678            .detach();
12679        }
12680    });
12681
12682    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12683        (
12684            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12685            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12686        )
12687    });
12688
12689    // Insert some excerpts.
12690    leader.update(cx, |leader, cx| {
12691        leader.buffer.update(cx, |multibuffer, cx| {
12692            multibuffer.set_excerpts_for_path(
12693                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12694                buffer_1.clone(),
12695                vec![
12696                    Point::row_range(0..3),
12697                    Point::row_range(1..6),
12698                    Point::row_range(12..15),
12699                ],
12700                0,
12701                cx,
12702            );
12703            multibuffer.set_excerpts_for_path(
12704                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12705                buffer_2.clone(),
12706                vec![Point::row_range(0..6), Point::row_range(8..12)],
12707                0,
12708                cx,
12709            );
12710        });
12711    });
12712
12713    // Apply the update of adding the excerpts.
12714    follower_1
12715        .update_in(cx, |follower, window, cx| {
12716            follower.apply_update_proto(
12717                &project,
12718                update_message.borrow().clone().unwrap(),
12719                window,
12720                cx,
12721            )
12722        })
12723        .await
12724        .unwrap();
12725    assert_eq!(
12726        follower_1.update(cx, |editor, cx| editor.text(cx)),
12727        leader.update(cx, |editor, cx| editor.text(cx))
12728    );
12729    update_message.borrow_mut().take();
12730
12731    // Start following separately after it already has excerpts.
12732    let mut state_message =
12733        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12734    let workspace_entity = workspace.root(cx).unwrap();
12735    let follower_2 = cx
12736        .update_window(*workspace.deref(), |_, window, cx| {
12737            Editor::from_state_proto(
12738                workspace_entity,
12739                ViewId {
12740                    creator: CollaboratorId::PeerId(PeerId::default()),
12741                    id: 0,
12742                },
12743                &mut state_message,
12744                window,
12745                cx,
12746            )
12747        })
12748        .unwrap()
12749        .unwrap()
12750        .await
12751        .unwrap();
12752    assert_eq!(
12753        follower_2.update(cx, |editor, cx| editor.text(cx)),
12754        leader.update(cx, |editor, cx| editor.text(cx))
12755    );
12756
12757    // Remove some excerpts.
12758    leader.update(cx, |leader, cx| {
12759        leader.buffer.update(cx, |multibuffer, cx| {
12760            let excerpt_ids = multibuffer.excerpt_ids();
12761            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12762            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12763        });
12764    });
12765
12766    // Apply the update of removing the excerpts.
12767    follower_1
12768        .update_in(cx, |follower, window, cx| {
12769            follower.apply_update_proto(
12770                &project,
12771                update_message.borrow().clone().unwrap(),
12772                window,
12773                cx,
12774            )
12775        })
12776        .await
12777        .unwrap();
12778    follower_2
12779        .update_in(cx, |follower, window, cx| {
12780            follower.apply_update_proto(
12781                &project,
12782                update_message.borrow().clone().unwrap(),
12783                window,
12784                cx,
12785            )
12786        })
12787        .await
12788        .unwrap();
12789    update_message.borrow_mut().take();
12790    assert_eq!(
12791        follower_1.update(cx, |editor, cx| editor.text(cx)),
12792        leader.update(cx, |editor, cx| editor.text(cx))
12793    );
12794}
12795
12796#[gpui::test]
12797async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12798    init_test(cx, |_| {});
12799
12800    let mut cx = EditorTestContext::new(cx).await;
12801    let lsp_store =
12802        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12803
12804    cx.set_state(indoc! {"
12805        ˇfn func(abc def: i32) -> u32 {
12806        }
12807    "});
12808
12809    cx.update(|_, cx| {
12810        lsp_store.update(cx, |lsp_store, cx| {
12811            lsp_store
12812                .update_diagnostics(
12813                    LanguageServerId(0),
12814                    lsp::PublishDiagnosticsParams {
12815                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12816                        version: None,
12817                        diagnostics: vec![
12818                            lsp::Diagnostic {
12819                                range: lsp::Range::new(
12820                                    lsp::Position::new(0, 11),
12821                                    lsp::Position::new(0, 12),
12822                                ),
12823                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12824                                ..Default::default()
12825                            },
12826                            lsp::Diagnostic {
12827                                range: lsp::Range::new(
12828                                    lsp::Position::new(0, 12),
12829                                    lsp::Position::new(0, 15),
12830                                ),
12831                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12832                                ..Default::default()
12833                            },
12834                            lsp::Diagnostic {
12835                                range: lsp::Range::new(
12836                                    lsp::Position::new(0, 25),
12837                                    lsp::Position::new(0, 28),
12838                                ),
12839                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12840                                ..Default::default()
12841                            },
12842                        ],
12843                    },
12844                    &[],
12845                    cx,
12846                )
12847                .unwrap()
12848        });
12849    });
12850
12851    executor.run_until_parked();
12852
12853    cx.update_editor(|editor, window, cx| {
12854        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12855    });
12856
12857    cx.assert_editor_state(indoc! {"
12858        fn func(abc def: i32) -> ˇu32 {
12859        }
12860    "});
12861
12862    cx.update_editor(|editor, window, cx| {
12863        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12864    });
12865
12866    cx.assert_editor_state(indoc! {"
12867        fn func(abc ˇdef: i32) -> u32 {
12868        }
12869    "});
12870
12871    cx.update_editor(|editor, window, cx| {
12872        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12873    });
12874
12875    cx.assert_editor_state(indoc! {"
12876        fn func(abcˇ def: i32) -> u32 {
12877        }
12878    "});
12879
12880    cx.update_editor(|editor, window, cx| {
12881        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12882    });
12883
12884    cx.assert_editor_state(indoc! {"
12885        fn func(abc def: i32) -> ˇu32 {
12886        }
12887    "});
12888}
12889
12890#[gpui::test]
12891async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12892    init_test(cx, |_| {});
12893
12894    let mut cx = EditorTestContext::new(cx).await;
12895
12896    let diff_base = r#"
12897        use some::mod;
12898
12899        const A: u32 = 42;
12900
12901        fn main() {
12902            println!("hello");
12903
12904            println!("world");
12905        }
12906        "#
12907    .unindent();
12908
12909    // Edits are modified, removed, modified, added
12910    cx.set_state(
12911        &r#"
12912        use some::modified;
12913
12914        ˇ
12915        fn main() {
12916            println!("hello there");
12917
12918            println!("around the");
12919            println!("world");
12920        }
12921        "#
12922        .unindent(),
12923    );
12924
12925    cx.set_head_text(&diff_base);
12926    executor.run_until_parked();
12927
12928    cx.update_editor(|editor, window, cx| {
12929        //Wrap around the bottom of the buffer
12930        for _ in 0..3 {
12931            editor.go_to_next_hunk(&GoToHunk, window, cx);
12932        }
12933    });
12934
12935    cx.assert_editor_state(
12936        &r#"
12937        ˇuse some::modified;
12938
12939
12940        fn main() {
12941            println!("hello there");
12942
12943            println!("around the");
12944            println!("world");
12945        }
12946        "#
12947        .unindent(),
12948    );
12949
12950    cx.update_editor(|editor, window, cx| {
12951        //Wrap around the top of the buffer
12952        for _ in 0..2 {
12953            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12954        }
12955    });
12956
12957    cx.assert_editor_state(
12958        &r#"
12959        use some::modified;
12960
12961
12962        fn main() {
12963        ˇ    println!("hello there");
12964
12965            println!("around the");
12966            println!("world");
12967        }
12968        "#
12969        .unindent(),
12970    );
12971
12972    cx.update_editor(|editor, window, cx| {
12973        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12974    });
12975
12976    cx.assert_editor_state(
12977        &r#"
12978        use some::modified;
12979
12980        ˇ
12981        fn main() {
12982            println!("hello there");
12983
12984            println!("around the");
12985            println!("world");
12986        }
12987        "#
12988        .unindent(),
12989    );
12990
12991    cx.update_editor(|editor, window, cx| {
12992        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12993    });
12994
12995    cx.assert_editor_state(
12996        &r#"
12997        ˇuse some::modified;
12998
12999
13000        fn main() {
13001            println!("hello there");
13002
13003            println!("around the");
13004            println!("world");
13005        }
13006        "#
13007        .unindent(),
13008    );
13009
13010    cx.update_editor(|editor, window, cx| {
13011        for _ in 0..2 {
13012            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13013        }
13014    });
13015
13016    cx.assert_editor_state(
13017        &r#"
13018        use some::modified;
13019
13020
13021        fn main() {
13022        ˇ    println!("hello there");
13023
13024            println!("around the");
13025            println!("world");
13026        }
13027        "#
13028        .unindent(),
13029    );
13030
13031    cx.update_editor(|editor, window, cx| {
13032        editor.fold(&Fold, window, cx);
13033    });
13034
13035    cx.update_editor(|editor, window, cx| {
13036        editor.go_to_next_hunk(&GoToHunk, window, cx);
13037    });
13038
13039    cx.assert_editor_state(
13040        &r#"
13041        ˇuse some::modified;
13042
13043
13044        fn main() {
13045            println!("hello there");
13046
13047            println!("around the");
13048            println!("world");
13049        }
13050        "#
13051        .unindent(),
13052    );
13053}
13054
13055#[test]
13056fn test_split_words() {
13057    fn split(text: &str) -> Vec<&str> {
13058        split_words(text).collect()
13059    }
13060
13061    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13062    assert_eq!(split("hello_world"), &["hello_", "world"]);
13063    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13064    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13065    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13066    assert_eq!(split("helloworld"), &["helloworld"]);
13067
13068    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13069}
13070
13071#[gpui::test]
13072async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13073    init_test(cx, |_| {});
13074
13075    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13076    let mut assert = |before, after| {
13077        let _state_context = cx.set_state(before);
13078        cx.run_until_parked();
13079        cx.update_editor(|editor, window, cx| {
13080            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13081        });
13082        cx.run_until_parked();
13083        cx.assert_editor_state(after);
13084    };
13085
13086    // Outside bracket jumps to outside of matching bracket
13087    assert("console.logˇ(var);", "console.log(var)ˇ;");
13088    assert("console.log(var)ˇ;", "console.logˇ(var);");
13089
13090    // Inside bracket jumps to inside of matching bracket
13091    assert("console.log(ˇvar);", "console.log(varˇ);");
13092    assert("console.log(varˇ);", "console.log(ˇvar);");
13093
13094    // When outside a bracket and inside, favor jumping to the inside bracket
13095    assert(
13096        "console.log('foo', [1, 2, 3]ˇ);",
13097        "console.log(ˇ'foo', [1, 2, 3]);",
13098    );
13099    assert(
13100        "console.log(ˇ'foo', [1, 2, 3]);",
13101        "console.log('foo', [1, 2, 3]ˇ);",
13102    );
13103
13104    // Bias forward if two options are equally likely
13105    assert(
13106        "let result = curried_fun()ˇ();",
13107        "let result = curried_fun()()ˇ;",
13108    );
13109
13110    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13111    assert(
13112        indoc! {"
13113            function test() {
13114                console.log('test')ˇ
13115            }"},
13116        indoc! {"
13117            function test() {
13118                console.logˇ('test')
13119            }"},
13120    );
13121}
13122
13123#[gpui::test]
13124async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13125    init_test(cx, |_| {});
13126
13127    let fs = FakeFs::new(cx.executor());
13128    fs.insert_tree(
13129        path!("/a"),
13130        json!({
13131            "main.rs": "fn main() { let a = 5; }",
13132            "other.rs": "// Test file",
13133        }),
13134    )
13135    .await;
13136    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13137
13138    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13139    language_registry.add(Arc::new(Language::new(
13140        LanguageConfig {
13141            name: "Rust".into(),
13142            matcher: LanguageMatcher {
13143                path_suffixes: vec!["rs".to_string()],
13144                ..Default::default()
13145            },
13146            brackets: BracketPairConfig {
13147                pairs: vec![BracketPair {
13148                    start: "{".to_string(),
13149                    end: "}".to_string(),
13150                    close: true,
13151                    surround: true,
13152                    newline: true,
13153                }],
13154                disabled_scopes_by_bracket_ix: Vec::new(),
13155            },
13156            ..Default::default()
13157        },
13158        Some(tree_sitter_rust::LANGUAGE.into()),
13159    )));
13160    let mut fake_servers = language_registry.register_fake_lsp(
13161        "Rust",
13162        FakeLspAdapter {
13163            capabilities: lsp::ServerCapabilities {
13164                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13165                    first_trigger_character: "{".to_string(),
13166                    more_trigger_character: None,
13167                }),
13168                ..Default::default()
13169            },
13170            ..Default::default()
13171        },
13172    );
13173
13174    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13175
13176    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13177
13178    let worktree_id = workspace
13179        .update(cx, |workspace, _, cx| {
13180            workspace.project().update(cx, |project, cx| {
13181                project.worktrees(cx).next().unwrap().read(cx).id()
13182            })
13183        })
13184        .unwrap();
13185
13186    let buffer = project
13187        .update(cx, |project, cx| {
13188            project.open_local_buffer(path!("/a/main.rs"), cx)
13189        })
13190        .await
13191        .unwrap();
13192    let editor_handle = workspace
13193        .update(cx, |workspace, window, cx| {
13194            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13195        })
13196        .unwrap()
13197        .await
13198        .unwrap()
13199        .downcast::<Editor>()
13200        .unwrap();
13201
13202    cx.executor().start_waiting();
13203    let fake_server = fake_servers.next().await.unwrap();
13204
13205    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13206        |params, _| async move {
13207            assert_eq!(
13208                params.text_document_position.text_document.uri,
13209                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13210            );
13211            assert_eq!(
13212                params.text_document_position.position,
13213                lsp::Position::new(0, 21),
13214            );
13215
13216            Ok(Some(vec![lsp::TextEdit {
13217                new_text: "]".to_string(),
13218                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13219            }]))
13220        },
13221    );
13222
13223    editor_handle.update_in(cx, |editor, window, cx| {
13224        window.focus(&editor.focus_handle(cx));
13225        editor.change_selections(None, window, cx, |s| {
13226            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13227        });
13228        editor.handle_input("{", window, cx);
13229    });
13230
13231    cx.executor().run_until_parked();
13232
13233    buffer.update(cx, |buffer, _| {
13234        assert_eq!(
13235            buffer.text(),
13236            "fn main() { let a = {5}; }",
13237            "No extra braces from on type formatting should appear in the buffer"
13238        )
13239    });
13240}
13241
13242#[gpui::test]
13243async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13244    init_test(cx, |_| {});
13245
13246    let fs = FakeFs::new(cx.executor());
13247    fs.insert_tree(
13248        path!("/a"),
13249        json!({
13250            "main.rs": "fn main() { let a = 5; }",
13251            "other.rs": "// Test file",
13252        }),
13253    )
13254    .await;
13255
13256    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13257
13258    let server_restarts = Arc::new(AtomicUsize::new(0));
13259    let closure_restarts = Arc::clone(&server_restarts);
13260    let language_server_name = "test language server";
13261    let language_name: LanguageName = "Rust".into();
13262
13263    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13264    language_registry.add(Arc::new(Language::new(
13265        LanguageConfig {
13266            name: language_name.clone(),
13267            matcher: LanguageMatcher {
13268                path_suffixes: vec!["rs".to_string()],
13269                ..Default::default()
13270            },
13271            ..Default::default()
13272        },
13273        Some(tree_sitter_rust::LANGUAGE.into()),
13274    )));
13275    let mut fake_servers = language_registry.register_fake_lsp(
13276        "Rust",
13277        FakeLspAdapter {
13278            name: language_server_name,
13279            initialization_options: Some(json!({
13280                "testOptionValue": true
13281            })),
13282            initializer: Some(Box::new(move |fake_server| {
13283                let task_restarts = Arc::clone(&closure_restarts);
13284                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13285                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13286                    futures::future::ready(Ok(()))
13287                });
13288            })),
13289            ..Default::default()
13290        },
13291    );
13292
13293    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13294    let _buffer = project
13295        .update(cx, |project, cx| {
13296            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13297        })
13298        .await
13299        .unwrap();
13300    let _fake_server = fake_servers.next().await.unwrap();
13301    update_test_language_settings(cx, |language_settings| {
13302        language_settings.languages.insert(
13303            language_name.clone(),
13304            LanguageSettingsContent {
13305                tab_size: NonZeroU32::new(8),
13306                ..Default::default()
13307            },
13308        );
13309    });
13310    cx.executor().run_until_parked();
13311    assert_eq!(
13312        server_restarts.load(atomic::Ordering::Acquire),
13313        0,
13314        "Should not restart LSP server on an unrelated change"
13315    );
13316
13317    update_test_project_settings(cx, |project_settings| {
13318        project_settings.lsp.insert(
13319            "Some other server name".into(),
13320            LspSettings {
13321                binary: None,
13322                settings: None,
13323                initialization_options: Some(json!({
13324                    "some other init value": false
13325                })),
13326                enable_lsp_tasks: false,
13327            },
13328        );
13329    });
13330    cx.executor().run_until_parked();
13331    assert_eq!(
13332        server_restarts.load(atomic::Ordering::Acquire),
13333        0,
13334        "Should not restart LSP server on an unrelated LSP settings change"
13335    );
13336
13337    update_test_project_settings(cx, |project_settings| {
13338        project_settings.lsp.insert(
13339            language_server_name.into(),
13340            LspSettings {
13341                binary: None,
13342                settings: None,
13343                initialization_options: Some(json!({
13344                    "anotherInitValue": false
13345                })),
13346                enable_lsp_tasks: false,
13347            },
13348        );
13349    });
13350    cx.executor().run_until_parked();
13351    assert_eq!(
13352        server_restarts.load(atomic::Ordering::Acquire),
13353        1,
13354        "Should restart LSP server on a related LSP settings change"
13355    );
13356
13357    update_test_project_settings(cx, |project_settings| {
13358        project_settings.lsp.insert(
13359            language_server_name.into(),
13360            LspSettings {
13361                binary: None,
13362                settings: None,
13363                initialization_options: Some(json!({
13364                    "anotherInitValue": false
13365                })),
13366                enable_lsp_tasks: false,
13367            },
13368        );
13369    });
13370    cx.executor().run_until_parked();
13371    assert_eq!(
13372        server_restarts.load(atomic::Ordering::Acquire),
13373        1,
13374        "Should not restart LSP server on a related LSP settings change that is the same"
13375    );
13376
13377    update_test_project_settings(cx, |project_settings| {
13378        project_settings.lsp.insert(
13379            language_server_name.into(),
13380            LspSettings {
13381                binary: None,
13382                settings: None,
13383                initialization_options: None,
13384                enable_lsp_tasks: false,
13385            },
13386        );
13387    });
13388    cx.executor().run_until_parked();
13389    assert_eq!(
13390        server_restarts.load(atomic::Ordering::Acquire),
13391        2,
13392        "Should restart LSP server on another related LSP settings change"
13393    );
13394}
13395
13396#[gpui::test]
13397async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13398    init_test(cx, |_| {});
13399
13400    let mut cx = EditorLspTestContext::new_rust(
13401        lsp::ServerCapabilities {
13402            completion_provider: Some(lsp::CompletionOptions {
13403                trigger_characters: Some(vec![".".to_string()]),
13404                resolve_provider: Some(true),
13405                ..Default::default()
13406            }),
13407            ..Default::default()
13408        },
13409        cx,
13410    )
13411    .await;
13412
13413    cx.set_state("fn main() { let a = 2ˇ; }");
13414    cx.simulate_keystroke(".");
13415    let completion_item = lsp::CompletionItem {
13416        label: "some".into(),
13417        kind: Some(lsp::CompletionItemKind::SNIPPET),
13418        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13419        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13420            kind: lsp::MarkupKind::Markdown,
13421            value: "```rust\nSome(2)\n```".to_string(),
13422        })),
13423        deprecated: Some(false),
13424        sort_text: Some("fffffff2".to_string()),
13425        filter_text: Some("some".to_string()),
13426        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13427        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13428            range: lsp::Range {
13429                start: lsp::Position {
13430                    line: 0,
13431                    character: 22,
13432                },
13433                end: lsp::Position {
13434                    line: 0,
13435                    character: 22,
13436                },
13437            },
13438            new_text: "Some(2)".to_string(),
13439        })),
13440        additional_text_edits: Some(vec![lsp::TextEdit {
13441            range: lsp::Range {
13442                start: lsp::Position {
13443                    line: 0,
13444                    character: 20,
13445                },
13446                end: lsp::Position {
13447                    line: 0,
13448                    character: 22,
13449                },
13450            },
13451            new_text: "".to_string(),
13452        }]),
13453        ..Default::default()
13454    };
13455
13456    let closure_completion_item = completion_item.clone();
13457    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13458        let task_completion_item = closure_completion_item.clone();
13459        async move {
13460            Ok(Some(lsp::CompletionResponse::Array(vec![
13461                task_completion_item,
13462            ])))
13463        }
13464    });
13465
13466    request.next().await;
13467
13468    cx.condition(|editor, _| editor.context_menu_visible())
13469        .await;
13470    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13471        editor
13472            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13473            .unwrap()
13474    });
13475    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13476
13477    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13478        let task_completion_item = completion_item.clone();
13479        async move { Ok(task_completion_item) }
13480    })
13481    .next()
13482    .await
13483    .unwrap();
13484    apply_additional_edits.await.unwrap();
13485    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13486}
13487
13488#[gpui::test]
13489async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13490    init_test(cx, |_| {});
13491
13492    let mut cx = EditorLspTestContext::new_rust(
13493        lsp::ServerCapabilities {
13494            completion_provider: Some(lsp::CompletionOptions {
13495                trigger_characters: Some(vec![".".to_string()]),
13496                resolve_provider: Some(true),
13497                ..Default::default()
13498            }),
13499            ..Default::default()
13500        },
13501        cx,
13502    )
13503    .await;
13504
13505    cx.set_state("fn main() { let a = 2ˇ; }");
13506    cx.simulate_keystroke(".");
13507
13508    let item1 = lsp::CompletionItem {
13509        label: "method id()".to_string(),
13510        filter_text: Some("id".to_string()),
13511        detail: None,
13512        documentation: None,
13513        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13514            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13515            new_text: ".id".to_string(),
13516        })),
13517        ..lsp::CompletionItem::default()
13518    };
13519
13520    let item2 = lsp::CompletionItem {
13521        label: "other".to_string(),
13522        filter_text: Some("other".to_string()),
13523        detail: None,
13524        documentation: None,
13525        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13526            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13527            new_text: ".other".to_string(),
13528        })),
13529        ..lsp::CompletionItem::default()
13530    };
13531
13532    let item1 = item1.clone();
13533    cx.set_request_handler::<lsp::request::Completion, _, _>({
13534        let item1 = item1.clone();
13535        move |_, _, _| {
13536            let item1 = item1.clone();
13537            let item2 = item2.clone();
13538            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13539        }
13540    })
13541    .next()
13542    .await;
13543
13544    cx.condition(|editor, _| editor.context_menu_visible())
13545        .await;
13546    cx.update_editor(|editor, _, _| {
13547        let context_menu = editor.context_menu.borrow_mut();
13548        let context_menu = context_menu
13549            .as_ref()
13550            .expect("Should have the context menu deployed");
13551        match context_menu {
13552            CodeContextMenu::Completions(completions_menu) => {
13553                let completions = completions_menu.completions.borrow_mut();
13554                assert_eq!(
13555                    completions
13556                        .iter()
13557                        .map(|completion| &completion.label.text)
13558                        .collect::<Vec<_>>(),
13559                    vec!["method id()", "other"]
13560                )
13561            }
13562            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13563        }
13564    });
13565
13566    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13567        let item1 = item1.clone();
13568        move |_, item_to_resolve, _| {
13569            let item1 = item1.clone();
13570            async move {
13571                if item1 == item_to_resolve {
13572                    Ok(lsp::CompletionItem {
13573                        label: "method id()".to_string(),
13574                        filter_text: Some("id".to_string()),
13575                        detail: Some("Now resolved!".to_string()),
13576                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13577                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13578                            range: lsp::Range::new(
13579                                lsp::Position::new(0, 22),
13580                                lsp::Position::new(0, 22),
13581                            ),
13582                            new_text: ".id".to_string(),
13583                        })),
13584                        ..lsp::CompletionItem::default()
13585                    })
13586                } else {
13587                    Ok(item_to_resolve)
13588                }
13589            }
13590        }
13591    })
13592    .next()
13593    .await
13594    .unwrap();
13595    cx.run_until_parked();
13596
13597    cx.update_editor(|editor, window, cx| {
13598        editor.context_menu_next(&Default::default(), window, cx);
13599    });
13600
13601    cx.update_editor(|editor, _, _| {
13602        let context_menu = editor.context_menu.borrow_mut();
13603        let context_menu = context_menu
13604            .as_ref()
13605            .expect("Should have the context menu deployed");
13606        match context_menu {
13607            CodeContextMenu::Completions(completions_menu) => {
13608                let completions = completions_menu.completions.borrow_mut();
13609                assert_eq!(
13610                    completions
13611                        .iter()
13612                        .map(|completion| &completion.label.text)
13613                        .collect::<Vec<_>>(),
13614                    vec!["method id() Now resolved!", "other"],
13615                    "Should update first completion label, but not second as the filter text did not match."
13616                );
13617            }
13618            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13619        }
13620    });
13621}
13622
13623#[gpui::test]
13624async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13625    init_test(cx, |_| {});
13626
13627    let mut cx = EditorLspTestContext::new_rust(
13628        lsp::ServerCapabilities {
13629            completion_provider: Some(lsp::CompletionOptions {
13630                trigger_characters: Some(vec![".".to_string()]),
13631                resolve_provider: Some(true),
13632                ..Default::default()
13633            }),
13634            ..Default::default()
13635        },
13636        cx,
13637    )
13638    .await;
13639
13640    cx.set_state("fn main() { let a = 2ˇ; }");
13641    cx.simulate_keystroke(".");
13642
13643    let unresolved_item_1 = lsp::CompletionItem {
13644        label: "id".to_string(),
13645        filter_text: Some("id".to_string()),
13646        detail: None,
13647        documentation: None,
13648        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13649            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13650            new_text: ".id".to_string(),
13651        })),
13652        ..lsp::CompletionItem::default()
13653    };
13654    let resolved_item_1 = lsp::CompletionItem {
13655        additional_text_edits: Some(vec![lsp::TextEdit {
13656            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13657            new_text: "!!".to_string(),
13658        }]),
13659        ..unresolved_item_1.clone()
13660    };
13661    let unresolved_item_2 = lsp::CompletionItem {
13662        label: "other".to_string(),
13663        filter_text: Some("other".to_string()),
13664        detail: None,
13665        documentation: None,
13666        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13667            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13668            new_text: ".other".to_string(),
13669        })),
13670        ..lsp::CompletionItem::default()
13671    };
13672    let resolved_item_2 = lsp::CompletionItem {
13673        additional_text_edits: Some(vec![lsp::TextEdit {
13674            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13675            new_text: "??".to_string(),
13676        }]),
13677        ..unresolved_item_2.clone()
13678    };
13679
13680    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13681    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13682    cx.lsp
13683        .server
13684        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13685            let unresolved_item_1 = unresolved_item_1.clone();
13686            let resolved_item_1 = resolved_item_1.clone();
13687            let unresolved_item_2 = unresolved_item_2.clone();
13688            let resolved_item_2 = resolved_item_2.clone();
13689            let resolve_requests_1 = resolve_requests_1.clone();
13690            let resolve_requests_2 = resolve_requests_2.clone();
13691            move |unresolved_request, _| {
13692                let unresolved_item_1 = unresolved_item_1.clone();
13693                let resolved_item_1 = resolved_item_1.clone();
13694                let unresolved_item_2 = unresolved_item_2.clone();
13695                let resolved_item_2 = resolved_item_2.clone();
13696                let resolve_requests_1 = resolve_requests_1.clone();
13697                let resolve_requests_2 = resolve_requests_2.clone();
13698                async move {
13699                    if unresolved_request == unresolved_item_1 {
13700                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13701                        Ok(resolved_item_1.clone())
13702                    } else if unresolved_request == unresolved_item_2 {
13703                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13704                        Ok(resolved_item_2.clone())
13705                    } else {
13706                        panic!("Unexpected completion item {unresolved_request:?}")
13707                    }
13708                }
13709            }
13710        })
13711        .detach();
13712
13713    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13714        let unresolved_item_1 = unresolved_item_1.clone();
13715        let unresolved_item_2 = unresolved_item_2.clone();
13716        async move {
13717            Ok(Some(lsp::CompletionResponse::Array(vec![
13718                unresolved_item_1,
13719                unresolved_item_2,
13720            ])))
13721        }
13722    })
13723    .next()
13724    .await;
13725
13726    cx.condition(|editor, _| editor.context_menu_visible())
13727        .await;
13728    cx.update_editor(|editor, _, _| {
13729        let context_menu = editor.context_menu.borrow_mut();
13730        let context_menu = context_menu
13731            .as_ref()
13732            .expect("Should have the context menu deployed");
13733        match context_menu {
13734            CodeContextMenu::Completions(completions_menu) => {
13735                let completions = completions_menu.completions.borrow_mut();
13736                assert_eq!(
13737                    completions
13738                        .iter()
13739                        .map(|completion| &completion.label.text)
13740                        .collect::<Vec<_>>(),
13741                    vec!["id", "other"]
13742                )
13743            }
13744            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13745        }
13746    });
13747    cx.run_until_parked();
13748
13749    cx.update_editor(|editor, window, cx| {
13750        editor.context_menu_next(&ContextMenuNext, window, cx);
13751    });
13752    cx.run_until_parked();
13753    cx.update_editor(|editor, window, cx| {
13754        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13755    });
13756    cx.run_until_parked();
13757    cx.update_editor(|editor, window, cx| {
13758        editor.context_menu_next(&ContextMenuNext, window, cx);
13759    });
13760    cx.run_until_parked();
13761    cx.update_editor(|editor, window, cx| {
13762        editor
13763            .compose_completion(&ComposeCompletion::default(), window, cx)
13764            .expect("No task returned")
13765    })
13766    .await
13767    .expect("Completion failed");
13768    cx.run_until_parked();
13769
13770    cx.update_editor(|editor, _, cx| {
13771        assert_eq!(
13772            resolve_requests_1.load(atomic::Ordering::Acquire),
13773            1,
13774            "Should always resolve once despite multiple selections"
13775        );
13776        assert_eq!(
13777            resolve_requests_2.load(atomic::Ordering::Acquire),
13778            1,
13779            "Should always resolve once after multiple selections and applying the completion"
13780        );
13781        assert_eq!(
13782            editor.text(cx),
13783            "fn main() { let a = ??.other; }",
13784            "Should use resolved data when applying the completion"
13785        );
13786    });
13787}
13788
13789#[gpui::test]
13790async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13791    init_test(cx, |_| {});
13792
13793    let item_0 = lsp::CompletionItem {
13794        label: "abs".into(),
13795        insert_text: Some("abs".into()),
13796        data: Some(json!({ "very": "special"})),
13797        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13798        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13799            lsp::InsertReplaceEdit {
13800                new_text: "abs".to_string(),
13801                insert: lsp::Range::default(),
13802                replace: lsp::Range::default(),
13803            },
13804        )),
13805        ..lsp::CompletionItem::default()
13806    };
13807    let items = iter::once(item_0.clone())
13808        .chain((11..51).map(|i| lsp::CompletionItem {
13809            label: format!("item_{}", i),
13810            insert_text: Some(format!("item_{}", i)),
13811            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13812            ..lsp::CompletionItem::default()
13813        }))
13814        .collect::<Vec<_>>();
13815
13816    let default_commit_characters = vec!["?".to_string()];
13817    let default_data = json!({ "default": "data"});
13818    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13819    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13820    let default_edit_range = lsp::Range {
13821        start: lsp::Position {
13822            line: 0,
13823            character: 5,
13824        },
13825        end: lsp::Position {
13826            line: 0,
13827            character: 5,
13828        },
13829    };
13830
13831    let mut cx = EditorLspTestContext::new_rust(
13832        lsp::ServerCapabilities {
13833            completion_provider: Some(lsp::CompletionOptions {
13834                trigger_characters: Some(vec![".".to_string()]),
13835                resolve_provider: Some(true),
13836                ..Default::default()
13837            }),
13838            ..Default::default()
13839        },
13840        cx,
13841    )
13842    .await;
13843
13844    cx.set_state("fn main() { let a = 2ˇ; }");
13845    cx.simulate_keystroke(".");
13846
13847    let completion_data = default_data.clone();
13848    let completion_characters = default_commit_characters.clone();
13849    let completion_items = items.clone();
13850    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13851        let default_data = completion_data.clone();
13852        let default_commit_characters = completion_characters.clone();
13853        let items = completion_items.clone();
13854        async move {
13855            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13856                items,
13857                item_defaults: Some(lsp::CompletionListItemDefaults {
13858                    data: Some(default_data.clone()),
13859                    commit_characters: Some(default_commit_characters.clone()),
13860                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13861                        default_edit_range,
13862                    )),
13863                    insert_text_format: Some(default_insert_text_format),
13864                    insert_text_mode: Some(default_insert_text_mode),
13865                }),
13866                ..lsp::CompletionList::default()
13867            })))
13868        }
13869    })
13870    .next()
13871    .await;
13872
13873    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13874    cx.lsp
13875        .server
13876        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13877            let closure_resolved_items = resolved_items.clone();
13878            move |item_to_resolve, _| {
13879                let closure_resolved_items = closure_resolved_items.clone();
13880                async move {
13881                    closure_resolved_items.lock().push(item_to_resolve.clone());
13882                    Ok(item_to_resolve)
13883                }
13884            }
13885        })
13886        .detach();
13887
13888    cx.condition(|editor, _| editor.context_menu_visible())
13889        .await;
13890    cx.run_until_parked();
13891    cx.update_editor(|editor, _, _| {
13892        let menu = editor.context_menu.borrow_mut();
13893        match menu.as_ref().expect("should have the completions menu") {
13894            CodeContextMenu::Completions(completions_menu) => {
13895                assert_eq!(
13896                    completions_menu
13897                        .entries
13898                        .borrow()
13899                        .iter()
13900                        .map(|mat| mat.string.clone())
13901                        .collect::<Vec<String>>(),
13902                    items
13903                        .iter()
13904                        .map(|completion| completion.label.clone())
13905                        .collect::<Vec<String>>()
13906                );
13907            }
13908            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13909        }
13910    });
13911    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13912    // with 4 from the end.
13913    assert_eq!(
13914        *resolved_items.lock(),
13915        [&items[0..16], &items[items.len() - 4..items.len()]]
13916            .concat()
13917            .iter()
13918            .cloned()
13919            .map(|mut item| {
13920                if item.data.is_none() {
13921                    item.data = Some(default_data.clone());
13922                }
13923                item
13924            })
13925            .collect::<Vec<lsp::CompletionItem>>(),
13926        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13927    );
13928    resolved_items.lock().clear();
13929
13930    cx.update_editor(|editor, window, cx| {
13931        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13932    });
13933    cx.run_until_parked();
13934    // Completions that have already been resolved are skipped.
13935    assert_eq!(
13936        *resolved_items.lock(),
13937        items[items.len() - 16..items.len() - 4]
13938            .iter()
13939            .cloned()
13940            .map(|mut item| {
13941                if item.data.is_none() {
13942                    item.data = Some(default_data.clone());
13943                }
13944                item
13945            })
13946            .collect::<Vec<lsp::CompletionItem>>()
13947    );
13948    resolved_items.lock().clear();
13949}
13950
13951#[gpui::test]
13952async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13953    init_test(cx, |_| {});
13954
13955    let mut cx = EditorLspTestContext::new(
13956        Language::new(
13957            LanguageConfig {
13958                matcher: LanguageMatcher {
13959                    path_suffixes: vec!["jsx".into()],
13960                    ..Default::default()
13961                },
13962                overrides: [(
13963                    "element".into(),
13964                    LanguageConfigOverride {
13965                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13966                        ..Default::default()
13967                    },
13968                )]
13969                .into_iter()
13970                .collect(),
13971                ..Default::default()
13972            },
13973            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13974        )
13975        .with_override_query("(jsx_self_closing_element) @element")
13976        .unwrap(),
13977        lsp::ServerCapabilities {
13978            completion_provider: Some(lsp::CompletionOptions {
13979                trigger_characters: Some(vec![":".to_string()]),
13980                ..Default::default()
13981            }),
13982            ..Default::default()
13983        },
13984        cx,
13985    )
13986    .await;
13987
13988    cx.lsp
13989        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13990            Ok(Some(lsp::CompletionResponse::Array(vec![
13991                lsp::CompletionItem {
13992                    label: "bg-blue".into(),
13993                    ..Default::default()
13994                },
13995                lsp::CompletionItem {
13996                    label: "bg-red".into(),
13997                    ..Default::default()
13998                },
13999                lsp::CompletionItem {
14000                    label: "bg-yellow".into(),
14001                    ..Default::default()
14002                },
14003            ])))
14004        });
14005
14006    cx.set_state(r#"<p class="bgˇ" />"#);
14007
14008    // Trigger completion when typing a dash, because the dash is an extra
14009    // word character in the 'element' scope, which contains the cursor.
14010    cx.simulate_keystroke("-");
14011    cx.executor().run_until_parked();
14012    cx.update_editor(|editor, _, _| {
14013        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14014        {
14015            assert_eq!(
14016                completion_menu_entries(&menu),
14017                &["bg-red", "bg-blue", "bg-yellow"]
14018            );
14019        } else {
14020            panic!("expected completion menu to be open");
14021        }
14022    });
14023
14024    cx.simulate_keystroke("l");
14025    cx.executor().run_until_parked();
14026    cx.update_editor(|editor, _, _| {
14027        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14028        {
14029            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14030        } else {
14031            panic!("expected completion menu to be open");
14032        }
14033    });
14034
14035    // When filtering completions, consider the character after the '-' to
14036    // be the start of a subword.
14037    cx.set_state(r#"<p class="yelˇ" />"#);
14038    cx.simulate_keystroke("l");
14039    cx.executor().run_until_parked();
14040    cx.update_editor(|editor, _, _| {
14041        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14042        {
14043            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14044        } else {
14045            panic!("expected completion menu to be open");
14046        }
14047    });
14048}
14049
14050fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14051    let entries = menu.entries.borrow();
14052    entries.iter().map(|mat| mat.string.clone()).collect()
14053}
14054
14055#[gpui::test]
14056async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14057    init_test(cx, |settings| {
14058        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14059            FormatterList(vec![Formatter::Prettier].into()),
14060        ))
14061    });
14062
14063    let fs = FakeFs::new(cx.executor());
14064    fs.insert_file(path!("/file.ts"), Default::default()).await;
14065
14066    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14067    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14068
14069    language_registry.add(Arc::new(Language::new(
14070        LanguageConfig {
14071            name: "TypeScript".into(),
14072            matcher: LanguageMatcher {
14073                path_suffixes: vec!["ts".to_string()],
14074                ..Default::default()
14075            },
14076            ..Default::default()
14077        },
14078        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14079    )));
14080    update_test_language_settings(cx, |settings| {
14081        settings.defaults.prettier = Some(PrettierSettings {
14082            allowed: true,
14083            ..PrettierSettings::default()
14084        });
14085    });
14086
14087    let test_plugin = "test_plugin";
14088    let _ = language_registry.register_fake_lsp(
14089        "TypeScript",
14090        FakeLspAdapter {
14091            prettier_plugins: vec![test_plugin],
14092            ..Default::default()
14093        },
14094    );
14095
14096    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14097    let buffer = project
14098        .update(cx, |project, cx| {
14099            project.open_local_buffer(path!("/file.ts"), cx)
14100        })
14101        .await
14102        .unwrap();
14103
14104    let buffer_text = "one\ntwo\nthree\n";
14105    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14106    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14107    editor.update_in(cx, |editor, window, cx| {
14108        editor.set_text(buffer_text, window, cx)
14109    });
14110
14111    editor
14112        .update_in(cx, |editor, window, cx| {
14113            editor.perform_format(
14114                project.clone(),
14115                FormatTrigger::Manual,
14116                FormatTarget::Buffers,
14117                window,
14118                cx,
14119            )
14120        })
14121        .unwrap()
14122        .await;
14123    assert_eq!(
14124        editor.update(cx, |editor, cx| editor.text(cx)),
14125        buffer_text.to_string() + prettier_format_suffix,
14126        "Test prettier formatting was not applied to the original buffer text",
14127    );
14128
14129    update_test_language_settings(cx, |settings| {
14130        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14131    });
14132    let format = editor.update_in(cx, |editor, window, cx| {
14133        editor.perform_format(
14134            project.clone(),
14135            FormatTrigger::Manual,
14136            FormatTarget::Buffers,
14137            window,
14138            cx,
14139        )
14140    });
14141    format.await.unwrap();
14142    assert_eq!(
14143        editor.update(cx, |editor, cx| editor.text(cx)),
14144        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14145        "Autoformatting (via test prettier) was not applied to the original buffer text",
14146    );
14147}
14148
14149#[gpui::test]
14150async fn test_addition_reverts(cx: &mut TestAppContext) {
14151    init_test(cx, |_| {});
14152    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14153    let base_text = indoc! {r#"
14154        struct Row;
14155        struct Row1;
14156        struct Row2;
14157
14158        struct Row4;
14159        struct Row5;
14160        struct Row6;
14161
14162        struct Row8;
14163        struct Row9;
14164        struct Row10;"#};
14165
14166    // When addition hunks are not adjacent to carets, no hunk revert is performed
14167    assert_hunk_revert(
14168        indoc! {r#"struct Row;
14169                   struct Row1;
14170                   struct Row1.1;
14171                   struct Row1.2;
14172                   struct Row2;ˇ
14173
14174                   struct Row4;
14175                   struct Row5;
14176                   struct Row6;
14177
14178                   struct Row8;
14179                   ˇstruct Row9;
14180                   struct Row9.1;
14181                   struct Row9.2;
14182                   struct Row9.3;
14183                   struct Row10;"#},
14184        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14185        indoc! {r#"struct Row;
14186                   struct Row1;
14187                   struct Row1.1;
14188                   struct Row1.2;
14189                   struct Row2;ˇ
14190
14191                   struct Row4;
14192                   struct Row5;
14193                   struct Row6;
14194
14195                   struct Row8;
14196                   ˇstruct Row9;
14197                   struct Row9.1;
14198                   struct Row9.2;
14199                   struct Row9.3;
14200                   struct Row10;"#},
14201        base_text,
14202        &mut cx,
14203    );
14204    // Same for selections
14205    assert_hunk_revert(
14206        indoc! {r#"struct Row;
14207                   struct Row1;
14208                   struct Row2;
14209                   struct Row2.1;
14210                   struct Row2.2;
14211                   «ˇ
14212                   struct Row4;
14213                   struct» Row5;
14214                   «struct Row6;
14215                   ˇ»
14216                   struct Row9.1;
14217                   struct Row9.2;
14218                   struct Row9.3;
14219                   struct Row8;
14220                   struct Row9;
14221                   struct Row10;"#},
14222        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14223        indoc! {r#"struct Row;
14224                   struct Row1;
14225                   struct Row2;
14226                   struct Row2.1;
14227                   struct Row2.2;
14228                   «ˇ
14229                   struct Row4;
14230                   struct» Row5;
14231                   «struct Row6;
14232                   ˇ»
14233                   struct Row9.1;
14234                   struct Row9.2;
14235                   struct Row9.3;
14236                   struct Row8;
14237                   struct Row9;
14238                   struct Row10;"#},
14239        base_text,
14240        &mut cx,
14241    );
14242
14243    // When carets and selections intersect the addition hunks, those are reverted.
14244    // Adjacent carets got merged.
14245    assert_hunk_revert(
14246        indoc! {r#"struct Row;
14247                   ˇ// something on the top
14248                   struct Row1;
14249                   struct Row2;
14250                   struct Roˇw3.1;
14251                   struct Row2.2;
14252                   struct Row2.3;ˇ
14253
14254                   struct Row4;
14255                   struct ˇRow5.1;
14256                   struct Row5.2;
14257                   struct «Rowˇ»5.3;
14258                   struct Row5;
14259                   struct Row6;
14260                   ˇ
14261                   struct Row9.1;
14262                   struct «Rowˇ»9.2;
14263                   struct «ˇRow»9.3;
14264                   struct Row8;
14265                   struct Row9;
14266                   «ˇ// something on bottom»
14267                   struct Row10;"#},
14268        vec![
14269            DiffHunkStatusKind::Added,
14270            DiffHunkStatusKind::Added,
14271            DiffHunkStatusKind::Added,
14272            DiffHunkStatusKind::Added,
14273            DiffHunkStatusKind::Added,
14274        ],
14275        indoc! {r#"struct Row;
14276                   ˇstruct Row1;
14277                   struct Row2;
14278                   ˇ
14279                   struct Row4;
14280                   ˇstruct Row5;
14281                   struct Row6;
14282                   ˇ
14283                   ˇstruct Row8;
14284                   struct Row9;
14285                   ˇstruct Row10;"#},
14286        base_text,
14287        &mut cx,
14288    );
14289}
14290
14291#[gpui::test]
14292async fn test_modification_reverts(cx: &mut TestAppContext) {
14293    init_test(cx, |_| {});
14294    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14295    let base_text = indoc! {r#"
14296        struct Row;
14297        struct Row1;
14298        struct Row2;
14299
14300        struct Row4;
14301        struct Row5;
14302        struct Row6;
14303
14304        struct Row8;
14305        struct Row9;
14306        struct Row10;"#};
14307
14308    // Modification hunks behave the same as the addition ones.
14309    assert_hunk_revert(
14310        indoc! {r#"struct Row;
14311                   struct Row1;
14312                   struct Row33;
14313                   ˇ
14314                   struct Row4;
14315                   struct Row5;
14316                   struct Row6;
14317                   ˇ
14318                   struct Row99;
14319                   struct Row9;
14320                   struct Row10;"#},
14321        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14322        indoc! {r#"struct Row;
14323                   struct Row1;
14324                   struct Row33;
14325                   ˇ
14326                   struct Row4;
14327                   struct Row5;
14328                   struct Row6;
14329                   ˇ
14330                   struct Row99;
14331                   struct Row9;
14332                   struct Row10;"#},
14333        base_text,
14334        &mut cx,
14335    );
14336    assert_hunk_revert(
14337        indoc! {r#"struct Row;
14338                   struct Row1;
14339                   struct Row33;
14340                   «ˇ
14341                   struct Row4;
14342                   struct» Row5;
14343                   «struct Row6;
14344                   ˇ»
14345                   struct Row99;
14346                   struct Row9;
14347                   struct Row10;"#},
14348        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14349        indoc! {r#"struct Row;
14350                   struct Row1;
14351                   struct Row33;
14352                   «ˇ
14353                   struct Row4;
14354                   struct» Row5;
14355                   «struct Row6;
14356                   ˇ»
14357                   struct Row99;
14358                   struct Row9;
14359                   struct Row10;"#},
14360        base_text,
14361        &mut cx,
14362    );
14363
14364    assert_hunk_revert(
14365        indoc! {r#"ˇstruct Row1.1;
14366                   struct Row1;
14367                   «ˇstr»uct Row22;
14368
14369                   struct ˇRow44;
14370                   struct Row5;
14371                   struct «Rˇ»ow66;ˇ
14372
14373                   «struˇ»ct Row88;
14374                   struct Row9;
14375                   struct Row1011;ˇ"#},
14376        vec![
14377            DiffHunkStatusKind::Modified,
14378            DiffHunkStatusKind::Modified,
14379            DiffHunkStatusKind::Modified,
14380            DiffHunkStatusKind::Modified,
14381            DiffHunkStatusKind::Modified,
14382            DiffHunkStatusKind::Modified,
14383        ],
14384        indoc! {r#"struct Row;
14385                   ˇstruct Row1;
14386                   struct Row2;
14387                   ˇ
14388                   struct Row4;
14389                   ˇstruct Row5;
14390                   struct Row6;
14391                   ˇ
14392                   struct Row8;
14393                   ˇstruct Row9;
14394                   struct Row10;ˇ"#},
14395        base_text,
14396        &mut cx,
14397    );
14398}
14399
14400#[gpui::test]
14401async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14402    init_test(cx, |_| {});
14403    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14404    let base_text = indoc! {r#"
14405        one
14406
14407        two
14408        three
14409        "#};
14410
14411    cx.set_head_text(base_text);
14412    cx.set_state("\nˇ\n");
14413    cx.executor().run_until_parked();
14414    cx.update_editor(|editor, _window, cx| {
14415        editor.expand_selected_diff_hunks(cx);
14416    });
14417    cx.executor().run_until_parked();
14418    cx.update_editor(|editor, window, cx| {
14419        editor.backspace(&Default::default(), window, cx);
14420    });
14421    cx.run_until_parked();
14422    cx.assert_state_with_diff(
14423        indoc! {r#"
14424
14425        - two
14426        - threeˇ
14427        +
14428        "#}
14429        .to_string(),
14430    );
14431}
14432
14433#[gpui::test]
14434async fn test_deletion_reverts(cx: &mut TestAppContext) {
14435    init_test(cx, |_| {});
14436    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14437    let base_text = indoc! {r#"struct Row;
14438struct Row1;
14439struct Row2;
14440
14441struct Row4;
14442struct Row5;
14443struct Row6;
14444
14445struct Row8;
14446struct Row9;
14447struct Row10;"#};
14448
14449    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14450    assert_hunk_revert(
14451        indoc! {r#"struct Row;
14452                   struct Row2;
14453
14454                   ˇstruct Row4;
14455                   struct Row5;
14456                   struct Row6;
14457                   ˇ
14458                   struct Row8;
14459                   struct Row10;"#},
14460        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14461        indoc! {r#"struct Row;
14462                   struct Row2;
14463
14464                   ˇstruct Row4;
14465                   struct Row5;
14466                   struct Row6;
14467                   ˇ
14468                   struct Row8;
14469                   struct Row10;"#},
14470        base_text,
14471        &mut cx,
14472    );
14473    assert_hunk_revert(
14474        indoc! {r#"struct Row;
14475                   struct Row2;
14476
14477                   «ˇstruct Row4;
14478                   struct» Row5;
14479                   «struct Row6;
14480                   ˇ»
14481                   struct Row8;
14482                   struct Row10;"#},
14483        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14484        indoc! {r#"struct Row;
14485                   struct Row2;
14486
14487                   «ˇstruct Row4;
14488                   struct» Row5;
14489                   «struct Row6;
14490                   ˇ»
14491                   struct Row8;
14492                   struct Row10;"#},
14493        base_text,
14494        &mut cx,
14495    );
14496
14497    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14498    assert_hunk_revert(
14499        indoc! {r#"struct Row;
14500                   ˇstruct Row2;
14501
14502                   struct Row4;
14503                   struct Row5;
14504                   struct Row6;
14505
14506                   struct Row8;ˇ
14507                   struct Row10;"#},
14508        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14509        indoc! {r#"struct Row;
14510                   struct Row1;
14511                   ˇstruct Row2;
14512
14513                   struct Row4;
14514                   struct Row5;
14515                   struct Row6;
14516
14517                   struct Row8;ˇ
14518                   struct Row9;
14519                   struct Row10;"#},
14520        base_text,
14521        &mut cx,
14522    );
14523    assert_hunk_revert(
14524        indoc! {r#"struct Row;
14525                   struct Row2«ˇ;
14526                   struct Row4;
14527                   struct» Row5;
14528                   «struct Row6;
14529
14530                   struct Row8;ˇ»
14531                   struct Row10;"#},
14532        vec![
14533            DiffHunkStatusKind::Deleted,
14534            DiffHunkStatusKind::Deleted,
14535            DiffHunkStatusKind::Deleted,
14536        ],
14537        indoc! {r#"struct Row;
14538                   struct Row1;
14539                   struct Row2«ˇ;
14540
14541                   struct Row4;
14542                   struct» Row5;
14543                   «struct Row6;
14544
14545                   struct Row8;ˇ»
14546                   struct Row9;
14547                   struct Row10;"#},
14548        base_text,
14549        &mut cx,
14550    );
14551}
14552
14553#[gpui::test]
14554async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14555    init_test(cx, |_| {});
14556
14557    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14558    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14559    let base_text_3 =
14560        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14561
14562    let text_1 = edit_first_char_of_every_line(base_text_1);
14563    let text_2 = edit_first_char_of_every_line(base_text_2);
14564    let text_3 = edit_first_char_of_every_line(base_text_3);
14565
14566    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14567    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14568    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14569
14570    let multibuffer = cx.new(|cx| {
14571        let mut multibuffer = MultiBuffer::new(ReadWrite);
14572        multibuffer.push_excerpts(
14573            buffer_1.clone(),
14574            [
14575                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14576                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14577                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14578            ],
14579            cx,
14580        );
14581        multibuffer.push_excerpts(
14582            buffer_2.clone(),
14583            [
14584                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14585                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14586                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14587            ],
14588            cx,
14589        );
14590        multibuffer.push_excerpts(
14591            buffer_3.clone(),
14592            [
14593                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14594                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14595                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14596            ],
14597            cx,
14598        );
14599        multibuffer
14600    });
14601
14602    let fs = FakeFs::new(cx.executor());
14603    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14604    let (editor, cx) = cx
14605        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14606    editor.update_in(cx, |editor, _window, cx| {
14607        for (buffer, diff_base) in [
14608            (buffer_1.clone(), base_text_1),
14609            (buffer_2.clone(), base_text_2),
14610            (buffer_3.clone(), base_text_3),
14611        ] {
14612            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14613            editor
14614                .buffer
14615                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14616        }
14617    });
14618    cx.executor().run_until_parked();
14619
14620    editor.update_in(cx, |editor, window, cx| {
14621        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}");
14622        editor.select_all(&SelectAll, window, cx);
14623        editor.git_restore(&Default::default(), window, cx);
14624    });
14625    cx.executor().run_until_parked();
14626
14627    // When all ranges are selected, all buffer hunks are reverted.
14628    editor.update(cx, |editor, cx| {
14629        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");
14630    });
14631    buffer_1.update(cx, |buffer, _| {
14632        assert_eq!(buffer.text(), base_text_1);
14633    });
14634    buffer_2.update(cx, |buffer, _| {
14635        assert_eq!(buffer.text(), base_text_2);
14636    });
14637    buffer_3.update(cx, |buffer, _| {
14638        assert_eq!(buffer.text(), base_text_3);
14639    });
14640
14641    editor.update_in(cx, |editor, window, cx| {
14642        editor.undo(&Default::default(), window, cx);
14643    });
14644
14645    editor.update_in(cx, |editor, window, cx| {
14646        editor.change_selections(None, window, cx, |s| {
14647            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14648        });
14649        editor.git_restore(&Default::default(), window, cx);
14650    });
14651
14652    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14653    // but not affect buffer_2 and its related excerpts.
14654    editor.update(cx, |editor, cx| {
14655        assert_eq!(
14656            editor.text(cx),
14657            "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}"
14658        );
14659    });
14660    buffer_1.update(cx, |buffer, _| {
14661        assert_eq!(buffer.text(), base_text_1);
14662    });
14663    buffer_2.update(cx, |buffer, _| {
14664        assert_eq!(
14665            buffer.text(),
14666            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14667        );
14668    });
14669    buffer_3.update(cx, |buffer, _| {
14670        assert_eq!(
14671            buffer.text(),
14672            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14673        );
14674    });
14675
14676    fn edit_first_char_of_every_line(text: &str) -> String {
14677        text.split('\n')
14678            .map(|line| format!("X{}", &line[1..]))
14679            .collect::<Vec<_>>()
14680            .join("\n")
14681    }
14682}
14683
14684#[gpui::test]
14685async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14686    init_test(cx, |_| {});
14687
14688    let cols = 4;
14689    let rows = 10;
14690    let sample_text_1 = sample_text(rows, cols, 'a');
14691    assert_eq!(
14692        sample_text_1,
14693        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14694    );
14695    let sample_text_2 = sample_text(rows, cols, 'l');
14696    assert_eq!(
14697        sample_text_2,
14698        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14699    );
14700    let sample_text_3 = sample_text(rows, cols, 'v');
14701    assert_eq!(
14702        sample_text_3,
14703        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14704    );
14705
14706    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14707    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14708    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14709
14710    let multi_buffer = cx.new(|cx| {
14711        let mut multibuffer = MultiBuffer::new(ReadWrite);
14712        multibuffer.push_excerpts(
14713            buffer_1.clone(),
14714            [
14715                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14716                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14717                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14718            ],
14719            cx,
14720        );
14721        multibuffer.push_excerpts(
14722            buffer_2.clone(),
14723            [
14724                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14725                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14726                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14727            ],
14728            cx,
14729        );
14730        multibuffer.push_excerpts(
14731            buffer_3.clone(),
14732            [
14733                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14734                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14735                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14736            ],
14737            cx,
14738        );
14739        multibuffer
14740    });
14741
14742    let fs = FakeFs::new(cx.executor());
14743    fs.insert_tree(
14744        "/a",
14745        json!({
14746            "main.rs": sample_text_1,
14747            "other.rs": sample_text_2,
14748            "lib.rs": sample_text_3,
14749        }),
14750    )
14751    .await;
14752    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14753    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14754    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14755    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14756        Editor::new(
14757            EditorMode::full(),
14758            multi_buffer,
14759            Some(project.clone()),
14760            window,
14761            cx,
14762        )
14763    });
14764    let multibuffer_item_id = workspace
14765        .update(cx, |workspace, window, cx| {
14766            assert!(
14767                workspace.active_item(cx).is_none(),
14768                "active item should be None before the first item is added"
14769            );
14770            workspace.add_item_to_active_pane(
14771                Box::new(multi_buffer_editor.clone()),
14772                None,
14773                true,
14774                window,
14775                cx,
14776            );
14777            let active_item = workspace
14778                .active_item(cx)
14779                .expect("should have an active item after adding the multi buffer");
14780            assert!(
14781                !active_item.is_singleton(cx),
14782                "A multi buffer was expected to active after adding"
14783            );
14784            active_item.item_id()
14785        })
14786        .unwrap();
14787    cx.executor().run_until_parked();
14788
14789    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14790        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14791            s.select_ranges(Some(1..2))
14792        });
14793        editor.open_excerpts(&OpenExcerpts, window, cx);
14794    });
14795    cx.executor().run_until_parked();
14796    let first_item_id = workspace
14797        .update(cx, |workspace, window, cx| {
14798            let active_item = workspace
14799                .active_item(cx)
14800                .expect("should have an active item after navigating into the 1st buffer");
14801            let first_item_id = active_item.item_id();
14802            assert_ne!(
14803                first_item_id, multibuffer_item_id,
14804                "Should navigate into the 1st buffer and activate it"
14805            );
14806            assert!(
14807                active_item.is_singleton(cx),
14808                "New active item should be a singleton buffer"
14809            );
14810            assert_eq!(
14811                active_item
14812                    .act_as::<Editor>(cx)
14813                    .expect("should have navigated into an editor for the 1st buffer")
14814                    .read(cx)
14815                    .text(cx),
14816                sample_text_1
14817            );
14818
14819            workspace
14820                .go_back(workspace.active_pane().downgrade(), window, cx)
14821                .detach_and_log_err(cx);
14822
14823            first_item_id
14824        })
14825        .unwrap();
14826    cx.executor().run_until_parked();
14827    workspace
14828        .update(cx, |workspace, _, cx| {
14829            let active_item = workspace
14830                .active_item(cx)
14831                .expect("should have an active item after navigating back");
14832            assert_eq!(
14833                active_item.item_id(),
14834                multibuffer_item_id,
14835                "Should navigate back to the multi buffer"
14836            );
14837            assert!(!active_item.is_singleton(cx));
14838        })
14839        .unwrap();
14840
14841    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14842        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14843            s.select_ranges(Some(39..40))
14844        });
14845        editor.open_excerpts(&OpenExcerpts, window, cx);
14846    });
14847    cx.executor().run_until_parked();
14848    let second_item_id = workspace
14849        .update(cx, |workspace, window, cx| {
14850            let active_item = workspace
14851                .active_item(cx)
14852                .expect("should have an active item after navigating into the 2nd buffer");
14853            let second_item_id = active_item.item_id();
14854            assert_ne!(
14855                second_item_id, multibuffer_item_id,
14856                "Should navigate away from the multibuffer"
14857            );
14858            assert_ne!(
14859                second_item_id, first_item_id,
14860                "Should navigate into the 2nd buffer and activate it"
14861            );
14862            assert!(
14863                active_item.is_singleton(cx),
14864                "New active item should be a singleton buffer"
14865            );
14866            assert_eq!(
14867                active_item
14868                    .act_as::<Editor>(cx)
14869                    .expect("should have navigated into an editor")
14870                    .read(cx)
14871                    .text(cx),
14872                sample_text_2
14873            );
14874
14875            workspace
14876                .go_back(workspace.active_pane().downgrade(), window, cx)
14877                .detach_and_log_err(cx);
14878
14879            second_item_id
14880        })
14881        .unwrap();
14882    cx.executor().run_until_parked();
14883    workspace
14884        .update(cx, |workspace, _, cx| {
14885            let active_item = workspace
14886                .active_item(cx)
14887                .expect("should have an active item after navigating back from the 2nd buffer");
14888            assert_eq!(
14889                active_item.item_id(),
14890                multibuffer_item_id,
14891                "Should navigate back from the 2nd buffer to the multi buffer"
14892            );
14893            assert!(!active_item.is_singleton(cx));
14894        })
14895        .unwrap();
14896
14897    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14898        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14899            s.select_ranges(Some(70..70))
14900        });
14901        editor.open_excerpts(&OpenExcerpts, window, cx);
14902    });
14903    cx.executor().run_until_parked();
14904    workspace
14905        .update(cx, |workspace, window, cx| {
14906            let active_item = workspace
14907                .active_item(cx)
14908                .expect("should have an active item after navigating into the 3rd buffer");
14909            let third_item_id = active_item.item_id();
14910            assert_ne!(
14911                third_item_id, multibuffer_item_id,
14912                "Should navigate into the 3rd buffer and activate it"
14913            );
14914            assert_ne!(third_item_id, first_item_id);
14915            assert_ne!(third_item_id, second_item_id);
14916            assert!(
14917                active_item.is_singleton(cx),
14918                "New active item should be a singleton buffer"
14919            );
14920            assert_eq!(
14921                active_item
14922                    .act_as::<Editor>(cx)
14923                    .expect("should have navigated into an editor")
14924                    .read(cx)
14925                    .text(cx),
14926                sample_text_3
14927            );
14928
14929            workspace
14930                .go_back(workspace.active_pane().downgrade(), window, cx)
14931                .detach_and_log_err(cx);
14932        })
14933        .unwrap();
14934    cx.executor().run_until_parked();
14935    workspace
14936        .update(cx, |workspace, _, cx| {
14937            let active_item = workspace
14938                .active_item(cx)
14939                .expect("should have an active item after navigating back from the 3rd buffer");
14940            assert_eq!(
14941                active_item.item_id(),
14942                multibuffer_item_id,
14943                "Should navigate back from the 3rd buffer to the multi buffer"
14944            );
14945            assert!(!active_item.is_singleton(cx));
14946        })
14947        .unwrap();
14948}
14949
14950#[gpui::test]
14951async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14952    init_test(cx, |_| {});
14953
14954    let mut cx = EditorTestContext::new(cx).await;
14955
14956    let diff_base = r#"
14957        use some::mod;
14958
14959        const A: u32 = 42;
14960
14961        fn main() {
14962            println!("hello");
14963
14964            println!("world");
14965        }
14966        "#
14967    .unindent();
14968
14969    cx.set_state(
14970        &r#"
14971        use some::modified;
14972
14973        ˇ
14974        fn main() {
14975            println!("hello there");
14976
14977            println!("around the");
14978            println!("world");
14979        }
14980        "#
14981        .unindent(),
14982    );
14983
14984    cx.set_head_text(&diff_base);
14985    executor.run_until_parked();
14986
14987    cx.update_editor(|editor, window, cx| {
14988        editor.go_to_next_hunk(&GoToHunk, window, cx);
14989        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14990    });
14991    executor.run_until_parked();
14992    cx.assert_state_with_diff(
14993        r#"
14994          use some::modified;
14995
14996
14997          fn main() {
14998        -     println!("hello");
14999        + ˇ    println!("hello there");
15000
15001              println!("around the");
15002              println!("world");
15003          }
15004        "#
15005        .unindent(),
15006    );
15007
15008    cx.update_editor(|editor, window, cx| {
15009        for _ in 0..2 {
15010            editor.go_to_next_hunk(&GoToHunk, window, cx);
15011            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15012        }
15013    });
15014    executor.run_until_parked();
15015    cx.assert_state_with_diff(
15016        r#"
15017        - use some::mod;
15018        + ˇuse some::modified;
15019
15020
15021          fn main() {
15022        -     println!("hello");
15023        +     println!("hello there");
15024
15025        +     println!("around the");
15026              println!("world");
15027          }
15028        "#
15029        .unindent(),
15030    );
15031
15032    cx.update_editor(|editor, window, cx| {
15033        editor.go_to_next_hunk(&GoToHunk, window, cx);
15034        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15035    });
15036    executor.run_until_parked();
15037    cx.assert_state_with_diff(
15038        r#"
15039        - use some::mod;
15040        + use some::modified;
15041
15042        - const A: u32 = 42;
15043          ˇ
15044          fn main() {
15045        -     println!("hello");
15046        +     println!("hello there");
15047
15048        +     println!("around the");
15049              println!("world");
15050          }
15051        "#
15052        .unindent(),
15053    );
15054
15055    cx.update_editor(|editor, window, cx| {
15056        editor.cancel(&Cancel, window, cx);
15057    });
15058
15059    cx.assert_state_with_diff(
15060        r#"
15061          use some::modified;
15062
15063          ˇ
15064          fn main() {
15065              println!("hello there");
15066
15067              println!("around the");
15068              println!("world");
15069          }
15070        "#
15071        .unindent(),
15072    );
15073}
15074
15075#[gpui::test]
15076async fn test_diff_base_change_with_expanded_diff_hunks(
15077    executor: BackgroundExecutor,
15078    cx: &mut TestAppContext,
15079) {
15080    init_test(cx, |_| {});
15081
15082    let mut cx = EditorTestContext::new(cx).await;
15083
15084    let diff_base = r#"
15085        use some::mod1;
15086        use some::mod2;
15087
15088        const A: u32 = 42;
15089        const B: u32 = 42;
15090        const C: u32 = 42;
15091
15092        fn main() {
15093            println!("hello");
15094
15095            println!("world");
15096        }
15097        "#
15098    .unindent();
15099
15100    cx.set_state(
15101        &r#"
15102        use some::mod2;
15103
15104        const A: u32 = 42;
15105        const C: u32 = 42;
15106
15107        fn main(ˇ) {
15108            //println!("hello");
15109
15110            println!("world");
15111            //
15112            //
15113        }
15114        "#
15115        .unindent(),
15116    );
15117
15118    cx.set_head_text(&diff_base);
15119    executor.run_until_parked();
15120
15121    cx.update_editor(|editor, window, cx| {
15122        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15123    });
15124    executor.run_until_parked();
15125    cx.assert_state_with_diff(
15126        r#"
15127        - use some::mod1;
15128          use some::mod2;
15129
15130          const A: u32 = 42;
15131        - const B: u32 = 42;
15132          const C: u32 = 42;
15133
15134          fn main(ˇ) {
15135        -     println!("hello");
15136        +     //println!("hello");
15137
15138              println!("world");
15139        +     //
15140        +     //
15141          }
15142        "#
15143        .unindent(),
15144    );
15145
15146    cx.set_head_text("new diff base!");
15147    executor.run_until_parked();
15148    cx.assert_state_with_diff(
15149        r#"
15150        - new diff base!
15151        + use some::mod2;
15152        +
15153        + const A: u32 = 42;
15154        + const C: u32 = 42;
15155        +
15156        + fn main(ˇ) {
15157        +     //println!("hello");
15158        +
15159        +     println!("world");
15160        +     //
15161        +     //
15162        + }
15163        "#
15164        .unindent(),
15165    );
15166}
15167
15168#[gpui::test]
15169async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15170    init_test(cx, |_| {});
15171
15172    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15173    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15174    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15175    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15176    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15177    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15178
15179    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15180    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15181    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15182
15183    let multi_buffer = cx.new(|cx| {
15184        let mut multibuffer = MultiBuffer::new(ReadWrite);
15185        multibuffer.push_excerpts(
15186            buffer_1.clone(),
15187            [
15188                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15189                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15190                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15191            ],
15192            cx,
15193        );
15194        multibuffer.push_excerpts(
15195            buffer_2.clone(),
15196            [
15197                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15198                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15199                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15200            ],
15201            cx,
15202        );
15203        multibuffer.push_excerpts(
15204            buffer_3.clone(),
15205            [
15206                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15207                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15208                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15209            ],
15210            cx,
15211        );
15212        multibuffer
15213    });
15214
15215    let editor =
15216        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15217    editor
15218        .update(cx, |editor, _window, cx| {
15219            for (buffer, diff_base) in [
15220                (buffer_1.clone(), file_1_old),
15221                (buffer_2.clone(), file_2_old),
15222                (buffer_3.clone(), file_3_old),
15223            ] {
15224                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15225                editor
15226                    .buffer
15227                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15228            }
15229        })
15230        .unwrap();
15231
15232    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15233    cx.run_until_parked();
15234
15235    cx.assert_editor_state(
15236        &"
15237            ˇaaa
15238            ccc
15239            ddd
15240
15241            ggg
15242            hhh
15243
15244
15245            lll
15246            mmm
15247            NNN
15248
15249            qqq
15250            rrr
15251
15252            uuu
15253            111
15254            222
15255            333
15256
15257            666
15258            777
15259
15260            000
15261            !!!"
15262        .unindent(),
15263    );
15264
15265    cx.update_editor(|editor, window, cx| {
15266        editor.select_all(&SelectAll, window, cx);
15267        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15268    });
15269    cx.executor().run_until_parked();
15270
15271    cx.assert_state_with_diff(
15272        "
15273            «aaa
15274          - bbb
15275            ccc
15276            ddd
15277
15278            ggg
15279            hhh
15280
15281
15282            lll
15283            mmm
15284          - nnn
15285          + NNN
15286
15287            qqq
15288            rrr
15289
15290            uuu
15291            111
15292            222
15293            333
15294
15295          + 666
15296            777
15297
15298            000
15299            !!!ˇ»"
15300            .unindent(),
15301    );
15302}
15303
15304#[gpui::test]
15305async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15306    init_test(cx, |_| {});
15307
15308    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15309    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15310
15311    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15312    let multi_buffer = cx.new(|cx| {
15313        let mut multibuffer = MultiBuffer::new(ReadWrite);
15314        multibuffer.push_excerpts(
15315            buffer.clone(),
15316            [
15317                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15318                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15319                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15320            ],
15321            cx,
15322        );
15323        multibuffer
15324    });
15325
15326    let editor =
15327        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15328    editor
15329        .update(cx, |editor, _window, cx| {
15330            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15331            editor
15332                .buffer
15333                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15334        })
15335        .unwrap();
15336
15337    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15338    cx.run_until_parked();
15339
15340    cx.update_editor(|editor, window, cx| {
15341        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15342    });
15343    cx.executor().run_until_parked();
15344
15345    // When the start of a hunk coincides with the start of its excerpt,
15346    // the hunk is expanded. When the start of a a hunk is earlier than
15347    // the start of its excerpt, the hunk is not expanded.
15348    cx.assert_state_with_diff(
15349        "
15350            ˇaaa
15351          - bbb
15352          + BBB
15353
15354          - ddd
15355          - eee
15356          + DDD
15357          + EEE
15358            fff
15359
15360            iii
15361        "
15362        .unindent(),
15363    );
15364}
15365
15366#[gpui::test]
15367async fn test_edits_around_expanded_insertion_hunks(
15368    executor: BackgroundExecutor,
15369    cx: &mut TestAppContext,
15370) {
15371    init_test(cx, |_| {});
15372
15373    let mut cx = EditorTestContext::new(cx).await;
15374
15375    let diff_base = r#"
15376        use some::mod1;
15377        use some::mod2;
15378
15379        const A: u32 = 42;
15380
15381        fn main() {
15382            println!("hello");
15383
15384            println!("world");
15385        }
15386        "#
15387    .unindent();
15388    executor.run_until_parked();
15389    cx.set_state(
15390        &r#"
15391        use some::mod1;
15392        use some::mod2;
15393
15394        const A: u32 = 42;
15395        const B: u32 = 42;
15396        const C: u32 = 42;
15397        ˇ
15398
15399        fn main() {
15400            println!("hello");
15401
15402            println!("world");
15403        }
15404        "#
15405        .unindent(),
15406    );
15407
15408    cx.set_head_text(&diff_base);
15409    executor.run_until_parked();
15410
15411    cx.update_editor(|editor, window, cx| {
15412        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15413    });
15414    executor.run_until_parked();
15415
15416    cx.assert_state_with_diff(
15417        r#"
15418        use some::mod1;
15419        use some::mod2;
15420
15421        const A: u32 = 42;
15422      + const B: u32 = 42;
15423      + const C: u32 = 42;
15424      + ˇ
15425
15426        fn main() {
15427            println!("hello");
15428
15429            println!("world");
15430        }
15431      "#
15432        .unindent(),
15433    );
15434
15435    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15436    executor.run_until_parked();
15437
15438    cx.assert_state_with_diff(
15439        r#"
15440        use some::mod1;
15441        use some::mod2;
15442
15443        const A: u32 = 42;
15444      + const B: u32 = 42;
15445      + const C: u32 = 42;
15446      + const D: u32 = 42;
15447      + ˇ
15448
15449        fn main() {
15450            println!("hello");
15451
15452            println!("world");
15453        }
15454      "#
15455        .unindent(),
15456    );
15457
15458    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15459    executor.run_until_parked();
15460
15461    cx.assert_state_with_diff(
15462        r#"
15463        use some::mod1;
15464        use some::mod2;
15465
15466        const A: u32 = 42;
15467      + const B: u32 = 42;
15468      + const C: u32 = 42;
15469      + const D: u32 = 42;
15470      + const E: u32 = 42;
15471      + ˇ
15472
15473        fn main() {
15474            println!("hello");
15475
15476            println!("world");
15477        }
15478      "#
15479        .unindent(),
15480    );
15481
15482    cx.update_editor(|editor, window, cx| {
15483        editor.delete_line(&DeleteLine, window, cx);
15484    });
15485    executor.run_until_parked();
15486
15487    cx.assert_state_with_diff(
15488        r#"
15489        use some::mod1;
15490        use some::mod2;
15491
15492        const A: u32 = 42;
15493      + const B: u32 = 42;
15494      + const C: u32 = 42;
15495      + const D: u32 = 42;
15496      + const E: u32 = 42;
15497        ˇ
15498        fn main() {
15499            println!("hello");
15500
15501            println!("world");
15502        }
15503      "#
15504        .unindent(),
15505    );
15506
15507    cx.update_editor(|editor, window, cx| {
15508        editor.move_up(&MoveUp, window, cx);
15509        editor.delete_line(&DeleteLine, window, cx);
15510        editor.move_up(&MoveUp, window, cx);
15511        editor.delete_line(&DeleteLine, window, cx);
15512        editor.move_up(&MoveUp, window, cx);
15513        editor.delete_line(&DeleteLine, window, cx);
15514    });
15515    executor.run_until_parked();
15516    cx.assert_state_with_diff(
15517        r#"
15518        use some::mod1;
15519        use some::mod2;
15520
15521        const A: u32 = 42;
15522      + const B: u32 = 42;
15523        ˇ
15524        fn main() {
15525            println!("hello");
15526
15527            println!("world");
15528        }
15529      "#
15530        .unindent(),
15531    );
15532
15533    cx.update_editor(|editor, window, cx| {
15534        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15535        editor.delete_line(&DeleteLine, window, cx);
15536    });
15537    executor.run_until_parked();
15538    cx.assert_state_with_diff(
15539        r#"
15540        ˇ
15541        fn main() {
15542            println!("hello");
15543
15544            println!("world");
15545        }
15546      "#
15547        .unindent(),
15548    );
15549}
15550
15551#[gpui::test]
15552async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15553    init_test(cx, |_| {});
15554
15555    let mut cx = EditorTestContext::new(cx).await;
15556    cx.set_head_text(indoc! { "
15557        one
15558        two
15559        three
15560        four
15561        five
15562        "
15563    });
15564    cx.set_state(indoc! { "
15565        one
15566        ˇthree
15567        five
15568    "});
15569    cx.run_until_parked();
15570    cx.update_editor(|editor, window, cx| {
15571        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15572    });
15573    cx.assert_state_with_diff(
15574        indoc! { "
15575        one
15576      - two
15577        ˇthree
15578      - four
15579        five
15580    "}
15581        .to_string(),
15582    );
15583    cx.update_editor(|editor, window, cx| {
15584        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15585    });
15586
15587    cx.assert_state_with_diff(
15588        indoc! { "
15589        one
15590        ˇthree
15591        five
15592    "}
15593        .to_string(),
15594    );
15595
15596    cx.set_state(indoc! { "
15597        one
15598        ˇTWO
15599        three
15600        four
15601        five
15602    "});
15603    cx.run_until_parked();
15604    cx.update_editor(|editor, window, cx| {
15605        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15606    });
15607
15608    cx.assert_state_with_diff(
15609        indoc! { "
15610            one
15611          - two
15612          + ˇTWO
15613            three
15614            four
15615            five
15616        "}
15617        .to_string(),
15618    );
15619    cx.update_editor(|editor, window, cx| {
15620        editor.move_up(&Default::default(), window, cx);
15621        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15622    });
15623    cx.assert_state_with_diff(
15624        indoc! { "
15625            one
15626            ˇTWO
15627            three
15628            four
15629            five
15630        "}
15631        .to_string(),
15632    );
15633}
15634
15635#[gpui::test]
15636async fn test_edits_around_expanded_deletion_hunks(
15637    executor: BackgroundExecutor,
15638    cx: &mut TestAppContext,
15639) {
15640    init_test(cx, |_| {});
15641
15642    let mut cx = EditorTestContext::new(cx).await;
15643
15644    let diff_base = r#"
15645        use some::mod1;
15646        use some::mod2;
15647
15648        const A: u32 = 42;
15649        const B: u32 = 42;
15650        const C: u32 = 42;
15651
15652
15653        fn main() {
15654            println!("hello");
15655
15656            println!("world");
15657        }
15658    "#
15659    .unindent();
15660    executor.run_until_parked();
15661    cx.set_state(
15662        &r#"
15663        use some::mod1;
15664        use some::mod2;
15665
15666        ˇconst B: u32 = 42;
15667        const C: u32 = 42;
15668
15669
15670        fn main() {
15671            println!("hello");
15672
15673            println!("world");
15674        }
15675        "#
15676        .unindent(),
15677    );
15678
15679    cx.set_head_text(&diff_base);
15680    executor.run_until_parked();
15681
15682    cx.update_editor(|editor, window, cx| {
15683        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15684    });
15685    executor.run_until_parked();
15686
15687    cx.assert_state_with_diff(
15688        r#"
15689        use some::mod1;
15690        use some::mod2;
15691
15692      - const A: u32 = 42;
15693        ˇconst B: u32 = 42;
15694        const C: u32 = 42;
15695
15696
15697        fn main() {
15698            println!("hello");
15699
15700            println!("world");
15701        }
15702      "#
15703        .unindent(),
15704    );
15705
15706    cx.update_editor(|editor, window, cx| {
15707        editor.delete_line(&DeleteLine, window, cx);
15708    });
15709    executor.run_until_parked();
15710    cx.assert_state_with_diff(
15711        r#"
15712        use some::mod1;
15713        use some::mod2;
15714
15715      - const A: u32 = 42;
15716      - const B: u32 = 42;
15717        ˇconst C: u32 = 42;
15718
15719
15720        fn main() {
15721            println!("hello");
15722
15723            println!("world");
15724        }
15725      "#
15726        .unindent(),
15727    );
15728
15729    cx.update_editor(|editor, window, cx| {
15730        editor.delete_line(&DeleteLine, window, cx);
15731    });
15732    executor.run_until_parked();
15733    cx.assert_state_with_diff(
15734        r#"
15735        use some::mod1;
15736        use some::mod2;
15737
15738      - const A: u32 = 42;
15739      - const B: u32 = 42;
15740      - const C: u32 = 42;
15741        ˇ
15742
15743        fn main() {
15744            println!("hello");
15745
15746            println!("world");
15747        }
15748      "#
15749        .unindent(),
15750    );
15751
15752    cx.update_editor(|editor, window, cx| {
15753        editor.handle_input("replacement", window, cx);
15754    });
15755    executor.run_until_parked();
15756    cx.assert_state_with_diff(
15757        r#"
15758        use some::mod1;
15759        use some::mod2;
15760
15761      - const A: u32 = 42;
15762      - const B: u32 = 42;
15763      - const C: u32 = 42;
15764      -
15765      + replacementˇ
15766
15767        fn main() {
15768            println!("hello");
15769
15770            println!("world");
15771        }
15772      "#
15773        .unindent(),
15774    );
15775}
15776
15777#[gpui::test]
15778async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15779    init_test(cx, |_| {});
15780
15781    let mut cx = EditorTestContext::new(cx).await;
15782
15783    let base_text = r#"
15784        one
15785        two
15786        three
15787        four
15788        five
15789    "#
15790    .unindent();
15791    executor.run_until_parked();
15792    cx.set_state(
15793        &r#"
15794        one
15795        two
15796        fˇour
15797        five
15798        "#
15799        .unindent(),
15800    );
15801
15802    cx.set_head_text(&base_text);
15803    executor.run_until_parked();
15804
15805    cx.update_editor(|editor, window, cx| {
15806        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15807    });
15808    executor.run_until_parked();
15809
15810    cx.assert_state_with_diff(
15811        r#"
15812          one
15813          two
15814        - three
15815          fˇour
15816          five
15817        "#
15818        .unindent(),
15819    );
15820
15821    cx.update_editor(|editor, window, cx| {
15822        editor.backspace(&Backspace, window, cx);
15823        editor.backspace(&Backspace, window, cx);
15824    });
15825    executor.run_until_parked();
15826    cx.assert_state_with_diff(
15827        r#"
15828          one
15829          two
15830        - threeˇ
15831        - four
15832        + our
15833          five
15834        "#
15835        .unindent(),
15836    );
15837}
15838
15839#[gpui::test]
15840async fn test_edit_after_expanded_modification_hunk(
15841    executor: BackgroundExecutor,
15842    cx: &mut TestAppContext,
15843) {
15844    init_test(cx, |_| {});
15845
15846    let mut cx = EditorTestContext::new(cx).await;
15847
15848    let diff_base = r#"
15849        use some::mod1;
15850        use some::mod2;
15851
15852        const A: u32 = 42;
15853        const B: u32 = 42;
15854        const C: u32 = 42;
15855        const D: u32 = 42;
15856
15857
15858        fn main() {
15859            println!("hello");
15860
15861            println!("world");
15862        }"#
15863    .unindent();
15864
15865    cx.set_state(
15866        &r#"
15867        use some::mod1;
15868        use some::mod2;
15869
15870        const A: u32 = 42;
15871        const B: u32 = 42;
15872        const C: u32 = 43ˇ
15873        const D: u32 = 42;
15874
15875
15876        fn main() {
15877            println!("hello");
15878
15879            println!("world");
15880        }"#
15881        .unindent(),
15882    );
15883
15884    cx.set_head_text(&diff_base);
15885    executor.run_until_parked();
15886    cx.update_editor(|editor, window, cx| {
15887        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15888    });
15889    executor.run_until_parked();
15890
15891    cx.assert_state_with_diff(
15892        r#"
15893        use some::mod1;
15894        use some::mod2;
15895
15896        const A: u32 = 42;
15897        const B: u32 = 42;
15898      - const C: u32 = 42;
15899      + const C: u32 = 43ˇ
15900        const D: u32 = 42;
15901
15902
15903        fn main() {
15904            println!("hello");
15905
15906            println!("world");
15907        }"#
15908        .unindent(),
15909    );
15910
15911    cx.update_editor(|editor, window, cx| {
15912        editor.handle_input("\nnew_line\n", window, cx);
15913    });
15914    executor.run_until_parked();
15915
15916    cx.assert_state_with_diff(
15917        r#"
15918        use some::mod1;
15919        use some::mod2;
15920
15921        const A: u32 = 42;
15922        const B: u32 = 42;
15923      - const C: u32 = 42;
15924      + const C: u32 = 43
15925      + new_line
15926      + ˇ
15927        const D: u32 = 42;
15928
15929
15930        fn main() {
15931            println!("hello");
15932
15933            println!("world");
15934        }"#
15935        .unindent(),
15936    );
15937}
15938
15939#[gpui::test]
15940async fn test_stage_and_unstage_added_file_hunk(
15941    executor: BackgroundExecutor,
15942    cx: &mut TestAppContext,
15943) {
15944    init_test(cx, |_| {});
15945
15946    let mut cx = EditorTestContext::new(cx).await;
15947    cx.update_editor(|editor, _, cx| {
15948        editor.set_expand_all_diff_hunks(cx);
15949    });
15950
15951    let working_copy = r#"
15952            ˇfn main() {
15953                println!("hello, world!");
15954            }
15955        "#
15956    .unindent();
15957
15958    cx.set_state(&working_copy);
15959    executor.run_until_parked();
15960
15961    cx.assert_state_with_diff(
15962        r#"
15963            + ˇfn main() {
15964            +     println!("hello, world!");
15965            + }
15966        "#
15967        .unindent(),
15968    );
15969    cx.assert_index_text(None);
15970
15971    cx.update_editor(|editor, window, cx| {
15972        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15973    });
15974    executor.run_until_parked();
15975    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15976    cx.assert_state_with_diff(
15977        r#"
15978            + ˇfn main() {
15979            +     println!("hello, world!");
15980            + }
15981        "#
15982        .unindent(),
15983    );
15984
15985    cx.update_editor(|editor, window, cx| {
15986        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15987    });
15988    executor.run_until_parked();
15989    cx.assert_index_text(None);
15990}
15991
15992async fn setup_indent_guides_editor(
15993    text: &str,
15994    cx: &mut TestAppContext,
15995) -> (BufferId, EditorTestContext) {
15996    init_test(cx, |_| {});
15997
15998    let mut cx = EditorTestContext::new(cx).await;
15999
16000    let buffer_id = cx.update_editor(|editor, window, cx| {
16001        editor.set_text(text, window, cx);
16002        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16003
16004        buffer_ids[0]
16005    });
16006
16007    (buffer_id, cx)
16008}
16009
16010fn assert_indent_guides(
16011    range: Range<u32>,
16012    expected: Vec<IndentGuide>,
16013    active_indices: Option<Vec<usize>>,
16014    cx: &mut EditorTestContext,
16015) {
16016    let indent_guides = cx.update_editor(|editor, window, cx| {
16017        let snapshot = editor.snapshot(window, cx).display_snapshot;
16018        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16019            editor,
16020            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16021            true,
16022            &snapshot,
16023            cx,
16024        );
16025
16026        indent_guides.sort_by(|a, b| {
16027            a.depth.cmp(&b.depth).then(
16028                a.start_row
16029                    .cmp(&b.start_row)
16030                    .then(a.end_row.cmp(&b.end_row)),
16031            )
16032        });
16033        indent_guides
16034    });
16035
16036    if let Some(expected) = active_indices {
16037        let active_indices = cx.update_editor(|editor, window, cx| {
16038            let snapshot = editor.snapshot(window, cx).display_snapshot;
16039            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16040        });
16041
16042        assert_eq!(
16043            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16044            expected,
16045            "Active indent guide indices do not match"
16046        );
16047    }
16048
16049    assert_eq!(indent_guides, expected, "Indent guides do not match");
16050}
16051
16052fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16053    IndentGuide {
16054        buffer_id,
16055        start_row: MultiBufferRow(start_row),
16056        end_row: MultiBufferRow(end_row),
16057        depth,
16058        tab_size: 4,
16059        settings: IndentGuideSettings {
16060            enabled: true,
16061            line_width: 1,
16062            active_line_width: 1,
16063            ..Default::default()
16064        },
16065    }
16066}
16067
16068#[gpui::test]
16069async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16070    let (buffer_id, mut cx) = setup_indent_guides_editor(
16071        &"
16072    fn main() {
16073        let a = 1;
16074    }"
16075        .unindent(),
16076        cx,
16077    )
16078    .await;
16079
16080    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16081}
16082
16083#[gpui::test]
16084async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16085    let (buffer_id, mut cx) = setup_indent_guides_editor(
16086        &"
16087    fn main() {
16088        let a = 1;
16089        let b = 2;
16090    }"
16091        .unindent(),
16092        cx,
16093    )
16094    .await;
16095
16096    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16097}
16098
16099#[gpui::test]
16100async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16101    let (buffer_id, mut cx) = setup_indent_guides_editor(
16102        &"
16103    fn main() {
16104        let a = 1;
16105        if a == 3 {
16106            let b = 2;
16107        } else {
16108            let c = 3;
16109        }
16110    }"
16111        .unindent(),
16112        cx,
16113    )
16114    .await;
16115
16116    assert_indent_guides(
16117        0..8,
16118        vec![
16119            indent_guide(buffer_id, 1, 6, 0),
16120            indent_guide(buffer_id, 3, 3, 1),
16121            indent_guide(buffer_id, 5, 5, 1),
16122        ],
16123        None,
16124        &mut cx,
16125    );
16126}
16127
16128#[gpui::test]
16129async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16130    let (buffer_id, mut cx) = setup_indent_guides_editor(
16131        &"
16132    fn main() {
16133        let a = 1;
16134            let b = 2;
16135        let c = 3;
16136    }"
16137        .unindent(),
16138        cx,
16139    )
16140    .await;
16141
16142    assert_indent_guides(
16143        0..5,
16144        vec![
16145            indent_guide(buffer_id, 1, 3, 0),
16146            indent_guide(buffer_id, 2, 2, 1),
16147        ],
16148        None,
16149        &mut cx,
16150    );
16151}
16152
16153#[gpui::test]
16154async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16155    let (buffer_id, mut cx) = setup_indent_guides_editor(
16156        &"
16157        fn main() {
16158            let a = 1;
16159
16160            let c = 3;
16161        }"
16162        .unindent(),
16163        cx,
16164    )
16165    .await;
16166
16167    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16168}
16169
16170#[gpui::test]
16171async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16172    let (buffer_id, mut cx) = setup_indent_guides_editor(
16173        &"
16174        fn main() {
16175            let a = 1;
16176
16177            let c = 3;
16178
16179            if a == 3 {
16180                let b = 2;
16181            } else {
16182                let c = 3;
16183            }
16184        }"
16185        .unindent(),
16186        cx,
16187    )
16188    .await;
16189
16190    assert_indent_guides(
16191        0..11,
16192        vec![
16193            indent_guide(buffer_id, 1, 9, 0),
16194            indent_guide(buffer_id, 6, 6, 1),
16195            indent_guide(buffer_id, 8, 8, 1),
16196        ],
16197        None,
16198        &mut cx,
16199    );
16200}
16201
16202#[gpui::test]
16203async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16204    let (buffer_id, mut cx) = setup_indent_guides_editor(
16205        &"
16206        fn main() {
16207            let a = 1;
16208
16209            let c = 3;
16210
16211            if a == 3 {
16212                let b = 2;
16213            } else {
16214                let c = 3;
16215            }
16216        }"
16217        .unindent(),
16218        cx,
16219    )
16220    .await;
16221
16222    assert_indent_guides(
16223        1..11,
16224        vec![
16225            indent_guide(buffer_id, 1, 9, 0),
16226            indent_guide(buffer_id, 6, 6, 1),
16227            indent_guide(buffer_id, 8, 8, 1),
16228        ],
16229        None,
16230        &mut cx,
16231    );
16232}
16233
16234#[gpui::test]
16235async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16236    let (buffer_id, mut cx) = setup_indent_guides_editor(
16237        &"
16238        fn main() {
16239            let a = 1;
16240
16241            let c = 3;
16242
16243            if a == 3 {
16244                let b = 2;
16245            } else {
16246                let c = 3;
16247            }
16248        }"
16249        .unindent(),
16250        cx,
16251    )
16252    .await;
16253
16254    assert_indent_guides(
16255        1..10,
16256        vec![
16257            indent_guide(buffer_id, 1, 9, 0),
16258            indent_guide(buffer_id, 6, 6, 1),
16259            indent_guide(buffer_id, 8, 8, 1),
16260        ],
16261        None,
16262        &mut cx,
16263    );
16264}
16265
16266#[gpui::test]
16267async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16268    let (buffer_id, mut cx) = setup_indent_guides_editor(
16269        &"
16270        block1
16271            block2
16272                block3
16273                    block4
16274            block2
16275        block1
16276        block1"
16277            .unindent(),
16278        cx,
16279    )
16280    .await;
16281
16282    assert_indent_guides(
16283        1..10,
16284        vec![
16285            indent_guide(buffer_id, 1, 4, 0),
16286            indent_guide(buffer_id, 2, 3, 1),
16287            indent_guide(buffer_id, 3, 3, 2),
16288        ],
16289        None,
16290        &mut cx,
16291    );
16292}
16293
16294#[gpui::test]
16295async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16296    let (buffer_id, mut cx) = setup_indent_guides_editor(
16297        &"
16298        block1
16299            block2
16300                block3
16301
16302        block1
16303        block1"
16304            .unindent(),
16305        cx,
16306    )
16307    .await;
16308
16309    assert_indent_guides(
16310        0..6,
16311        vec![
16312            indent_guide(buffer_id, 1, 2, 0),
16313            indent_guide(buffer_id, 2, 2, 1),
16314        ],
16315        None,
16316        &mut cx,
16317    );
16318}
16319
16320#[gpui::test]
16321async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16322    let (buffer_id, mut cx) = setup_indent_guides_editor(
16323        &"
16324        block1
16325
16326
16327
16328            block2
16329        "
16330        .unindent(),
16331        cx,
16332    )
16333    .await;
16334
16335    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16336}
16337
16338#[gpui::test]
16339async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16340    let (buffer_id, mut cx) = setup_indent_guides_editor(
16341        &"
16342        def a:
16343        \tb = 3
16344        \tif True:
16345        \t\tc = 4
16346        \t\td = 5
16347        \tprint(b)
16348        "
16349        .unindent(),
16350        cx,
16351    )
16352    .await;
16353
16354    assert_indent_guides(
16355        0..6,
16356        vec![
16357            indent_guide(buffer_id, 1, 6, 0),
16358            indent_guide(buffer_id, 3, 4, 1),
16359        ],
16360        None,
16361        &mut cx,
16362    );
16363}
16364
16365#[gpui::test]
16366async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16367    let (buffer_id, mut cx) = setup_indent_guides_editor(
16368        &"
16369    fn main() {
16370        let a = 1;
16371    }"
16372        .unindent(),
16373        cx,
16374    )
16375    .await;
16376
16377    cx.update_editor(|editor, window, cx| {
16378        editor.change_selections(None, window, cx, |s| {
16379            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16380        });
16381    });
16382
16383    assert_indent_guides(
16384        0..3,
16385        vec![indent_guide(buffer_id, 1, 1, 0)],
16386        Some(vec![0]),
16387        &mut cx,
16388    );
16389}
16390
16391#[gpui::test]
16392async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16393    let (buffer_id, mut cx) = setup_indent_guides_editor(
16394        &"
16395    fn main() {
16396        if 1 == 2 {
16397            let a = 1;
16398        }
16399    }"
16400        .unindent(),
16401        cx,
16402    )
16403    .await;
16404
16405    cx.update_editor(|editor, window, cx| {
16406        editor.change_selections(None, window, cx, |s| {
16407            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16408        });
16409    });
16410
16411    assert_indent_guides(
16412        0..4,
16413        vec![
16414            indent_guide(buffer_id, 1, 3, 0),
16415            indent_guide(buffer_id, 2, 2, 1),
16416        ],
16417        Some(vec![1]),
16418        &mut cx,
16419    );
16420
16421    cx.update_editor(|editor, window, cx| {
16422        editor.change_selections(None, window, cx, |s| {
16423            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16424        });
16425    });
16426
16427    assert_indent_guides(
16428        0..4,
16429        vec![
16430            indent_guide(buffer_id, 1, 3, 0),
16431            indent_guide(buffer_id, 2, 2, 1),
16432        ],
16433        Some(vec![1]),
16434        &mut cx,
16435    );
16436
16437    cx.update_editor(|editor, window, cx| {
16438        editor.change_selections(None, window, cx, |s| {
16439            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16440        });
16441    });
16442
16443    assert_indent_guides(
16444        0..4,
16445        vec![
16446            indent_guide(buffer_id, 1, 3, 0),
16447            indent_guide(buffer_id, 2, 2, 1),
16448        ],
16449        Some(vec![0]),
16450        &mut cx,
16451    );
16452}
16453
16454#[gpui::test]
16455async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16456    let (buffer_id, mut cx) = setup_indent_guides_editor(
16457        &"
16458    fn main() {
16459        let a = 1;
16460
16461        let b = 2;
16462    }"
16463        .unindent(),
16464        cx,
16465    )
16466    .await;
16467
16468    cx.update_editor(|editor, window, cx| {
16469        editor.change_selections(None, window, cx, |s| {
16470            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16471        });
16472    });
16473
16474    assert_indent_guides(
16475        0..5,
16476        vec![indent_guide(buffer_id, 1, 3, 0)],
16477        Some(vec![0]),
16478        &mut cx,
16479    );
16480}
16481
16482#[gpui::test]
16483async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16484    let (buffer_id, mut cx) = setup_indent_guides_editor(
16485        &"
16486    def m:
16487        a = 1
16488        pass"
16489            .unindent(),
16490        cx,
16491    )
16492    .await;
16493
16494    cx.update_editor(|editor, window, cx| {
16495        editor.change_selections(None, window, cx, |s| {
16496            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16497        });
16498    });
16499
16500    assert_indent_guides(
16501        0..3,
16502        vec![indent_guide(buffer_id, 1, 2, 0)],
16503        Some(vec![0]),
16504        &mut cx,
16505    );
16506}
16507
16508#[gpui::test]
16509async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16510    init_test(cx, |_| {});
16511    let mut cx = EditorTestContext::new(cx).await;
16512    let text = indoc! {
16513        "
16514        impl A {
16515            fn b() {
16516                0;
16517                3;
16518                5;
16519                6;
16520                7;
16521            }
16522        }
16523        "
16524    };
16525    let base_text = indoc! {
16526        "
16527        impl A {
16528            fn b() {
16529                0;
16530                1;
16531                2;
16532                3;
16533                4;
16534            }
16535            fn c() {
16536                5;
16537                6;
16538                7;
16539            }
16540        }
16541        "
16542    };
16543
16544    cx.update_editor(|editor, window, cx| {
16545        editor.set_text(text, window, cx);
16546
16547        editor.buffer().update(cx, |multibuffer, cx| {
16548            let buffer = multibuffer.as_singleton().unwrap();
16549            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16550
16551            multibuffer.set_all_diff_hunks_expanded(cx);
16552            multibuffer.add_diff(diff, cx);
16553
16554            buffer.read(cx).remote_id()
16555        })
16556    });
16557    cx.run_until_parked();
16558
16559    cx.assert_state_with_diff(
16560        indoc! { "
16561          impl A {
16562              fn b() {
16563                  0;
16564        -         1;
16565        -         2;
16566                  3;
16567        -         4;
16568        -     }
16569        -     fn c() {
16570                  5;
16571                  6;
16572                  7;
16573              }
16574          }
16575          ˇ"
16576        }
16577        .to_string(),
16578    );
16579
16580    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16581        editor
16582            .snapshot(window, cx)
16583            .buffer_snapshot
16584            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16585            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16586            .collect::<Vec<_>>()
16587    });
16588    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16589    assert_eq!(
16590        actual_guides,
16591        vec![
16592            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16593            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16594            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16595        ]
16596    );
16597}
16598
16599#[gpui::test]
16600async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16601    init_test(cx, |_| {});
16602    let mut cx = EditorTestContext::new(cx).await;
16603
16604    let diff_base = r#"
16605        a
16606        b
16607        c
16608        "#
16609    .unindent();
16610
16611    cx.set_state(
16612        &r#"
16613        ˇA
16614        b
16615        C
16616        "#
16617        .unindent(),
16618    );
16619    cx.set_head_text(&diff_base);
16620    cx.update_editor(|editor, window, cx| {
16621        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16622    });
16623    executor.run_until_parked();
16624
16625    let both_hunks_expanded = r#"
16626        - a
16627        + ˇA
16628          b
16629        - c
16630        + C
16631        "#
16632    .unindent();
16633
16634    cx.assert_state_with_diff(both_hunks_expanded.clone());
16635
16636    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16637        let snapshot = editor.snapshot(window, cx);
16638        let hunks = editor
16639            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16640            .collect::<Vec<_>>();
16641        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16642        let buffer_id = hunks[0].buffer_id;
16643        hunks
16644            .into_iter()
16645            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16646            .collect::<Vec<_>>()
16647    });
16648    assert_eq!(hunk_ranges.len(), 2);
16649
16650    cx.update_editor(|editor, _, cx| {
16651        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16652    });
16653    executor.run_until_parked();
16654
16655    let second_hunk_expanded = r#"
16656          ˇA
16657          b
16658        - c
16659        + C
16660        "#
16661    .unindent();
16662
16663    cx.assert_state_with_diff(second_hunk_expanded);
16664
16665    cx.update_editor(|editor, _, cx| {
16666        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16667    });
16668    executor.run_until_parked();
16669
16670    cx.assert_state_with_diff(both_hunks_expanded.clone());
16671
16672    cx.update_editor(|editor, _, cx| {
16673        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16674    });
16675    executor.run_until_parked();
16676
16677    let first_hunk_expanded = r#"
16678        - a
16679        + ˇA
16680          b
16681          C
16682        "#
16683    .unindent();
16684
16685    cx.assert_state_with_diff(first_hunk_expanded);
16686
16687    cx.update_editor(|editor, _, cx| {
16688        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16689    });
16690    executor.run_until_parked();
16691
16692    cx.assert_state_with_diff(both_hunks_expanded);
16693
16694    cx.set_state(
16695        &r#"
16696        ˇA
16697        b
16698        "#
16699        .unindent(),
16700    );
16701    cx.run_until_parked();
16702
16703    // TODO this cursor position seems bad
16704    cx.assert_state_with_diff(
16705        r#"
16706        - ˇa
16707        + A
16708          b
16709        "#
16710        .unindent(),
16711    );
16712
16713    cx.update_editor(|editor, window, cx| {
16714        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16715    });
16716
16717    cx.assert_state_with_diff(
16718        r#"
16719            - ˇa
16720            + A
16721              b
16722            - c
16723            "#
16724        .unindent(),
16725    );
16726
16727    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16728        let snapshot = editor.snapshot(window, cx);
16729        let hunks = editor
16730            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16731            .collect::<Vec<_>>();
16732        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16733        let buffer_id = hunks[0].buffer_id;
16734        hunks
16735            .into_iter()
16736            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16737            .collect::<Vec<_>>()
16738    });
16739    assert_eq!(hunk_ranges.len(), 2);
16740
16741    cx.update_editor(|editor, _, cx| {
16742        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16743    });
16744    executor.run_until_parked();
16745
16746    cx.assert_state_with_diff(
16747        r#"
16748        - ˇa
16749        + A
16750          b
16751        "#
16752        .unindent(),
16753    );
16754}
16755
16756#[gpui::test]
16757async fn test_toggle_deletion_hunk_at_start_of_file(
16758    executor: BackgroundExecutor,
16759    cx: &mut TestAppContext,
16760) {
16761    init_test(cx, |_| {});
16762    let mut cx = EditorTestContext::new(cx).await;
16763
16764    let diff_base = r#"
16765        a
16766        b
16767        c
16768        "#
16769    .unindent();
16770
16771    cx.set_state(
16772        &r#"
16773        ˇb
16774        c
16775        "#
16776        .unindent(),
16777    );
16778    cx.set_head_text(&diff_base);
16779    cx.update_editor(|editor, window, cx| {
16780        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16781    });
16782    executor.run_until_parked();
16783
16784    let hunk_expanded = r#"
16785        - a
16786          ˇb
16787          c
16788        "#
16789    .unindent();
16790
16791    cx.assert_state_with_diff(hunk_expanded.clone());
16792
16793    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16794        let snapshot = editor.snapshot(window, cx);
16795        let hunks = editor
16796            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16797            .collect::<Vec<_>>();
16798        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16799        let buffer_id = hunks[0].buffer_id;
16800        hunks
16801            .into_iter()
16802            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16803            .collect::<Vec<_>>()
16804    });
16805    assert_eq!(hunk_ranges.len(), 1);
16806
16807    cx.update_editor(|editor, _, cx| {
16808        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16809    });
16810    executor.run_until_parked();
16811
16812    let hunk_collapsed = r#"
16813          ˇb
16814          c
16815        "#
16816    .unindent();
16817
16818    cx.assert_state_with_diff(hunk_collapsed);
16819
16820    cx.update_editor(|editor, _, cx| {
16821        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16822    });
16823    executor.run_until_parked();
16824
16825    cx.assert_state_with_diff(hunk_expanded.clone());
16826}
16827
16828#[gpui::test]
16829async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16830    init_test(cx, |_| {});
16831
16832    let fs = FakeFs::new(cx.executor());
16833    fs.insert_tree(
16834        path!("/test"),
16835        json!({
16836            ".git": {},
16837            "file-1": "ONE\n",
16838            "file-2": "TWO\n",
16839            "file-3": "THREE\n",
16840        }),
16841    )
16842    .await;
16843
16844    fs.set_head_for_repo(
16845        path!("/test/.git").as_ref(),
16846        &[
16847            ("file-1".into(), "one\n".into()),
16848            ("file-2".into(), "two\n".into()),
16849            ("file-3".into(), "three\n".into()),
16850        ],
16851    );
16852
16853    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16854    let mut buffers = vec![];
16855    for i in 1..=3 {
16856        let buffer = project
16857            .update(cx, |project, cx| {
16858                let path = format!(path!("/test/file-{}"), i);
16859                project.open_local_buffer(path, cx)
16860            })
16861            .await
16862            .unwrap();
16863        buffers.push(buffer);
16864    }
16865
16866    let multibuffer = cx.new(|cx| {
16867        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16868        multibuffer.set_all_diff_hunks_expanded(cx);
16869        for buffer in &buffers {
16870            let snapshot = buffer.read(cx).snapshot();
16871            multibuffer.set_excerpts_for_path(
16872                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16873                buffer.clone(),
16874                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16875                DEFAULT_MULTIBUFFER_CONTEXT,
16876                cx,
16877            );
16878        }
16879        multibuffer
16880    });
16881
16882    let editor = cx.add_window(|window, cx| {
16883        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16884    });
16885    cx.run_until_parked();
16886
16887    let snapshot = editor
16888        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16889        .unwrap();
16890    let hunks = snapshot
16891        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16892        .map(|hunk| match hunk {
16893            DisplayDiffHunk::Unfolded {
16894                display_row_range, ..
16895            } => display_row_range,
16896            DisplayDiffHunk::Folded { .. } => unreachable!(),
16897        })
16898        .collect::<Vec<_>>();
16899    assert_eq!(
16900        hunks,
16901        [
16902            DisplayRow(2)..DisplayRow(4),
16903            DisplayRow(7)..DisplayRow(9),
16904            DisplayRow(12)..DisplayRow(14),
16905        ]
16906    );
16907}
16908
16909#[gpui::test]
16910async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16911    init_test(cx, |_| {});
16912
16913    let mut cx = EditorTestContext::new(cx).await;
16914    cx.set_head_text(indoc! { "
16915        one
16916        two
16917        three
16918        four
16919        five
16920        "
16921    });
16922    cx.set_index_text(indoc! { "
16923        one
16924        two
16925        three
16926        four
16927        five
16928        "
16929    });
16930    cx.set_state(indoc! {"
16931        one
16932        TWO
16933        ˇTHREE
16934        FOUR
16935        five
16936    "});
16937    cx.run_until_parked();
16938    cx.update_editor(|editor, window, cx| {
16939        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16940    });
16941    cx.run_until_parked();
16942    cx.assert_index_text(Some(indoc! {"
16943        one
16944        TWO
16945        THREE
16946        FOUR
16947        five
16948    "}));
16949    cx.set_state(indoc! { "
16950        one
16951        TWO
16952        ˇTHREE-HUNDRED
16953        FOUR
16954        five
16955    "});
16956    cx.run_until_parked();
16957    cx.update_editor(|editor, window, cx| {
16958        let snapshot = editor.snapshot(window, cx);
16959        let hunks = editor
16960            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16961            .collect::<Vec<_>>();
16962        assert_eq!(hunks.len(), 1);
16963        assert_eq!(
16964            hunks[0].status(),
16965            DiffHunkStatus {
16966                kind: DiffHunkStatusKind::Modified,
16967                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16968            }
16969        );
16970
16971        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16972    });
16973    cx.run_until_parked();
16974    cx.assert_index_text(Some(indoc! {"
16975        one
16976        TWO
16977        THREE-HUNDRED
16978        FOUR
16979        five
16980    "}));
16981}
16982
16983#[gpui::test]
16984fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16985    init_test(cx, |_| {});
16986
16987    let editor = cx.add_window(|window, cx| {
16988        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16989        build_editor(buffer, window, cx)
16990    });
16991
16992    let render_args = Arc::new(Mutex::new(None));
16993    let snapshot = editor
16994        .update(cx, |editor, window, cx| {
16995            let snapshot = editor.buffer().read(cx).snapshot(cx);
16996            let range =
16997                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16998
16999            struct RenderArgs {
17000                row: MultiBufferRow,
17001                folded: bool,
17002                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17003            }
17004
17005            let crease = Crease::inline(
17006                range,
17007                FoldPlaceholder::test(),
17008                {
17009                    let toggle_callback = render_args.clone();
17010                    move |row, folded, callback, _window, _cx| {
17011                        *toggle_callback.lock() = Some(RenderArgs {
17012                            row,
17013                            folded,
17014                            callback,
17015                        });
17016                        div()
17017                    }
17018                },
17019                |_row, _folded, _window, _cx| div(),
17020            );
17021
17022            editor.insert_creases(Some(crease), cx);
17023            let snapshot = editor.snapshot(window, cx);
17024            let _div = snapshot.render_crease_toggle(
17025                MultiBufferRow(1),
17026                false,
17027                cx.entity().clone(),
17028                window,
17029                cx,
17030            );
17031            snapshot
17032        })
17033        .unwrap();
17034
17035    let render_args = render_args.lock().take().unwrap();
17036    assert_eq!(render_args.row, MultiBufferRow(1));
17037    assert!(!render_args.folded);
17038    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17039
17040    cx.update_window(*editor, |_, window, cx| {
17041        (render_args.callback)(true, window, cx)
17042    })
17043    .unwrap();
17044    let snapshot = editor
17045        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17046        .unwrap();
17047    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17048
17049    cx.update_window(*editor, |_, window, cx| {
17050        (render_args.callback)(false, window, cx)
17051    })
17052    .unwrap();
17053    let snapshot = editor
17054        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17055        .unwrap();
17056    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17057}
17058
17059#[gpui::test]
17060async fn test_input_text(cx: &mut TestAppContext) {
17061    init_test(cx, |_| {});
17062    let mut cx = EditorTestContext::new(cx).await;
17063
17064    cx.set_state(
17065        &r#"ˇone
17066        two
17067
17068        three
17069        fourˇ
17070        five
17071
17072        siˇx"#
17073            .unindent(),
17074    );
17075
17076    cx.dispatch_action(HandleInput(String::new()));
17077    cx.assert_editor_state(
17078        &r#"ˇone
17079        two
17080
17081        three
17082        fourˇ
17083        five
17084
17085        siˇx"#
17086            .unindent(),
17087    );
17088
17089    cx.dispatch_action(HandleInput("AAAA".to_string()));
17090    cx.assert_editor_state(
17091        &r#"AAAAˇone
17092        two
17093
17094        three
17095        fourAAAAˇ
17096        five
17097
17098        siAAAAˇx"#
17099            .unindent(),
17100    );
17101}
17102
17103#[gpui::test]
17104async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17105    init_test(cx, |_| {});
17106
17107    let mut cx = EditorTestContext::new(cx).await;
17108    cx.set_state(
17109        r#"let foo = 1;
17110let foo = 2;
17111let foo = 3;
17112let fooˇ = 4;
17113let foo = 5;
17114let foo = 6;
17115let foo = 7;
17116let foo = 8;
17117let foo = 9;
17118let foo = 10;
17119let foo = 11;
17120let foo = 12;
17121let foo = 13;
17122let foo = 14;
17123let foo = 15;"#,
17124    );
17125
17126    cx.update_editor(|e, window, cx| {
17127        assert_eq!(
17128            e.next_scroll_position,
17129            NextScrollCursorCenterTopBottom::Center,
17130            "Default next scroll direction is center",
17131        );
17132
17133        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17134        assert_eq!(
17135            e.next_scroll_position,
17136            NextScrollCursorCenterTopBottom::Top,
17137            "After center, next scroll direction should be top",
17138        );
17139
17140        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17141        assert_eq!(
17142            e.next_scroll_position,
17143            NextScrollCursorCenterTopBottom::Bottom,
17144            "After top, next scroll direction should be bottom",
17145        );
17146
17147        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17148        assert_eq!(
17149            e.next_scroll_position,
17150            NextScrollCursorCenterTopBottom::Center,
17151            "After bottom, scrolling should start over",
17152        );
17153
17154        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17155        assert_eq!(
17156            e.next_scroll_position,
17157            NextScrollCursorCenterTopBottom::Top,
17158            "Scrolling continues if retriggered fast enough"
17159        );
17160    });
17161
17162    cx.executor()
17163        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17164    cx.executor().run_until_parked();
17165    cx.update_editor(|e, _, _| {
17166        assert_eq!(
17167            e.next_scroll_position,
17168            NextScrollCursorCenterTopBottom::Center,
17169            "If scrolling is not triggered fast enough, it should reset"
17170        );
17171    });
17172}
17173
17174#[gpui::test]
17175async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17176    init_test(cx, |_| {});
17177    let mut cx = EditorLspTestContext::new_rust(
17178        lsp::ServerCapabilities {
17179            definition_provider: Some(lsp::OneOf::Left(true)),
17180            references_provider: Some(lsp::OneOf::Left(true)),
17181            ..lsp::ServerCapabilities::default()
17182        },
17183        cx,
17184    )
17185    .await;
17186
17187    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17188        let go_to_definition = cx
17189            .lsp
17190            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17191                move |params, _| async move {
17192                    if empty_go_to_definition {
17193                        Ok(None)
17194                    } else {
17195                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17196                            uri: params.text_document_position_params.text_document.uri,
17197                            range: lsp::Range::new(
17198                                lsp::Position::new(4, 3),
17199                                lsp::Position::new(4, 6),
17200                            ),
17201                        })))
17202                    }
17203                },
17204            );
17205        let references = cx
17206            .lsp
17207            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17208                Ok(Some(vec![lsp::Location {
17209                    uri: params.text_document_position.text_document.uri,
17210                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17211                }]))
17212            });
17213        (go_to_definition, references)
17214    };
17215
17216    cx.set_state(
17217        &r#"fn one() {
17218            let mut a = ˇtwo();
17219        }
17220
17221        fn two() {}"#
17222            .unindent(),
17223    );
17224    set_up_lsp_handlers(false, &mut cx);
17225    let navigated = cx
17226        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17227        .await
17228        .expect("Failed to navigate to definition");
17229    assert_eq!(
17230        navigated,
17231        Navigated::Yes,
17232        "Should have navigated to definition from the GetDefinition response"
17233    );
17234    cx.assert_editor_state(
17235        &r#"fn one() {
17236            let mut a = two();
17237        }
17238
17239        fn «twoˇ»() {}"#
17240            .unindent(),
17241    );
17242
17243    let editors = cx.update_workspace(|workspace, _, cx| {
17244        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17245    });
17246    cx.update_editor(|_, _, test_editor_cx| {
17247        assert_eq!(
17248            editors.len(),
17249            1,
17250            "Initially, only one, test, editor should be open in the workspace"
17251        );
17252        assert_eq!(
17253            test_editor_cx.entity(),
17254            editors.last().expect("Asserted len is 1").clone()
17255        );
17256    });
17257
17258    set_up_lsp_handlers(true, &mut cx);
17259    let navigated = cx
17260        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17261        .await
17262        .expect("Failed to navigate to lookup references");
17263    assert_eq!(
17264        navigated,
17265        Navigated::Yes,
17266        "Should have navigated to references as a fallback after empty GoToDefinition response"
17267    );
17268    // We should not change the selections in the existing file,
17269    // if opening another milti buffer with the references
17270    cx.assert_editor_state(
17271        &r#"fn one() {
17272            let mut a = two();
17273        }
17274
17275        fn «twoˇ»() {}"#
17276            .unindent(),
17277    );
17278    let editors = cx.update_workspace(|workspace, _, cx| {
17279        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17280    });
17281    cx.update_editor(|_, _, test_editor_cx| {
17282        assert_eq!(
17283            editors.len(),
17284            2,
17285            "After falling back to references search, we open a new editor with the results"
17286        );
17287        let references_fallback_text = editors
17288            .into_iter()
17289            .find(|new_editor| *new_editor != test_editor_cx.entity())
17290            .expect("Should have one non-test editor now")
17291            .read(test_editor_cx)
17292            .text(test_editor_cx);
17293        assert_eq!(
17294            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17295            "Should use the range from the references response and not the GoToDefinition one"
17296        );
17297    });
17298}
17299
17300#[gpui::test]
17301async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17302    init_test(cx, |_| {});
17303    cx.update(|cx| {
17304        let mut editor_settings = EditorSettings::get_global(cx).clone();
17305        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17306        EditorSettings::override_global(editor_settings, cx);
17307    });
17308    let mut cx = EditorLspTestContext::new_rust(
17309        lsp::ServerCapabilities {
17310            definition_provider: Some(lsp::OneOf::Left(true)),
17311            references_provider: Some(lsp::OneOf::Left(true)),
17312            ..lsp::ServerCapabilities::default()
17313        },
17314        cx,
17315    )
17316    .await;
17317    let original_state = r#"fn one() {
17318        let mut a = ˇtwo();
17319    }
17320
17321    fn two() {}"#
17322        .unindent();
17323    cx.set_state(&original_state);
17324
17325    let mut go_to_definition = cx
17326        .lsp
17327        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17328            move |_, _| async move { Ok(None) },
17329        );
17330    let _references = cx
17331        .lsp
17332        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17333            panic!("Should not call for references with no go to definition fallback")
17334        });
17335
17336    let navigated = cx
17337        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17338        .await
17339        .expect("Failed to navigate to lookup references");
17340    go_to_definition
17341        .next()
17342        .await
17343        .expect("Should have called the go_to_definition handler");
17344
17345    assert_eq!(
17346        navigated,
17347        Navigated::No,
17348        "Should have navigated to references as a fallback after empty GoToDefinition response"
17349    );
17350    cx.assert_editor_state(&original_state);
17351    let editors = cx.update_workspace(|workspace, _, cx| {
17352        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17353    });
17354    cx.update_editor(|_, _, _| {
17355        assert_eq!(
17356            editors.len(),
17357            1,
17358            "After unsuccessful fallback, no other editor should have been opened"
17359        );
17360    });
17361}
17362
17363#[gpui::test]
17364async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17365    init_test(cx, |_| {});
17366
17367    let language = Arc::new(Language::new(
17368        LanguageConfig::default(),
17369        Some(tree_sitter_rust::LANGUAGE.into()),
17370    ));
17371
17372    let text = r#"
17373        #[cfg(test)]
17374        mod tests() {
17375            #[test]
17376            fn runnable_1() {
17377                let a = 1;
17378            }
17379
17380            #[test]
17381            fn runnable_2() {
17382                let a = 1;
17383                let b = 2;
17384            }
17385        }
17386    "#
17387    .unindent();
17388
17389    let fs = FakeFs::new(cx.executor());
17390    fs.insert_file("/file.rs", Default::default()).await;
17391
17392    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17393    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17394    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17395    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17396    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17397
17398    let editor = cx.new_window_entity(|window, cx| {
17399        Editor::new(
17400            EditorMode::full(),
17401            multi_buffer,
17402            Some(project.clone()),
17403            window,
17404            cx,
17405        )
17406    });
17407
17408    editor.update_in(cx, |editor, window, cx| {
17409        let snapshot = editor.buffer().read(cx).snapshot(cx);
17410        editor.tasks.insert(
17411            (buffer.read(cx).remote_id(), 3),
17412            RunnableTasks {
17413                templates: vec![],
17414                offset: snapshot.anchor_before(43),
17415                column: 0,
17416                extra_variables: HashMap::default(),
17417                context_range: BufferOffset(43)..BufferOffset(85),
17418            },
17419        );
17420        editor.tasks.insert(
17421            (buffer.read(cx).remote_id(), 8),
17422            RunnableTasks {
17423                templates: vec![],
17424                offset: snapshot.anchor_before(86),
17425                column: 0,
17426                extra_variables: HashMap::default(),
17427                context_range: BufferOffset(86)..BufferOffset(191),
17428            },
17429        );
17430
17431        // Test finding task when cursor is inside function body
17432        editor.change_selections(None, window, cx, |s| {
17433            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17434        });
17435        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17436        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17437
17438        // Test finding task when cursor is on function name
17439        editor.change_selections(None, window, cx, |s| {
17440            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17441        });
17442        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17443        assert_eq!(row, 8, "Should find task when cursor is on function name");
17444    });
17445}
17446
17447#[gpui::test]
17448async fn test_folding_buffers(cx: &mut TestAppContext) {
17449    init_test(cx, |_| {});
17450
17451    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17452    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17453    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17454
17455    let fs = FakeFs::new(cx.executor());
17456    fs.insert_tree(
17457        path!("/a"),
17458        json!({
17459            "first.rs": sample_text_1,
17460            "second.rs": sample_text_2,
17461            "third.rs": sample_text_3,
17462        }),
17463    )
17464    .await;
17465    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17466    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17467    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17468    let worktree = project.update(cx, |project, cx| {
17469        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17470        assert_eq!(worktrees.len(), 1);
17471        worktrees.pop().unwrap()
17472    });
17473    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17474
17475    let buffer_1 = project
17476        .update(cx, |project, cx| {
17477            project.open_buffer((worktree_id, "first.rs"), cx)
17478        })
17479        .await
17480        .unwrap();
17481    let buffer_2 = project
17482        .update(cx, |project, cx| {
17483            project.open_buffer((worktree_id, "second.rs"), cx)
17484        })
17485        .await
17486        .unwrap();
17487    let buffer_3 = project
17488        .update(cx, |project, cx| {
17489            project.open_buffer((worktree_id, "third.rs"), cx)
17490        })
17491        .await
17492        .unwrap();
17493
17494    let multi_buffer = cx.new(|cx| {
17495        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17496        multi_buffer.push_excerpts(
17497            buffer_1.clone(),
17498            [
17499                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17500                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17501                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17502            ],
17503            cx,
17504        );
17505        multi_buffer.push_excerpts(
17506            buffer_2.clone(),
17507            [
17508                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17509                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17510                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17511            ],
17512            cx,
17513        );
17514        multi_buffer.push_excerpts(
17515            buffer_3.clone(),
17516            [
17517                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17518                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17519                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17520            ],
17521            cx,
17522        );
17523        multi_buffer
17524    });
17525    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17526        Editor::new(
17527            EditorMode::full(),
17528            multi_buffer.clone(),
17529            Some(project.clone()),
17530            window,
17531            cx,
17532        )
17533    });
17534
17535    assert_eq!(
17536        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17537        "\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",
17538    );
17539
17540    multi_buffer_editor.update(cx, |editor, cx| {
17541        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17542    });
17543    assert_eq!(
17544        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17545        "\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",
17546        "After folding the first buffer, its text should not be displayed"
17547    );
17548
17549    multi_buffer_editor.update(cx, |editor, cx| {
17550        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17551    });
17552    assert_eq!(
17553        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17554        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17555        "After folding the second buffer, its text should not be displayed"
17556    );
17557
17558    multi_buffer_editor.update(cx, |editor, cx| {
17559        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17560    });
17561    assert_eq!(
17562        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17563        "\n\n\n\n\n",
17564        "After folding the third buffer, its text should not be displayed"
17565    );
17566
17567    // Emulate selection inside the fold logic, that should work
17568    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17569        editor
17570            .snapshot(window, cx)
17571            .next_line_boundary(Point::new(0, 4));
17572    });
17573
17574    multi_buffer_editor.update(cx, |editor, cx| {
17575        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17576    });
17577    assert_eq!(
17578        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17579        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17580        "After unfolding the second buffer, its text should be displayed"
17581    );
17582
17583    // Typing inside of buffer 1 causes that buffer to be unfolded.
17584    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17585        assert_eq!(
17586            multi_buffer
17587                .read(cx)
17588                .snapshot(cx)
17589                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17590                .collect::<String>(),
17591            "bbbb"
17592        );
17593        editor.change_selections(None, window, cx, |selections| {
17594            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17595        });
17596        editor.handle_input("B", window, cx);
17597    });
17598
17599    assert_eq!(
17600        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17601        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17602        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17603    );
17604
17605    multi_buffer_editor.update(cx, |editor, cx| {
17606        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17607    });
17608    assert_eq!(
17609        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17610        "\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",
17611        "After unfolding the all buffers, all original text should be displayed"
17612    );
17613}
17614
17615#[gpui::test]
17616async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17617    init_test(cx, |_| {});
17618
17619    let sample_text_1 = "1111\n2222\n3333".to_string();
17620    let sample_text_2 = "4444\n5555\n6666".to_string();
17621    let sample_text_3 = "7777\n8888\n9999".to_string();
17622
17623    let fs = FakeFs::new(cx.executor());
17624    fs.insert_tree(
17625        path!("/a"),
17626        json!({
17627            "first.rs": sample_text_1,
17628            "second.rs": sample_text_2,
17629            "third.rs": sample_text_3,
17630        }),
17631    )
17632    .await;
17633    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17634    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17635    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17636    let worktree = project.update(cx, |project, cx| {
17637        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17638        assert_eq!(worktrees.len(), 1);
17639        worktrees.pop().unwrap()
17640    });
17641    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17642
17643    let buffer_1 = project
17644        .update(cx, |project, cx| {
17645            project.open_buffer((worktree_id, "first.rs"), cx)
17646        })
17647        .await
17648        .unwrap();
17649    let buffer_2 = project
17650        .update(cx, |project, cx| {
17651            project.open_buffer((worktree_id, "second.rs"), cx)
17652        })
17653        .await
17654        .unwrap();
17655    let buffer_3 = project
17656        .update(cx, |project, cx| {
17657            project.open_buffer((worktree_id, "third.rs"), cx)
17658        })
17659        .await
17660        .unwrap();
17661
17662    let multi_buffer = cx.new(|cx| {
17663        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17664        multi_buffer.push_excerpts(
17665            buffer_1.clone(),
17666            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17667            cx,
17668        );
17669        multi_buffer.push_excerpts(
17670            buffer_2.clone(),
17671            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17672            cx,
17673        );
17674        multi_buffer.push_excerpts(
17675            buffer_3.clone(),
17676            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17677            cx,
17678        );
17679        multi_buffer
17680    });
17681
17682    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17683        Editor::new(
17684            EditorMode::full(),
17685            multi_buffer,
17686            Some(project.clone()),
17687            window,
17688            cx,
17689        )
17690    });
17691
17692    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17693    assert_eq!(
17694        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17695        full_text,
17696    );
17697
17698    multi_buffer_editor.update(cx, |editor, cx| {
17699        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17700    });
17701    assert_eq!(
17702        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17703        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17704        "After folding the first buffer, its text should not be displayed"
17705    );
17706
17707    multi_buffer_editor.update(cx, |editor, cx| {
17708        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17709    });
17710
17711    assert_eq!(
17712        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17713        "\n\n\n\n\n\n7777\n8888\n9999",
17714        "After folding the second buffer, its text should not be displayed"
17715    );
17716
17717    multi_buffer_editor.update(cx, |editor, cx| {
17718        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17719    });
17720    assert_eq!(
17721        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17722        "\n\n\n\n\n",
17723        "After folding the third buffer, its text should not be displayed"
17724    );
17725
17726    multi_buffer_editor.update(cx, |editor, cx| {
17727        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17728    });
17729    assert_eq!(
17730        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17731        "\n\n\n\n4444\n5555\n6666\n\n",
17732        "After unfolding the second buffer, its text should be displayed"
17733    );
17734
17735    multi_buffer_editor.update(cx, |editor, cx| {
17736        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17737    });
17738    assert_eq!(
17739        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17740        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17741        "After unfolding the first buffer, its text should be displayed"
17742    );
17743
17744    multi_buffer_editor.update(cx, |editor, cx| {
17745        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17746    });
17747    assert_eq!(
17748        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17749        full_text,
17750        "After unfolding all buffers, all original text should be displayed"
17751    );
17752}
17753
17754#[gpui::test]
17755async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17756    init_test(cx, |_| {});
17757
17758    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17759
17760    let fs = FakeFs::new(cx.executor());
17761    fs.insert_tree(
17762        path!("/a"),
17763        json!({
17764            "main.rs": sample_text,
17765        }),
17766    )
17767    .await;
17768    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17769    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17770    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17771    let worktree = project.update(cx, |project, cx| {
17772        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17773        assert_eq!(worktrees.len(), 1);
17774        worktrees.pop().unwrap()
17775    });
17776    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17777
17778    let buffer_1 = project
17779        .update(cx, |project, cx| {
17780            project.open_buffer((worktree_id, "main.rs"), cx)
17781        })
17782        .await
17783        .unwrap();
17784
17785    let multi_buffer = cx.new(|cx| {
17786        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17787        multi_buffer.push_excerpts(
17788            buffer_1.clone(),
17789            [ExcerptRange::new(
17790                Point::new(0, 0)
17791                    ..Point::new(
17792                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17793                        0,
17794                    ),
17795            )],
17796            cx,
17797        );
17798        multi_buffer
17799    });
17800    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17801        Editor::new(
17802            EditorMode::full(),
17803            multi_buffer,
17804            Some(project.clone()),
17805            window,
17806            cx,
17807        )
17808    });
17809
17810    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17811    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17812        enum TestHighlight {}
17813        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17814        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17815        editor.highlight_text::<TestHighlight>(
17816            vec![highlight_range.clone()],
17817            HighlightStyle::color(Hsla::green()),
17818            cx,
17819        );
17820        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17821    });
17822
17823    let full_text = format!("\n\n{sample_text}");
17824    assert_eq!(
17825        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17826        full_text,
17827    );
17828}
17829
17830#[gpui::test]
17831async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17832    init_test(cx, |_| {});
17833    cx.update(|cx| {
17834        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17835            "keymaps/default-linux.json",
17836            cx,
17837        )
17838        .unwrap();
17839        cx.bind_keys(default_key_bindings);
17840    });
17841
17842    let (editor, cx) = cx.add_window_view(|window, cx| {
17843        let multi_buffer = MultiBuffer::build_multi(
17844            [
17845                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17846                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17847                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17848                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17849            ],
17850            cx,
17851        );
17852        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17853
17854        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17855        // fold all but the second buffer, so that we test navigating between two
17856        // adjacent folded buffers, as well as folded buffers at the start and
17857        // end the multibuffer
17858        editor.fold_buffer(buffer_ids[0], cx);
17859        editor.fold_buffer(buffer_ids[2], cx);
17860        editor.fold_buffer(buffer_ids[3], cx);
17861
17862        editor
17863    });
17864    cx.simulate_resize(size(px(1000.), px(1000.)));
17865
17866    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17867    cx.assert_excerpts_with_selections(indoc! {"
17868        [EXCERPT]
17869        ˇ[FOLDED]
17870        [EXCERPT]
17871        a1
17872        b1
17873        [EXCERPT]
17874        [FOLDED]
17875        [EXCERPT]
17876        [FOLDED]
17877        "
17878    });
17879    cx.simulate_keystroke("down");
17880    cx.assert_excerpts_with_selections(indoc! {"
17881        [EXCERPT]
17882        [FOLDED]
17883        [EXCERPT]
17884        ˇa1
17885        b1
17886        [EXCERPT]
17887        [FOLDED]
17888        [EXCERPT]
17889        [FOLDED]
17890        "
17891    });
17892    cx.simulate_keystroke("down");
17893    cx.assert_excerpts_with_selections(indoc! {"
17894        [EXCERPT]
17895        [FOLDED]
17896        [EXCERPT]
17897        a1
17898        ˇb1
17899        [EXCERPT]
17900        [FOLDED]
17901        [EXCERPT]
17902        [FOLDED]
17903        "
17904    });
17905    cx.simulate_keystroke("down");
17906    cx.assert_excerpts_with_selections(indoc! {"
17907        [EXCERPT]
17908        [FOLDED]
17909        [EXCERPT]
17910        a1
17911        b1
17912        ˇ[EXCERPT]
17913        [FOLDED]
17914        [EXCERPT]
17915        [FOLDED]
17916        "
17917    });
17918    cx.simulate_keystroke("down");
17919    cx.assert_excerpts_with_selections(indoc! {"
17920        [EXCERPT]
17921        [FOLDED]
17922        [EXCERPT]
17923        a1
17924        b1
17925        [EXCERPT]
17926        ˇ[FOLDED]
17927        [EXCERPT]
17928        [FOLDED]
17929        "
17930    });
17931    for _ in 0..5 {
17932        cx.simulate_keystroke("down");
17933        cx.assert_excerpts_with_selections(indoc! {"
17934            [EXCERPT]
17935            [FOLDED]
17936            [EXCERPT]
17937            a1
17938            b1
17939            [EXCERPT]
17940            [FOLDED]
17941            [EXCERPT]
17942            ˇ[FOLDED]
17943            "
17944        });
17945    }
17946
17947    cx.simulate_keystroke("up");
17948    cx.assert_excerpts_with_selections(indoc! {"
17949        [EXCERPT]
17950        [FOLDED]
17951        [EXCERPT]
17952        a1
17953        b1
17954        [EXCERPT]
17955        ˇ[FOLDED]
17956        [EXCERPT]
17957        [FOLDED]
17958        "
17959    });
17960    cx.simulate_keystroke("up");
17961    cx.assert_excerpts_with_selections(indoc! {"
17962        [EXCERPT]
17963        [FOLDED]
17964        [EXCERPT]
17965        a1
17966        b1
17967        ˇ[EXCERPT]
17968        [FOLDED]
17969        [EXCERPT]
17970        [FOLDED]
17971        "
17972    });
17973    cx.simulate_keystroke("up");
17974    cx.assert_excerpts_with_selections(indoc! {"
17975        [EXCERPT]
17976        [FOLDED]
17977        [EXCERPT]
17978        a1
17979        ˇb1
17980        [EXCERPT]
17981        [FOLDED]
17982        [EXCERPT]
17983        [FOLDED]
17984        "
17985    });
17986    cx.simulate_keystroke("up");
17987    cx.assert_excerpts_with_selections(indoc! {"
17988        [EXCERPT]
17989        [FOLDED]
17990        [EXCERPT]
17991        ˇa1
17992        b1
17993        [EXCERPT]
17994        [FOLDED]
17995        [EXCERPT]
17996        [FOLDED]
17997        "
17998    });
17999    for _ in 0..5 {
18000        cx.simulate_keystroke("up");
18001        cx.assert_excerpts_with_selections(indoc! {"
18002            [EXCERPT]
18003            ˇ[FOLDED]
18004            [EXCERPT]
18005            a1
18006            b1
18007            [EXCERPT]
18008            [FOLDED]
18009            [EXCERPT]
18010            [FOLDED]
18011            "
18012        });
18013    }
18014}
18015
18016#[gpui::test]
18017async fn test_inline_completion_text(cx: &mut TestAppContext) {
18018    init_test(cx, |_| {});
18019
18020    // Simple insertion
18021    assert_highlighted_edits(
18022        "Hello, world!",
18023        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18024        true,
18025        cx,
18026        |highlighted_edits, cx| {
18027            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18028            assert_eq!(highlighted_edits.highlights.len(), 1);
18029            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18030            assert_eq!(
18031                highlighted_edits.highlights[0].1.background_color,
18032                Some(cx.theme().status().created_background)
18033            );
18034        },
18035    )
18036    .await;
18037
18038    // Replacement
18039    assert_highlighted_edits(
18040        "This is a test.",
18041        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18042        false,
18043        cx,
18044        |highlighted_edits, cx| {
18045            assert_eq!(highlighted_edits.text, "That is a test.");
18046            assert_eq!(highlighted_edits.highlights.len(), 1);
18047            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18048            assert_eq!(
18049                highlighted_edits.highlights[0].1.background_color,
18050                Some(cx.theme().status().created_background)
18051            );
18052        },
18053    )
18054    .await;
18055
18056    // Multiple edits
18057    assert_highlighted_edits(
18058        "Hello, world!",
18059        vec![
18060            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18061            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18062        ],
18063        false,
18064        cx,
18065        |highlighted_edits, cx| {
18066            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18067            assert_eq!(highlighted_edits.highlights.len(), 2);
18068            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18069            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18070            assert_eq!(
18071                highlighted_edits.highlights[0].1.background_color,
18072                Some(cx.theme().status().created_background)
18073            );
18074            assert_eq!(
18075                highlighted_edits.highlights[1].1.background_color,
18076                Some(cx.theme().status().created_background)
18077            );
18078        },
18079    )
18080    .await;
18081
18082    // Multiple lines with edits
18083    assert_highlighted_edits(
18084        "First line\nSecond line\nThird line\nFourth line",
18085        vec![
18086            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18087            (
18088                Point::new(2, 0)..Point::new(2, 10),
18089                "New third line".to_string(),
18090            ),
18091            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18092        ],
18093        false,
18094        cx,
18095        |highlighted_edits, cx| {
18096            assert_eq!(
18097                highlighted_edits.text,
18098                "Second modified\nNew third line\nFourth updated line"
18099            );
18100            assert_eq!(highlighted_edits.highlights.len(), 3);
18101            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18102            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18103            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18104            for highlight in &highlighted_edits.highlights {
18105                assert_eq!(
18106                    highlight.1.background_color,
18107                    Some(cx.theme().status().created_background)
18108                );
18109            }
18110        },
18111    )
18112    .await;
18113}
18114
18115#[gpui::test]
18116async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18117    init_test(cx, |_| {});
18118
18119    // Deletion
18120    assert_highlighted_edits(
18121        "Hello, world!",
18122        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18123        true,
18124        cx,
18125        |highlighted_edits, cx| {
18126            assert_eq!(highlighted_edits.text, "Hello, world!");
18127            assert_eq!(highlighted_edits.highlights.len(), 1);
18128            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18129            assert_eq!(
18130                highlighted_edits.highlights[0].1.background_color,
18131                Some(cx.theme().status().deleted_background)
18132            );
18133        },
18134    )
18135    .await;
18136
18137    // Insertion
18138    assert_highlighted_edits(
18139        "Hello, world!",
18140        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18141        true,
18142        cx,
18143        |highlighted_edits, cx| {
18144            assert_eq!(highlighted_edits.highlights.len(), 1);
18145            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18146            assert_eq!(
18147                highlighted_edits.highlights[0].1.background_color,
18148                Some(cx.theme().status().created_background)
18149            );
18150        },
18151    )
18152    .await;
18153}
18154
18155async fn assert_highlighted_edits(
18156    text: &str,
18157    edits: Vec<(Range<Point>, String)>,
18158    include_deletions: bool,
18159    cx: &mut TestAppContext,
18160    assertion_fn: impl Fn(HighlightedText, &App),
18161) {
18162    let window = cx.add_window(|window, cx| {
18163        let buffer = MultiBuffer::build_simple(text, cx);
18164        Editor::new(EditorMode::full(), buffer, None, window, cx)
18165    });
18166    let cx = &mut VisualTestContext::from_window(*window, cx);
18167
18168    let (buffer, snapshot) = window
18169        .update(cx, |editor, _window, cx| {
18170            (
18171                editor.buffer().clone(),
18172                editor.buffer().read(cx).snapshot(cx),
18173            )
18174        })
18175        .unwrap();
18176
18177    let edits = edits
18178        .into_iter()
18179        .map(|(range, edit)| {
18180            (
18181                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18182                edit,
18183            )
18184        })
18185        .collect::<Vec<_>>();
18186
18187    let text_anchor_edits = edits
18188        .clone()
18189        .into_iter()
18190        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18191        .collect::<Vec<_>>();
18192
18193    let edit_preview = window
18194        .update(cx, |_, _window, cx| {
18195            buffer
18196                .read(cx)
18197                .as_singleton()
18198                .unwrap()
18199                .read(cx)
18200                .preview_edits(text_anchor_edits.into(), cx)
18201        })
18202        .unwrap()
18203        .await;
18204
18205    cx.update(|_window, cx| {
18206        let highlighted_edits = inline_completion_edit_text(
18207            &snapshot.as_singleton().unwrap().2,
18208            &edits,
18209            &edit_preview,
18210            include_deletions,
18211            cx,
18212        );
18213        assertion_fn(highlighted_edits, cx)
18214    });
18215}
18216
18217#[track_caller]
18218fn assert_breakpoint(
18219    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18220    path: &Arc<Path>,
18221    expected: Vec<(u32, Breakpoint)>,
18222) {
18223    if expected.len() == 0usize {
18224        assert!(!breakpoints.contains_key(path), "{}", path.display());
18225    } else {
18226        let mut breakpoint = breakpoints
18227            .get(path)
18228            .unwrap()
18229            .into_iter()
18230            .map(|breakpoint| {
18231                (
18232                    breakpoint.row,
18233                    Breakpoint {
18234                        message: breakpoint.message.clone(),
18235                        state: breakpoint.state,
18236                        condition: breakpoint.condition.clone(),
18237                        hit_condition: breakpoint.hit_condition.clone(),
18238                    },
18239                )
18240            })
18241            .collect::<Vec<_>>();
18242
18243        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18244
18245        assert_eq!(expected, breakpoint);
18246    }
18247}
18248
18249fn add_log_breakpoint_at_cursor(
18250    editor: &mut Editor,
18251    log_message: &str,
18252    window: &mut Window,
18253    cx: &mut Context<Editor>,
18254) {
18255    let (anchor, bp) = editor
18256        .breakpoints_at_cursors(window, cx)
18257        .first()
18258        .and_then(|(anchor, bp)| {
18259            if let Some(bp) = bp {
18260                Some((*anchor, bp.clone()))
18261            } else {
18262                None
18263            }
18264        })
18265        .unwrap_or_else(|| {
18266            let cursor_position: Point = editor.selections.newest(cx).head();
18267
18268            let breakpoint_position = editor
18269                .snapshot(window, cx)
18270                .display_snapshot
18271                .buffer_snapshot
18272                .anchor_before(Point::new(cursor_position.row, 0));
18273
18274            (breakpoint_position, Breakpoint::new_log(&log_message))
18275        });
18276
18277    editor.edit_breakpoint_at_anchor(
18278        anchor,
18279        bp,
18280        BreakpointEditAction::EditLogMessage(log_message.into()),
18281        cx,
18282    );
18283}
18284
18285#[gpui::test]
18286async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18287    init_test(cx, |_| {});
18288
18289    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18290    let fs = FakeFs::new(cx.executor());
18291    fs.insert_tree(
18292        path!("/a"),
18293        json!({
18294            "main.rs": sample_text,
18295        }),
18296    )
18297    .await;
18298    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18299    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18300    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18301
18302    let fs = FakeFs::new(cx.executor());
18303    fs.insert_tree(
18304        path!("/a"),
18305        json!({
18306            "main.rs": sample_text,
18307        }),
18308    )
18309    .await;
18310    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18311    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18312    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18313    let worktree_id = workspace
18314        .update(cx, |workspace, _window, cx| {
18315            workspace.project().update(cx, |project, cx| {
18316                project.worktrees(cx).next().unwrap().read(cx).id()
18317            })
18318        })
18319        .unwrap();
18320
18321    let buffer = project
18322        .update(cx, |project, cx| {
18323            project.open_buffer((worktree_id, "main.rs"), cx)
18324        })
18325        .await
18326        .unwrap();
18327
18328    let (editor, cx) = cx.add_window_view(|window, cx| {
18329        Editor::new(
18330            EditorMode::full(),
18331            MultiBuffer::build_from_buffer(buffer, cx),
18332            Some(project.clone()),
18333            window,
18334            cx,
18335        )
18336    });
18337
18338    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18339    let abs_path = project.read_with(cx, |project, cx| {
18340        project
18341            .absolute_path(&project_path, cx)
18342            .map(|path_buf| Arc::from(path_buf.to_owned()))
18343            .unwrap()
18344    });
18345
18346    // assert we can add breakpoint on the first line
18347    editor.update_in(cx, |editor, window, cx| {
18348        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18349        editor.move_to_end(&MoveToEnd, window, cx);
18350        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18351    });
18352
18353    let breakpoints = editor.update(cx, |editor, cx| {
18354        editor
18355            .breakpoint_store()
18356            .as_ref()
18357            .unwrap()
18358            .read(cx)
18359            .all_breakpoints(cx)
18360            .clone()
18361    });
18362
18363    assert_eq!(1, breakpoints.len());
18364    assert_breakpoint(
18365        &breakpoints,
18366        &abs_path,
18367        vec![
18368            (0, Breakpoint::new_standard()),
18369            (3, Breakpoint::new_standard()),
18370        ],
18371    );
18372
18373    editor.update_in(cx, |editor, window, cx| {
18374        editor.move_to_beginning(&MoveToBeginning, window, cx);
18375        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18376    });
18377
18378    let breakpoints = editor.update(cx, |editor, cx| {
18379        editor
18380            .breakpoint_store()
18381            .as_ref()
18382            .unwrap()
18383            .read(cx)
18384            .all_breakpoints(cx)
18385            .clone()
18386    });
18387
18388    assert_eq!(1, breakpoints.len());
18389    assert_breakpoint(
18390        &breakpoints,
18391        &abs_path,
18392        vec![(3, Breakpoint::new_standard())],
18393    );
18394
18395    editor.update_in(cx, |editor, window, cx| {
18396        editor.move_to_end(&MoveToEnd, window, cx);
18397        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18398    });
18399
18400    let breakpoints = editor.update(cx, |editor, cx| {
18401        editor
18402            .breakpoint_store()
18403            .as_ref()
18404            .unwrap()
18405            .read(cx)
18406            .all_breakpoints(cx)
18407            .clone()
18408    });
18409
18410    assert_eq!(0, breakpoints.len());
18411    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18412}
18413
18414#[gpui::test]
18415async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18416    init_test(cx, |_| {});
18417
18418    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18419
18420    let fs = FakeFs::new(cx.executor());
18421    fs.insert_tree(
18422        path!("/a"),
18423        json!({
18424            "main.rs": sample_text,
18425        }),
18426    )
18427    .await;
18428    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18429    let (workspace, cx) =
18430        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18431
18432    let worktree_id = workspace.update(cx, |workspace, cx| {
18433        workspace.project().update(cx, |project, cx| {
18434            project.worktrees(cx).next().unwrap().read(cx).id()
18435        })
18436    });
18437
18438    let buffer = project
18439        .update(cx, |project, cx| {
18440            project.open_buffer((worktree_id, "main.rs"), cx)
18441        })
18442        .await
18443        .unwrap();
18444
18445    let (editor, cx) = cx.add_window_view(|window, cx| {
18446        Editor::new(
18447            EditorMode::full(),
18448            MultiBuffer::build_from_buffer(buffer, cx),
18449            Some(project.clone()),
18450            window,
18451            cx,
18452        )
18453    });
18454
18455    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18456    let abs_path = project.read_with(cx, |project, cx| {
18457        project
18458            .absolute_path(&project_path, cx)
18459            .map(|path_buf| Arc::from(path_buf.to_owned()))
18460            .unwrap()
18461    });
18462
18463    editor.update_in(cx, |editor, window, cx| {
18464        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18465    });
18466
18467    let breakpoints = editor.update(cx, |editor, cx| {
18468        editor
18469            .breakpoint_store()
18470            .as_ref()
18471            .unwrap()
18472            .read(cx)
18473            .all_breakpoints(cx)
18474            .clone()
18475    });
18476
18477    assert_breakpoint(
18478        &breakpoints,
18479        &abs_path,
18480        vec![(0, Breakpoint::new_log("hello world"))],
18481    );
18482
18483    // Removing a log message from a log breakpoint should remove it
18484    editor.update_in(cx, |editor, window, cx| {
18485        add_log_breakpoint_at_cursor(editor, "", window, cx);
18486    });
18487
18488    let breakpoints = editor.update(cx, |editor, cx| {
18489        editor
18490            .breakpoint_store()
18491            .as_ref()
18492            .unwrap()
18493            .read(cx)
18494            .all_breakpoints(cx)
18495            .clone()
18496    });
18497
18498    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18499
18500    editor.update_in(cx, |editor, window, cx| {
18501        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18502        editor.move_to_end(&MoveToEnd, window, cx);
18503        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18504        // Not adding a log message to a standard breakpoint shouldn't remove it
18505        add_log_breakpoint_at_cursor(editor, "", window, cx);
18506    });
18507
18508    let breakpoints = editor.update(cx, |editor, cx| {
18509        editor
18510            .breakpoint_store()
18511            .as_ref()
18512            .unwrap()
18513            .read(cx)
18514            .all_breakpoints(cx)
18515            .clone()
18516    });
18517
18518    assert_breakpoint(
18519        &breakpoints,
18520        &abs_path,
18521        vec![
18522            (0, Breakpoint::new_standard()),
18523            (3, Breakpoint::new_standard()),
18524        ],
18525    );
18526
18527    editor.update_in(cx, |editor, window, cx| {
18528        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18529    });
18530
18531    let breakpoints = editor.update(cx, |editor, cx| {
18532        editor
18533            .breakpoint_store()
18534            .as_ref()
18535            .unwrap()
18536            .read(cx)
18537            .all_breakpoints(cx)
18538            .clone()
18539    });
18540
18541    assert_breakpoint(
18542        &breakpoints,
18543        &abs_path,
18544        vec![
18545            (0, Breakpoint::new_standard()),
18546            (3, Breakpoint::new_log("hello world")),
18547        ],
18548    );
18549
18550    editor.update_in(cx, |editor, window, cx| {
18551        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18552    });
18553
18554    let breakpoints = editor.update(cx, |editor, cx| {
18555        editor
18556            .breakpoint_store()
18557            .as_ref()
18558            .unwrap()
18559            .read(cx)
18560            .all_breakpoints(cx)
18561            .clone()
18562    });
18563
18564    assert_breakpoint(
18565        &breakpoints,
18566        &abs_path,
18567        vec![
18568            (0, Breakpoint::new_standard()),
18569            (3, Breakpoint::new_log("hello Earth!!")),
18570        ],
18571    );
18572}
18573
18574/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18575/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18576/// or when breakpoints were placed out of order. This tests for a regression too
18577#[gpui::test]
18578async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18579    init_test(cx, |_| {});
18580
18581    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18582    let fs = FakeFs::new(cx.executor());
18583    fs.insert_tree(
18584        path!("/a"),
18585        json!({
18586            "main.rs": sample_text,
18587        }),
18588    )
18589    .await;
18590    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18591    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18592    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18593
18594    let fs = FakeFs::new(cx.executor());
18595    fs.insert_tree(
18596        path!("/a"),
18597        json!({
18598            "main.rs": sample_text,
18599        }),
18600    )
18601    .await;
18602    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18603    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18604    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18605    let worktree_id = workspace
18606        .update(cx, |workspace, _window, cx| {
18607            workspace.project().update(cx, |project, cx| {
18608                project.worktrees(cx).next().unwrap().read(cx).id()
18609            })
18610        })
18611        .unwrap();
18612
18613    let buffer = project
18614        .update(cx, |project, cx| {
18615            project.open_buffer((worktree_id, "main.rs"), cx)
18616        })
18617        .await
18618        .unwrap();
18619
18620    let (editor, cx) = cx.add_window_view(|window, cx| {
18621        Editor::new(
18622            EditorMode::full(),
18623            MultiBuffer::build_from_buffer(buffer, cx),
18624            Some(project.clone()),
18625            window,
18626            cx,
18627        )
18628    });
18629
18630    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18631    let abs_path = project.read_with(cx, |project, cx| {
18632        project
18633            .absolute_path(&project_path, cx)
18634            .map(|path_buf| Arc::from(path_buf.to_owned()))
18635            .unwrap()
18636    });
18637
18638    // assert we can add breakpoint on the first line
18639    editor.update_in(cx, |editor, window, cx| {
18640        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18641        editor.move_to_end(&MoveToEnd, window, cx);
18642        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18643        editor.move_up(&MoveUp, window, cx);
18644        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18645    });
18646
18647    let breakpoints = editor.update(cx, |editor, cx| {
18648        editor
18649            .breakpoint_store()
18650            .as_ref()
18651            .unwrap()
18652            .read(cx)
18653            .all_breakpoints(cx)
18654            .clone()
18655    });
18656
18657    assert_eq!(1, breakpoints.len());
18658    assert_breakpoint(
18659        &breakpoints,
18660        &abs_path,
18661        vec![
18662            (0, Breakpoint::new_standard()),
18663            (2, Breakpoint::new_standard()),
18664            (3, Breakpoint::new_standard()),
18665        ],
18666    );
18667
18668    editor.update_in(cx, |editor, window, cx| {
18669        editor.move_to_beginning(&MoveToBeginning, window, cx);
18670        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18671        editor.move_to_end(&MoveToEnd, window, cx);
18672        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18673        // Disabling a breakpoint that doesn't exist should do nothing
18674        editor.move_up(&MoveUp, window, cx);
18675        editor.move_up(&MoveUp, window, cx);
18676        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18677    });
18678
18679    let breakpoints = editor.update(cx, |editor, cx| {
18680        editor
18681            .breakpoint_store()
18682            .as_ref()
18683            .unwrap()
18684            .read(cx)
18685            .all_breakpoints(cx)
18686            .clone()
18687    });
18688
18689    let disable_breakpoint = {
18690        let mut bp = Breakpoint::new_standard();
18691        bp.state = BreakpointState::Disabled;
18692        bp
18693    };
18694
18695    assert_eq!(1, breakpoints.len());
18696    assert_breakpoint(
18697        &breakpoints,
18698        &abs_path,
18699        vec![
18700            (0, disable_breakpoint.clone()),
18701            (2, Breakpoint::new_standard()),
18702            (3, disable_breakpoint.clone()),
18703        ],
18704    );
18705
18706    editor.update_in(cx, |editor, window, cx| {
18707        editor.move_to_beginning(&MoveToBeginning, window, cx);
18708        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18709        editor.move_to_end(&MoveToEnd, window, cx);
18710        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18711        editor.move_up(&MoveUp, window, cx);
18712        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18713    });
18714
18715    let breakpoints = editor.update(cx, |editor, cx| {
18716        editor
18717            .breakpoint_store()
18718            .as_ref()
18719            .unwrap()
18720            .read(cx)
18721            .all_breakpoints(cx)
18722            .clone()
18723    });
18724
18725    assert_eq!(1, breakpoints.len());
18726    assert_breakpoint(
18727        &breakpoints,
18728        &abs_path,
18729        vec![
18730            (0, Breakpoint::new_standard()),
18731            (2, disable_breakpoint),
18732            (3, Breakpoint::new_standard()),
18733        ],
18734    );
18735}
18736
18737#[gpui::test]
18738async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18739    init_test(cx, |_| {});
18740    let capabilities = lsp::ServerCapabilities {
18741        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18742            prepare_provider: Some(true),
18743            work_done_progress_options: Default::default(),
18744        })),
18745        ..Default::default()
18746    };
18747    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18748
18749    cx.set_state(indoc! {"
18750        struct Fˇoo {}
18751    "});
18752
18753    cx.update_editor(|editor, _, cx| {
18754        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18755        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18756        editor.highlight_background::<DocumentHighlightRead>(
18757            &[highlight_range],
18758            |c| c.editor_document_highlight_read_background,
18759            cx,
18760        );
18761    });
18762
18763    let mut prepare_rename_handler = cx
18764        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18765            move |_, _, _| async move {
18766                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18767                    start: lsp::Position {
18768                        line: 0,
18769                        character: 7,
18770                    },
18771                    end: lsp::Position {
18772                        line: 0,
18773                        character: 10,
18774                    },
18775                })))
18776            },
18777        );
18778    let prepare_rename_task = cx
18779        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18780        .expect("Prepare rename was not started");
18781    prepare_rename_handler.next().await.unwrap();
18782    prepare_rename_task.await.expect("Prepare rename failed");
18783
18784    let mut rename_handler =
18785        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18786            let edit = lsp::TextEdit {
18787                range: lsp::Range {
18788                    start: lsp::Position {
18789                        line: 0,
18790                        character: 7,
18791                    },
18792                    end: lsp::Position {
18793                        line: 0,
18794                        character: 10,
18795                    },
18796                },
18797                new_text: "FooRenamed".to_string(),
18798            };
18799            Ok(Some(lsp::WorkspaceEdit::new(
18800                // Specify the same edit twice
18801                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18802            )))
18803        });
18804    let rename_task = cx
18805        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18806        .expect("Confirm rename was not started");
18807    rename_handler.next().await.unwrap();
18808    rename_task.await.expect("Confirm rename failed");
18809    cx.run_until_parked();
18810
18811    // Despite two edits, only one is actually applied as those are identical
18812    cx.assert_editor_state(indoc! {"
18813        struct FooRenamedˇ {}
18814    "});
18815}
18816
18817#[gpui::test]
18818async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18819    init_test(cx, |_| {});
18820    // These capabilities indicate that the server does not support prepare rename.
18821    let capabilities = lsp::ServerCapabilities {
18822        rename_provider: Some(lsp::OneOf::Left(true)),
18823        ..Default::default()
18824    };
18825    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18826
18827    cx.set_state(indoc! {"
18828        struct Fˇoo {}
18829    "});
18830
18831    cx.update_editor(|editor, _window, cx| {
18832        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18833        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18834        editor.highlight_background::<DocumentHighlightRead>(
18835            &[highlight_range],
18836            |c| c.editor_document_highlight_read_background,
18837            cx,
18838        );
18839    });
18840
18841    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18842        .expect("Prepare rename was not started")
18843        .await
18844        .expect("Prepare rename failed");
18845
18846    let mut rename_handler =
18847        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18848            let edit = lsp::TextEdit {
18849                range: lsp::Range {
18850                    start: lsp::Position {
18851                        line: 0,
18852                        character: 7,
18853                    },
18854                    end: lsp::Position {
18855                        line: 0,
18856                        character: 10,
18857                    },
18858                },
18859                new_text: "FooRenamed".to_string(),
18860            };
18861            Ok(Some(lsp::WorkspaceEdit::new(
18862                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18863            )))
18864        });
18865    let rename_task = cx
18866        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18867        .expect("Confirm rename was not started");
18868    rename_handler.next().await.unwrap();
18869    rename_task.await.expect("Confirm rename failed");
18870    cx.run_until_parked();
18871
18872    // Correct range is renamed, as `surrounding_word` is used to find it.
18873    cx.assert_editor_state(indoc! {"
18874        struct FooRenamedˇ {}
18875    "});
18876}
18877
18878#[gpui::test]
18879async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18880    init_test(cx, |_| {});
18881    let mut cx = EditorTestContext::new(cx).await;
18882
18883    let language = Arc::new(
18884        Language::new(
18885            LanguageConfig::default(),
18886            Some(tree_sitter_html::LANGUAGE.into()),
18887        )
18888        .with_brackets_query(
18889            r#"
18890            ("<" @open "/>" @close)
18891            ("</" @open ">" @close)
18892            ("<" @open ">" @close)
18893            ("\"" @open "\"" @close)
18894            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18895        "#,
18896        )
18897        .unwrap(),
18898    );
18899    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18900
18901    cx.set_state(indoc! {"
18902        <span>ˇ</span>
18903    "});
18904    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18905    cx.assert_editor_state(indoc! {"
18906        <span>
18907        ˇ
18908        </span>
18909    "});
18910
18911    cx.set_state(indoc! {"
18912        <span><span></span>ˇ</span>
18913    "});
18914    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18915    cx.assert_editor_state(indoc! {"
18916        <span><span></span>
18917        ˇ</span>
18918    "});
18919
18920    cx.set_state(indoc! {"
18921        <span>ˇ
18922        </span>
18923    "});
18924    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18925    cx.assert_editor_state(indoc! {"
18926        <span>
18927        ˇ
18928        </span>
18929    "});
18930}
18931
18932#[gpui::test(iterations = 10)]
18933async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18934    init_test(cx, |_| {});
18935
18936    let fs = FakeFs::new(cx.executor());
18937    fs.insert_tree(
18938        path!("/dir"),
18939        json!({
18940            "a.ts": "a",
18941        }),
18942    )
18943    .await;
18944
18945    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18946    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18947    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18948
18949    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18950    language_registry.add(Arc::new(Language::new(
18951        LanguageConfig {
18952            name: "TypeScript".into(),
18953            matcher: LanguageMatcher {
18954                path_suffixes: vec!["ts".to_string()],
18955                ..Default::default()
18956            },
18957            ..Default::default()
18958        },
18959        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18960    )));
18961    let mut fake_language_servers = language_registry.register_fake_lsp(
18962        "TypeScript",
18963        FakeLspAdapter {
18964            capabilities: lsp::ServerCapabilities {
18965                code_lens_provider: Some(lsp::CodeLensOptions {
18966                    resolve_provider: Some(true),
18967                }),
18968                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18969                    commands: vec!["_the/command".to_string()],
18970                    ..lsp::ExecuteCommandOptions::default()
18971                }),
18972                ..lsp::ServerCapabilities::default()
18973            },
18974            ..FakeLspAdapter::default()
18975        },
18976    );
18977
18978    let (buffer, _handle) = project
18979        .update(cx, |p, cx| {
18980            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18981        })
18982        .await
18983        .unwrap();
18984    cx.executor().run_until_parked();
18985
18986    let fake_server = fake_language_servers.next().await.unwrap();
18987
18988    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18989    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18990    drop(buffer_snapshot);
18991    let actions = cx
18992        .update_window(*workspace, |_, window, cx| {
18993            project.code_actions(&buffer, anchor..anchor, window, cx)
18994        })
18995        .unwrap();
18996
18997    fake_server
18998        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18999            Ok(Some(vec![
19000                lsp::CodeLens {
19001                    range: lsp::Range::default(),
19002                    command: Some(lsp::Command {
19003                        title: "Code lens command".to_owned(),
19004                        command: "_the/command".to_owned(),
19005                        arguments: None,
19006                    }),
19007                    data: None,
19008                },
19009                lsp::CodeLens {
19010                    range: lsp::Range::default(),
19011                    command: Some(lsp::Command {
19012                        title: "Command not in capabilities".to_owned(),
19013                        command: "not in capabilities".to_owned(),
19014                        arguments: None,
19015                    }),
19016                    data: None,
19017                },
19018                lsp::CodeLens {
19019                    range: lsp::Range {
19020                        start: lsp::Position {
19021                            line: 1,
19022                            character: 1,
19023                        },
19024                        end: lsp::Position {
19025                            line: 1,
19026                            character: 1,
19027                        },
19028                    },
19029                    command: Some(lsp::Command {
19030                        title: "Command not in range".to_owned(),
19031                        command: "_the/command".to_owned(),
19032                        arguments: None,
19033                    }),
19034                    data: None,
19035                },
19036            ]))
19037        })
19038        .next()
19039        .await;
19040
19041    let actions = actions.await.unwrap();
19042    assert_eq!(
19043        actions.len(),
19044        1,
19045        "Should have only one valid action for the 0..0 range"
19046    );
19047    let action = actions[0].clone();
19048    let apply = project.update(cx, |project, cx| {
19049        project.apply_code_action(buffer.clone(), action, true, cx)
19050    });
19051
19052    // Resolving the code action does not populate its edits. In absence of
19053    // edits, we must execute the given command.
19054    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19055        |mut lens, _| async move {
19056            let lens_command = lens.command.as_mut().expect("should have a command");
19057            assert_eq!(lens_command.title, "Code lens command");
19058            lens_command.arguments = Some(vec![json!("the-argument")]);
19059            Ok(lens)
19060        },
19061    );
19062
19063    // While executing the command, the language server sends the editor
19064    // a `workspaceEdit` request.
19065    fake_server
19066        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19067            let fake = fake_server.clone();
19068            move |params, _| {
19069                assert_eq!(params.command, "_the/command");
19070                let fake = fake.clone();
19071                async move {
19072                    fake.server
19073                        .request::<lsp::request::ApplyWorkspaceEdit>(
19074                            lsp::ApplyWorkspaceEditParams {
19075                                label: None,
19076                                edit: lsp::WorkspaceEdit {
19077                                    changes: Some(
19078                                        [(
19079                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19080                                            vec![lsp::TextEdit {
19081                                                range: lsp::Range::new(
19082                                                    lsp::Position::new(0, 0),
19083                                                    lsp::Position::new(0, 0),
19084                                                ),
19085                                                new_text: "X".into(),
19086                                            }],
19087                                        )]
19088                                        .into_iter()
19089                                        .collect(),
19090                                    ),
19091                                    ..Default::default()
19092                                },
19093                            },
19094                        )
19095                        .await
19096                        .unwrap();
19097                    Ok(Some(json!(null)))
19098                }
19099            }
19100        })
19101        .next()
19102        .await;
19103
19104    // Applying the code lens command returns a project transaction containing the edits
19105    // sent by the language server in its `workspaceEdit` request.
19106    let transaction = apply.await.unwrap();
19107    assert!(transaction.0.contains_key(&buffer));
19108    buffer.update(cx, |buffer, cx| {
19109        assert_eq!(buffer.text(), "Xa");
19110        buffer.undo(cx);
19111        assert_eq!(buffer.text(), "a");
19112    });
19113}
19114
19115#[gpui::test]
19116async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19117    init_test(cx, |_| {});
19118
19119    let fs = FakeFs::new(cx.executor());
19120    let main_text = r#"fn main() {
19121println!("1");
19122println!("2");
19123println!("3");
19124println!("4");
19125println!("5");
19126}"#;
19127    let lib_text = "mod foo {}";
19128    fs.insert_tree(
19129        path!("/a"),
19130        json!({
19131            "lib.rs": lib_text,
19132            "main.rs": main_text,
19133        }),
19134    )
19135    .await;
19136
19137    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19138    let (workspace, cx) =
19139        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19140    let worktree_id = workspace.update(cx, |workspace, cx| {
19141        workspace.project().update(cx, |project, cx| {
19142            project.worktrees(cx).next().unwrap().read(cx).id()
19143        })
19144    });
19145
19146    let expected_ranges = vec![
19147        Point::new(0, 0)..Point::new(0, 0),
19148        Point::new(1, 0)..Point::new(1, 1),
19149        Point::new(2, 0)..Point::new(2, 2),
19150        Point::new(3, 0)..Point::new(3, 3),
19151    ];
19152
19153    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19154    let editor_1 = workspace
19155        .update_in(cx, |workspace, window, cx| {
19156            workspace.open_path(
19157                (worktree_id, "main.rs"),
19158                Some(pane_1.downgrade()),
19159                true,
19160                window,
19161                cx,
19162            )
19163        })
19164        .unwrap()
19165        .await
19166        .downcast::<Editor>()
19167        .unwrap();
19168    pane_1.update(cx, |pane, cx| {
19169        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19170        open_editor.update(cx, |editor, cx| {
19171            assert_eq!(
19172                editor.display_text(cx),
19173                main_text,
19174                "Original main.rs text on initial open",
19175            );
19176            assert_eq!(
19177                editor
19178                    .selections
19179                    .all::<Point>(cx)
19180                    .into_iter()
19181                    .map(|s| s.range())
19182                    .collect::<Vec<_>>(),
19183                vec![Point::zero()..Point::zero()],
19184                "Default selections on initial open",
19185            );
19186        })
19187    });
19188    editor_1.update_in(cx, |editor, window, cx| {
19189        editor.change_selections(None, window, cx, |s| {
19190            s.select_ranges(expected_ranges.clone());
19191        });
19192    });
19193
19194    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19195        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19196    });
19197    let editor_2 = workspace
19198        .update_in(cx, |workspace, window, cx| {
19199            workspace.open_path(
19200                (worktree_id, "main.rs"),
19201                Some(pane_2.downgrade()),
19202                true,
19203                window,
19204                cx,
19205            )
19206        })
19207        .unwrap()
19208        .await
19209        .downcast::<Editor>()
19210        .unwrap();
19211    pane_2.update(cx, |pane, cx| {
19212        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19213        open_editor.update(cx, |editor, cx| {
19214            assert_eq!(
19215                editor.display_text(cx),
19216                main_text,
19217                "Original main.rs text on initial open in another panel",
19218            );
19219            assert_eq!(
19220                editor
19221                    .selections
19222                    .all::<Point>(cx)
19223                    .into_iter()
19224                    .map(|s| s.range())
19225                    .collect::<Vec<_>>(),
19226                vec![Point::zero()..Point::zero()],
19227                "Default selections on initial open in another panel",
19228            );
19229        })
19230    });
19231
19232    editor_2.update_in(cx, |editor, window, cx| {
19233        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19234    });
19235
19236    let _other_editor_1 = workspace
19237        .update_in(cx, |workspace, window, cx| {
19238            workspace.open_path(
19239                (worktree_id, "lib.rs"),
19240                Some(pane_1.downgrade()),
19241                true,
19242                window,
19243                cx,
19244            )
19245        })
19246        .unwrap()
19247        .await
19248        .downcast::<Editor>()
19249        .unwrap();
19250    pane_1
19251        .update_in(cx, |pane, window, cx| {
19252            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19253                .unwrap()
19254        })
19255        .await
19256        .unwrap();
19257    drop(editor_1);
19258    pane_1.update(cx, |pane, cx| {
19259        pane.active_item()
19260            .unwrap()
19261            .downcast::<Editor>()
19262            .unwrap()
19263            .update(cx, |editor, cx| {
19264                assert_eq!(
19265                    editor.display_text(cx),
19266                    lib_text,
19267                    "Other file should be open and active",
19268                );
19269            });
19270        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19271    });
19272
19273    let _other_editor_2 = workspace
19274        .update_in(cx, |workspace, window, cx| {
19275            workspace.open_path(
19276                (worktree_id, "lib.rs"),
19277                Some(pane_2.downgrade()),
19278                true,
19279                window,
19280                cx,
19281            )
19282        })
19283        .unwrap()
19284        .await
19285        .downcast::<Editor>()
19286        .unwrap();
19287    pane_2
19288        .update_in(cx, |pane, window, cx| {
19289            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19290                .unwrap()
19291        })
19292        .await
19293        .unwrap();
19294    drop(editor_2);
19295    pane_2.update(cx, |pane, cx| {
19296        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19297        open_editor.update(cx, |editor, cx| {
19298            assert_eq!(
19299                editor.display_text(cx),
19300                lib_text,
19301                "Other file should be open and active in another panel too",
19302            );
19303        });
19304        assert_eq!(
19305            pane.items().count(),
19306            1,
19307            "No other editors should be open in another pane",
19308        );
19309    });
19310
19311    let _editor_1_reopened = workspace
19312        .update_in(cx, |workspace, window, cx| {
19313            workspace.open_path(
19314                (worktree_id, "main.rs"),
19315                Some(pane_1.downgrade()),
19316                true,
19317                window,
19318                cx,
19319            )
19320        })
19321        .unwrap()
19322        .await
19323        .downcast::<Editor>()
19324        .unwrap();
19325    let _editor_2_reopened = workspace
19326        .update_in(cx, |workspace, window, cx| {
19327            workspace.open_path(
19328                (worktree_id, "main.rs"),
19329                Some(pane_2.downgrade()),
19330                true,
19331                window,
19332                cx,
19333            )
19334        })
19335        .unwrap()
19336        .await
19337        .downcast::<Editor>()
19338        .unwrap();
19339    pane_1.update(cx, |pane, cx| {
19340        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19341        open_editor.update(cx, |editor, cx| {
19342            assert_eq!(
19343                editor.display_text(cx),
19344                main_text,
19345                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19346            );
19347            assert_eq!(
19348                editor
19349                    .selections
19350                    .all::<Point>(cx)
19351                    .into_iter()
19352                    .map(|s| s.range())
19353                    .collect::<Vec<_>>(),
19354                expected_ranges,
19355                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19356            );
19357        })
19358    });
19359    pane_2.update(cx, |pane, cx| {
19360        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19361        open_editor.update(cx, |editor, cx| {
19362            assert_eq!(
19363                editor.display_text(cx),
19364                r#"fn main() {
19365⋯rintln!("1");
19366⋯intln!("2");
19367⋯ntln!("3");
19368println!("4");
19369println!("5");
19370}"#,
19371                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19372            );
19373            assert_eq!(
19374                editor
19375                    .selections
19376                    .all::<Point>(cx)
19377                    .into_iter()
19378                    .map(|s| s.range())
19379                    .collect::<Vec<_>>(),
19380                vec![Point::zero()..Point::zero()],
19381                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19382            );
19383        })
19384    });
19385}
19386
19387#[gpui::test]
19388async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19389    init_test(cx, |_| {});
19390
19391    let fs = FakeFs::new(cx.executor());
19392    let main_text = r#"fn main() {
19393println!("1");
19394println!("2");
19395println!("3");
19396println!("4");
19397println!("5");
19398}"#;
19399    let lib_text = "mod foo {}";
19400    fs.insert_tree(
19401        path!("/a"),
19402        json!({
19403            "lib.rs": lib_text,
19404            "main.rs": main_text,
19405        }),
19406    )
19407    .await;
19408
19409    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19410    let (workspace, cx) =
19411        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19412    let worktree_id = workspace.update(cx, |workspace, cx| {
19413        workspace.project().update(cx, |project, cx| {
19414            project.worktrees(cx).next().unwrap().read(cx).id()
19415        })
19416    });
19417
19418    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19419    let editor = workspace
19420        .update_in(cx, |workspace, window, cx| {
19421            workspace.open_path(
19422                (worktree_id, "main.rs"),
19423                Some(pane.downgrade()),
19424                true,
19425                window,
19426                cx,
19427            )
19428        })
19429        .unwrap()
19430        .await
19431        .downcast::<Editor>()
19432        .unwrap();
19433    pane.update(cx, |pane, cx| {
19434        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19435        open_editor.update(cx, |editor, cx| {
19436            assert_eq!(
19437                editor.display_text(cx),
19438                main_text,
19439                "Original main.rs text on initial open",
19440            );
19441        })
19442    });
19443    editor.update_in(cx, |editor, window, cx| {
19444        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19445    });
19446
19447    cx.update_global(|store: &mut SettingsStore, cx| {
19448        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19449            s.restore_on_file_reopen = Some(false);
19450        });
19451    });
19452    editor.update_in(cx, |editor, window, cx| {
19453        editor.fold_ranges(
19454            vec![
19455                Point::new(1, 0)..Point::new(1, 1),
19456                Point::new(2, 0)..Point::new(2, 2),
19457                Point::new(3, 0)..Point::new(3, 3),
19458            ],
19459            false,
19460            window,
19461            cx,
19462        );
19463    });
19464    pane.update_in(cx, |pane, window, cx| {
19465        pane.close_all_items(&CloseAllItems::default(), window, cx)
19466            .unwrap()
19467    })
19468    .await
19469    .unwrap();
19470    pane.update(cx, |pane, _| {
19471        assert!(pane.active_item().is_none());
19472    });
19473    cx.update_global(|store: &mut SettingsStore, cx| {
19474        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19475            s.restore_on_file_reopen = Some(true);
19476        });
19477    });
19478
19479    let _editor_reopened = workspace
19480        .update_in(cx, |workspace, window, cx| {
19481            workspace.open_path(
19482                (worktree_id, "main.rs"),
19483                Some(pane.downgrade()),
19484                true,
19485                window,
19486                cx,
19487            )
19488        })
19489        .unwrap()
19490        .await
19491        .downcast::<Editor>()
19492        .unwrap();
19493    pane.update(cx, |pane, cx| {
19494        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19495        open_editor.update(cx, |editor, cx| {
19496            assert_eq!(
19497                editor.display_text(cx),
19498                main_text,
19499                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19500            );
19501        })
19502    });
19503}
19504
19505#[gpui::test]
19506async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19507    struct EmptyModalView {
19508        focus_handle: gpui::FocusHandle,
19509    }
19510    impl EventEmitter<DismissEvent> for EmptyModalView {}
19511    impl Render for EmptyModalView {
19512        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19513            div()
19514        }
19515    }
19516    impl Focusable for EmptyModalView {
19517        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19518            self.focus_handle.clone()
19519        }
19520    }
19521    impl workspace::ModalView for EmptyModalView {}
19522    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19523        EmptyModalView {
19524            focus_handle: cx.focus_handle(),
19525        }
19526    }
19527
19528    init_test(cx, |_| {});
19529
19530    let fs = FakeFs::new(cx.executor());
19531    let project = Project::test(fs, [], cx).await;
19532    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19533    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19534    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19535    let editor = cx.new_window_entity(|window, cx| {
19536        Editor::new(
19537            EditorMode::full(),
19538            buffer,
19539            Some(project.clone()),
19540            window,
19541            cx,
19542        )
19543    });
19544    workspace
19545        .update(cx, |workspace, window, cx| {
19546            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19547        })
19548        .unwrap();
19549    editor.update_in(cx, |editor, window, cx| {
19550        editor.open_context_menu(&OpenContextMenu, window, cx);
19551        assert!(editor.mouse_context_menu.is_some());
19552    });
19553    workspace
19554        .update(cx, |workspace, window, cx| {
19555            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19556        })
19557        .unwrap();
19558    cx.read(|cx| {
19559        assert!(editor.read(cx).mouse_context_menu.is_none());
19560    });
19561}
19562
19563#[gpui::test]
19564async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19565    init_test(cx, |_| {});
19566
19567    let fs = FakeFs::new(cx.executor());
19568    fs.insert_file(path!("/file.html"), Default::default())
19569        .await;
19570
19571    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19572
19573    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19574    let html_language = Arc::new(Language::new(
19575        LanguageConfig {
19576            name: "HTML".into(),
19577            matcher: LanguageMatcher {
19578                path_suffixes: vec!["html".to_string()],
19579                ..LanguageMatcher::default()
19580            },
19581            brackets: BracketPairConfig {
19582                pairs: vec![BracketPair {
19583                    start: "<".into(),
19584                    end: ">".into(),
19585                    close: true,
19586                    ..Default::default()
19587                }],
19588                ..Default::default()
19589            },
19590            ..Default::default()
19591        },
19592        Some(tree_sitter_html::LANGUAGE.into()),
19593    ));
19594    language_registry.add(html_language);
19595    let mut fake_servers = language_registry.register_fake_lsp(
19596        "HTML",
19597        FakeLspAdapter {
19598            capabilities: lsp::ServerCapabilities {
19599                completion_provider: Some(lsp::CompletionOptions {
19600                    resolve_provider: Some(true),
19601                    ..Default::default()
19602                }),
19603                ..Default::default()
19604            },
19605            ..Default::default()
19606        },
19607    );
19608
19609    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19610    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19611
19612    let worktree_id = workspace
19613        .update(cx, |workspace, _window, cx| {
19614            workspace.project().update(cx, |project, cx| {
19615                project.worktrees(cx).next().unwrap().read(cx).id()
19616            })
19617        })
19618        .unwrap();
19619    project
19620        .update(cx, |project, cx| {
19621            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19622        })
19623        .await
19624        .unwrap();
19625    let editor = workspace
19626        .update(cx, |workspace, window, cx| {
19627            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19628        })
19629        .unwrap()
19630        .await
19631        .unwrap()
19632        .downcast::<Editor>()
19633        .unwrap();
19634
19635    let fake_server = fake_servers.next().await.unwrap();
19636    editor.update_in(cx, |editor, window, cx| {
19637        editor.set_text("<ad></ad>", window, cx);
19638        editor.change_selections(None, window, cx, |selections| {
19639            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19640        });
19641        let Some((buffer, _)) = editor
19642            .buffer
19643            .read(cx)
19644            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19645        else {
19646            panic!("Failed to get buffer for selection position");
19647        };
19648        let buffer = buffer.read(cx);
19649        let buffer_id = buffer.remote_id();
19650        let opening_range =
19651            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19652        let closing_range =
19653            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19654        let mut linked_ranges = HashMap::default();
19655        linked_ranges.insert(
19656            buffer_id,
19657            vec![(opening_range.clone(), vec![closing_range.clone()])],
19658        );
19659        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19660    });
19661    let mut completion_handle =
19662        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19663            Ok(Some(lsp::CompletionResponse::Array(vec![
19664                lsp::CompletionItem {
19665                    label: "head".to_string(),
19666                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19667                        lsp::InsertReplaceEdit {
19668                            new_text: "head".to_string(),
19669                            insert: lsp::Range::new(
19670                                lsp::Position::new(0, 1),
19671                                lsp::Position::new(0, 3),
19672                            ),
19673                            replace: lsp::Range::new(
19674                                lsp::Position::new(0, 1),
19675                                lsp::Position::new(0, 3),
19676                            ),
19677                        },
19678                    )),
19679                    ..Default::default()
19680                },
19681            ])))
19682        });
19683    editor.update_in(cx, |editor, window, cx| {
19684        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19685    });
19686    cx.run_until_parked();
19687    completion_handle.next().await.unwrap();
19688    editor.update(cx, |editor, _| {
19689        assert!(
19690            editor.context_menu_visible(),
19691            "Completion menu should be visible"
19692        );
19693    });
19694    editor.update_in(cx, |editor, window, cx| {
19695        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19696    });
19697    cx.executor().run_until_parked();
19698    editor.update(cx, |editor, cx| {
19699        assert_eq!(editor.text(cx), "<head></head>");
19700    });
19701}
19702
19703fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19704    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19705    point..point
19706}
19707
19708fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19709    let (text, ranges) = marked_text_ranges(marked_text, true);
19710    assert_eq!(editor.text(cx), text);
19711    assert_eq!(
19712        editor.selections.ranges(cx),
19713        ranges,
19714        "Assert selections are {}",
19715        marked_text
19716    );
19717}
19718
19719pub fn handle_signature_help_request(
19720    cx: &mut EditorLspTestContext,
19721    mocked_response: lsp::SignatureHelp,
19722) -> impl Future<Output = ()> + use<> {
19723    let mut request =
19724        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19725            let mocked_response = mocked_response.clone();
19726            async move { Ok(Some(mocked_response)) }
19727        });
19728
19729    async move {
19730        request.next().await;
19731    }
19732}
19733
19734/// Handle completion request passing a marked string specifying where the completion
19735/// should be triggered from using '|' character, what range should be replaced, and what completions
19736/// should be returned using '<' and '>' to delimit the range.
19737///
19738/// Also see `handle_completion_request_with_insert_and_replace`.
19739#[track_caller]
19740pub fn handle_completion_request(
19741    cx: &mut EditorLspTestContext,
19742    marked_string: &str,
19743    completions: Vec<&'static str>,
19744    counter: Arc<AtomicUsize>,
19745) -> impl Future<Output = ()> {
19746    let complete_from_marker: TextRangeMarker = '|'.into();
19747    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19748    let (_, mut marked_ranges) = marked_text_ranges_by(
19749        marked_string,
19750        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19751    );
19752
19753    let complete_from_position =
19754        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19755    let replace_range =
19756        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19757
19758    let mut request =
19759        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19760            let completions = completions.clone();
19761            counter.fetch_add(1, atomic::Ordering::Release);
19762            async move {
19763                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19764                assert_eq!(
19765                    params.text_document_position.position,
19766                    complete_from_position
19767                );
19768                Ok(Some(lsp::CompletionResponse::Array(
19769                    completions
19770                        .iter()
19771                        .map(|completion_text| lsp::CompletionItem {
19772                            label: completion_text.to_string(),
19773                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19774                                range: replace_range,
19775                                new_text: completion_text.to_string(),
19776                            })),
19777                            ..Default::default()
19778                        })
19779                        .collect(),
19780                )))
19781            }
19782        });
19783
19784    async move {
19785        request.next().await;
19786    }
19787}
19788
19789/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19790/// given instead, which also contains an `insert` range.
19791///
19792/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19793/// that is, `replace_range.start..cursor_pos`.
19794pub fn handle_completion_request_with_insert_and_replace(
19795    cx: &mut EditorLspTestContext,
19796    marked_string: &str,
19797    completions: Vec<&'static str>,
19798    counter: Arc<AtomicUsize>,
19799) -> impl Future<Output = ()> {
19800    let complete_from_marker: TextRangeMarker = '|'.into();
19801    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19802    let (_, mut marked_ranges) = marked_text_ranges_by(
19803        marked_string,
19804        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19805    );
19806
19807    let complete_from_position =
19808        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19809    let replace_range =
19810        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19811
19812    let mut request =
19813        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19814            let completions = completions.clone();
19815            counter.fetch_add(1, atomic::Ordering::Release);
19816            async move {
19817                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19818                assert_eq!(
19819                    params.text_document_position.position, complete_from_position,
19820                    "marker `|` position doesn't match",
19821                );
19822                Ok(Some(lsp::CompletionResponse::Array(
19823                    completions
19824                        .iter()
19825                        .map(|completion_text| lsp::CompletionItem {
19826                            label: completion_text.to_string(),
19827                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19828                                lsp::InsertReplaceEdit {
19829                                    insert: lsp::Range {
19830                                        start: replace_range.start,
19831                                        end: complete_from_position,
19832                                    },
19833                                    replace: replace_range,
19834                                    new_text: completion_text.to_string(),
19835                                },
19836                            )),
19837                            ..Default::default()
19838                        })
19839                        .collect(),
19840                )))
19841            }
19842        });
19843
19844    async move {
19845        request.next().await;
19846    }
19847}
19848
19849fn handle_resolve_completion_request(
19850    cx: &mut EditorLspTestContext,
19851    edits: Option<Vec<(&'static str, &'static str)>>,
19852) -> impl Future<Output = ()> {
19853    let edits = edits.map(|edits| {
19854        edits
19855            .iter()
19856            .map(|(marked_string, new_text)| {
19857                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19858                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19859                lsp::TextEdit::new(replace_range, new_text.to_string())
19860            })
19861            .collect::<Vec<_>>()
19862    });
19863
19864    let mut request =
19865        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19866            let edits = edits.clone();
19867            async move {
19868                Ok(lsp::CompletionItem {
19869                    additional_text_edits: edits,
19870                    ..Default::default()
19871                })
19872            }
19873        });
19874
19875    async move {
19876        request.next().await;
19877    }
19878}
19879
19880pub(crate) fn update_test_language_settings(
19881    cx: &mut TestAppContext,
19882    f: impl Fn(&mut AllLanguageSettingsContent),
19883) {
19884    cx.update(|cx| {
19885        SettingsStore::update_global(cx, |store, cx| {
19886            store.update_user_settings::<AllLanguageSettings>(cx, f);
19887        });
19888    });
19889}
19890
19891pub(crate) fn update_test_project_settings(
19892    cx: &mut TestAppContext,
19893    f: impl Fn(&mut ProjectSettings),
19894) {
19895    cx.update(|cx| {
19896        SettingsStore::update_global(cx, |store, cx| {
19897            store.update_user_settings::<ProjectSettings>(cx, f);
19898        });
19899    });
19900}
19901
19902pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19903    cx.update(|cx| {
19904        assets::Assets.load_test_fonts(cx);
19905        let store = SettingsStore::test(cx);
19906        cx.set_global(store);
19907        theme::init(theme::LoadThemes::JustBase, cx);
19908        release_channel::init(SemanticVersion::default(), cx);
19909        client::init_settings(cx);
19910        language::init(cx);
19911        Project::init_settings(cx);
19912        workspace::init_settings(cx);
19913        crate::init(cx);
19914    });
19915
19916    update_test_language_settings(cx, f);
19917}
19918
19919#[track_caller]
19920fn assert_hunk_revert(
19921    not_reverted_text_with_selections: &str,
19922    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19923    expected_reverted_text_with_selections: &str,
19924    base_text: &str,
19925    cx: &mut EditorLspTestContext,
19926) {
19927    cx.set_state(not_reverted_text_with_selections);
19928    cx.set_head_text(base_text);
19929    cx.executor().run_until_parked();
19930
19931    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19932        let snapshot = editor.snapshot(window, cx);
19933        let reverted_hunk_statuses = snapshot
19934            .buffer_snapshot
19935            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19936            .map(|hunk| hunk.status().kind)
19937            .collect::<Vec<_>>();
19938
19939        editor.git_restore(&Default::default(), window, cx);
19940        reverted_hunk_statuses
19941    });
19942    cx.executor().run_until_parked();
19943    cx.assert_editor_state(expected_reverted_text_with_selections);
19944    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19945}