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    // test when all cursors are not at suggested indent
 2875    // then simply move to their suggested indent location
 2876    cx.set_state(indoc! {"
 2877        const a: B = (
 2878            c(
 2879        ˇ
 2880        ˇ    )
 2881        );
 2882    "});
 2883    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2884    cx.assert_editor_state(indoc! {"
 2885        const a: B = (
 2886            c(
 2887                ˇ
 2888            ˇ)
 2889        );
 2890    "});
 2891
 2892    // test cursor already at suggested indent not moving when
 2893    // other cursors are yet to reach their suggested indents
 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    // test when all cursors are at suggested indent then tab is inserted
 2918    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2919    cx.assert_editor_state(indoc! {"
 2920            ˇ
 2921        const a: B = (
 2922            c(
 2923                d(
 2924                        ˇ
 2925                )
 2926                    ˇ
 2927                ˇ)
 2928        );
 2929    "});
 2930
 2931    // test when current indent is less than suggested indent,
 2932    // we adjust line to match suggested indent and move cursor to it
 2933    //
 2934    // when no other cursor is at word boundary, all of them should move
 2935    cx.set_state(indoc! {"
 2936        const a: B = (
 2937            c(
 2938                d(
 2939        ˇ
 2940        ˇ   )
 2941        ˇ   )
 2942        );
 2943    "});
 2944    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2945    cx.assert_editor_state(indoc! {"
 2946        const a: B = (
 2947            c(
 2948                d(
 2949                    ˇ
 2950                ˇ)
 2951            ˇ)
 2952        );
 2953    "});
 2954
 2955    // test when current indent is less than suggested indent,
 2956    // we adjust line to match suggested indent and move cursor to it
 2957    //
 2958    // when some other cursor is at word boundary, it should not move
 2959    cx.set_state(indoc! {"
 2960        const a: B = (
 2961            c(
 2962                d(
 2963        ˇ
 2964        ˇ   )
 2965           ˇ)
 2966        );
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        const a: B = (
 2971            c(
 2972                d(
 2973                    ˇ
 2974                ˇ)
 2975            ˇ)
 2976        );
 2977    "});
 2978
 2979    // test when current indent is more than suggested indent,
 2980    // we just move cursor to current indent instead of suggested indent
 2981    //
 2982    // when no other cursor is at word boundary, all of them should move
 2983    cx.set_state(indoc! {"
 2984        const a: B = (
 2985            c(
 2986                d(
 2987        ˇ
 2988        ˇ                )
 2989        ˇ   )
 2990        );
 2991    "});
 2992    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        const a: B = (
 2995            c(
 2996                d(
 2997                    ˇ
 2998                        ˇ)
 2999            ˇ)
 3000        );
 3001    "});
 3002    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3003    cx.assert_editor_state(indoc! {"
 3004        const a: B = (
 3005            c(
 3006                d(
 3007                        ˇ
 3008                            ˇ)
 3009                ˇ)
 3010        );
 3011    "});
 3012
 3013    // test when current indent is more than suggested indent,
 3014    // we just move cursor to current indent instead of suggested indent
 3015    //
 3016    // when some other cursor is at word boundary, it doesn't move
 3017    cx.set_state(indoc! {"
 3018        const a: B = (
 3019            c(
 3020                d(
 3021        ˇ
 3022        ˇ                )
 3023            ˇ)
 3024        );
 3025    "});
 3026    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3027    cx.assert_editor_state(indoc! {"
 3028        const a: B = (
 3029            c(
 3030                d(
 3031                    ˇ
 3032                        ˇ)
 3033            ˇ)
 3034        );
 3035    "});
 3036
 3037    // handle auto-indent when there are multiple cursors on the same line
 3038    cx.set_state(indoc! {"
 3039        const a: B = (
 3040            c(
 3041        ˇ    ˇ
 3042        ˇ    )
 3043        );
 3044    "});
 3045    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3046    cx.assert_editor_state(indoc! {"
 3047        const a: B = (
 3048            c(
 3049                ˇ
 3050            ˇ)
 3051        );
 3052    "});
 3053}
 3054
 3055#[gpui::test]
 3056async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3057    init_test(cx, |settings| {
 3058        settings.defaults.tab_size = NonZeroU32::new(3)
 3059    });
 3060
 3061    let mut cx = EditorTestContext::new(cx).await;
 3062    cx.set_state(indoc! {"
 3063         ˇ
 3064        \t ˇ
 3065        \t  ˇ
 3066        \t   ˇ
 3067         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3068    "});
 3069
 3070    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3071    cx.assert_editor_state(indoc! {"
 3072           ˇ
 3073        \t   ˇ
 3074        \t   ˇ
 3075        \t      ˇ
 3076         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3077    "});
 3078}
 3079
 3080#[gpui::test]
 3081async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3082    init_test(cx, |settings| {
 3083        settings.defaults.tab_size = NonZeroU32::new(4)
 3084    });
 3085
 3086    let language = Arc::new(
 3087        Language::new(
 3088            LanguageConfig::default(),
 3089            Some(tree_sitter_rust::LANGUAGE.into()),
 3090        )
 3091        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3092        .unwrap(),
 3093    );
 3094
 3095    let mut cx = EditorTestContext::new(cx).await;
 3096    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3097    cx.set_state(indoc! {"
 3098        fn a() {
 3099            if b {
 3100        \t ˇc
 3101            }
 3102        }
 3103    "});
 3104
 3105    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3106    cx.assert_editor_state(indoc! {"
 3107        fn a() {
 3108            if b {
 3109                ˇc
 3110            }
 3111        }
 3112    "});
 3113}
 3114
 3115#[gpui::test]
 3116async fn test_indent_outdent(cx: &mut TestAppContext) {
 3117    init_test(cx, |settings| {
 3118        settings.defaults.tab_size = NonZeroU32::new(4);
 3119    });
 3120
 3121    let mut cx = EditorTestContext::new(cx).await;
 3122
 3123    cx.set_state(indoc! {"
 3124          «oneˇ» «twoˇ»
 3125        three
 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        three
 3132         four
 3133    "});
 3134
 3135    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3136    cx.assert_editor_state(indoc! {"
 3137        «oneˇ» «twoˇ»
 3138        three
 3139         four
 3140    "});
 3141
 3142    // select across line ending
 3143    cx.set_state(indoc! {"
 3144        one two
 3145        t«hree
 3146        ˇ» four
 3147    "});
 3148    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3149    cx.assert_editor_state(indoc! {"
 3150        one two
 3151            t«hree
 3152        ˇ» four
 3153    "});
 3154
 3155    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3156    cx.assert_editor_state(indoc! {"
 3157        one two
 3158        t«hree
 3159        ˇ» four
 3160    "});
 3161
 3162    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3163    cx.set_state(indoc! {"
 3164        one two
 3165        ˇthree
 3166            four
 3167    "});
 3168    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3169    cx.assert_editor_state(indoc! {"
 3170        one two
 3171            ˇthree
 3172            four
 3173    "});
 3174
 3175    cx.set_state(indoc! {"
 3176        one two
 3177        ˇ    three
 3178            four
 3179    "});
 3180    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3181    cx.assert_editor_state(indoc! {"
 3182        one two
 3183        ˇthree
 3184            four
 3185    "});
 3186}
 3187
 3188#[gpui::test]
 3189async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3190    init_test(cx, |settings| {
 3191        settings.defaults.hard_tabs = Some(true);
 3192    });
 3193
 3194    let mut cx = EditorTestContext::new(cx).await;
 3195
 3196    // select two ranges on one line
 3197    cx.set_state(indoc! {"
 3198        «oneˇ» «twoˇ»
 3199        three
 3200        four
 3201    "});
 3202    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3203    cx.assert_editor_state(indoc! {"
 3204        \t«oneˇ» «twoˇ»
 3205        three
 3206        four
 3207    "});
 3208    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3209    cx.assert_editor_state(indoc! {"
 3210        \t\t«oneˇ» «twoˇ»
 3211        three
 3212        four
 3213    "});
 3214    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3215    cx.assert_editor_state(indoc! {"
 3216        \t«oneˇ» «twoˇ»
 3217        three
 3218        four
 3219    "});
 3220    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3221    cx.assert_editor_state(indoc! {"
 3222        «oneˇ» «twoˇ»
 3223        three
 3224        four
 3225    "});
 3226
 3227    // select across a line ending
 3228    cx.set_state(indoc! {"
 3229        one two
 3230        t«hree
 3231        ˇ»four
 3232    "});
 3233    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3234    cx.assert_editor_state(indoc! {"
 3235        one two
 3236        \tt«hree
 3237        ˇ»four
 3238    "});
 3239    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3240    cx.assert_editor_state(indoc! {"
 3241        one two
 3242        \t\tt«hree
 3243        ˇ»four
 3244    "});
 3245    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3246    cx.assert_editor_state(indoc! {"
 3247        one two
 3248        \tt«hree
 3249        ˇ»four
 3250    "});
 3251    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3252    cx.assert_editor_state(indoc! {"
 3253        one two
 3254        t«hree
 3255        ˇ»four
 3256    "});
 3257
 3258    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3259    cx.set_state(indoc! {"
 3260        one two
 3261        ˇthree
 3262        four
 3263    "});
 3264    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3265    cx.assert_editor_state(indoc! {"
 3266        one two
 3267        ˇthree
 3268        four
 3269    "});
 3270    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3271    cx.assert_editor_state(indoc! {"
 3272        one two
 3273        \tˇthree
 3274        four
 3275    "});
 3276    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3277    cx.assert_editor_state(indoc! {"
 3278        one two
 3279        ˇthree
 3280        four
 3281    "});
 3282}
 3283
 3284#[gpui::test]
 3285fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3286    init_test(cx, |settings| {
 3287        settings.languages.extend([
 3288            (
 3289                "TOML".into(),
 3290                LanguageSettingsContent {
 3291                    tab_size: NonZeroU32::new(2),
 3292                    ..Default::default()
 3293                },
 3294            ),
 3295            (
 3296                "Rust".into(),
 3297                LanguageSettingsContent {
 3298                    tab_size: NonZeroU32::new(4),
 3299                    ..Default::default()
 3300                },
 3301            ),
 3302        ]);
 3303    });
 3304
 3305    let toml_language = Arc::new(Language::new(
 3306        LanguageConfig {
 3307            name: "TOML".into(),
 3308            ..Default::default()
 3309        },
 3310        None,
 3311    ));
 3312    let rust_language = Arc::new(Language::new(
 3313        LanguageConfig {
 3314            name: "Rust".into(),
 3315            ..Default::default()
 3316        },
 3317        None,
 3318    ));
 3319
 3320    let toml_buffer =
 3321        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3322    let rust_buffer =
 3323        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3324    let multibuffer = cx.new(|cx| {
 3325        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3326        multibuffer.push_excerpts(
 3327            toml_buffer.clone(),
 3328            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3329            cx,
 3330        );
 3331        multibuffer.push_excerpts(
 3332            rust_buffer.clone(),
 3333            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3334            cx,
 3335        );
 3336        multibuffer
 3337    });
 3338
 3339    cx.add_window(|window, cx| {
 3340        let mut editor = build_editor(multibuffer, window, cx);
 3341
 3342        assert_eq!(
 3343            editor.text(cx),
 3344            indoc! {"
 3345                a = 1
 3346                b = 2
 3347
 3348                const c: usize = 3;
 3349            "}
 3350        );
 3351
 3352        select_ranges(
 3353            &mut editor,
 3354            indoc! {"
 3355                «aˇ» = 1
 3356                b = 2
 3357
 3358                «const c:ˇ» usize = 3;
 3359            "},
 3360            window,
 3361            cx,
 3362        );
 3363
 3364        editor.tab(&Tab, window, cx);
 3365        assert_text_with_selections(
 3366            &mut editor,
 3367            indoc! {"
 3368                  «aˇ» = 1
 3369                b = 2
 3370
 3371                    «const c:ˇ» usize = 3;
 3372            "},
 3373            cx,
 3374        );
 3375        editor.backtab(&Backtab, window, cx);
 3376        assert_text_with_selections(
 3377            &mut editor,
 3378            indoc! {"
 3379                «aˇ» = 1
 3380                b = 2
 3381
 3382                «const c:ˇ» usize = 3;
 3383            "},
 3384            cx,
 3385        );
 3386
 3387        editor
 3388    });
 3389}
 3390
 3391#[gpui::test]
 3392async fn test_backspace(cx: &mut TestAppContext) {
 3393    init_test(cx, |_| {});
 3394
 3395    let mut cx = EditorTestContext::new(cx).await;
 3396
 3397    // Basic backspace
 3398    cx.set_state(indoc! {"
 3399        onˇe two three
 3400        fou«rˇ» five six
 3401        seven «ˇeight nine
 3402        »ten
 3403    "});
 3404    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3405    cx.assert_editor_state(indoc! {"
 3406        oˇe two three
 3407        fouˇ five six
 3408        seven ˇten
 3409    "});
 3410
 3411    // Test backspace inside and around indents
 3412    cx.set_state(indoc! {"
 3413        zero
 3414            ˇone
 3415                ˇtwo
 3416            ˇ ˇ ˇ  three
 3417        ˇ  ˇ  four
 3418    "});
 3419    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3420    cx.assert_editor_state(indoc! {"
 3421        zero
 3422        ˇone
 3423            ˇtwo
 3424        ˇ  threeˇ  four
 3425    "});
 3426}
 3427
 3428#[gpui::test]
 3429async fn test_delete(cx: &mut TestAppContext) {
 3430    init_test(cx, |_| {});
 3431
 3432    let mut cx = EditorTestContext::new(cx).await;
 3433    cx.set_state(indoc! {"
 3434        onˇe two three
 3435        fou«rˇ» five six
 3436        seven «ˇeight nine
 3437        »ten
 3438    "});
 3439    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3440    cx.assert_editor_state(indoc! {"
 3441        onˇ two three
 3442        fouˇ five six
 3443        seven ˇten
 3444    "});
 3445}
 3446
 3447#[gpui::test]
 3448fn test_delete_line(cx: &mut TestAppContext) {
 3449    init_test(cx, |_| {});
 3450
 3451    let editor = cx.add_window(|window, cx| {
 3452        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3453        build_editor(buffer, window, cx)
 3454    });
 3455    _ = editor.update(cx, |editor, window, cx| {
 3456        editor.change_selections(None, window, cx, |s| {
 3457            s.select_display_ranges([
 3458                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3459                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3460                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3461            ])
 3462        });
 3463        editor.delete_line(&DeleteLine, window, cx);
 3464        assert_eq!(editor.display_text(cx), "ghi");
 3465        assert_eq!(
 3466            editor.selections.display_ranges(cx),
 3467            vec![
 3468                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3469                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3470            ]
 3471        );
 3472    });
 3473
 3474    let editor = cx.add_window(|window, cx| {
 3475        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3476        build_editor(buffer, window, cx)
 3477    });
 3478    _ = editor.update(cx, |editor, window, cx| {
 3479        editor.change_selections(None, window, cx, |s| {
 3480            s.select_display_ranges([
 3481                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3482            ])
 3483        });
 3484        editor.delete_line(&DeleteLine, window, cx);
 3485        assert_eq!(editor.display_text(cx), "ghi\n");
 3486        assert_eq!(
 3487            editor.selections.display_ranges(cx),
 3488            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3489        );
 3490    });
 3491}
 3492
 3493#[gpui::test]
 3494fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3495    init_test(cx, |_| {});
 3496
 3497    cx.add_window(|window, cx| {
 3498        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3499        let mut editor = build_editor(buffer.clone(), window, cx);
 3500        let buffer = buffer.read(cx).as_singleton().unwrap();
 3501
 3502        assert_eq!(
 3503            editor.selections.ranges::<Point>(cx),
 3504            &[Point::new(0, 0)..Point::new(0, 0)]
 3505        );
 3506
 3507        // When on single line, replace newline at end by space
 3508        editor.join_lines(&JoinLines, window, cx);
 3509        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3510        assert_eq!(
 3511            editor.selections.ranges::<Point>(cx),
 3512            &[Point::new(0, 3)..Point::new(0, 3)]
 3513        );
 3514
 3515        // When multiple lines are selected, remove newlines that are spanned by the selection
 3516        editor.change_selections(None, window, cx, |s| {
 3517            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3518        });
 3519        editor.join_lines(&JoinLines, window, cx);
 3520        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3521        assert_eq!(
 3522            editor.selections.ranges::<Point>(cx),
 3523            &[Point::new(0, 11)..Point::new(0, 11)]
 3524        );
 3525
 3526        // Undo should be transactional
 3527        editor.undo(&Undo, window, cx);
 3528        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3529        assert_eq!(
 3530            editor.selections.ranges::<Point>(cx),
 3531            &[Point::new(0, 5)..Point::new(2, 2)]
 3532        );
 3533
 3534        // When joining an empty line don't insert a space
 3535        editor.change_selections(None, window, cx, |s| {
 3536            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3537        });
 3538        editor.join_lines(&JoinLines, window, cx);
 3539        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3540        assert_eq!(
 3541            editor.selections.ranges::<Point>(cx),
 3542            [Point::new(2, 3)..Point::new(2, 3)]
 3543        );
 3544
 3545        // We can remove trailing newlines
 3546        editor.join_lines(&JoinLines, window, cx);
 3547        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3548        assert_eq!(
 3549            editor.selections.ranges::<Point>(cx),
 3550            [Point::new(2, 3)..Point::new(2, 3)]
 3551        );
 3552
 3553        // We don't blow up on the last line
 3554        editor.join_lines(&JoinLines, window, cx);
 3555        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3556        assert_eq!(
 3557            editor.selections.ranges::<Point>(cx),
 3558            [Point::new(2, 3)..Point::new(2, 3)]
 3559        );
 3560
 3561        // reset to test indentation
 3562        editor.buffer.update(cx, |buffer, cx| {
 3563            buffer.edit(
 3564                [
 3565                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3566                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3567                ],
 3568                None,
 3569                cx,
 3570            )
 3571        });
 3572
 3573        // We remove any leading spaces
 3574        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3575        editor.change_selections(None, window, cx, |s| {
 3576            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3577        });
 3578        editor.join_lines(&JoinLines, window, cx);
 3579        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3580
 3581        // We don't insert a space for a line containing only spaces
 3582        editor.join_lines(&JoinLines, window, cx);
 3583        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3584
 3585        // We ignore any leading tabs
 3586        editor.join_lines(&JoinLines, window, cx);
 3587        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3588
 3589        editor
 3590    });
 3591}
 3592
 3593#[gpui::test]
 3594fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3595    init_test(cx, |_| {});
 3596
 3597    cx.add_window(|window, cx| {
 3598        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3599        let mut editor = build_editor(buffer.clone(), window, cx);
 3600        let buffer = buffer.read(cx).as_singleton().unwrap();
 3601
 3602        editor.change_selections(None, window, cx, |s| {
 3603            s.select_ranges([
 3604                Point::new(0, 2)..Point::new(1, 1),
 3605                Point::new(1, 2)..Point::new(1, 2),
 3606                Point::new(3, 1)..Point::new(3, 2),
 3607            ])
 3608        });
 3609
 3610        editor.join_lines(&JoinLines, window, cx);
 3611        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3612
 3613        assert_eq!(
 3614            editor.selections.ranges::<Point>(cx),
 3615            [
 3616                Point::new(0, 7)..Point::new(0, 7),
 3617                Point::new(1, 3)..Point::new(1, 3)
 3618            ]
 3619        );
 3620        editor
 3621    });
 3622}
 3623
 3624#[gpui::test]
 3625async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3626    init_test(cx, |_| {});
 3627
 3628    let mut cx = EditorTestContext::new(cx).await;
 3629
 3630    let diff_base = r#"
 3631        Line 0
 3632        Line 1
 3633        Line 2
 3634        Line 3
 3635        "#
 3636    .unindent();
 3637
 3638    cx.set_state(
 3639        &r#"
 3640        ˇLine 0
 3641        Line 1
 3642        Line 2
 3643        Line 3
 3644        "#
 3645        .unindent(),
 3646    );
 3647
 3648    cx.set_head_text(&diff_base);
 3649    executor.run_until_parked();
 3650
 3651    // Join lines
 3652    cx.update_editor(|editor, window, cx| {
 3653        editor.join_lines(&JoinLines, window, cx);
 3654    });
 3655    executor.run_until_parked();
 3656
 3657    cx.assert_editor_state(
 3658        &r#"
 3659        Line 0ˇ Line 1
 3660        Line 2
 3661        Line 3
 3662        "#
 3663        .unindent(),
 3664    );
 3665    // Join again
 3666    cx.update_editor(|editor, window, cx| {
 3667        editor.join_lines(&JoinLines, window, cx);
 3668    });
 3669    executor.run_until_parked();
 3670
 3671    cx.assert_editor_state(
 3672        &r#"
 3673        Line 0 Line 1ˇ Line 2
 3674        Line 3
 3675        "#
 3676        .unindent(),
 3677    );
 3678}
 3679
 3680#[gpui::test]
 3681async fn test_custom_newlines_cause_no_false_positive_diffs(
 3682    executor: BackgroundExecutor,
 3683    cx: &mut TestAppContext,
 3684) {
 3685    init_test(cx, |_| {});
 3686    let mut cx = EditorTestContext::new(cx).await;
 3687    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3688    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3689    executor.run_until_parked();
 3690
 3691    cx.update_editor(|editor, window, cx| {
 3692        let snapshot = editor.snapshot(window, cx);
 3693        assert_eq!(
 3694            snapshot
 3695                .buffer_snapshot
 3696                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3697                .collect::<Vec<_>>(),
 3698            Vec::new(),
 3699            "Should not have any diffs for files with custom newlines"
 3700        );
 3701    });
 3702}
 3703
 3704#[gpui::test]
 3705async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3706    init_test(cx, |_| {});
 3707
 3708    let mut cx = EditorTestContext::new(cx).await;
 3709
 3710    // Test sort_lines_case_insensitive()
 3711    cx.set_state(indoc! {"
 3712        «z
 3713        y
 3714        x
 3715        Z
 3716        Y
 3717        Xˇ»
 3718    "});
 3719    cx.update_editor(|e, window, cx| {
 3720        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3721    });
 3722    cx.assert_editor_state(indoc! {"
 3723        «x
 3724        X
 3725        y
 3726        Y
 3727        z
 3728        Zˇ»
 3729    "});
 3730
 3731    // Test reverse_lines()
 3732    cx.set_state(indoc! {"
 3733        «5
 3734        4
 3735        3
 3736        2
 3737        1ˇ»
 3738    "});
 3739    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3740    cx.assert_editor_state(indoc! {"
 3741        «1
 3742        2
 3743        3
 3744        4
 3745        5ˇ»
 3746    "});
 3747
 3748    // Skip testing shuffle_line()
 3749
 3750    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3751    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3752
 3753    // Don't manipulate when cursor is on single line, but expand the selection
 3754    cx.set_state(indoc! {"
 3755        ddˇdd
 3756        ccc
 3757        bb
 3758        a
 3759    "});
 3760    cx.update_editor(|e, window, cx| {
 3761        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3762    });
 3763    cx.assert_editor_state(indoc! {"
 3764        «ddddˇ»
 3765        ccc
 3766        bb
 3767        a
 3768    "});
 3769
 3770    // Basic manipulate case
 3771    // Start selection moves to column 0
 3772    // End of selection shrinks to fit shorter line
 3773    cx.set_state(indoc! {"
 3774        dd«d
 3775        ccc
 3776        bb
 3777        aaaaaˇ»
 3778    "});
 3779    cx.update_editor(|e, window, cx| {
 3780        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3781    });
 3782    cx.assert_editor_state(indoc! {"
 3783        «aaaaa
 3784        bb
 3785        ccc
 3786        dddˇ»
 3787    "});
 3788
 3789    // Manipulate case with newlines
 3790    cx.set_state(indoc! {"
 3791        dd«d
 3792        ccc
 3793
 3794        bb
 3795        aaaaa
 3796
 3797        ˇ»
 3798    "});
 3799    cx.update_editor(|e, window, cx| {
 3800        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3801    });
 3802    cx.assert_editor_state(indoc! {"
 3803        «
 3804
 3805        aaaaa
 3806        bb
 3807        ccc
 3808        dddˇ»
 3809
 3810    "});
 3811
 3812    // Adding new line
 3813    cx.set_state(indoc! {"
 3814        aa«a
 3815        bbˇ»b
 3816    "});
 3817    cx.update_editor(|e, window, cx| {
 3818        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3819    });
 3820    cx.assert_editor_state(indoc! {"
 3821        «aaa
 3822        bbb
 3823        added_lineˇ»
 3824    "});
 3825
 3826    // Removing line
 3827    cx.set_state(indoc! {"
 3828        aa«a
 3829        bbbˇ»
 3830    "});
 3831    cx.update_editor(|e, window, cx| {
 3832        e.manipulate_lines(window, cx, |lines| {
 3833            lines.pop();
 3834        })
 3835    });
 3836    cx.assert_editor_state(indoc! {"
 3837        «aaaˇ»
 3838    "});
 3839
 3840    // Removing all lines
 3841    cx.set_state(indoc! {"
 3842        aa«a
 3843        bbbˇ»
 3844    "});
 3845    cx.update_editor(|e, window, cx| {
 3846        e.manipulate_lines(window, cx, |lines| {
 3847            lines.drain(..);
 3848        })
 3849    });
 3850    cx.assert_editor_state(indoc! {"
 3851        ˇ
 3852    "});
 3853}
 3854
 3855#[gpui::test]
 3856async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3857    init_test(cx, |_| {});
 3858
 3859    let mut cx = EditorTestContext::new(cx).await;
 3860
 3861    // Consider continuous selection as single selection
 3862    cx.set_state(indoc! {"
 3863        Aaa«aa
 3864        cˇ»c«c
 3865        bb
 3866        aaaˇ»aa
 3867    "});
 3868    cx.update_editor(|e, window, cx| {
 3869        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «Aaaaa
 3873        ccc
 3874        bb
 3875        aaaaaˇ»
 3876    "});
 3877
 3878    cx.set_state(indoc! {"
 3879        Aaa«aa
 3880        cˇ»c«c
 3881        bb
 3882        aaaˇ»aa
 3883    "});
 3884    cx.update_editor(|e, window, cx| {
 3885        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3886    });
 3887    cx.assert_editor_state(indoc! {"
 3888        «Aaaaa
 3889        ccc
 3890        bbˇ»
 3891    "});
 3892
 3893    // Consider non continuous selection as distinct dedup operations
 3894    cx.set_state(indoc! {"
 3895        «aaaaa
 3896        bb
 3897        aaaaa
 3898        aaaaaˇ»
 3899
 3900        aaa«aaˇ»
 3901    "});
 3902    cx.update_editor(|e, window, cx| {
 3903        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3904    });
 3905    cx.assert_editor_state(indoc! {"
 3906        «aaaaa
 3907        bbˇ»
 3908
 3909        «aaaaaˇ»
 3910    "});
 3911}
 3912
 3913#[gpui::test]
 3914async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3915    init_test(cx, |_| {});
 3916
 3917    let mut cx = EditorTestContext::new(cx).await;
 3918
 3919    cx.set_state(indoc! {"
 3920        «Aaa
 3921        aAa
 3922        Aaaˇ»
 3923    "});
 3924    cx.update_editor(|e, window, cx| {
 3925        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3926    });
 3927    cx.assert_editor_state(indoc! {"
 3928        «Aaa
 3929        aAaˇ»
 3930    "});
 3931
 3932    cx.set_state(indoc! {"
 3933        «Aaa
 3934        aAa
 3935        aaAˇ»
 3936    "});
 3937    cx.update_editor(|e, window, cx| {
 3938        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3939    });
 3940    cx.assert_editor_state(indoc! {"
 3941        «Aaaˇ»
 3942    "});
 3943}
 3944
 3945#[gpui::test]
 3946async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3947    init_test(cx, |_| {});
 3948
 3949    let mut cx = EditorTestContext::new(cx).await;
 3950
 3951    // Manipulate with multiple selections on a single line
 3952    cx.set_state(indoc! {"
 3953        dd«dd
 3954        cˇ»c«c
 3955        bb
 3956        aaaˇ»aa
 3957    "});
 3958    cx.update_editor(|e, window, cx| {
 3959        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3960    });
 3961    cx.assert_editor_state(indoc! {"
 3962        «aaaaa
 3963        bb
 3964        ccc
 3965        ddddˇ»
 3966    "});
 3967
 3968    // Manipulate with multiple disjoin selections
 3969    cx.set_state(indoc! {"
 3970 3971        4
 3972        3
 3973        2
 3974        1ˇ»
 3975
 3976        dd«dd
 3977        ccc
 3978        bb
 3979        aaaˇ»aa
 3980    "});
 3981    cx.update_editor(|e, window, cx| {
 3982        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3983    });
 3984    cx.assert_editor_state(indoc! {"
 3985        «1
 3986        2
 3987        3
 3988        4
 3989        5ˇ»
 3990
 3991        «aaaaa
 3992        bb
 3993        ccc
 3994        ddddˇ»
 3995    "});
 3996
 3997    // Adding lines on each selection
 3998    cx.set_state(indoc! {"
 3999 4000        1ˇ»
 4001
 4002        bb«bb
 4003        aaaˇ»aa
 4004    "});
 4005    cx.update_editor(|e, window, cx| {
 4006        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4007    });
 4008    cx.assert_editor_state(indoc! {"
 4009        «2
 4010        1
 4011        added lineˇ»
 4012
 4013        «bbbb
 4014        aaaaa
 4015        added lineˇ»
 4016    "});
 4017
 4018    // Removing lines on each selection
 4019    cx.set_state(indoc! {"
 4020 4021        1ˇ»
 4022
 4023        bb«bb
 4024        aaaˇ»aa
 4025    "});
 4026    cx.update_editor(|e, window, cx| {
 4027        e.manipulate_lines(window, cx, |lines| {
 4028            lines.pop();
 4029        })
 4030    });
 4031    cx.assert_editor_state(indoc! {"
 4032        «2ˇ»
 4033
 4034        «bbbbˇ»
 4035    "});
 4036}
 4037
 4038#[gpui::test]
 4039async fn test_toggle_case(cx: &mut TestAppContext) {
 4040    init_test(cx, |_| {});
 4041
 4042    let mut cx = EditorTestContext::new(cx).await;
 4043
 4044    // If all lower case -> upper case
 4045    cx.set_state(indoc! {"
 4046        «hello worldˇ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4049    cx.assert_editor_state(indoc! {"
 4050        «HELLO WORLDˇ»
 4051    "});
 4052
 4053    // If all upper case -> lower case
 4054    cx.set_state(indoc! {"
 4055        «HELLO WORLDˇ»
 4056    "});
 4057    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4058    cx.assert_editor_state(indoc! {"
 4059        «hello worldˇ»
 4060    "});
 4061
 4062    // If any upper case characters are identified -> lower case
 4063    // This matches JetBrains IDEs
 4064    cx.set_state(indoc! {"
 4065        «hEllo worldˇ»
 4066    "});
 4067    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4068    cx.assert_editor_state(indoc! {"
 4069        «hello worldˇ»
 4070    "});
 4071}
 4072
 4073#[gpui::test]
 4074async fn test_manipulate_text(cx: &mut TestAppContext) {
 4075    init_test(cx, |_| {});
 4076
 4077    let mut cx = EditorTestContext::new(cx).await;
 4078
 4079    // Test convert_to_upper_case()
 4080    cx.set_state(indoc! {"
 4081        «hello worldˇ»
 4082    "});
 4083    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4084    cx.assert_editor_state(indoc! {"
 4085        «HELLO WORLDˇ»
 4086    "});
 4087
 4088    // Test convert_to_lower_case()
 4089    cx.set_state(indoc! {"
 4090        «HELLO WORLDˇ»
 4091    "});
 4092    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4093    cx.assert_editor_state(indoc! {"
 4094        «hello worldˇ»
 4095    "});
 4096
 4097    // Test multiple line, single selection case
 4098    cx.set_state(indoc! {"
 4099        «The quick brown
 4100        fox jumps over
 4101        the lazy dogˇ»
 4102    "});
 4103    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4104    cx.assert_editor_state(indoc! {"
 4105        «The Quick Brown
 4106        Fox Jumps Over
 4107        The Lazy Dogˇ»
 4108    "});
 4109
 4110    // Test multiple line, single selection case
 4111    cx.set_state(indoc! {"
 4112        «The quick brown
 4113        fox jumps over
 4114        the lazy dogˇ»
 4115    "});
 4116    cx.update_editor(|e, window, cx| {
 4117        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4118    });
 4119    cx.assert_editor_state(indoc! {"
 4120        «TheQuickBrown
 4121        FoxJumpsOver
 4122        TheLazyDogˇ»
 4123    "});
 4124
 4125    // From here on out, test more complex cases of manipulate_text()
 4126
 4127    // Test no selection case - should affect words cursors are in
 4128    // Cursor at beginning, middle, and end of word
 4129    cx.set_state(indoc! {"
 4130        ˇhello big beauˇtiful worldˇ
 4131    "});
 4132    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4133    cx.assert_editor_state(indoc! {"
 4134        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4135    "});
 4136
 4137    // Test multiple selections on a single line and across multiple lines
 4138    cx.set_state(indoc! {"
 4139        «Theˇ» quick «brown
 4140        foxˇ» jumps «overˇ»
 4141        the «lazyˇ» dog
 4142    "});
 4143    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4144    cx.assert_editor_state(indoc! {"
 4145        «THEˇ» quick «BROWN
 4146        FOXˇ» jumps «OVERˇ»
 4147        the «LAZYˇ» dog
 4148    "});
 4149
 4150    // Test case where text length grows
 4151    cx.set_state(indoc! {"
 4152        «tschüߡ»
 4153    "});
 4154    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4155    cx.assert_editor_state(indoc! {"
 4156        «TSCHÜSSˇ»
 4157    "});
 4158
 4159    // Test to make sure we don't crash when text shrinks
 4160    cx.set_state(indoc! {"
 4161        aaa_bbbˇ
 4162    "});
 4163    cx.update_editor(|e, window, cx| {
 4164        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4165    });
 4166    cx.assert_editor_state(indoc! {"
 4167        «aaaBbbˇ»
 4168    "});
 4169
 4170    // Test to make sure we all aware of the fact that each word can grow and shrink
 4171    // Final selections should be aware of this fact
 4172    cx.set_state(indoc! {"
 4173        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4174    "});
 4175    cx.update_editor(|e, window, cx| {
 4176        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4177    });
 4178    cx.assert_editor_state(indoc! {"
 4179        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4180    "});
 4181
 4182    cx.set_state(indoc! {"
 4183        «hElLo, WoRld!ˇ»
 4184    "});
 4185    cx.update_editor(|e, window, cx| {
 4186        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4187    });
 4188    cx.assert_editor_state(indoc! {"
 4189        «HeLlO, wOrLD!ˇ»
 4190    "});
 4191}
 4192
 4193#[gpui::test]
 4194fn test_duplicate_line(cx: &mut TestAppContext) {
 4195    init_test(cx, |_| {});
 4196
 4197    let editor = cx.add_window(|window, cx| {
 4198        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4199        build_editor(buffer, window, cx)
 4200    });
 4201    _ = editor.update(cx, |editor, window, cx| {
 4202        editor.change_selections(None, window, cx, |s| {
 4203            s.select_display_ranges([
 4204                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4205                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4206                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4207                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4208            ])
 4209        });
 4210        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4211        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4212        assert_eq!(
 4213            editor.selections.display_ranges(cx),
 4214            vec![
 4215                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4216                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4217                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4218                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4219            ]
 4220        );
 4221    });
 4222
 4223    let editor = cx.add_window(|window, cx| {
 4224        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4225        build_editor(buffer, window, cx)
 4226    });
 4227    _ = editor.update(cx, |editor, window, cx| {
 4228        editor.change_selections(None, window, cx, |s| {
 4229            s.select_display_ranges([
 4230                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4231                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4232            ])
 4233        });
 4234        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4235        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4236        assert_eq!(
 4237            editor.selections.display_ranges(cx),
 4238            vec![
 4239                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4240                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4241            ]
 4242        );
 4243    });
 4244
 4245    // With `move_upwards` the selections stay in place, except for
 4246    // the lines inserted above them
 4247    let editor = cx.add_window(|window, cx| {
 4248        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4249        build_editor(buffer, window, cx)
 4250    });
 4251    _ = editor.update(cx, |editor, window, cx| {
 4252        editor.change_selections(None, window, cx, |s| {
 4253            s.select_display_ranges([
 4254                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4255                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4256                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4257                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4258            ])
 4259        });
 4260        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4261        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4262        assert_eq!(
 4263            editor.selections.display_ranges(cx),
 4264            vec![
 4265                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4266                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4267                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4268                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4269            ]
 4270        );
 4271    });
 4272
 4273    let editor = cx.add_window(|window, cx| {
 4274        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4275        build_editor(buffer, window, cx)
 4276    });
 4277    _ = editor.update(cx, |editor, window, cx| {
 4278        editor.change_selections(None, window, cx, |s| {
 4279            s.select_display_ranges([
 4280                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4281                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4282            ])
 4283        });
 4284        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4285        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4286        assert_eq!(
 4287            editor.selections.display_ranges(cx),
 4288            vec![
 4289                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4290                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4291            ]
 4292        );
 4293    });
 4294
 4295    let editor = cx.add_window(|window, cx| {
 4296        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4297        build_editor(buffer, window, cx)
 4298    });
 4299    _ = editor.update(cx, |editor, window, cx| {
 4300        editor.change_selections(None, window, cx, |s| {
 4301            s.select_display_ranges([
 4302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4303                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4304            ])
 4305        });
 4306        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4307        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4308        assert_eq!(
 4309            editor.selections.display_ranges(cx),
 4310            vec![
 4311                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4312                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4313            ]
 4314        );
 4315    });
 4316}
 4317
 4318#[gpui::test]
 4319fn test_move_line_up_down(cx: &mut TestAppContext) {
 4320    init_test(cx, |_| {});
 4321
 4322    let editor = cx.add_window(|window, cx| {
 4323        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4324        build_editor(buffer, window, cx)
 4325    });
 4326    _ = editor.update(cx, |editor, window, cx| {
 4327        editor.fold_creases(
 4328            vec![
 4329                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4330                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4331                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4332            ],
 4333            true,
 4334            window,
 4335            cx,
 4336        );
 4337        editor.change_selections(None, window, cx, |s| {
 4338            s.select_display_ranges([
 4339                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4340                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4341                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4342                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4343            ])
 4344        });
 4345        assert_eq!(
 4346            editor.display_text(cx),
 4347            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4348        );
 4349
 4350        editor.move_line_up(&MoveLineUp, window, cx);
 4351        assert_eq!(
 4352            editor.display_text(cx),
 4353            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4354        );
 4355        assert_eq!(
 4356            editor.selections.display_ranges(cx),
 4357            vec![
 4358                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4359                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4360                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4361                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4362            ]
 4363        );
 4364    });
 4365
 4366    _ = editor.update(cx, |editor, window, cx| {
 4367        editor.move_line_down(&MoveLineDown, window, cx);
 4368        assert_eq!(
 4369            editor.display_text(cx),
 4370            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4371        );
 4372        assert_eq!(
 4373            editor.selections.display_ranges(cx),
 4374            vec![
 4375                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4376                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4377                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4378                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4379            ]
 4380        );
 4381    });
 4382
 4383    _ = editor.update(cx, |editor, window, cx| {
 4384        editor.move_line_down(&MoveLineDown, window, cx);
 4385        assert_eq!(
 4386            editor.display_text(cx),
 4387            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4388        );
 4389        assert_eq!(
 4390            editor.selections.display_ranges(cx),
 4391            vec![
 4392                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4393                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4394                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4395                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4396            ]
 4397        );
 4398    });
 4399
 4400    _ = editor.update(cx, |editor, window, cx| {
 4401        editor.move_line_up(&MoveLineUp, window, cx);
 4402        assert_eq!(
 4403            editor.display_text(cx),
 4404            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4405        );
 4406        assert_eq!(
 4407            editor.selections.display_ranges(cx),
 4408            vec![
 4409                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4410                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4411                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4412                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4413            ]
 4414        );
 4415    });
 4416}
 4417
 4418#[gpui::test]
 4419fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4420    init_test(cx, |_| {});
 4421
 4422    let editor = cx.add_window(|window, cx| {
 4423        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4424        build_editor(buffer, window, cx)
 4425    });
 4426    _ = editor.update(cx, |editor, window, cx| {
 4427        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4428        editor.insert_blocks(
 4429            [BlockProperties {
 4430                style: BlockStyle::Fixed,
 4431                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4432                height: Some(1),
 4433                render: Arc::new(|_| div().into_any()),
 4434                priority: 0,
 4435                render_in_minimap: true,
 4436            }],
 4437            Some(Autoscroll::fit()),
 4438            cx,
 4439        );
 4440        editor.change_selections(None, window, cx, |s| {
 4441            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4442        });
 4443        editor.move_line_down(&MoveLineDown, window, cx);
 4444    });
 4445}
 4446
 4447#[gpui::test]
 4448async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4449    init_test(cx, |_| {});
 4450
 4451    let mut cx = EditorTestContext::new(cx).await;
 4452    cx.set_state(
 4453        &"
 4454            ˇzero
 4455            one
 4456            two
 4457            three
 4458            four
 4459            five
 4460        "
 4461        .unindent(),
 4462    );
 4463
 4464    // Create a four-line block that replaces three lines of text.
 4465    cx.update_editor(|editor, window, cx| {
 4466        let snapshot = editor.snapshot(window, cx);
 4467        let snapshot = &snapshot.buffer_snapshot;
 4468        let placement = BlockPlacement::Replace(
 4469            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4470        );
 4471        editor.insert_blocks(
 4472            [BlockProperties {
 4473                placement,
 4474                height: Some(4),
 4475                style: BlockStyle::Sticky,
 4476                render: Arc::new(|_| gpui::div().into_any_element()),
 4477                priority: 0,
 4478                render_in_minimap: true,
 4479            }],
 4480            None,
 4481            cx,
 4482        );
 4483    });
 4484
 4485    // Move down so that the cursor touches the block.
 4486    cx.update_editor(|editor, window, cx| {
 4487        editor.move_down(&Default::default(), window, cx);
 4488    });
 4489    cx.assert_editor_state(
 4490        &"
 4491            zero
 4492            «one
 4493            two
 4494            threeˇ»
 4495            four
 4496            five
 4497        "
 4498        .unindent(),
 4499    );
 4500
 4501    // Move down past the block.
 4502    cx.update_editor(|editor, window, cx| {
 4503        editor.move_down(&Default::default(), window, cx);
 4504    });
 4505    cx.assert_editor_state(
 4506        &"
 4507            zero
 4508            one
 4509            two
 4510            three
 4511            ˇfour
 4512            five
 4513        "
 4514        .unindent(),
 4515    );
 4516}
 4517
 4518#[gpui::test]
 4519fn test_transpose(cx: &mut TestAppContext) {
 4520    init_test(cx, |_| {});
 4521
 4522    _ = cx.add_window(|window, cx| {
 4523        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4524        editor.set_style(EditorStyle::default(), window, cx);
 4525        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4526        editor.transpose(&Default::default(), window, cx);
 4527        assert_eq!(editor.text(cx), "bac");
 4528        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4529
 4530        editor.transpose(&Default::default(), window, cx);
 4531        assert_eq!(editor.text(cx), "bca");
 4532        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4533
 4534        editor.transpose(&Default::default(), window, cx);
 4535        assert_eq!(editor.text(cx), "bac");
 4536        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4537
 4538        editor
 4539    });
 4540
 4541    _ = cx.add_window(|window, cx| {
 4542        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4543        editor.set_style(EditorStyle::default(), window, cx);
 4544        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4545        editor.transpose(&Default::default(), window, cx);
 4546        assert_eq!(editor.text(cx), "acb\nde");
 4547        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4548
 4549        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4550        editor.transpose(&Default::default(), window, cx);
 4551        assert_eq!(editor.text(cx), "acbd\ne");
 4552        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4553
 4554        editor.transpose(&Default::default(), window, cx);
 4555        assert_eq!(editor.text(cx), "acbde\n");
 4556        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4557
 4558        editor.transpose(&Default::default(), window, cx);
 4559        assert_eq!(editor.text(cx), "acbd\ne");
 4560        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4561
 4562        editor
 4563    });
 4564
 4565    _ = cx.add_window(|window, cx| {
 4566        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4567        editor.set_style(EditorStyle::default(), window, cx);
 4568        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4569        editor.transpose(&Default::default(), window, cx);
 4570        assert_eq!(editor.text(cx), "bacd\ne");
 4571        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4572
 4573        editor.transpose(&Default::default(), window, cx);
 4574        assert_eq!(editor.text(cx), "bcade\n");
 4575        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4576
 4577        editor.transpose(&Default::default(), window, cx);
 4578        assert_eq!(editor.text(cx), "bcda\ne");
 4579        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4580
 4581        editor.transpose(&Default::default(), window, cx);
 4582        assert_eq!(editor.text(cx), "bcade\n");
 4583        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4584
 4585        editor.transpose(&Default::default(), window, cx);
 4586        assert_eq!(editor.text(cx), "bcaed\n");
 4587        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4588
 4589        editor
 4590    });
 4591
 4592    _ = cx.add_window(|window, cx| {
 4593        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4594        editor.set_style(EditorStyle::default(), window, cx);
 4595        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4596        editor.transpose(&Default::default(), window, cx);
 4597        assert_eq!(editor.text(cx), "🏀🍐✋");
 4598        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4599
 4600        editor.transpose(&Default::default(), window, cx);
 4601        assert_eq!(editor.text(cx), "🏀✋🍐");
 4602        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4603
 4604        editor.transpose(&Default::default(), window, cx);
 4605        assert_eq!(editor.text(cx), "🏀🍐✋");
 4606        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4607
 4608        editor
 4609    });
 4610}
 4611
 4612#[gpui::test]
 4613async fn test_rewrap(cx: &mut TestAppContext) {
 4614    init_test(cx, |settings| {
 4615        settings.languages.extend([
 4616            (
 4617                "Markdown".into(),
 4618                LanguageSettingsContent {
 4619                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4620                    ..Default::default()
 4621                },
 4622            ),
 4623            (
 4624                "Plain Text".into(),
 4625                LanguageSettingsContent {
 4626                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4627                    ..Default::default()
 4628                },
 4629            ),
 4630        ])
 4631    });
 4632
 4633    let mut cx = EditorTestContext::new(cx).await;
 4634
 4635    let language_with_c_comments = Arc::new(Language::new(
 4636        LanguageConfig {
 4637            line_comments: vec!["// ".into()],
 4638            ..LanguageConfig::default()
 4639        },
 4640        None,
 4641    ));
 4642    let language_with_pound_comments = Arc::new(Language::new(
 4643        LanguageConfig {
 4644            line_comments: vec!["# ".into()],
 4645            ..LanguageConfig::default()
 4646        },
 4647        None,
 4648    ));
 4649    let markdown_language = Arc::new(Language::new(
 4650        LanguageConfig {
 4651            name: "Markdown".into(),
 4652            ..LanguageConfig::default()
 4653        },
 4654        None,
 4655    ));
 4656    let language_with_doc_comments = Arc::new(Language::new(
 4657        LanguageConfig {
 4658            line_comments: vec!["// ".into(), "/// ".into()],
 4659            ..LanguageConfig::default()
 4660        },
 4661        Some(tree_sitter_rust::LANGUAGE.into()),
 4662    ));
 4663
 4664    let plaintext_language = Arc::new(Language::new(
 4665        LanguageConfig {
 4666            name: "Plain Text".into(),
 4667            ..LanguageConfig::default()
 4668        },
 4669        None,
 4670    ));
 4671
 4672    assert_rewrap(
 4673        indoc! {"
 4674            // ˇ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.
 4675        "},
 4676        indoc! {"
 4677            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4678            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4679            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4680            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4681            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4682            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4683            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4684            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4685            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4686            // porttitor id. Aliquam id accumsan eros.
 4687        "},
 4688        language_with_c_comments.clone(),
 4689        &mut cx,
 4690    );
 4691
 4692    // Test that rewrapping works inside of a selection
 4693    assert_rewrap(
 4694        indoc! {"
 4695            «// 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.ˇ»
 4696        "},
 4697        indoc! {"
 4698            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4699            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4700            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4701            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4702            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4703            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4704            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4705            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4706            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4707            // porttitor id. Aliquam id accumsan eros.ˇ»
 4708        "},
 4709        language_with_c_comments.clone(),
 4710        &mut cx,
 4711    );
 4712
 4713    // Test that cursors that expand to the same region are collapsed.
 4714    assert_rewrap(
 4715        indoc! {"
 4716            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4717            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4718            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4719            // ˇ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.
 4720        "},
 4721        indoc! {"
 4722            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4723            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4724            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4725            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4726            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4727            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4728            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4729            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4730            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4731            // porttitor id. Aliquam id accumsan eros.
 4732        "},
 4733        language_with_c_comments.clone(),
 4734        &mut cx,
 4735    );
 4736
 4737    // Test that non-contiguous selections are treated separately.
 4738    assert_rewrap(
 4739        indoc! {"
 4740            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4741            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4742            //
 4743            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4744            // ˇ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.
 4745        "},
 4746        indoc! {"
 4747            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4748            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4749            // auctor, eu lacinia sapien scelerisque.
 4750            //
 4751            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4752            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4753            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4754            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4755            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4756            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4757            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4758        "},
 4759        language_with_c_comments.clone(),
 4760        &mut cx,
 4761    );
 4762
 4763    // Test that different comment prefixes are supported.
 4764    assert_rewrap(
 4765        indoc! {"
 4766            # ˇ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.
 4767        "},
 4768        indoc! {"
 4769            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4770            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4771            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4772            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4773            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4774            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4775            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4776            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4777            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4778            # accumsan eros.
 4779        "},
 4780        language_with_pound_comments.clone(),
 4781        &mut cx,
 4782    );
 4783
 4784    // Test that rewrapping is ignored outside of comments in most languages.
 4785    assert_rewrap(
 4786        indoc! {"
 4787            /// Adds two numbers.
 4788            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4789            fn add(a: u32, b: u32) -> u32 {
 4790                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ˇ
 4791            }
 4792        "},
 4793        indoc! {"
 4794            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4795            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4796            fn add(a: u32, b: u32) -> u32 {
 4797                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ˇ
 4798            }
 4799        "},
 4800        language_with_doc_comments.clone(),
 4801        &mut cx,
 4802    );
 4803
 4804    // Test that rewrapping works in Markdown and Plain Text languages.
 4805    assert_rewrap(
 4806        indoc! {"
 4807            # Hello
 4808
 4809            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.
 4810        "},
 4811        indoc! {"
 4812            # Hello
 4813
 4814            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4815            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4816            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4817            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4818            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4819            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4820            Integer sit amet scelerisque nisi.
 4821        "},
 4822        markdown_language,
 4823        &mut cx,
 4824    );
 4825
 4826    assert_rewrap(
 4827        indoc! {"
 4828            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.
 4829        "},
 4830        indoc! {"
 4831            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4832            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4833            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4834            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4835            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4836            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4837            Integer sit amet scelerisque nisi.
 4838        "},
 4839        plaintext_language,
 4840        &mut cx,
 4841    );
 4842
 4843    // Test rewrapping unaligned comments in a selection.
 4844    assert_rewrap(
 4845        indoc! {"
 4846            fn foo() {
 4847                if true {
 4848            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4849            // Praesent semper egestas tellus id dignissim.ˇ»
 4850                    do_something();
 4851                } else {
 4852                    //
 4853                }
 4854            }
 4855        "},
 4856        indoc! {"
 4857            fn foo() {
 4858                if true {
 4859            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4860                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4861                    // egestas tellus id dignissim.ˇ»
 4862                    do_something();
 4863                } else {
 4864                    //
 4865                }
 4866            }
 4867        "},
 4868        language_with_doc_comments.clone(),
 4869        &mut cx,
 4870    );
 4871
 4872    assert_rewrap(
 4873        indoc! {"
 4874            fn foo() {
 4875                if true {
 4876            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4877            // Praesent semper egestas tellus id dignissim.»
 4878                    do_something();
 4879                } else {
 4880                    //
 4881                }
 4882
 4883            }
 4884        "},
 4885        indoc! {"
 4886            fn foo() {
 4887                if true {
 4888            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4889                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4890                    // egestas tellus id dignissim.»
 4891                    do_something();
 4892                } else {
 4893                    //
 4894                }
 4895
 4896            }
 4897        "},
 4898        language_with_doc_comments.clone(),
 4899        &mut cx,
 4900    );
 4901
 4902    #[track_caller]
 4903    fn assert_rewrap(
 4904        unwrapped_text: &str,
 4905        wrapped_text: &str,
 4906        language: Arc<Language>,
 4907        cx: &mut EditorTestContext,
 4908    ) {
 4909        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4910        cx.set_state(unwrapped_text);
 4911        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4912        cx.assert_editor_state(wrapped_text);
 4913    }
 4914}
 4915
 4916#[gpui::test]
 4917async fn test_hard_wrap(cx: &mut TestAppContext) {
 4918    init_test(cx, |_| {});
 4919    let mut cx = EditorTestContext::new(cx).await;
 4920
 4921    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4922    cx.update_editor(|editor, _, cx| {
 4923        editor.set_hard_wrap(Some(14), cx);
 4924    });
 4925
 4926    cx.set_state(indoc!(
 4927        "
 4928        one two three ˇ
 4929        "
 4930    ));
 4931    cx.simulate_input("four");
 4932    cx.run_until_parked();
 4933
 4934    cx.assert_editor_state(indoc!(
 4935        "
 4936        one two three
 4937        fourˇ
 4938        "
 4939    ));
 4940
 4941    cx.update_editor(|editor, window, cx| {
 4942        editor.newline(&Default::default(), window, cx);
 4943    });
 4944    cx.run_until_parked();
 4945    cx.assert_editor_state(indoc!(
 4946        "
 4947        one two three
 4948        four
 4949        ˇ
 4950        "
 4951    ));
 4952
 4953    cx.simulate_input("five");
 4954    cx.run_until_parked();
 4955    cx.assert_editor_state(indoc!(
 4956        "
 4957        one two three
 4958        four
 4959        fiveˇ
 4960        "
 4961    ));
 4962
 4963    cx.update_editor(|editor, window, cx| {
 4964        editor.newline(&Default::default(), window, cx);
 4965    });
 4966    cx.run_until_parked();
 4967    cx.simulate_input("# ");
 4968    cx.run_until_parked();
 4969    cx.assert_editor_state(indoc!(
 4970        "
 4971        one two three
 4972        four
 4973        five
 4974        # ˇ
 4975        "
 4976    ));
 4977
 4978    cx.update_editor(|editor, window, cx| {
 4979        editor.newline(&Default::default(), window, cx);
 4980    });
 4981    cx.run_until_parked();
 4982    cx.assert_editor_state(indoc!(
 4983        "
 4984        one two three
 4985        four
 4986        five
 4987        #\x20
 4988 4989        "
 4990    ));
 4991
 4992    cx.simulate_input(" 6");
 4993    cx.run_until_parked();
 4994    cx.assert_editor_state(indoc!(
 4995        "
 4996        one two three
 4997        four
 4998        five
 4999        #
 5000        # 6ˇ
 5001        "
 5002    ));
 5003}
 5004
 5005#[gpui::test]
 5006async fn test_clipboard(cx: &mut TestAppContext) {
 5007    init_test(cx, |_| {});
 5008
 5009    let mut cx = EditorTestContext::new(cx).await;
 5010
 5011    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5012    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5013    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5014
 5015    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5016    cx.set_state("two ˇfour ˇsix ˇ");
 5017    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5018    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5019
 5020    // Paste again but with only two cursors. Since the number of cursors doesn't
 5021    // match the number of slices in the clipboard, the entire clipboard text
 5022    // is pasted at each cursor.
 5023    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5024    cx.update_editor(|e, window, cx| {
 5025        e.handle_input("( ", window, cx);
 5026        e.paste(&Paste, window, cx);
 5027        e.handle_input(") ", window, cx);
 5028    });
 5029    cx.assert_editor_state(
 5030        &([
 5031            "( one✅ ",
 5032            "three ",
 5033            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5034            "three ",
 5035            "five ) ˇ",
 5036        ]
 5037        .join("\n")),
 5038    );
 5039
 5040    // Cut with three selections, one of which is full-line.
 5041    cx.set_state(indoc! {"
 5042        1«2ˇ»3
 5043        4ˇ567
 5044        «8ˇ»9"});
 5045    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5046    cx.assert_editor_state(indoc! {"
 5047        1ˇ3
 5048        ˇ9"});
 5049
 5050    // Paste with three selections, noticing how the copied selection that was full-line
 5051    // gets inserted before the second cursor.
 5052    cx.set_state(indoc! {"
 5053        1ˇ3
 5054 5055        «oˇ»ne"});
 5056    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5057    cx.assert_editor_state(indoc! {"
 5058        12ˇ3
 5059        4567
 5060 5061        8ˇne"});
 5062
 5063    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5064    cx.set_state(indoc! {"
 5065        The quick brown
 5066        fox juˇmps over
 5067        the lazy dog"});
 5068    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5069    assert_eq!(
 5070        cx.read_from_clipboard()
 5071            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5072        Some("fox jumps over\n".to_string())
 5073    );
 5074
 5075    // Paste with three selections, noticing how the copied full-line selection is inserted
 5076    // before the empty selections but replaces the selection that is non-empty.
 5077    cx.set_state(indoc! {"
 5078        Tˇhe quick brown
 5079        «foˇ»x jumps over
 5080        tˇhe lazy dog"});
 5081    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5082    cx.assert_editor_state(indoc! {"
 5083        fox jumps over
 5084        Tˇhe quick brown
 5085        fox jumps over
 5086        ˇx jumps over
 5087        fox jumps over
 5088        tˇhe lazy dog"});
 5089}
 5090
 5091#[gpui::test]
 5092async fn test_copy_trim(cx: &mut TestAppContext) {
 5093    init_test(cx, |_| {});
 5094
 5095    let mut cx = EditorTestContext::new(cx).await;
 5096    cx.set_state(
 5097        r#"            «for selection in selections.iter() {
 5098            let mut start = selection.start;
 5099            let mut end = selection.end;
 5100            let is_entire_line = selection.is_empty();
 5101            if is_entire_line {
 5102                start = Point::new(start.row, 0);ˇ»
 5103                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5104            }
 5105        "#,
 5106    );
 5107    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5108    assert_eq!(
 5109        cx.read_from_clipboard()
 5110            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5111        Some(
 5112            "for selection in selections.iter() {
 5113            let mut start = selection.start;
 5114            let mut end = selection.end;
 5115            let is_entire_line = selection.is_empty();
 5116            if is_entire_line {
 5117                start = Point::new(start.row, 0);"
 5118                .to_string()
 5119        ),
 5120        "Regular copying preserves all indentation selected",
 5121    );
 5122    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5123    assert_eq!(
 5124        cx.read_from_clipboard()
 5125            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5126        Some(
 5127            "for selection in selections.iter() {
 5128let mut start = selection.start;
 5129let mut end = selection.end;
 5130let is_entire_line = selection.is_empty();
 5131if is_entire_line {
 5132    start = Point::new(start.row, 0);"
 5133                .to_string()
 5134        ),
 5135        "Copying with stripping should strip all leading whitespaces"
 5136    );
 5137
 5138    cx.set_state(
 5139        r#"       «     for selection in selections.iter() {
 5140            let mut start = selection.start;
 5141            let mut end = selection.end;
 5142            let is_entire_line = selection.is_empty();
 5143            if is_entire_line {
 5144                start = Point::new(start.row, 0);ˇ»
 5145                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5146            }
 5147        "#,
 5148    );
 5149    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5150    assert_eq!(
 5151        cx.read_from_clipboard()
 5152            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5153        Some(
 5154            "     for selection in selections.iter() {
 5155            let mut start = selection.start;
 5156            let mut end = selection.end;
 5157            let is_entire_line = selection.is_empty();
 5158            if is_entire_line {
 5159                start = Point::new(start.row, 0);"
 5160                .to_string()
 5161        ),
 5162        "Regular copying preserves all indentation selected",
 5163    );
 5164    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5165    assert_eq!(
 5166        cx.read_from_clipboard()
 5167            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5168        Some(
 5169            "for selection in selections.iter() {
 5170let mut start = selection.start;
 5171let mut end = selection.end;
 5172let is_entire_line = selection.is_empty();
 5173if is_entire_line {
 5174    start = Point::new(start.row, 0);"
 5175                .to_string()
 5176        ),
 5177        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5178    );
 5179
 5180    cx.set_state(
 5181        r#"       «ˇ     for selection in selections.iter() {
 5182            let mut start = selection.start;
 5183            let mut end = selection.end;
 5184            let is_entire_line = selection.is_empty();
 5185            if is_entire_line {
 5186                start = Point::new(start.row, 0);»
 5187                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5188            }
 5189        "#,
 5190    );
 5191    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5192    assert_eq!(
 5193        cx.read_from_clipboard()
 5194            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5195        Some(
 5196            "     for selection in selections.iter() {
 5197            let mut start = selection.start;
 5198            let mut end = selection.end;
 5199            let is_entire_line = selection.is_empty();
 5200            if is_entire_line {
 5201                start = Point::new(start.row, 0);"
 5202                .to_string()
 5203        ),
 5204        "Regular copying for reverse selection works the same",
 5205    );
 5206    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5207    assert_eq!(
 5208        cx.read_from_clipboard()
 5209            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5210        Some(
 5211            "for selection in selections.iter() {
 5212let mut start = selection.start;
 5213let mut end = selection.end;
 5214let is_entire_line = selection.is_empty();
 5215if is_entire_line {
 5216    start = Point::new(start.row, 0);"
 5217                .to_string()
 5218        ),
 5219        "Copying with stripping for reverse selection works the same"
 5220    );
 5221
 5222    cx.set_state(
 5223        r#"            for selection «in selections.iter() {
 5224            let mut start = selection.start;
 5225            let mut end = selection.end;
 5226            let is_entire_line = selection.is_empty();
 5227            if is_entire_line {
 5228                start = Point::new(start.row, 0);ˇ»
 5229                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5230            }
 5231        "#,
 5232    );
 5233    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5234    assert_eq!(
 5235        cx.read_from_clipboard()
 5236            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5237        Some(
 5238            "in selections.iter() {
 5239            let mut start = selection.start;
 5240            let mut end = selection.end;
 5241            let is_entire_line = selection.is_empty();
 5242            if is_entire_line {
 5243                start = Point::new(start.row, 0);"
 5244                .to_string()
 5245        ),
 5246        "When selecting past the indent, the copying works as usual",
 5247    );
 5248    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5249    assert_eq!(
 5250        cx.read_from_clipboard()
 5251            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5252        Some(
 5253            "in selections.iter() {
 5254            let mut start = selection.start;
 5255            let mut end = selection.end;
 5256            let is_entire_line = selection.is_empty();
 5257            if is_entire_line {
 5258                start = Point::new(start.row, 0);"
 5259                .to_string()
 5260        ),
 5261        "When selecting past the indent, nothing is trimmed"
 5262    );
 5263
 5264    cx.set_state(
 5265        r#"            «for selection in selections.iter() {
 5266            let mut start = selection.start;
 5267
 5268            let mut end = selection.end;
 5269            let is_entire_line = selection.is_empty();
 5270            if is_entire_line {
 5271                start = Point::new(start.row, 0);
 5272ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5273            }
 5274        "#,
 5275    );
 5276    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5277    assert_eq!(
 5278        cx.read_from_clipboard()
 5279            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5280        Some(
 5281            "for selection in selections.iter() {
 5282let mut start = selection.start;
 5283
 5284let mut end = selection.end;
 5285let is_entire_line = selection.is_empty();
 5286if is_entire_line {
 5287    start = Point::new(start.row, 0);
 5288"
 5289            .to_string()
 5290        ),
 5291        "Copying with stripping should ignore empty lines"
 5292    );
 5293}
 5294
 5295#[gpui::test]
 5296async fn test_paste_multiline(cx: &mut TestAppContext) {
 5297    init_test(cx, |_| {});
 5298
 5299    let mut cx = EditorTestContext::new(cx).await;
 5300    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5301
 5302    // Cut an indented block, without the leading whitespace.
 5303    cx.set_state(indoc! {"
 5304        const a: B = (
 5305            c(),
 5306            «d(
 5307                e,
 5308                f
 5309            )ˇ»
 5310        );
 5311    "});
 5312    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5313    cx.assert_editor_state(indoc! {"
 5314        const a: B = (
 5315            c(),
 5316            ˇ
 5317        );
 5318    "});
 5319
 5320    // Paste it at the same position.
 5321    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5322    cx.assert_editor_state(indoc! {"
 5323        const a: B = (
 5324            c(),
 5325            d(
 5326                e,
 5327                f
 5328 5329        );
 5330    "});
 5331
 5332    // Paste it at a line with a lower indent level.
 5333    cx.set_state(indoc! {"
 5334        ˇ
 5335        const a: B = (
 5336            c(),
 5337        );
 5338    "});
 5339    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5340    cx.assert_editor_state(indoc! {"
 5341        d(
 5342            e,
 5343            f
 5344 5345        const a: B = (
 5346            c(),
 5347        );
 5348    "});
 5349
 5350    // Cut an indented block, with the leading whitespace.
 5351    cx.set_state(indoc! {"
 5352        const a: B = (
 5353            c(),
 5354        «    d(
 5355                e,
 5356                f
 5357            )
 5358        ˇ»);
 5359    "});
 5360    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5361    cx.assert_editor_state(indoc! {"
 5362        const a: B = (
 5363            c(),
 5364        ˇ);
 5365    "});
 5366
 5367    // Paste it at the same position.
 5368    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5369    cx.assert_editor_state(indoc! {"
 5370        const a: B = (
 5371            c(),
 5372            d(
 5373                e,
 5374                f
 5375            )
 5376        ˇ);
 5377    "});
 5378
 5379    // Paste it at a line with a higher indent level.
 5380    cx.set_state(indoc! {"
 5381        const a: B = (
 5382            c(),
 5383            d(
 5384                e,
 5385 5386            )
 5387        );
 5388    "});
 5389    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5390    cx.assert_editor_state(indoc! {"
 5391        const a: B = (
 5392            c(),
 5393            d(
 5394                e,
 5395                f    d(
 5396                    e,
 5397                    f
 5398                )
 5399        ˇ
 5400            )
 5401        );
 5402    "});
 5403
 5404    // Copy an indented block, starting mid-line
 5405    cx.set_state(indoc! {"
 5406        const a: B = (
 5407            c(),
 5408            somethin«g(
 5409                e,
 5410                f
 5411            )ˇ»
 5412        );
 5413    "});
 5414    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5415
 5416    // Paste it on a line with a lower indent level
 5417    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5418    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5419    cx.assert_editor_state(indoc! {"
 5420        const a: B = (
 5421            c(),
 5422            something(
 5423                e,
 5424                f
 5425            )
 5426        );
 5427        g(
 5428            e,
 5429            f
 5430"});
 5431}
 5432
 5433#[gpui::test]
 5434async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5435    init_test(cx, |_| {});
 5436
 5437    cx.write_to_clipboard(ClipboardItem::new_string(
 5438        "    d(\n        e\n    );\n".into(),
 5439    ));
 5440
 5441    let mut cx = EditorTestContext::new(cx).await;
 5442    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5443
 5444    cx.set_state(indoc! {"
 5445        fn a() {
 5446            b();
 5447            if c() {
 5448                ˇ
 5449            }
 5450        }
 5451    "});
 5452
 5453    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5454    cx.assert_editor_state(indoc! {"
 5455        fn a() {
 5456            b();
 5457            if c() {
 5458                d(
 5459                    e
 5460                );
 5461        ˇ
 5462            }
 5463        }
 5464    "});
 5465
 5466    cx.set_state(indoc! {"
 5467        fn a() {
 5468            b();
 5469            ˇ
 5470        }
 5471    "});
 5472
 5473    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5474    cx.assert_editor_state(indoc! {"
 5475        fn a() {
 5476            b();
 5477            d(
 5478                e
 5479            );
 5480        ˇ
 5481        }
 5482    "});
 5483}
 5484
 5485#[gpui::test]
 5486fn test_select_all(cx: &mut TestAppContext) {
 5487    init_test(cx, |_| {});
 5488
 5489    let editor = cx.add_window(|window, cx| {
 5490        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5491        build_editor(buffer, window, cx)
 5492    });
 5493    _ = editor.update(cx, |editor, window, cx| {
 5494        editor.select_all(&SelectAll, window, cx);
 5495        assert_eq!(
 5496            editor.selections.display_ranges(cx),
 5497            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5498        );
 5499    });
 5500}
 5501
 5502#[gpui::test]
 5503fn test_select_line(cx: &mut TestAppContext) {
 5504    init_test(cx, |_| {});
 5505
 5506    let editor = cx.add_window(|window, cx| {
 5507        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5508        build_editor(buffer, window, cx)
 5509    });
 5510    _ = editor.update(cx, |editor, window, cx| {
 5511        editor.change_selections(None, window, cx, |s| {
 5512            s.select_display_ranges([
 5513                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5514                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5515                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5516                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5517            ])
 5518        });
 5519        editor.select_line(&SelectLine, window, cx);
 5520        assert_eq!(
 5521            editor.selections.display_ranges(cx),
 5522            vec![
 5523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5524                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5525            ]
 5526        );
 5527    });
 5528
 5529    _ = editor.update(cx, |editor, window, cx| {
 5530        editor.select_line(&SelectLine, window, cx);
 5531        assert_eq!(
 5532            editor.selections.display_ranges(cx),
 5533            vec![
 5534                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5535                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5536            ]
 5537        );
 5538    });
 5539
 5540    _ = editor.update(cx, |editor, window, cx| {
 5541        editor.select_line(&SelectLine, window, cx);
 5542        assert_eq!(
 5543            editor.selections.display_ranges(cx),
 5544            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5545        );
 5546    });
 5547}
 5548
 5549#[gpui::test]
 5550async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5551    init_test(cx, |_| {});
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    #[track_caller]
 5555    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5556        cx.set_state(initial_state);
 5557        cx.update_editor(|e, window, cx| {
 5558            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5559        });
 5560        cx.assert_editor_state(expected_state);
 5561    }
 5562
 5563    // Selection starts and ends at the middle of lines, left-to-right
 5564    test(
 5565        &mut cx,
 5566        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5567        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5568    );
 5569    // Same thing, right-to-left
 5570    test(
 5571        &mut cx,
 5572        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5573        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5574    );
 5575
 5576    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5577    test(
 5578        &mut cx,
 5579        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5580        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5581    );
 5582    // Same thing, right-to-left
 5583    test(
 5584        &mut cx,
 5585        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5586        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5587    );
 5588
 5589    // Whole buffer, left-to-right, last line ends with newline
 5590    test(
 5591        &mut cx,
 5592        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5593        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5594    );
 5595    // Same thing, right-to-left
 5596    test(
 5597        &mut cx,
 5598        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5599        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5600    );
 5601
 5602    // Starts at the end of a line, ends at the start of another
 5603    test(
 5604        &mut cx,
 5605        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5606        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5607    );
 5608}
 5609
 5610#[gpui::test]
 5611async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5612    init_test(cx, |_| {});
 5613
 5614    let editor = cx.add_window(|window, cx| {
 5615        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5616        build_editor(buffer, window, cx)
 5617    });
 5618
 5619    // setup
 5620    _ = editor.update(cx, |editor, window, cx| {
 5621        editor.fold_creases(
 5622            vec![
 5623                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5624                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5625                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5626            ],
 5627            true,
 5628            window,
 5629            cx,
 5630        );
 5631        assert_eq!(
 5632            editor.display_text(cx),
 5633            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5634        );
 5635    });
 5636
 5637    _ = editor.update(cx, |editor, window, cx| {
 5638        editor.change_selections(None, window, cx, |s| {
 5639            s.select_display_ranges([
 5640                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5641                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5642                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5643                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5644            ])
 5645        });
 5646        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5647        assert_eq!(
 5648            editor.display_text(cx),
 5649            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5650        );
 5651    });
 5652    EditorTestContext::for_editor(editor, cx)
 5653        .await
 5654        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5655
 5656    _ = editor.update(cx, |editor, window, cx| {
 5657        editor.change_selections(None, window, cx, |s| {
 5658            s.select_display_ranges([
 5659                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5660            ])
 5661        });
 5662        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5663        assert_eq!(
 5664            editor.display_text(cx),
 5665            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5666        );
 5667        assert_eq!(
 5668            editor.selections.display_ranges(cx),
 5669            [
 5670                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5671                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5672                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5673                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5674                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5675                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5676                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5677            ]
 5678        );
 5679    });
 5680    EditorTestContext::for_editor(editor, cx)
 5681        .await
 5682        .assert_editor_state(
 5683            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5684        );
 5685}
 5686
 5687#[gpui::test]
 5688async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5689    init_test(cx, |_| {});
 5690
 5691    let mut cx = EditorTestContext::new(cx).await;
 5692
 5693    cx.set_state(indoc!(
 5694        r#"abc
 5695           defˇghi
 5696
 5697           jk
 5698           nlmo
 5699           "#
 5700    ));
 5701
 5702    cx.update_editor(|editor, window, cx| {
 5703        editor.add_selection_above(&Default::default(), window, cx);
 5704    });
 5705
 5706    cx.assert_editor_state(indoc!(
 5707        r#"abcˇ
 5708           defˇghi
 5709
 5710           jk
 5711           nlmo
 5712           "#
 5713    ));
 5714
 5715    cx.update_editor(|editor, window, cx| {
 5716        editor.add_selection_above(&Default::default(), window, cx);
 5717    });
 5718
 5719    cx.assert_editor_state(indoc!(
 5720        r#"abcˇ
 5721            defˇghi
 5722
 5723            jk
 5724            nlmo
 5725            "#
 5726    ));
 5727
 5728    cx.update_editor(|editor, window, cx| {
 5729        editor.add_selection_below(&Default::default(), window, cx);
 5730    });
 5731
 5732    cx.assert_editor_state(indoc!(
 5733        r#"abc
 5734           defˇghi
 5735
 5736           jk
 5737           nlmo
 5738           "#
 5739    ));
 5740
 5741    cx.update_editor(|editor, window, cx| {
 5742        editor.undo_selection(&Default::default(), window, cx);
 5743    });
 5744
 5745    cx.assert_editor_state(indoc!(
 5746        r#"abcˇ
 5747           defˇghi
 5748
 5749           jk
 5750           nlmo
 5751           "#
 5752    ));
 5753
 5754    cx.update_editor(|editor, window, cx| {
 5755        editor.redo_selection(&Default::default(), window, cx);
 5756    });
 5757
 5758    cx.assert_editor_state(indoc!(
 5759        r#"abc
 5760           defˇghi
 5761
 5762           jk
 5763           nlmo
 5764           "#
 5765    ));
 5766
 5767    cx.update_editor(|editor, window, cx| {
 5768        editor.add_selection_below(&Default::default(), window, cx);
 5769    });
 5770
 5771    cx.assert_editor_state(indoc!(
 5772        r#"abc
 5773           defˇghi
 5774
 5775           jk
 5776           nlmˇo
 5777           "#
 5778    ));
 5779
 5780    cx.update_editor(|editor, window, cx| {
 5781        editor.add_selection_below(&Default::default(), window, cx);
 5782    });
 5783
 5784    cx.assert_editor_state(indoc!(
 5785        r#"abc
 5786           defˇghi
 5787
 5788           jk
 5789           nlmˇo
 5790           "#
 5791    ));
 5792
 5793    // change selections
 5794    cx.set_state(indoc!(
 5795        r#"abc
 5796           def«ˇg»hi
 5797
 5798           jk
 5799           nlmo
 5800           "#
 5801    ));
 5802
 5803    cx.update_editor(|editor, window, cx| {
 5804        editor.add_selection_below(&Default::default(), window, cx);
 5805    });
 5806
 5807    cx.assert_editor_state(indoc!(
 5808        r#"abc
 5809           def«ˇg»hi
 5810
 5811           jk
 5812           nlm«ˇo»
 5813           "#
 5814    ));
 5815
 5816    cx.update_editor(|editor, window, cx| {
 5817        editor.add_selection_below(&Default::default(), window, cx);
 5818    });
 5819
 5820    cx.assert_editor_state(indoc!(
 5821        r#"abc
 5822           def«ˇg»hi
 5823
 5824           jk
 5825           nlm«ˇo»
 5826           "#
 5827    ));
 5828
 5829    cx.update_editor(|editor, window, cx| {
 5830        editor.add_selection_above(&Default::default(), window, cx);
 5831    });
 5832
 5833    cx.assert_editor_state(indoc!(
 5834        r#"abc
 5835           def«ˇg»hi
 5836
 5837           jk
 5838           nlmo
 5839           "#
 5840    ));
 5841
 5842    cx.update_editor(|editor, window, cx| {
 5843        editor.add_selection_above(&Default::default(), window, cx);
 5844    });
 5845
 5846    cx.assert_editor_state(indoc!(
 5847        r#"abc
 5848           def«ˇg»hi
 5849
 5850           jk
 5851           nlmo
 5852           "#
 5853    ));
 5854
 5855    // Change selections again
 5856    cx.set_state(indoc!(
 5857        r#"a«bc
 5858           defgˇ»hi
 5859
 5860           jk
 5861           nlmo
 5862           "#
 5863    ));
 5864
 5865    cx.update_editor(|editor, window, cx| {
 5866        editor.add_selection_below(&Default::default(), window, cx);
 5867    });
 5868
 5869    cx.assert_editor_state(indoc!(
 5870        r#"a«bcˇ»
 5871           d«efgˇ»hi
 5872
 5873           j«kˇ»
 5874           nlmo
 5875           "#
 5876    ));
 5877
 5878    cx.update_editor(|editor, window, cx| {
 5879        editor.add_selection_below(&Default::default(), window, cx);
 5880    });
 5881    cx.assert_editor_state(indoc!(
 5882        r#"a«bcˇ»
 5883           d«efgˇ»hi
 5884
 5885           j«kˇ»
 5886           n«lmoˇ»
 5887           "#
 5888    ));
 5889    cx.update_editor(|editor, window, cx| {
 5890        editor.add_selection_above(&Default::default(), window, cx);
 5891    });
 5892
 5893    cx.assert_editor_state(indoc!(
 5894        r#"a«bcˇ»
 5895           d«efgˇ»hi
 5896
 5897           j«kˇ»
 5898           nlmo
 5899           "#
 5900    ));
 5901
 5902    // Change selections again
 5903    cx.set_state(indoc!(
 5904        r#"abc
 5905           d«ˇefghi
 5906
 5907           jk
 5908           nlm»o
 5909           "#
 5910    ));
 5911
 5912    cx.update_editor(|editor, window, cx| {
 5913        editor.add_selection_above(&Default::default(), window, cx);
 5914    });
 5915
 5916    cx.assert_editor_state(indoc!(
 5917        r#"a«ˇbc»
 5918           d«ˇef»ghi
 5919
 5920           j«ˇk»
 5921           n«ˇlm»o
 5922           "#
 5923    ));
 5924
 5925    cx.update_editor(|editor, window, cx| {
 5926        editor.add_selection_below(&Default::default(), window, cx);
 5927    });
 5928
 5929    cx.assert_editor_state(indoc!(
 5930        r#"abc
 5931           d«ˇef»ghi
 5932
 5933           j«ˇk»
 5934           n«ˇlm»o
 5935           "#
 5936    ));
 5937}
 5938
 5939#[gpui::test]
 5940async fn test_select_next(cx: &mut TestAppContext) {
 5941    init_test(cx, |_| {});
 5942
 5943    let mut cx = EditorTestContext::new(cx).await;
 5944    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5945
 5946    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5947        .unwrap();
 5948    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5949
 5950    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5951        .unwrap();
 5952    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5953
 5954    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5955    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5956
 5957    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5958    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5959
 5960    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5961        .unwrap();
 5962    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5963
 5964    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5965        .unwrap();
 5966    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5967
 5968    // Test selection direction should be preserved
 5969    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5970
 5971    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5972        .unwrap();
 5973    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5974}
 5975
 5976#[gpui::test]
 5977async fn test_select_all_matches(cx: &mut TestAppContext) {
 5978    init_test(cx, |_| {});
 5979
 5980    let mut cx = EditorTestContext::new(cx).await;
 5981
 5982    // Test caret-only selections
 5983    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5984    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5985        .unwrap();
 5986    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5987
 5988    // Test left-to-right selections
 5989    cx.set_state("abc\n«abcˇ»\nabc");
 5990    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5991        .unwrap();
 5992    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5993
 5994    // Test right-to-left selections
 5995    cx.set_state("abc\n«ˇabc»\nabc");
 5996    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5997        .unwrap();
 5998    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5999
 6000    // Test selecting whitespace with caret selection
 6001    cx.set_state("abc\nˇ   abc\nabc");
 6002    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6003        .unwrap();
 6004    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6005
 6006    // Test selecting whitespace with left-to-right selection
 6007    cx.set_state("abc\n«ˇ  »abc\nabc");
 6008    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6009        .unwrap();
 6010    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6011
 6012    // Test no matches with right-to-left selection
 6013    cx.set_state("abc\n«  ˇ»abc\nabc");
 6014    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6015        .unwrap();
 6016    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6017}
 6018
 6019#[gpui::test]
 6020async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6021    init_test(cx, |_| {});
 6022
 6023    let mut cx = EditorTestContext::new(cx).await;
 6024
 6025    let large_body_1 = "\nd".repeat(200);
 6026    let large_body_2 = "\ne".repeat(200);
 6027
 6028    cx.set_state(&format!(
 6029        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6030    ));
 6031    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6032        let scroll_position = editor.scroll_position(cx);
 6033        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6034        scroll_position
 6035    });
 6036
 6037    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6038        .unwrap();
 6039    cx.assert_editor_state(&format!(
 6040        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6041    ));
 6042    let scroll_position_after_selection =
 6043        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6044    assert_eq!(
 6045        initial_scroll_position, scroll_position_after_selection,
 6046        "Scroll position should not change after selecting all matches"
 6047    );
 6048}
 6049
 6050#[gpui::test]
 6051async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6052    init_test(cx, |_| {});
 6053
 6054    let mut cx = EditorLspTestContext::new_rust(
 6055        lsp::ServerCapabilities {
 6056            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6057            ..Default::default()
 6058        },
 6059        cx,
 6060    )
 6061    .await;
 6062
 6063    cx.set_state(indoc! {"
 6064        line 1
 6065        line 2
 6066        linˇe 3
 6067        line 4
 6068        line 5
 6069    "});
 6070
 6071    // Make an edit
 6072    cx.update_editor(|editor, window, cx| {
 6073        editor.handle_input("X", window, cx);
 6074    });
 6075
 6076    // Move cursor to a different position
 6077    cx.update_editor(|editor, window, cx| {
 6078        editor.change_selections(None, window, cx, |s| {
 6079            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6080        });
 6081    });
 6082
 6083    cx.assert_editor_state(indoc! {"
 6084        line 1
 6085        line 2
 6086        linXe 3
 6087        line 4
 6088        liˇne 5
 6089    "});
 6090
 6091    cx.lsp
 6092        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6093            Ok(Some(vec![lsp::TextEdit::new(
 6094                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6095                "PREFIX ".to_string(),
 6096            )]))
 6097        });
 6098
 6099    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6100        .unwrap()
 6101        .await
 6102        .unwrap();
 6103
 6104    cx.assert_editor_state(indoc! {"
 6105        PREFIX line 1
 6106        line 2
 6107        linXe 3
 6108        line 4
 6109        liˇne 5
 6110    "});
 6111
 6112    // Undo formatting
 6113    cx.update_editor(|editor, window, cx| {
 6114        editor.undo(&Default::default(), window, cx);
 6115    });
 6116
 6117    // Verify cursor moved back to position after edit
 6118    cx.assert_editor_state(indoc! {"
 6119        line 1
 6120        line 2
 6121        linXˇe 3
 6122        line 4
 6123        line 5
 6124    "});
 6125}
 6126
 6127#[gpui::test]
 6128async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6129    init_test(cx, |_| {});
 6130
 6131    let mut cx = EditorTestContext::new(cx).await;
 6132    cx.set_state(
 6133        r#"let foo = 2;
 6134lˇet foo = 2;
 6135let fooˇ = 2;
 6136let foo = 2;
 6137let foo = ˇ2;"#,
 6138    );
 6139
 6140    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6141        .unwrap();
 6142    cx.assert_editor_state(
 6143        r#"let foo = 2;
 6144«letˇ» foo = 2;
 6145let «fooˇ» = 2;
 6146let foo = 2;
 6147let foo = «2ˇ»;"#,
 6148    );
 6149
 6150    // noop for multiple selections with different contents
 6151    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6152        .unwrap();
 6153    cx.assert_editor_state(
 6154        r#"let foo = 2;
 6155«letˇ» foo = 2;
 6156let «fooˇ» = 2;
 6157let foo = 2;
 6158let foo = «2ˇ»;"#,
 6159    );
 6160
 6161    // Test last selection direction should be preserved
 6162    cx.set_state(
 6163        r#"let foo = 2;
 6164let foo = 2;
 6165let «fooˇ» = 2;
 6166let «ˇfoo» = 2;
 6167let foo = 2;"#,
 6168    );
 6169
 6170    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6171        .unwrap();
 6172    cx.assert_editor_state(
 6173        r#"let foo = 2;
 6174let foo = 2;
 6175let «fooˇ» = 2;
 6176let «ˇfoo» = 2;
 6177let «ˇfoo» = 2;"#,
 6178    );
 6179}
 6180
 6181#[gpui::test]
 6182async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6183    init_test(cx, |_| {});
 6184
 6185    let mut cx =
 6186        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6187
 6188    cx.assert_editor_state(indoc! {"
 6189        ˇbbb
 6190        ccc
 6191
 6192        bbb
 6193        ccc
 6194        "});
 6195    cx.dispatch_action(SelectPrevious::default());
 6196    cx.assert_editor_state(indoc! {"
 6197                «bbbˇ»
 6198                ccc
 6199
 6200                bbb
 6201                ccc
 6202                "});
 6203    cx.dispatch_action(SelectPrevious::default());
 6204    cx.assert_editor_state(indoc! {"
 6205                «bbbˇ»
 6206                ccc
 6207
 6208                «bbbˇ»
 6209                ccc
 6210                "});
 6211}
 6212
 6213#[gpui::test]
 6214async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6215    init_test(cx, |_| {});
 6216
 6217    let mut cx = EditorTestContext::new(cx).await;
 6218    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6219
 6220    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6221        .unwrap();
 6222    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6223
 6224    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6225        .unwrap();
 6226    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6227
 6228    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6229    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6230
 6231    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6232    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6233
 6234    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6235        .unwrap();
 6236    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6237
 6238    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6239        .unwrap();
 6240    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6241}
 6242
 6243#[gpui::test]
 6244async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6245    init_test(cx, |_| {});
 6246
 6247    let mut cx = EditorTestContext::new(cx).await;
 6248    cx.set_state("");
 6249
 6250    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6251        .unwrap();
 6252    cx.assert_editor_state("«aˇ»");
 6253    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6254        .unwrap();
 6255    cx.assert_editor_state("«aˇ»");
 6256}
 6257
 6258#[gpui::test]
 6259async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6260    init_test(cx, |_| {});
 6261
 6262    let mut cx = EditorTestContext::new(cx).await;
 6263    cx.set_state(
 6264        r#"let foo = 2;
 6265lˇet foo = 2;
 6266let fooˇ = 2;
 6267let foo = 2;
 6268let foo = ˇ2;"#,
 6269    );
 6270
 6271    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6272        .unwrap();
 6273    cx.assert_editor_state(
 6274        r#"let foo = 2;
 6275«letˇ» foo = 2;
 6276let «fooˇ» = 2;
 6277let foo = 2;
 6278let foo = «2ˇ»;"#,
 6279    );
 6280
 6281    // noop for multiple selections with different contents
 6282    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6283        .unwrap();
 6284    cx.assert_editor_state(
 6285        r#"let foo = 2;
 6286«letˇ» foo = 2;
 6287let «fooˇ» = 2;
 6288let foo = 2;
 6289let foo = «2ˇ»;"#,
 6290    );
 6291}
 6292
 6293#[gpui::test]
 6294async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6295    init_test(cx, |_| {});
 6296
 6297    let mut cx = EditorTestContext::new(cx).await;
 6298    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6299
 6300    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6301        .unwrap();
 6302    // selection direction is preserved
 6303    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6304
 6305    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6306        .unwrap();
 6307    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6308
 6309    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6310    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6311
 6312    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6313    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6314
 6315    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6316        .unwrap();
 6317    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6318
 6319    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6320        .unwrap();
 6321    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6322}
 6323
 6324#[gpui::test]
 6325async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6326    init_test(cx, |_| {});
 6327
 6328    let language = Arc::new(Language::new(
 6329        LanguageConfig::default(),
 6330        Some(tree_sitter_rust::LANGUAGE.into()),
 6331    ));
 6332
 6333    let text = r#"
 6334        use mod1::mod2::{mod3, mod4};
 6335
 6336        fn fn_1(param1: bool, param2: &str) {
 6337            let var1 = "text";
 6338        }
 6339    "#
 6340    .unindent();
 6341
 6342    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6343    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6344    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6345
 6346    editor
 6347        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6348        .await;
 6349
 6350    editor.update_in(cx, |editor, window, cx| {
 6351        editor.change_selections(None, window, cx, |s| {
 6352            s.select_display_ranges([
 6353                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6354                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6355                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6356            ]);
 6357        });
 6358        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6359    });
 6360    editor.update(cx, |editor, cx| {
 6361        assert_text_with_selections(
 6362            editor,
 6363            indoc! {r#"
 6364                use mod1::mod2::{mod3, «mod4ˇ»};
 6365
 6366                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6367                    let var1 = "«ˇtext»";
 6368                }
 6369            "#},
 6370            cx,
 6371        );
 6372    });
 6373
 6374    editor.update_in(cx, |editor, window, cx| {
 6375        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6376    });
 6377    editor.update(cx, |editor, cx| {
 6378        assert_text_with_selections(
 6379            editor,
 6380            indoc! {r#"
 6381                use mod1::mod2::«{mod3, mod4}ˇ»;
 6382
 6383                «ˇfn fn_1(param1: bool, param2: &str) {
 6384                    let var1 = "text";
 6385 6386            "#},
 6387            cx,
 6388        );
 6389    });
 6390
 6391    editor.update_in(cx, |editor, window, cx| {
 6392        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6393    });
 6394    assert_eq!(
 6395        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6396        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6397    );
 6398
 6399    // Trying to expand the selected syntax node one more time has no effect.
 6400    editor.update_in(cx, |editor, window, cx| {
 6401        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6402    });
 6403    assert_eq!(
 6404        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6405        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6406    );
 6407
 6408    editor.update_in(cx, |editor, window, cx| {
 6409        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6410    });
 6411    editor.update(cx, |editor, cx| {
 6412        assert_text_with_selections(
 6413            editor,
 6414            indoc! {r#"
 6415                use mod1::mod2::«{mod3, mod4}ˇ»;
 6416
 6417                «ˇfn fn_1(param1: bool, param2: &str) {
 6418                    let var1 = "text";
 6419 6420            "#},
 6421            cx,
 6422        );
 6423    });
 6424
 6425    editor.update_in(cx, |editor, window, cx| {
 6426        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6427    });
 6428    editor.update(cx, |editor, cx| {
 6429        assert_text_with_selections(
 6430            editor,
 6431            indoc! {r#"
 6432                use mod1::mod2::{mod3, «mod4ˇ»};
 6433
 6434                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6435                    let var1 = "«ˇtext»";
 6436                }
 6437            "#},
 6438            cx,
 6439        );
 6440    });
 6441
 6442    editor.update_in(cx, |editor, window, cx| {
 6443        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6444    });
 6445    editor.update(cx, |editor, cx| {
 6446        assert_text_with_selections(
 6447            editor,
 6448            indoc! {r#"
 6449                use mod1::mod2::{mod3, mo«ˇ»d4};
 6450
 6451                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6452                    let var1 = "te«ˇ»xt";
 6453                }
 6454            "#},
 6455            cx,
 6456        );
 6457    });
 6458
 6459    // Trying to shrink the selected syntax node one more time has no effect.
 6460    editor.update_in(cx, |editor, window, cx| {
 6461        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6462    });
 6463    editor.update_in(cx, |editor, _, cx| {
 6464        assert_text_with_selections(
 6465            editor,
 6466            indoc! {r#"
 6467                use mod1::mod2::{mod3, mo«ˇ»d4};
 6468
 6469                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6470                    let var1 = "te«ˇ»xt";
 6471                }
 6472            "#},
 6473            cx,
 6474        );
 6475    });
 6476
 6477    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6478    // a fold.
 6479    editor.update_in(cx, |editor, window, cx| {
 6480        editor.fold_creases(
 6481            vec![
 6482                Crease::simple(
 6483                    Point::new(0, 21)..Point::new(0, 24),
 6484                    FoldPlaceholder::test(),
 6485                ),
 6486                Crease::simple(
 6487                    Point::new(3, 20)..Point::new(3, 22),
 6488                    FoldPlaceholder::test(),
 6489                ),
 6490            ],
 6491            true,
 6492            window,
 6493            cx,
 6494        );
 6495        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6496    });
 6497    editor.update(cx, |editor, cx| {
 6498        assert_text_with_selections(
 6499            editor,
 6500            indoc! {r#"
 6501                use mod1::mod2::«{mod3, mod4}ˇ»;
 6502
 6503                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6504                    let var1 = "«ˇtext»";
 6505                }
 6506            "#},
 6507            cx,
 6508        );
 6509    });
 6510}
 6511
 6512#[gpui::test]
 6513async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6514    init_test(cx, |_| {});
 6515
 6516    let language = Arc::new(Language::new(
 6517        LanguageConfig::default(),
 6518        Some(tree_sitter_rust::LANGUAGE.into()),
 6519    ));
 6520
 6521    let text = "let a = 2;";
 6522
 6523    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6524    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6525    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6526
 6527    editor
 6528        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6529        .await;
 6530
 6531    // Test case 1: Cursor at end of word
 6532    editor.update_in(cx, |editor, window, cx| {
 6533        editor.change_selections(None, window, cx, |s| {
 6534            s.select_display_ranges([
 6535                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6536            ]);
 6537        });
 6538    });
 6539    editor.update(cx, |editor, cx| {
 6540        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6541    });
 6542    editor.update_in(cx, |editor, window, cx| {
 6543        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6544    });
 6545    editor.update(cx, |editor, cx| {
 6546        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6547    });
 6548    editor.update_in(cx, |editor, window, cx| {
 6549        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6550    });
 6551    editor.update(cx, |editor, cx| {
 6552        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6553    });
 6554
 6555    // Test case 2: Cursor at end of statement
 6556    editor.update_in(cx, |editor, window, cx| {
 6557        editor.change_selections(None, window, cx, |s| {
 6558            s.select_display_ranges([
 6559                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6560            ]);
 6561        });
 6562    });
 6563    editor.update(cx, |editor, cx| {
 6564        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6565    });
 6566    editor.update_in(cx, |editor, window, cx| {
 6567        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6568    });
 6569    editor.update(cx, |editor, cx| {
 6570        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6571    });
 6572}
 6573
 6574#[gpui::test]
 6575async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6576    init_test(cx, |_| {});
 6577
 6578    let language = Arc::new(Language::new(
 6579        LanguageConfig::default(),
 6580        Some(tree_sitter_rust::LANGUAGE.into()),
 6581    ));
 6582
 6583    let text = r#"
 6584        use mod1::mod2::{mod3, mod4};
 6585
 6586        fn fn_1(param1: bool, param2: &str) {
 6587            let var1 = "hello world";
 6588        }
 6589    "#
 6590    .unindent();
 6591
 6592    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6593    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6594    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6595
 6596    editor
 6597        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6598        .await;
 6599
 6600    // Test 1: Cursor on a letter of a string word
 6601    editor.update_in(cx, |editor, window, cx| {
 6602        editor.change_selections(None, window, cx, |s| {
 6603            s.select_display_ranges([
 6604                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6605            ]);
 6606        });
 6607    });
 6608    editor.update_in(cx, |editor, window, cx| {
 6609        assert_text_with_selections(
 6610            editor,
 6611            indoc! {r#"
 6612                use mod1::mod2::{mod3, mod4};
 6613
 6614                fn fn_1(param1: bool, param2: &str) {
 6615                    let var1 = "hˇello world";
 6616                }
 6617            "#},
 6618            cx,
 6619        );
 6620        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6621        assert_text_with_selections(
 6622            editor,
 6623            indoc! {r#"
 6624                use mod1::mod2::{mod3, mod4};
 6625
 6626                fn fn_1(param1: bool, param2: &str) {
 6627                    let var1 = "«ˇhello» world";
 6628                }
 6629            "#},
 6630            cx,
 6631        );
 6632    });
 6633
 6634    // Test 2: Partial selection within a word
 6635    editor.update_in(cx, |editor, window, cx| {
 6636        editor.change_selections(None, window, cx, |s| {
 6637            s.select_display_ranges([
 6638                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6639            ]);
 6640        });
 6641    });
 6642    editor.update_in(cx, |editor, window, cx| {
 6643        assert_text_with_selections(
 6644            editor,
 6645            indoc! {r#"
 6646                use mod1::mod2::{mod3, mod4};
 6647
 6648                fn fn_1(param1: bool, param2: &str) {
 6649                    let var1 = "h«elˇ»lo world";
 6650                }
 6651            "#},
 6652            cx,
 6653        );
 6654        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6655        assert_text_with_selections(
 6656            editor,
 6657            indoc! {r#"
 6658                use mod1::mod2::{mod3, mod4};
 6659
 6660                fn fn_1(param1: bool, param2: &str) {
 6661                    let var1 = "«ˇhello» world";
 6662                }
 6663            "#},
 6664            cx,
 6665        );
 6666    });
 6667
 6668    // Test 3: Complete word already selected
 6669    editor.update_in(cx, |editor, window, cx| {
 6670        editor.change_selections(None, window, cx, |s| {
 6671            s.select_display_ranges([
 6672                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6673            ]);
 6674        });
 6675    });
 6676    editor.update_in(cx, |editor, window, cx| {
 6677        assert_text_with_selections(
 6678            editor,
 6679            indoc! {r#"
 6680                use mod1::mod2::{mod3, mod4};
 6681
 6682                fn fn_1(param1: bool, param2: &str) {
 6683                    let var1 = "«helloˇ» world";
 6684                }
 6685            "#},
 6686            cx,
 6687        );
 6688        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6689        assert_text_with_selections(
 6690            editor,
 6691            indoc! {r#"
 6692                use mod1::mod2::{mod3, mod4};
 6693
 6694                fn fn_1(param1: bool, param2: &str) {
 6695                    let var1 = "«hello worldˇ»";
 6696                }
 6697            "#},
 6698            cx,
 6699        );
 6700    });
 6701
 6702    // Test 4: Selection spanning across words
 6703    editor.update_in(cx, |editor, window, cx| {
 6704        editor.change_selections(None, window, cx, |s| {
 6705            s.select_display_ranges([
 6706                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6707            ]);
 6708        });
 6709    });
 6710    editor.update_in(cx, |editor, window, cx| {
 6711        assert_text_with_selections(
 6712            editor,
 6713            indoc! {r#"
 6714                use mod1::mod2::{mod3, mod4};
 6715
 6716                fn fn_1(param1: bool, param2: &str) {
 6717                    let var1 = "hel«lo woˇ»rld";
 6718                }
 6719            "#},
 6720            cx,
 6721        );
 6722        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6723        assert_text_with_selections(
 6724            editor,
 6725            indoc! {r#"
 6726                use mod1::mod2::{mod3, mod4};
 6727
 6728                fn fn_1(param1: bool, param2: &str) {
 6729                    let var1 = "«ˇhello world»";
 6730                }
 6731            "#},
 6732            cx,
 6733        );
 6734    });
 6735
 6736    // Test 5: Expansion beyond string
 6737    editor.update_in(cx, |editor, window, cx| {
 6738        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6739        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6740        assert_text_with_selections(
 6741            editor,
 6742            indoc! {r#"
 6743                use mod1::mod2::{mod3, mod4};
 6744
 6745                fn fn_1(param1: bool, param2: &str) {
 6746                    «ˇlet var1 = "hello world";»
 6747                }
 6748            "#},
 6749            cx,
 6750        );
 6751    });
 6752}
 6753
 6754#[gpui::test]
 6755async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6756    init_test(cx, |_| {});
 6757
 6758    let base_text = r#"
 6759        impl A {
 6760            // this is an uncommitted comment
 6761
 6762            fn b() {
 6763                c();
 6764            }
 6765
 6766            // this is another uncommitted comment
 6767
 6768            fn d() {
 6769                // e
 6770                // f
 6771            }
 6772        }
 6773
 6774        fn g() {
 6775            // h
 6776        }
 6777    "#
 6778    .unindent();
 6779
 6780    let text = r#"
 6781        ˇimpl A {
 6782
 6783            fn b() {
 6784                c();
 6785            }
 6786
 6787            fn d() {
 6788                // e
 6789                // f
 6790            }
 6791        }
 6792
 6793        fn g() {
 6794            // h
 6795        }
 6796    "#
 6797    .unindent();
 6798
 6799    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6800    cx.set_state(&text);
 6801    cx.set_head_text(&base_text);
 6802    cx.update_editor(|editor, window, cx| {
 6803        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6804    });
 6805
 6806    cx.assert_state_with_diff(
 6807        "
 6808        ˇimpl A {
 6809      -     // this is an uncommitted comment
 6810
 6811            fn b() {
 6812                c();
 6813            }
 6814
 6815      -     // this is another uncommitted comment
 6816      -
 6817            fn d() {
 6818                // e
 6819                // f
 6820            }
 6821        }
 6822
 6823        fn g() {
 6824            // h
 6825        }
 6826    "
 6827        .unindent(),
 6828    );
 6829
 6830    let expected_display_text = "
 6831        impl A {
 6832            // this is an uncommitted comment
 6833
 6834            fn b() {
 6835 6836            }
 6837
 6838            // this is another uncommitted comment
 6839
 6840            fn d() {
 6841 6842            }
 6843        }
 6844
 6845        fn g() {
 6846 6847        }
 6848        "
 6849    .unindent();
 6850
 6851    cx.update_editor(|editor, window, cx| {
 6852        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6853        assert_eq!(editor.display_text(cx), expected_display_text);
 6854    });
 6855}
 6856
 6857#[gpui::test]
 6858async fn test_autoindent(cx: &mut TestAppContext) {
 6859    init_test(cx, |_| {});
 6860
 6861    let language = Arc::new(
 6862        Language::new(
 6863            LanguageConfig {
 6864                brackets: BracketPairConfig {
 6865                    pairs: vec![
 6866                        BracketPair {
 6867                            start: "{".to_string(),
 6868                            end: "}".to_string(),
 6869                            close: false,
 6870                            surround: false,
 6871                            newline: true,
 6872                        },
 6873                        BracketPair {
 6874                            start: "(".to_string(),
 6875                            end: ")".to_string(),
 6876                            close: false,
 6877                            surround: false,
 6878                            newline: true,
 6879                        },
 6880                    ],
 6881                    ..Default::default()
 6882                },
 6883                ..Default::default()
 6884            },
 6885            Some(tree_sitter_rust::LANGUAGE.into()),
 6886        )
 6887        .with_indents_query(
 6888            r#"
 6889                (_ "(" ")" @end) @indent
 6890                (_ "{" "}" @end) @indent
 6891            "#,
 6892        )
 6893        .unwrap(),
 6894    );
 6895
 6896    let text = "fn a() {}";
 6897
 6898    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6899    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6900    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6901    editor
 6902        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6903        .await;
 6904
 6905    editor.update_in(cx, |editor, window, cx| {
 6906        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6907        editor.newline(&Newline, window, cx);
 6908        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6909        assert_eq!(
 6910            editor.selections.ranges(cx),
 6911            &[
 6912                Point::new(1, 4)..Point::new(1, 4),
 6913                Point::new(3, 4)..Point::new(3, 4),
 6914                Point::new(5, 0)..Point::new(5, 0)
 6915            ]
 6916        );
 6917    });
 6918}
 6919
 6920#[gpui::test]
 6921async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6922    init_test(cx, |_| {});
 6923
 6924    {
 6925        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6926        cx.set_state(indoc! {"
 6927            impl A {
 6928
 6929                fn b() {}
 6930
 6931            «fn c() {
 6932
 6933            }ˇ»
 6934            }
 6935        "});
 6936
 6937        cx.update_editor(|editor, window, cx| {
 6938            editor.autoindent(&Default::default(), window, cx);
 6939        });
 6940
 6941        cx.assert_editor_state(indoc! {"
 6942            impl A {
 6943
 6944                fn b() {}
 6945
 6946                «fn c() {
 6947
 6948                }ˇ»
 6949            }
 6950        "});
 6951    }
 6952
 6953    {
 6954        let mut cx = EditorTestContext::new_multibuffer(
 6955            cx,
 6956            [indoc! { "
 6957                impl A {
 6958                «
 6959                // a
 6960                fn b(){}
 6961                »
 6962                «
 6963                    }
 6964                    fn c(){}
 6965                »
 6966            "}],
 6967        );
 6968
 6969        let buffer = cx.update_editor(|editor, _, cx| {
 6970            let buffer = editor.buffer().update(cx, |buffer, _| {
 6971                buffer.all_buffers().iter().next().unwrap().clone()
 6972            });
 6973            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6974            buffer
 6975        });
 6976
 6977        cx.run_until_parked();
 6978        cx.update_editor(|editor, window, cx| {
 6979            editor.select_all(&Default::default(), window, cx);
 6980            editor.autoindent(&Default::default(), window, cx)
 6981        });
 6982        cx.run_until_parked();
 6983
 6984        cx.update(|_, cx| {
 6985            assert_eq!(
 6986                buffer.read(cx).text(),
 6987                indoc! { "
 6988                    impl A {
 6989
 6990                        // a
 6991                        fn b(){}
 6992
 6993
 6994                    }
 6995                    fn c(){}
 6996
 6997                " }
 6998            )
 6999        });
 7000    }
 7001}
 7002
 7003#[gpui::test]
 7004async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7005    init_test(cx, |_| {});
 7006
 7007    let mut cx = EditorTestContext::new(cx).await;
 7008
 7009    let language = Arc::new(Language::new(
 7010        LanguageConfig {
 7011            brackets: BracketPairConfig {
 7012                pairs: vec![
 7013                    BracketPair {
 7014                        start: "{".to_string(),
 7015                        end: "}".to_string(),
 7016                        close: true,
 7017                        surround: true,
 7018                        newline: true,
 7019                    },
 7020                    BracketPair {
 7021                        start: "(".to_string(),
 7022                        end: ")".to_string(),
 7023                        close: true,
 7024                        surround: true,
 7025                        newline: true,
 7026                    },
 7027                    BracketPair {
 7028                        start: "/*".to_string(),
 7029                        end: " */".to_string(),
 7030                        close: true,
 7031                        surround: true,
 7032                        newline: true,
 7033                    },
 7034                    BracketPair {
 7035                        start: "[".to_string(),
 7036                        end: "]".to_string(),
 7037                        close: false,
 7038                        surround: false,
 7039                        newline: true,
 7040                    },
 7041                    BracketPair {
 7042                        start: "\"".to_string(),
 7043                        end: "\"".to_string(),
 7044                        close: true,
 7045                        surround: true,
 7046                        newline: false,
 7047                    },
 7048                    BracketPair {
 7049                        start: "<".to_string(),
 7050                        end: ">".to_string(),
 7051                        close: false,
 7052                        surround: true,
 7053                        newline: true,
 7054                    },
 7055                ],
 7056                ..Default::default()
 7057            },
 7058            autoclose_before: "})]".to_string(),
 7059            ..Default::default()
 7060        },
 7061        Some(tree_sitter_rust::LANGUAGE.into()),
 7062    ));
 7063
 7064    cx.language_registry().add(language.clone());
 7065    cx.update_buffer(|buffer, cx| {
 7066        buffer.set_language(Some(language), cx);
 7067    });
 7068
 7069    cx.set_state(
 7070        &r#"
 7071            🏀ˇ
 7072            εˇ
 7073            ❤️ˇ
 7074        "#
 7075        .unindent(),
 7076    );
 7077
 7078    // autoclose multiple nested brackets at multiple cursors
 7079    cx.update_editor(|editor, window, cx| {
 7080        editor.handle_input("{", window, cx);
 7081        editor.handle_input("{", window, cx);
 7082        editor.handle_input("{", window, cx);
 7083    });
 7084    cx.assert_editor_state(
 7085        &"
 7086            🏀{{{ˇ}}}
 7087            ε{{{ˇ}}}
 7088            ❤️{{{ˇ}}}
 7089        "
 7090        .unindent(),
 7091    );
 7092
 7093    // insert a different closing bracket
 7094    cx.update_editor(|editor, window, cx| {
 7095        editor.handle_input(")", window, cx);
 7096    });
 7097    cx.assert_editor_state(
 7098        &"
 7099            🏀{{{)ˇ}}}
 7100            ε{{{)ˇ}}}
 7101            ❤️{{{)ˇ}}}
 7102        "
 7103        .unindent(),
 7104    );
 7105
 7106    // skip over the auto-closed brackets when typing a closing bracket
 7107    cx.update_editor(|editor, window, cx| {
 7108        editor.move_right(&MoveRight, window, cx);
 7109        editor.handle_input("}", window, cx);
 7110        editor.handle_input("}", window, cx);
 7111        editor.handle_input("}", window, cx);
 7112    });
 7113    cx.assert_editor_state(
 7114        &"
 7115            🏀{{{)}}}}ˇ
 7116            ε{{{)}}}}ˇ
 7117            ❤️{{{)}}}}ˇ
 7118        "
 7119        .unindent(),
 7120    );
 7121
 7122    // autoclose multi-character pairs
 7123    cx.set_state(
 7124        &"
 7125            ˇ
 7126            ˇ
 7127        "
 7128        .unindent(),
 7129    );
 7130    cx.update_editor(|editor, window, cx| {
 7131        editor.handle_input("/", window, cx);
 7132        editor.handle_input("*", window, cx);
 7133    });
 7134    cx.assert_editor_state(
 7135        &"
 7136            /*ˇ */
 7137            /*ˇ */
 7138        "
 7139        .unindent(),
 7140    );
 7141
 7142    // one cursor autocloses a multi-character pair, one cursor
 7143    // does not autoclose.
 7144    cx.set_state(
 7145        &"
 7146 7147            ˇ
 7148        "
 7149        .unindent(),
 7150    );
 7151    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7152    cx.assert_editor_state(
 7153        &"
 7154            /*ˇ */
 7155 7156        "
 7157        .unindent(),
 7158    );
 7159
 7160    // Don't autoclose if the next character isn't whitespace and isn't
 7161    // listed in the language's "autoclose_before" section.
 7162    cx.set_state("ˇa b");
 7163    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7164    cx.assert_editor_state("{ˇa b");
 7165
 7166    // Don't autoclose if `close` is false for the bracket pair
 7167    cx.set_state("ˇ");
 7168    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7169    cx.assert_editor_state("");
 7170
 7171    // Surround with brackets if text is selected
 7172    cx.set_state("«aˇ» b");
 7173    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7174    cx.assert_editor_state("{«aˇ»} b");
 7175
 7176    // Autoclose when not immediately after a word character
 7177    cx.set_state("a ˇ");
 7178    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7179    cx.assert_editor_state("a \"ˇ\"");
 7180
 7181    // Autoclose pair where the start and end characters are the same
 7182    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7183    cx.assert_editor_state("a \"\"ˇ");
 7184
 7185    // Don't autoclose when immediately after a word character
 7186    cx.set_state("");
 7187    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7188    cx.assert_editor_state("a\"ˇ");
 7189
 7190    // Do autoclose when after a non-word character
 7191    cx.set_state("");
 7192    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7193    cx.assert_editor_state("{\"ˇ\"");
 7194
 7195    // Non identical pairs autoclose regardless of preceding character
 7196    cx.set_state("");
 7197    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7198    cx.assert_editor_state("a{ˇ}");
 7199
 7200    // Don't autoclose pair if autoclose is disabled
 7201    cx.set_state("ˇ");
 7202    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7203    cx.assert_editor_state("");
 7204
 7205    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7206    cx.set_state("«aˇ» b");
 7207    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7208    cx.assert_editor_state("<«aˇ»> b");
 7209}
 7210
 7211#[gpui::test]
 7212async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7213    init_test(cx, |settings| {
 7214        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7215    });
 7216
 7217    let mut cx = EditorTestContext::new(cx).await;
 7218
 7219    let language = Arc::new(Language::new(
 7220        LanguageConfig {
 7221            brackets: BracketPairConfig {
 7222                pairs: vec![
 7223                    BracketPair {
 7224                        start: "{".to_string(),
 7225                        end: "}".to_string(),
 7226                        close: true,
 7227                        surround: true,
 7228                        newline: true,
 7229                    },
 7230                    BracketPair {
 7231                        start: "(".to_string(),
 7232                        end: ")".to_string(),
 7233                        close: true,
 7234                        surround: true,
 7235                        newline: true,
 7236                    },
 7237                    BracketPair {
 7238                        start: "[".to_string(),
 7239                        end: "]".to_string(),
 7240                        close: false,
 7241                        surround: false,
 7242                        newline: true,
 7243                    },
 7244                ],
 7245                ..Default::default()
 7246            },
 7247            autoclose_before: "})]".to_string(),
 7248            ..Default::default()
 7249        },
 7250        Some(tree_sitter_rust::LANGUAGE.into()),
 7251    ));
 7252
 7253    cx.language_registry().add(language.clone());
 7254    cx.update_buffer(|buffer, cx| {
 7255        buffer.set_language(Some(language), cx);
 7256    });
 7257
 7258    cx.set_state(
 7259        &"
 7260            ˇ
 7261            ˇ
 7262            ˇ
 7263        "
 7264        .unindent(),
 7265    );
 7266
 7267    // ensure only matching closing brackets are skipped over
 7268    cx.update_editor(|editor, window, cx| {
 7269        editor.handle_input("}", window, cx);
 7270        editor.move_left(&MoveLeft, window, cx);
 7271        editor.handle_input(")", window, cx);
 7272        editor.move_left(&MoveLeft, window, cx);
 7273    });
 7274    cx.assert_editor_state(
 7275        &"
 7276            ˇ)}
 7277            ˇ)}
 7278            ˇ)}
 7279        "
 7280        .unindent(),
 7281    );
 7282
 7283    // skip-over closing brackets at multiple cursors
 7284    cx.update_editor(|editor, window, cx| {
 7285        editor.handle_input(")", window, cx);
 7286        editor.handle_input("}", window, cx);
 7287    });
 7288    cx.assert_editor_state(
 7289        &"
 7290            )}ˇ
 7291            )}ˇ
 7292            )}ˇ
 7293        "
 7294        .unindent(),
 7295    );
 7296
 7297    // ignore non-close brackets
 7298    cx.update_editor(|editor, window, cx| {
 7299        editor.handle_input("]", window, cx);
 7300        editor.move_left(&MoveLeft, window, cx);
 7301        editor.handle_input("]", window, cx);
 7302    });
 7303    cx.assert_editor_state(
 7304        &"
 7305            )}]ˇ]
 7306            )}]ˇ]
 7307            )}]ˇ]
 7308        "
 7309        .unindent(),
 7310    );
 7311}
 7312
 7313#[gpui::test]
 7314async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7315    init_test(cx, |_| {});
 7316
 7317    let mut cx = EditorTestContext::new(cx).await;
 7318
 7319    let html_language = Arc::new(
 7320        Language::new(
 7321            LanguageConfig {
 7322                name: "HTML".into(),
 7323                brackets: BracketPairConfig {
 7324                    pairs: vec![
 7325                        BracketPair {
 7326                            start: "<".into(),
 7327                            end: ">".into(),
 7328                            close: true,
 7329                            ..Default::default()
 7330                        },
 7331                        BracketPair {
 7332                            start: "{".into(),
 7333                            end: "}".into(),
 7334                            close: true,
 7335                            ..Default::default()
 7336                        },
 7337                        BracketPair {
 7338                            start: "(".into(),
 7339                            end: ")".into(),
 7340                            close: true,
 7341                            ..Default::default()
 7342                        },
 7343                    ],
 7344                    ..Default::default()
 7345                },
 7346                autoclose_before: "})]>".into(),
 7347                ..Default::default()
 7348            },
 7349            Some(tree_sitter_html::LANGUAGE.into()),
 7350        )
 7351        .with_injection_query(
 7352            r#"
 7353            (script_element
 7354                (raw_text) @injection.content
 7355                (#set! injection.language "javascript"))
 7356            "#,
 7357        )
 7358        .unwrap(),
 7359    );
 7360
 7361    let javascript_language = Arc::new(Language::new(
 7362        LanguageConfig {
 7363            name: "JavaScript".into(),
 7364            brackets: BracketPairConfig {
 7365                pairs: vec![
 7366                    BracketPair {
 7367                        start: "/*".into(),
 7368                        end: " */".into(),
 7369                        close: true,
 7370                        ..Default::default()
 7371                    },
 7372                    BracketPair {
 7373                        start: "{".into(),
 7374                        end: "}".into(),
 7375                        close: true,
 7376                        ..Default::default()
 7377                    },
 7378                    BracketPair {
 7379                        start: "(".into(),
 7380                        end: ")".into(),
 7381                        close: true,
 7382                        ..Default::default()
 7383                    },
 7384                ],
 7385                ..Default::default()
 7386            },
 7387            autoclose_before: "})]>".into(),
 7388            ..Default::default()
 7389        },
 7390        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7391    ));
 7392
 7393    cx.language_registry().add(html_language.clone());
 7394    cx.language_registry().add(javascript_language.clone());
 7395
 7396    cx.update_buffer(|buffer, cx| {
 7397        buffer.set_language(Some(html_language), cx);
 7398    });
 7399
 7400    cx.set_state(
 7401        &r#"
 7402            <body>ˇ
 7403                <script>
 7404                    var x = 1;ˇ
 7405                </script>
 7406            </body>ˇ
 7407        "#
 7408        .unindent(),
 7409    );
 7410
 7411    // Precondition: different languages are active at different locations.
 7412    cx.update_editor(|editor, window, cx| {
 7413        let snapshot = editor.snapshot(window, cx);
 7414        let cursors = editor.selections.ranges::<usize>(cx);
 7415        let languages = cursors
 7416            .iter()
 7417            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7418            .collect::<Vec<_>>();
 7419        assert_eq!(
 7420            languages,
 7421            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7422        );
 7423    });
 7424
 7425    // Angle brackets autoclose in HTML, but not JavaScript.
 7426    cx.update_editor(|editor, window, cx| {
 7427        editor.handle_input("<", window, cx);
 7428        editor.handle_input("a", window, cx);
 7429    });
 7430    cx.assert_editor_state(
 7431        &r#"
 7432            <body><aˇ>
 7433                <script>
 7434                    var x = 1;<aˇ
 7435                </script>
 7436            </body><aˇ>
 7437        "#
 7438        .unindent(),
 7439    );
 7440
 7441    // Curly braces and parens autoclose in both HTML and JavaScript.
 7442    cx.update_editor(|editor, window, cx| {
 7443        editor.handle_input(" b=", window, cx);
 7444        editor.handle_input("{", window, cx);
 7445        editor.handle_input("c", window, cx);
 7446        editor.handle_input("(", window, cx);
 7447    });
 7448    cx.assert_editor_state(
 7449        &r#"
 7450            <body><a b={c(ˇ)}>
 7451                <script>
 7452                    var x = 1;<a b={c(ˇ)}
 7453                </script>
 7454            </body><a b={c(ˇ)}>
 7455        "#
 7456        .unindent(),
 7457    );
 7458
 7459    // Brackets that were already autoclosed are skipped.
 7460    cx.update_editor(|editor, window, cx| {
 7461        editor.handle_input(")", window, cx);
 7462        editor.handle_input("d", window, cx);
 7463        editor.handle_input("}", window, cx);
 7464    });
 7465    cx.assert_editor_state(
 7466        &r#"
 7467            <body><a b={c()d}ˇ>
 7468                <script>
 7469                    var x = 1;<a b={c()d}ˇ
 7470                </script>
 7471            </body><a b={c()d}ˇ>
 7472        "#
 7473        .unindent(),
 7474    );
 7475    cx.update_editor(|editor, window, cx| {
 7476        editor.handle_input(">", window, cx);
 7477    });
 7478    cx.assert_editor_state(
 7479        &r#"
 7480            <body><a b={c()d}>ˇ
 7481                <script>
 7482                    var x = 1;<a b={c()d}>ˇ
 7483                </script>
 7484            </body><a b={c()d}>ˇ
 7485        "#
 7486        .unindent(),
 7487    );
 7488
 7489    // Reset
 7490    cx.set_state(
 7491        &r#"
 7492            <body>ˇ
 7493                <script>
 7494                    var x = 1;ˇ
 7495                </script>
 7496            </body>ˇ
 7497        "#
 7498        .unindent(),
 7499    );
 7500
 7501    cx.update_editor(|editor, window, cx| {
 7502        editor.handle_input("<", window, cx);
 7503    });
 7504    cx.assert_editor_state(
 7505        &r#"
 7506            <body><ˇ>
 7507                <script>
 7508                    var x = 1;<ˇ
 7509                </script>
 7510            </body><ˇ>
 7511        "#
 7512        .unindent(),
 7513    );
 7514
 7515    // When backspacing, the closing angle brackets are removed.
 7516    cx.update_editor(|editor, window, cx| {
 7517        editor.backspace(&Backspace, window, cx);
 7518    });
 7519    cx.assert_editor_state(
 7520        &r#"
 7521            <body>ˇ
 7522                <script>
 7523                    var x = 1;ˇ
 7524                </script>
 7525            </body>ˇ
 7526        "#
 7527        .unindent(),
 7528    );
 7529
 7530    // Block comments autoclose in JavaScript, but not HTML.
 7531    cx.update_editor(|editor, window, cx| {
 7532        editor.handle_input("/", window, cx);
 7533        editor.handle_input("*", window, cx);
 7534    });
 7535    cx.assert_editor_state(
 7536        &r#"
 7537            <body>/*ˇ
 7538                <script>
 7539                    var x = 1;/*ˇ */
 7540                </script>
 7541            </body>/*ˇ
 7542        "#
 7543        .unindent(),
 7544    );
 7545}
 7546
 7547#[gpui::test]
 7548async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7549    init_test(cx, |_| {});
 7550
 7551    let mut cx = EditorTestContext::new(cx).await;
 7552
 7553    let rust_language = Arc::new(
 7554        Language::new(
 7555            LanguageConfig {
 7556                name: "Rust".into(),
 7557                brackets: serde_json::from_value(json!([
 7558                    { "start": "{", "end": "}", "close": true, "newline": true },
 7559                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7560                ]))
 7561                .unwrap(),
 7562                autoclose_before: "})]>".into(),
 7563                ..Default::default()
 7564            },
 7565            Some(tree_sitter_rust::LANGUAGE.into()),
 7566        )
 7567        .with_override_query("(string_literal) @string")
 7568        .unwrap(),
 7569    );
 7570
 7571    cx.language_registry().add(rust_language.clone());
 7572    cx.update_buffer(|buffer, cx| {
 7573        buffer.set_language(Some(rust_language), cx);
 7574    });
 7575
 7576    cx.set_state(
 7577        &r#"
 7578            let x = ˇ
 7579        "#
 7580        .unindent(),
 7581    );
 7582
 7583    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7584    cx.update_editor(|editor, window, cx| {
 7585        editor.handle_input("\"", window, cx);
 7586    });
 7587    cx.assert_editor_state(
 7588        &r#"
 7589            let x = "ˇ"
 7590        "#
 7591        .unindent(),
 7592    );
 7593
 7594    // Inserting another quotation mark. The cursor moves across the existing
 7595    // automatically-inserted quotation mark.
 7596    cx.update_editor(|editor, window, cx| {
 7597        editor.handle_input("\"", window, cx);
 7598    });
 7599    cx.assert_editor_state(
 7600        &r#"
 7601            let x = ""ˇ
 7602        "#
 7603        .unindent(),
 7604    );
 7605
 7606    // Reset
 7607    cx.set_state(
 7608        &r#"
 7609            let x = ˇ
 7610        "#
 7611        .unindent(),
 7612    );
 7613
 7614    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7615    cx.update_editor(|editor, window, cx| {
 7616        editor.handle_input("\"", window, cx);
 7617        editor.handle_input(" ", window, cx);
 7618        editor.move_left(&Default::default(), window, cx);
 7619        editor.handle_input("\\", window, cx);
 7620        editor.handle_input("\"", window, cx);
 7621    });
 7622    cx.assert_editor_state(
 7623        &r#"
 7624            let x = "\"ˇ "
 7625        "#
 7626        .unindent(),
 7627    );
 7628
 7629    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7630    // mark. Nothing is inserted.
 7631    cx.update_editor(|editor, window, cx| {
 7632        editor.move_right(&Default::default(), window, cx);
 7633        editor.handle_input("\"", window, cx);
 7634    });
 7635    cx.assert_editor_state(
 7636        &r#"
 7637            let x = "\" "ˇ
 7638        "#
 7639        .unindent(),
 7640    );
 7641}
 7642
 7643#[gpui::test]
 7644async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7645    init_test(cx, |_| {});
 7646
 7647    let language = Arc::new(Language::new(
 7648        LanguageConfig {
 7649            brackets: BracketPairConfig {
 7650                pairs: vec![
 7651                    BracketPair {
 7652                        start: "{".to_string(),
 7653                        end: "}".to_string(),
 7654                        close: true,
 7655                        surround: true,
 7656                        newline: true,
 7657                    },
 7658                    BracketPair {
 7659                        start: "/* ".to_string(),
 7660                        end: "*/".to_string(),
 7661                        close: true,
 7662                        surround: true,
 7663                        ..Default::default()
 7664                    },
 7665                ],
 7666                ..Default::default()
 7667            },
 7668            ..Default::default()
 7669        },
 7670        Some(tree_sitter_rust::LANGUAGE.into()),
 7671    ));
 7672
 7673    let text = r#"
 7674        a
 7675        b
 7676        c
 7677    "#
 7678    .unindent();
 7679
 7680    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7681    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7682    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7683    editor
 7684        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7685        .await;
 7686
 7687    editor.update_in(cx, |editor, window, cx| {
 7688        editor.change_selections(None, window, cx, |s| {
 7689            s.select_display_ranges([
 7690                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7691                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7692                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7693            ])
 7694        });
 7695
 7696        editor.handle_input("{", window, cx);
 7697        editor.handle_input("{", window, cx);
 7698        editor.handle_input("{", window, cx);
 7699        assert_eq!(
 7700            editor.text(cx),
 7701            "
 7702                {{{a}}}
 7703                {{{b}}}
 7704                {{{c}}}
 7705            "
 7706            .unindent()
 7707        );
 7708        assert_eq!(
 7709            editor.selections.display_ranges(cx),
 7710            [
 7711                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7712                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7713                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7714            ]
 7715        );
 7716
 7717        editor.undo(&Undo, window, cx);
 7718        editor.undo(&Undo, window, cx);
 7719        editor.undo(&Undo, window, cx);
 7720        assert_eq!(
 7721            editor.text(cx),
 7722            "
 7723                a
 7724                b
 7725                c
 7726            "
 7727            .unindent()
 7728        );
 7729        assert_eq!(
 7730            editor.selections.display_ranges(cx),
 7731            [
 7732                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7733                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7734                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7735            ]
 7736        );
 7737
 7738        // Ensure inserting the first character of a multi-byte bracket pair
 7739        // doesn't surround the selections with the bracket.
 7740        editor.handle_input("/", window, cx);
 7741        assert_eq!(
 7742            editor.text(cx),
 7743            "
 7744                /
 7745                /
 7746                /
 7747            "
 7748            .unindent()
 7749        );
 7750        assert_eq!(
 7751            editor.selections.display_ranges(cx),
 7752            [
 7753                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7754                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7755                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7756            ]
 7757        );
 7758
 7759        editor.undo(&Undo, window, cx);
 7760        assert_eq!(
 7761            editor.text(cx),
 7762            "
 7763                a
 7764                b
 7765                c
 7766            "
 7767            .unindent()
 7768        );
 7769        assert_eq!(
 7770            editor.selections.display_ranges(cx),
 7771            [
 7772                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7773                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7774                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7775            ]
 7776        );
 7777
 7778        // Ensure inserting the last character of a multi-byte bracket pair
 7779        // doesn't surround the selections with the bracket.
 7780        editor.handle_input("*", window, cx);
 7781        assert_eq!(
 7782            editor.text(cx),
 7783            "
 7784                *
 7785                *
 7786                *
 7787            "
 7788            .unindent()
 7789        );
 7790        assert_eq!(
 7791            editor.selections.display_ranges(cx),
 7792            [
 7793                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7794                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7795                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7796            ]
 7797        );
 7798    });
 7799}
 7800
 7801#[gpui::test]
 7802async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7803    init_test(cx, |_| {});
 7804
 7805    let language = Arc::new(Language::new(
 7806        LanguageConfig {
 7807            brackets: BracketPairConfig {
 7808                pairs: vec![BracketPair {
 7809                    start: "{".to_string(),
 7810                    end: "}".to_string(),
 7811                    close: true,
 7812                    surround: true,
 7813                    newline: true,
 7814                }],
 7815                ..Default::default()
 7816            },
 7817            autoclose_before: "}".to_string(),
 7818            ..Default::default()
 7819        },
 7820        Some(tree_sitter_rust::LANGUAGE.into()),
 7821    ));
 7822
 7823    let text = r#"
 7824        a
 7825        b
 7826        c
 7827    "#
 7828    .unindent();
 7829
 7830    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7831    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7832    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7833    editor
 7834        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7835        .await;
 7836
 7837    editor.update_in(cx, |editor, window, cx| {
 7838        editor.change_selections(None, window, cx, |s| {
 7839            s.select_ranges([
 7840                Point::new(0, 1)..Point::new(0, 1),
 7841                Point::new(1, 1)..Point::new(1, 1),
 7842                Point::new(2, 1)..Point::new(2, 1),
 7843            ])
 7844        });
 7845
 7846        editor.handle_input("{", window, cx);
 7847        editor.handle_input("{", window, cx);
 7848        editor.handle_input("_", window, cx);
 7849        assert_eq!(
 7850            editor.text(cx),
 7851            "
 7852                a{{_}}
 7853                b{{_}}
 7854                c{{_}}
 7855            "
 7856            .unindent()
 7857        );
 7858        assert_eq!(
 7859            editor.selections.ranges::<Point>(cx),
 7860            [
 7861                Point::new(0, 4)..Point::new(0, 4),
 7862                Point::new(1, 4)..Point::new(1, 4),
 7863                Point::new(2, 4)..Point::new(2, 4)
 7864            ]
 7865        );
 7866
 7867        editor.backspace(&Default::default(), window, cx);
 7868        editor.backspace(&Default::default(), window, cx);
 7869        assert_eq!(
 7870            editor.text(cx),
 7871            "
 7872                a{}
 7873                b{}
 7874                c{}
 7875            "
 7876            .unindent()
 7877        );
 7878        assert_eq!(
 7879            editor.selections.ranges::<Point>(cx),
 7880            [
 7881                Point::new(0, 2)..Point::new(0, 2),
 7882                Point::new(1, 2)..Point::new(1, 2),
 7883                Point::new(2, 2)..Point::new(2, 2)
 7884            ]
 7885        );
 7886
 7887        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7888        assert_eq!(
 7889            editor.text(cx),
 7890            "
 7891                a
 7892                b
 7893                c
 7894            "
 7895            .unindent()
 7896        );
 7897        assert_eq!(
 7898            editor.selections.ranges::<Point>(cx),
 7899            [
 7900                Point::new(0, 1)..Point::new(0, 1),
 7901                Point::new(1, 1)..Point::new(1, 1),
 7902                Point::new(2, 1)..Point::new(2, 1)
 7903            ]
 7904        );
 7905    });
 7906}
 7907
 7908#[gpui::test]
 7909async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7910    init_test(cx, |settings| {
 7911        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7912    });
 7913
 7914    let mut cx = EditorTestContext::new(cx).await;
 7915
 7916    let language = Arc::new(Language::new(
 7917        LanguageConfig {
 7918            brackets: BracketPairConfig {
 7919                pairs: vec![
 7920                    BracketPair {
 7921                        start: "{".to_string(),
 7922                        end: "}".to_string(),
 7923                        close: true,
 7924                        surround: true,
 7925                        newline: true,
 7926                    },
 7927                    BracketPair {
 7928                        start: "(".to_string(),
 7929                        end: ")".to_string(),
 7930                        close: true,
 7931                        surround: true,
 7932                        newline: true,
 7933                    },
 7934                    BracketPair {
 7935                        start: "[".to_string(),
 7936                        end: "]".to_string(),
 7937                        close: false,
 7938                        surround: true,
 7939                        newline: true,
 7940                    },
 7941                ],
 7942                ..Default::default()
 7943            },
 7944            autoclose_before: "})]".to_string(),
 7945            ..Default::default()
 7946        },
 7947        Some(tree_sitter_rust::LANGUAGE.into()),
 7948    ));
 7949
 7950    cx.language_registry().add(language.clone());
 7951    cx.update_buffer(|buffer, cx| {
 7952        buffer.set_language(Some(language), cx);
 7953    });
 7954
 7955    cx.set_state(
 7956        &"
 7957            {(ˇ)}
 7958            [[ˇ]]
 7959            {(ˇ)}
 7960        "
 7961        .unindent(),
 7962    );
 7963
 7964    cx.update_editor(|editor, window, cx| {
 7965        editor.backspace(&Default::default(), window, cx);
 7966        editor.backspace(&Default::default(), window, cx);
 7967    });
 7968
 7969    cx.assert_editor_state(
 7970        &"
 7971            ˇ
 7972            ˇ]]
 7973            ˇ
 7974        "
 7975        .unindent(),
 7976    );
 7977
 7978    cx.update_editor(|editor, window, cx| {
 7979        editor.handle_input("{", window, cx);
 7980        editor.handle_input("{", window, cx);
 7981        editor.move_right(&MoveRight, window, cx);
 7982        editor.move_right(&MoveRight, window, cx);
 7983        editor.move_left(&MoveLeft, window, cx);
 7984        editor.move_left(&MoveLeft, window, cx);
 7985        editor.backspace(&Default::default(), window, cx);
 7986    });
 7987
 7988    cx.assert_editor_state(
 7989        &"
 7990            {ˇ}
 7991            {ˇ}]]
 7992            {ˇ}
 7993        "
 7994        .unindent(),
 7995    );
 7996
 7997    cx.update_editor(|editor, window, cx| {
 7998        editor.backspace(&Default::default(), window, cx);
 7999    });
 8000
 8001    cx.assert_editor_state(
 8002        &"
 8003            ˇ
 8004            ˇ]]
 8005            ˇ
 8006        "
 8007        .unindent(),
 8008    );
 8009}
 8010
 8011#[gpui::test]
 8012async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8013    init_test(cx, |_| {});
 8014
 8015    let language = Arc::new(Language::new(
 8016        LanguageConfig::default(),
 8017        Some(tree_sitter_rust::LANGUAGE.into()),
 8018    ));
 8019
 8020    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8021    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8022    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8023    editor
 8024        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8025        .await;
 8026
 8027    editor.update_in(cx, |editor, window, cx| {
 8028        editor.set_auto_replace_emoji_shortcode(true);
 8029
 8030        editor.handle_input("Hello ", window, cx);
 8031        editor.handle_input(":wave", window, cx);
 8032        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8033
 8034        editor.handle_input(":", window, cx);
 8035        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8036
 8037        editor.handle_input(" :smile", window, cx);
 8038        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8039
 8040        editor.handle_input(":", window, cx);
 8041        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8042
 8043        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8044        editor.handle_input(":wave", window, cx);
 8045        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8046
 8047        editor.handle_input(":", window, cx);
 8048        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8049
 8050        editor.handle_input(":1", window, cx);
 8051        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8052
 8053        editor.handle_input(":", window, cx);
 8054        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8055
 8056        // Ensure shortcode does not get replaced when it is part of a word
 8057        editor.handle_input(" Test:wave", window, cx);
 8058        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8059
 8060        editor.handle_input(":", window, cx);
 8061        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8062
 8063        editor.set_auto_replace_emoji_shortcode(false);
 8064
 8065        // Ensure shortcode does not get replaced when auto replace is off
 8066        editor.handle_input(" :wave", window, cx);
 8067        assert_eq!(
 8068            editor.text(cx),
 8069            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8070        );
 8071
 8072        editor.handle_input(":", window, cx);
 8073        assert_eq!(
 8074            editor.text(cx),
 8075            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8076        );
 8077    });
 8078}
 8079
 8080#[gpui::test]
 8081async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8082    init_test(cx, |_| {});
 8083
 8084    let (text, insertion_ranges) = marked_text_ranges(
 8085        indoc! {"
 8086            ˇ
 8087        "},
 8088        false,
 8089    );
 8090
 8091    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8092    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8093
 8094    _ = editor.update_in(cx, |editor, window, cx| {
 8095        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8096
 8097        editor
 8098            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8099            .unwrap();
 8100
 8101        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8102            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8103            assert_eq!(editor.text(cx), expected_text);
 8104            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8105        }
 8106
 8107        assert(
 8108            editor,
 8109            cx,
 8110            indoc! {"
 8111            type «» =•
 8112            "},
 8113        );
 8114
 8115        assert!(editor.context_menu_visible(), "There should be a matches");
 8116    });
 8117}
 8118
 8119#[gpui::test]
 8120async fn test_snippets(cx: &mut TestAppContext) {
 8121    init_test(cx, |_| {});
 8122
 8123    let (text, insertion_ranges) = marked_text_ranges(
 8124        indoc! {"
 8125            a.ˇ b
 8126            a.ˇ b
 8127            a.ˇ b
 8128        "},
 8129        false,
 8130    );
 8131
 8132    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8133    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8134
 8135    editor.update_in(cx, |editor, window, cx| {
 8136        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8137
 8138        editor
 8139            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8140            .unwrap();
 8141
 8142        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8143            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8144            assert_eq!(editor.text(cx), expected_text);
 8145            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8146        }
 8147
 8148        assert(
 8149            editor,
 8150            cx,
 8151            indoc! {"
 8152                a.f(«one», two, «three») b
 8153                a.f(«one», two, «three») b
 8154                a.f(«one», two, «three») b
 8155            "},
 8156        );
 8157
 8158        // Can't move earlier than the first tab stop
 8159        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8160        assert(
 8161            editor,
 8162            cx,
 8163            indoc! {"
 8164                a.f(«one», two, «three») b
 8165                a.f(«one», two, «three») b
 8166                a.f(«one», two, «three») b
 8167            "},
 8168        );
 8169
 8170        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8171        assert(
 8172            editor,
 8173            cx,
 8174            indoc! {"
 8175                a.f(one, «two», three) b
 8176                a.f(one, «two», three) b
 8177                a.f(one, «two», three) b
 8178            "},
 8179        );
 8180
 8181        editor.move_to_prev_snippet_tabstop(window, cx);
 8182        assert(
 8183            editor,
 8184            cx,
 8185            indoc! {"
 8186                a.f(«one», two, «three») b
 8187                a.f(«one», two, «three») b
 8188                a.f(«one», two, «three») b
 8189            "},
 8190        );
 8191
 8192        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8193        assert(
 8194            editor,
 8195            cx,
 8196            indoc! {"
 8197                a.f(one, «two», three) b
 8198                a.f(one, «two», three) b
 8199                a.f(one, «two», three) b
 8200            "},
 8201        );
 8202        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8203        assert(
 8204            editor,
 8205            cx,
 8206            indoc! {"
 8207                a.f(one, two, three)ˇ b
 8208                a.f(one, two, three)ˇ b
 8209                a.f(one, two, three)ˇ b
 8210            "},
 8211        );
 8212
 8213        // As soon as the last tab stop is reached, snippet state is gone
 8214        editor.move_to_prev_snippet_tabstop(window, cx);
 8215        assert(
 8216            editor,
 8217            cx,
 8218            indoc! {"
 8219                a.f(one, two, three)ˇ b
 8220                a.f(one, two, three)ˇ b
 8221                a.f(one, two, three)ˇ b
 8222            "},
 8223        );
 8224    });
 8225}
 8226
 8227#[gpui::test]
 8228async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8229    init_test(cx, |_| {});
 8230
 8231    let fs = FakeFs::new(cx.executor());
 8232    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8233
 8234    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8235
 8236    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8237    language_registry.add(rust_lang());
 8238    let mut fake_servers = language_registry.register_fake_lsp(
 8239        "Rust",
 8240        FakeLspAdapter {
 8241            capabilities: lsp::ServerCapabilities {
 8242                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8243                ..Default::default()
 8244            },
 8245            ..Default::default()
 8246        },
 8247    );
 8248
 8249    let buffer = project
 8250        .update(cx, |project, cx| {
 8251            project.open_local_buffer(path!("/file.rs"), cx)
 8252        })
 8253        .await
 8254        .unwrap();
 8255
 8256    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8257    let (editor, cx) = cx.add_window_view(|window, cx| {
 8258        build_editor_with_project(project.clone(), buffer, window, cx)
 8259    });
 8260    editor.update_in(cx, |editor, window, cx| {
 8261        editor.set_text("one\ntwo\nthree\n", window, cx)
 8262    });
 8263    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8264
 8265    cx.executor().start_waiting();
 8266    let fake_server = fake_servers.next().await.unwrap();
 8267
 8268    {
 8269        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8270            move |params, _| async move {
 8271                assert_eq!(
 8272                    params.text_document.uri,
 8273                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8274                );
 8275                assert_eq!(params.options.tab_size, 4);
 8276                Ok(Some(vec![lsp::TextEdit::new(
 8277                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8278                    ", ".to_string(),
 8279                )]))
 8280            },
 8281        );
 8282        let save = editor
 8283            .update_in(cx, |editor, window, cx| {
 8284                editor.save(true, project.clone(), window, cx)
 8285            })
 8286            .unwrap();
 8287        cx.executor().start_waiting();
 8288        save.await;
 8289
 8290        assert_eq!(
 8291            editor.update(cx, |editor, cx| editor.text(cx)),
 8292            "one, two\nthree\n"
 8293        );
 8294        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8295    }
 8296
 8297    {
 8298        editor.update_in(cx, |editor, window, cx| {
 8299            editor.set_text("one\ntwo\nthree\n", window, cx)
 8300        });
 8301        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8302
 8303        // Ensure we can still save even if formatting hangs.
 8304        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8305            move |params, _| async move {
 8306                assert_eq!(
 8307                    params.text_document.uri,
 8308                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8309                );
 8310                futures::future::pending::<()>().await;
 8311                unreachable!()
 8312            },
 8313        );
 8314        let save = editor
 8315            .update_in(cx, |editor, window, cx| {
 8316                editor.save(true, project.clone(), window, cx)
 8317            })
 8318            .unwrap();
 8319        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8320        cx.executor().start_waiting();
 8321        save.await;
 8322        assert_eq!(
 8323            editor.update(cx, |editor, cx| editor.text(cx)),
 8324            "one\ntwo\nthree\n"
 8325        );
 8326    }
 8327
 8328    // For non-dirty buffer, no formatting request should be sent
 8329    {
 8330        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8331
 8332        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8333            panic!("Should not be invoked on non-dirty buffer");
 8334        });
 8335        let save = editor
 8336            .update_in(cx, |editor, window, cx| {
 8337                editor.save(true, project.clone(), window, cx)
 8338            })
 8339            .unwrap();
 8340        cx.executor().start_waiting();
 8341        save.await;
 8342    }
 8343
 8344    // Set rust language override and assert overridden tabsize is sent to language server
 8345    update_test_language_settings(cx, |settings| {
 8346        settings.languages.insert(
 8347            "Rust".into(),
 8348            LanguageSettingsContent {
 8349                tab_size: NonZeroU32::new(8),
 8350                ..Default::default()
 8351            },
 8352        );
 8353    });
 8354
 8355    {
 8356        editor.update_in(cx, |editor, window, cx| {
 8357            editor.set_text("somehting_new\n", window, cx)
 8358        });
 8359        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8360        let _formatting_request_signal = fake_server
 8361            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8362                assert_eq!(
 8363                    params.text_document.uri,
 8364                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8365                );
 8366                assert_eq!(params.options.tab_size, 8);
 8367                Ok(Some(vec![]))
 8368            });
 8369        let save = editor
 8370            .update_in(cx, |editor, window, cx| {
 8371                editor.save(true, project.clone(), window, cx)
 8372            })
 8373            .unwrap();
 8374        cx.executor().start_waiting();
 8375        save.await;
 8376    }
 8377}
 8378
 8379#[gpui::test]
 8380async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8381    init_test(cx, |_| {});
 8382
 8383    let cols = 4;
 8384    let rows = 10;
 8385    let sample_text_1 = sample_text(rows, cols, 'a');
 8386    assert_eq!(
 8387        sample_text_1,
 8388        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8389    );
 8390    let sample_text_2 = sample_text(rows, cols, 'l');
 8391    assert_eq!(
 8392        sample_text_2,
 8393        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8394    );
 8395    let sample_text_3 = sample_text(rows, cols, 'v');
 8396    assert_eq!(
 8397        sample_text_3,
 8398        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8399    );
 8400
 8401    let fs = FakeFs::new(cx.executor());
 8402    fs.insert_tree(
 8403        path!("/a"),
 8404        json!({
 8405            "main.rs": sample_text_1,
 8406            "other.rs": sample_text_2,
 8407            "lib.rs": sample_text_3,
 8408        }),
 8409    )
 8410    .await;
 8411
 8412    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8413    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8414    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8415
 8416    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8417    language_registry.add(rust_lang());
 8418    let mut fake_servers = language_registry.register_fake_lsp(
 8419        "Rust",
 8420        FakeLspAdapter {
 8421            capabilities: lsp::ServerCapabilities {
 8422                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8423                ..Default::default()
 8424            },
 8425            ..Default::default()
 8426        },
 8427    );
 8428
 8429    let worktree = project.update(cx, |project, cx| {
 8430        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8431        assert_eq!(worktrees.len(), 1);
 8432        worktrees.pop().unwrap()
 8433    });
 8434    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8435
 8436    let buffer_1 = project
 8437        .update(cx, |project, cx| {
 8438            project.open_buffer((worktree_id, "main.rs"), cx)
 8439        })
 8440        .await
 8441        .unwrap();
 8442    let buffer_2 = project
 8443        .update(cx, |project, cx| {
 8444            project.open_buffer((worktree_id, "other.rs"), cx)
 8445        })
 8446        .await
 8447        .unwrap();
 8448    let buffer_3 = project
 8449        .update(cx, |project, cx| {
 8450            project.open_buffer((worktree_id, "lib.rs"), cx)
 8451        })
 8452        .await
 8453        .unwrap();
 8454
 8455    let multi_buffer = cx.new(|cx| {
 8456        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8457        multi_buffer.push_excerpts(
 8458            buffer_1.clone(),
 8459            [
 8460                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8461                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8462                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8463            ],
 8464            cx,
 8465        );
 8466        multi_buffer.push_excerpts(
 8467            buffer_2.clone(),
 8468            [
 8469                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8470                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8471                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8472            ],
 8473            cx,
 8474        );
 8475        multi_buffer.push_excerpts(
 8476            buffer_3.clone(),
 8477            [
 8478                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8479                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8480                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8481            ],
 8482            cx,
 8483        );
 8484        multi_buffer
 8485    });
 8486    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8487        Editor::new(
 8488            EditorMode::full(),
 8489            multi_buffer,
 8490            Some(project.clone()),
 8491            window,
 8492            cx,
 8493        )
 8494    });
 8495
 8496    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8497        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8498            s.select_ranges(Some(1..2))
 8499        });
 8500        editor.insert("|one|two|three|", window, cx);
 8501    });
 8502    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8503    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8504        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8505            s.select_ranges(Some(60..70))
 8506        });
 8507        editor.insert("|four|five|six|", window, cx);
 8508    });
 8509    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8510
 8511    // First two buffers should be edited, but not the third one.
 8512    assert_eq!(
 8513        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8514        "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}",
 8515    );
 8516    buffer_1.update(cx, |buffer, _| {
 8517        assert!(buffer.is_dirty());
 8518        assert_eq!(
 8519            buffer.text(),
 8520            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8521        )
 8522    });
 8523    buffer_2.update(cx, |buffer, _| {
 8524        assert!(buffer.is_dirty());
 8525        assert_eq!(
 8526            buffer.text(),
 8527            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8528        )
 8529    });
 8530    buffer_3.update(cx, |buffer, _| {
 8531        assert!(!buffer.is_dirty());
 8532        assert_eq!(buffer.text(), sample_text_3,)
 8533    });
 8534    cx.executor().run_until_parked();
 8535
 8536    cx.executor().start_waiting();
 8537    let save = multi_buffer_editor
 8538        .update_in(cx, |editor, window, cx| {
 8539            editor.save(true, project.clone(), window, cx)
 8540        })
 8541        .unwrap();
 8542
 8543    let fake_server = fake_servers.next().await.unwrap();
 8544    fake_server
 8545        .server
 8546        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8547            Ok(Some(vec![lsp::TextEdit::new(
 8548                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8549                format!("[{} formatted]", params.text_document.uri),
 8550            )]))
 8551        })
 8552        .detach();
 8553    save.await;
 8554
 8555    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8556    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8557    assert_eq!(
 8558        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8559        uri!(
 8560            "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}"
 8561        ),
 8562    );
 8563    buffer_1.update(cx, |buffer, _| {
 8564        assert!(!buffer.is_dirty());
 8565        assert_eq!(
 8566            buffer.text(),
 8567            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8568        )
 8569    });
 8570    buffer_2.update(cx, |buffer, _| {
 8571        assert!(!buffer.is_dirty());
 8572        assert_eq!(
 8573            buffer.text(),
 8574            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8575        )
 8576    });
 8577    buffer_3.update(cx, |buffer, _| {
 8578        assert!(!buffer.is_dirty());
 8579        assert_eq!(buffer.text(), sample_text_3,)
 8580    });
 8581}
 8582
 8583#[gpui::test]
 8584async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8585    init_test(cx, |_| {});
 8586
 8587    let fs = FakeFs::new(cx.executor());
 8588    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8589
 8590    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8591
 8592    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8593    language_registry.add(rust_lang());
 8594    let mut fake_servers = language_registry.register_fake_lsp(
 8595        "Rust",
 8596        FakeLspAdapter {
 8597            capabilities: lsp::ServerCapabilities {
 8598                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8599                ..Default::default()
 8600            },
 8601            ..Default::default()
 8602        },
 8603    );
 8604
 8605    let buffer = project
 8606        .update(cx, |project, cx| {
 8607            project.open_local_buffer(path!("/file.rs"), cx)
 8608        })
 8609        .await
 8610        .unwrap();
 8611
 8612    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8613    let (editor, cx) = cx.add_window_view(|window, cx| {
 8614        build_editor_with_project(project.clone(), buffer, window, cx)
 8615    });
 8616    editor.update_in(cx, |editor, window, cx| {
 8617        editor.set_text("one\ntwo\nthree\n", window, cx)
 8618    });
 8619    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8620
 8621    cx.executor().start_waiting();
 8622    let fake_server = fake_servers.next().await.unwrap();
 8623
 8624    let save = editor
 8625        .update_in(cx, |editor, window, cx| {
 8626            editor.save(true, project.clone(), window, cx)
 8627        })
 8628        .unwrap();
 8629    fake_server
 8630        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8631            assert_eq!(
 8632                params.text_document.uri,
 8633                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8634            );
 8635            assert_eq!(params.options.tab_size, 4);
 8636            Ok(Some(vec![lsp::TextEdit::new(
 8637                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8638                ", ".to_string(),
 8639            )]))
 8640        })
 8641        .next()
 8642        .await;
 8643    cx.executor().start_waiting();
 8644    save.await;
 8645    assert_eq!(
 8646        editor.update(cx, |editor, cx| editor.text(cx)),
 8647        "one, two\nthree\n"
 8648    );
 8649    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8650
 8651    editor.update_in(cx, |editor, window, cx| {
 8652        editor.set_text("one\ntwo\nthree\n", window, cx)
 8653    });
 8654    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8655
 8656    // Ensure we can still save even if formatting hangs.
 8657    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8658        move |params, _| async move {
 8659            assert_eq!(
 8660                params.text_document.uri,
 8661                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8662            );
 8663            futures::future::pending::<()>().await;
 8664            unreachable!()
 8665        },
 8666    );
 8667    let save = editor
 8668        .update_in(cx, |editor, window, cx| {
 8669            editor.save(true, project.clone(), window, cx)
 8670        })
 8671        .unwrap();
 8672    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8673    cx.executor().start_waiting();
 8674    save.await;
 8675    assert_eq!(
 8676        editor.update(cx, |editor, cx| editor.text(cx)),
 8677        "one\ntwo\nthree\n"
 8678    );
 8679    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8680
 8681    // For non-dirty buffer, no formatting request should be sent
 8682    let save = editor
 8683        .update_in(cx, |editor, window, cx| {
 8684            editor.save(true, project.clone(), window, cx)
 8685        })
 8686        .unwrap();
 8687    let _pending_format_request = fake_server
 8688        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8689            panic!("Should not be invoked on non-dirty buffer");
 8690        })
 8691        .next();
 8692    cx.executor().start_waiting();
 8693    save.await;
 8694
 8695    // Set Rust language override and assert overridden tabsize is sent to language server
 8696    update_test_language_settings(cx, |settings| {
 8697        settings.languages.insert(
 8698            "Rust".into(),
 8699            LanguageSettingsContent {
 8700                tab_size: NonZeroU32::new(8),
 8701                ..Default::default()
 8702            },
 8703        );
 8704    });
 8705
 8706    editor.update_in(cx, |editor, window, cx| {
 8707        editor.set_text("somehting_new\n", window, cx)
 8708    });
 8709    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8710    let save = editor
 8711        .update_in(cx, |editor, window, cx| {
 8712            editor.save(true, project.clone(), window, cx)
 8713        })
 8714        .unwrap();
 8715    fake_server
 8716        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8717            assert_eq!(
 8718                params.text_document.uri,
 8719                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8720            );
 8721            assert_eq!(params.options.tab_size, 8);
 8722            Ok(Some(vec![]))
 8723        })
 8724        .next()
 8725        .await;
 8726    cx.executor().start_waiting();
 8727    save.await;
 8728}
 8729
 8730#[gpui::test]
 8731async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8732    init_test(cx, |settings| {
 8733        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8734            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8735        ))
 8736    });
 8737
 8738    let fs = FakeFs::new(cx.executor());
 8739    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8740
 8741    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8742
 8743    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8744    language_registry.add(Arc::new(Language::new(
 8745        LanguageConfig {
 8746            name: "Rust".into(),
 8747            matcher: LanguageMatcher {
 8748                path_suffixes: vec!["rs".to_string()],
 8749                ..Default::default()
 8750            },
 8751            ..LanguageConfig::default()
 8752        },
 8753        Some(tree_sitter_rust::LANGUAGE.into()),
 8754    )));
 8755    update_test_language_settings(cx, |settings| {
 8756        // Enable Prettier formatting for the same buffer, and ensure
 8757        // LSP is called instead of Prettier.
 8758        settings.defaults.prettier = Some(PrettierSettings {
 8759            allowed: true,
 8760            ..PrettierSettings::default()
 8761        });
 8762    });
 8763    let mut fake_servers = language_registry.register_fake_lsp(
 8764        "Rust",
 8765        FakeLspAdapter {
 8766            capabilities: lsp::ServerCapabilities {
 8767                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8768                ..Default::default()
 8769            },
 8770            ..Default::default()
 8771        },
 8772    );
 8773
 8774    let buffer = project
 8775        .update(cx, |project, cx| {
 8776            project.open_local_buffer(path!("/file.rs"), cx)
 8777        })
 8778        .await
 8779        .unwrap();
 8780
 8781    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8782    let (editor, cx) = cx.add_window_view(|window, cx| {
 8783        build_editor_with_project(project.clone(), buffer, window, cx)
 8784    });
 8785    editor.update_in(cx, |editor, window, cx| {
 8786        editor.set_text("one\ntwo\nthree\n", window, cx)
 8787    });
 8788
 8789    cx.executor().start_waiting();
 8790    let fake_server = fake_servers.next().await.unwrap();
 8791
 8792    let format = editor
 8793        .update_in(cx, |editor, window, cx| {
 8794            editor.perform_format(
 8795                project.clone(),
 8796                FormatTrigger::Manual,
 8797                FormatTarget::Buffers,
 8798                window,
 8799                cx,
 8800            )
 8801        })
 8802        .unwrap();
 8803    fake_server
 8804        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8805            assert_eq!(
 8806                params.text_document.uri,
 8807                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8808            );
 8809            assert_eq!(params.options.tab_size, 4);
 8810            Ok(Some(vec![lsp::TextEdit::new(
 8811                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8812                ", ".to_string(),
 8813            )]))
 8814        })
 8815        .next()
 8816        .await;
 8817    cx.executor().start_waiting();
 8818    format.await;
 8819    assert_eq!(
 8820        editor.update(cx, |editor, cx| editor.text(cx)),
 8821        "one, two\nthree\n"
 8822    );
 8823
 8824    editor.update_in(cx, |editor, window, cx| {
 8825        editor.set_text("one\ntwo\nthree\n", window, cx)
 8826    });
 8827    // Ensure we don't lock if formatting hangs.
 8828    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8829        move |params, _| async move {
 8830            assert_eq!(
 8831                params.text_document.uri,
 8832                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8833            );
 8834            futures::future::pending::<()>().await;
 8835            unreachable!()
 8836        },
 8837    );
 8838    let format = editor
 8839        .update_in(cx, |editor, window, cx| {
 8840            editor.perform_format(
 8841                project,
 8842                FormatTrigger::Manual,
 8843                FormatTarget::Buffers,
 8844                window,
 8845                cx,
 8846            )
 8847        })
 8848        .unwrap();
 8849    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8850    cx.executor().start_waiting();
 8851    format.await;
 8852    assert_eq!(
 8853        editor.update(cx, |editor, cx| editor.text(cx)),
 8854        "one\ntwo\nthree\n"
 8855    );
 8856}
 8857
 8858#[gpui::test]
 8859async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8860    init_test(cx, |settings| {
 8861        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8862        settings.defaults.formatter =
 8863            Some(language_settings::SelectedFormatter::List(FormatterList(
 8864                vec![
 8865                    Formatter::LanguageServer { name: None },
 8866                    Formatter::CodeActions(
 8867                        [
 8868                            ("code-action-1".into(), true),
 8869                            ("code-action-2".into(), true),
 8870                        ]
 8871                        .into_iter()
 8872                        .collect(),
 8873                    ),
 8874                ]
 8875                .into(),
 8876            )))
 8877    });
 8878
 8879    let fs = FakeFs::new(cx.executor());
 8880    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8881        .await;
 8882
 8883    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8884    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8885    language_registry.add(rust_lang());
 8886
 8887    let mut fake_servers = language_registry.register_fake_lsp(
 8888        "Rust",
 8889        FakeLspAdapter {
 8890            capabilities: lsp::ServerCapabilities {
 8891                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8892                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8893                    commands: vec!["the-command-for-code-action-1".into()],
 8894                    ..Default::default()
 8895                }),
 8896                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8897                ..Default::default()
 8898            },
 8899            ..Default::default()
 8900        },
 8901    );
 8902
 8903    let buffer = project
 8904        .update(cx, |project, cx| {
 8905            project.open_local_buffer(path!("/file.rs"), cx)
 8906        })
 8907        .await
 8908        .unwrap();
 8909
 8910    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8911    let (editor, cx) = cx.add_window_view(|window, cx| {
 8912        build_editor_with_project(project.clone(), buffer, window, cx)
 8913    });
 8914
 8915    cx.executor().start_waiting();
 8916
 8917    let fake_server = fake_servers.next().await.unwrap();
 8918    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8919        move |_params, _| async move {
 8920            Ok(Some(vec![lsp::TextEdit::new(
 8921                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8922                "applied-formatting\n".to_string(),
 8923            )]))
 8924        },
 8925    );
 8926    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8927        move |params, _| async move {
 8928            assert_eq!(
 8929                params.context.only,
 8930                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8931            );
 8932            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8933            Ok(Some(vec![
 8934                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8935                    kind: Some("code-action-1".into()),
 8936                    edit: Some(lsp::WorkspaceEdit::new(
 8937                        [(
 8938                            uri.clone(),
 8939                            vec![lsp::TextEdit::new(
 8940                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8941                                "applied-code-action-1-edit\n".to_string(),
 8942                            )],
 8943                        )]
 8944                        .into_iter()
 8945                        .collect(),
 8946                    )),
 8947                    command: Some(lsp::Command {
 8948                        command: "the-command-for-code-action-1".into(),
 8949                        ..Default::default()
 8950                    }),
 8951                    ..Default::default()
 8952                }),
 8953                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8954                    kind: Some("code-action-2".into()),
 8955                    edit: Some(lsp::WorkspaceEdit::new(
 8956                        [(
 8957                            uri.clone(),
 8958                            vec![lsp::TextEdit::new(
 8959                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8960                                "applied-code-action-2-edit\n".to_string(),
 8961                            )],
 8962                        )]
 8963                        .into_iter()
 8964                        .collect(),
 8965                    )),
 8966                    ..Default::default()
 8967                }),
 8968            ]))
 8969        },
 8970    );
 8971
 8972    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8973        move |params, _| async move { Ok(params) }
 8974    });
 8975
 8976    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8977    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8978        let fake = fake_server.clone();
 8979        let lock = command_lock.clone();
 8980        move |params, _| {
 8981            assert_eq!(params.command, "the-command-for-code-action-1");
 8982            let fake = fake.clone();
 8983            let lock = lock.clone();
 8984            async move {
 8985                lock.lock().await;
 8986                fake.server
 8987                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8988                        label: None,
 8989                        edit: lsp::WorkspaceEdit {
 8990                            changes: Some(
 8991                                [(
 8992                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8993                                    vec![lsp::TextEdit {
 8994                                        range: lsp::Range::new(
 8995                                            lsp::Position::new(0, 0),
 8996                                            lsp::Position::new(0, 0),
 8997                                        ),
 8998                                        new_text: "applied-code-action-1-command\n".into(),
 8999                                    }],
 9000                                )]
 9001                                .into_iter()
 9002                                .collect(),
 9003                            ),
 9004                            ..Default::default()
 9005                        },
 9006                    })
 9007                    .await
 9008                    .into_response()
 9009                    .unwrap();
 9010                Ok(Some(json!(null)))
 9011            }
 9012        }
 9013    });
 9014
 9015    cx.executor().start_waiting();
 9016    editor
 9017        .update_in(cx, |editor, window, cx| {
 9018            editor.perform_format(
 9019                project.clone(),
 9020                FormatTrigger::Manual,
 9021                FormatTarget::Buffers,
 9022                window,
 9023                cx,
 9024            )
 9025        })
 9026        .unwrap()
 9027        .await;
 9028    editor.update(cx, |editor, cx| {
 9029        assert_eq!(
 9030            editor.text(cx),
 9031            r#"
 9032                applied-code-action-2-edit
 9033                applied-code-action-1-command
 9034                applied-code-action-1-edit
 9035                applied-formatting
 9036                one
 9037                two
 9038                three
 9039            "#
 9040            .unindent()
 9041        );
 9042    });
 9043
 9044    editor.update_in(cx, |editor, window, cx| {
 9045        editor.undo(&Default::default(), window, cx);
 9046        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9047    });
 9048
 9049    // Perform a manual edit while waiting for an LSP command
 9050    // that's being run as part of a formatting code action.
 9051    let lock_guard = command_lock.lock().await;
 9052    let format = editor
 9053        .update_in(cx, |editor, window, cx| {
 9054            editor.perform_format(
 9055                project.clone(),
 9056                FormatTrigger::Manual,
 9057                FormatTarget::Buffers,
 9058                window,
 9059                cx,
 9060            )
 9061        })
 9062        .unwrap();
 9063    cx.run_until_parked();
 9064    editor.update(cx, |editor, cx| {
 9065        assert_eq!(
 9066            editor.text(cx),
 9067            r#"
 9068                applied-code-action-1-edit
 9069                applied-formatting
 9070                one
 9071                two
 9072                three
 9073            "#
 9074            .unindent()
 9075        );
 9076
 9077        editor.buffer.update(cx, |buffer, cx| {
 9078            let ix = buffer.len(cx);
 9079            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9080        });
 9081    });
 9082
 9083    // Allow the LSP command to proceed. Because the buffer was edited,
 9084    // the second code action will not be run.
 9085    drop(lock_guard);
 9086    format.await;
 9087    editor.update_in(cx, |editor, window, cx| {
 9088        assert_eq!(
 9089            editor.text(cx),
 9090            r#"
 9091                applied-code-action-1-command
 9092                applied-code-action-1-edit
 9093                applied-formatting
 9094                one
 9095                two
 9096                three
 9097                edited
 9098            "#
 9099            .unindent()
 9100        );
 9101
 9102        // The manual edit is undone first, because it is the last thing the user did
 9103        // (even though the command completed afterwards).
 9104        editor.undo(&Default::default(), window, cx);
 9105        assert_eq!(
 9106            editor.text(cx),
 9107            r#"
 9108                applied-code-action-1-command
 9109                applied-code-action-1-edit
 9110                applied-formatting
 9111                one
 9112                two
 9113                three
 9114            "#
 9115            .unindent()
 9116        );
 9117
 9118        // All the formatting (including the command, which completed after the manual edit)
 9119        // is undone together.
 9120        editor.undo(&Default::default(), window, cx);
 9121        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9122    });
 9123}
 9124
 9125#[gpui::test]
 9126async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9127    init_test(cx, |settings| {
 9128        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9129            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9130        ))
 9131    });
 9132
 9133    let fs = FakeFs::new(cx.executor());
 9134    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9135
 9136    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9137
 9138    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9139    language_registry.add(Arc::new(Language::new(
 9140        LanguageConfig {
 9141            name: "TypeScript".into(),
 9142            matcher: LanguageMatcher {
 9143                path_suffixes: vec!["ts".to_string()],
 9144                ..Default::default()
 9145            },
 9146            ..LanguageConfig::default()
 9147        },
 9148        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9149    )));
 9150    update_test_language_settings(cx, |settings| {
 9151        settings.defaults.prettier = Some(PrettierSettings {
 9152            allowed: true,
 9153            ..PrettierSettings::default()
 9154        });
 9155    });
 9156    let mut fake_servers = language_registry.register_fake_lsp(
 9157        "TypeScript",
 9158        FakeLspAdapter {
 9159            capabilities: lsp::ServerCapabilities {
 9160                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9161                ..Default::default()
 9162            },
 9163            ..Default::default()
 9164        },
 9165    );
 9166
 9167    let buffer = project
 9168        .update(cx, |project, cx| {
 9169            project.open_local_buffer(path!("/file.ts"), cx)
 9170        })
 9171        .await
 9172        .unwrap();
 9173
 9174    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9175    let (editor, cx) = cx.add_window_view(|window, cx| {
 9176        build_editor_with_project(project.clone(), buffer, window, cx)
 9177    });
 9178    editor.update_in(cx, |editor, window, cx| {
 9179        editor.set_text(
 9180            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9181            window,
 9182            cx,
 9183        )
 9184    });
 9185
 9186    cx.executor().start_waiting();
 9187    let fake_server = fake_servers.next().await.unwrap();
 9188
 9189    let format = editor
 9190        .update_in(cx, |editor, window, cx| {
 9191            editor.perform_code_action_kind(
 9192                project.clone(),
 9193                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9194                window,
 9195                cx,
 9196            )
 9197        })
 9198        .unwrap();
 9199    fake_server
 9200        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9201            assert_eq!(
 9202                params.text_document.uri,
 9203                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9204            );
 9205            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9206                lsp::CodeAction {
 9207                    title: "Organize Imports".to_string(),
 9208                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9209                    edit: Some(lsp::WorkspaceEdit {
 9210                        changes: Some(
 9211                            [(
 9212                                params.text_document.uri.clone(),
 9213                                vec![lsp::TextEdit::new(
 9214                                    lsp::Range::new(
 9215                                        lsp::Position::new(1, 0),
 9216                                        lsp::Position::new(2, 0),
 9217                                    ),
 9218                                    "".to_string(),
 9219                                )],
 9220                            )]
 9221                            .into_iter()
 9222                            .collect(),
 9223                        ),
 9224                        ..Default::default()
 9225                    }),
 9226                    ..Default::default()
 9227                },
 9228            )]))
 9229        })
 9230        .next()
 9231        .await;
 9232    cx.executor().start_waiting();
 9233    format.await;
 9234    assert_eq!(
 9235        editor.update(cx, |editor, cx| editor.text(cx)),
 9236        "import { a } from 'module';\n\nconst x = a;\n"
 9237    );
 9238
 9239    editor.update_in(cx, |editor, window, cx| {
 9240        editor.set_text(
 9241            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9242            window,
 9243            cx,
 9244        )
 9245    });
 9246    // Ensure we don't lock if code action hangs.
 9247    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9248        move |params, _| async move {
 9249            assert_eq!(
 9250                params.text_document.uri,
 9251                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9252            );
 9253            futures::future::pending::<()>().await;
 9254            unreachable!()
 9255        },
 9256    );
 9257    let format = editor
 9258        .update_in(cx, |editor, window, cx| {
 9259            editor.perform_code_action_kind(
 9260                project,
 9261                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9262                window,
 9263                cx,
 9264            )
 9265        })
 9266        .unwrap();
 9267    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9268    cx.executor().start_waiting();
 9269    format.await;
 9270    assert_eq!(
 9271        editor.update(cx, |editor, cx| editor.text(cx)),
 9272        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9273    );
 9274}
 9275
 9276#[gpui::test]
 9277async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9278    init_test(cx, |_| {});
 9279
 9280    let mut cx = EditorLspTestContext::new_rust(
 9281        lsp::ServerCapabilities {
 9282            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9283            ..Default::default()
 9284        },
 9285        cx,
 9286    )
 9287    .await;
 9288
 9289    cx.set_state(indoc! {"
 9290        one.twoˇ
 9291    "});
 9292
 9293    // The format request takes a long time. When it completes, it inserts
 9294    // a newline and an indent before the `.`
 9295    cx.lsp
 9296        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9297            let executor = cx.background_executor().clone();
 9298            async move {
 9299                executor.timer(Duration::from_millis(100)).await;
 9300                Ok(Some(vec![lsp::TextEdit {
 9301                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9302                    new_text: "\n    ".into(),
 9303                }]))
 9304            }
 9305        });
 9306
 9307    // Submit a format request.
 9308    let format_1 = cx
 9309        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9310        .unwrap();
 9311    cx.executor().run_until_parked();
 9312
 9313    // Submit a second format request.
 9314    let format_2 = cx
 9315        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9316        .unwrap();
 9317    cx.executor().run_until_parked();
 9318
 9319    // Wait for both format requests to complete
 9320    cx.executor().advance_clock(Duration::from_millis(200));
 9321    cx.executor().start_waiting();
 9322    format_1.await.unwrap();
 9323    cx.executor().start_waiting();
 9324    format_2.await.unwrap();
 9325
 9326    // The formatting edits only happens once.
 9327    cx.assert_editor_state(indoc! {"
 9328        one
 9329            .twoˇ
 9330    "});
 9331}
 9332
 9333#[gpui::test]
 9334async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9335    init_test(cx, |settings| {
 9336        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9337    });
 9338
 9339    let mut cx = EditorLspTestContext::new_rust(
 9340        lsp::ServerCapabilities {
 9341            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9342            ..Default::default()
 9343        },
 9344        cx,
 9345    )
 9346    .await;
 9347
 9348    // Set up a buffer white some trailing whitespace and no trailing newline.
 9349    cx.set_state(
 9350        &[
 9351            "one ",   //
 9352            "twoˇ",   //
 9353            "three ", //
 9354            "four",   //
 9355        ]
 9356        .join("\n"),
 9357    );
 9358
 9359    // Submit a format request.
 9360    let format = cx
 9361        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9362        .unwrap();
 9363
 9364    // Record which buffer changes have been sent to the language server
 9365    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9366    cx.lsp
 9367        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9368            let buffer_changes = buffer_changes.clone();
 9369            move |params, _| {
 9370                buffer_changes.lock().extend(
 9371                    params
 9372                        .content_changes
 9373                        .into_iter()
 9374                        .map(|e| (e.range.unwrap(), e.text)),
 9375                );
 9376            }
 9377        });
 9378
 9379    // Handle formatting requests to the language server.
 9380    cx.lsp
 9381        .set_request_handler::<lsp::request::Formatting, _, _>({
 9382            let buffer_changes = buffer_changes.clone();
 9383            move |_, _| {
 9384                // When formatting is requested, trailing whitespace has already been stripped,
 9385                // and the trailing newline has already been added.
 9386                assert_eq!(
 9387                    &buffer_changes.lock()[1..],
 9388                    &[
 9389                        (
 9390                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9391                            "".into()
 9392                        ),
 9393                        (
 9394                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9395                            "".into()
 9396                        ),
 9397                        (
 9398                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9399                            "\n".into()
 9400                        ),
 9401                    ]
 9402                );
 9403
 9404                // Insert blank lines between each line of the buffer.
 9405                async move {
 9406                    Ok(Some(vec![
 9407                        lsp::TextEdit {
 9408                            range: lsp::Range::new(
 9409                                lsp::Position::new(1, 0),
 9410                                lsp::Position::new(1, 0),
 9411                            ),
 9412                            new_text: "\n".into(),
 9413                        },
 9414                        lsp::TextEdit {
 9415                            range: lsp::Range::new(
 9416                                lsp::Position::new(2, 0),
 9417                                lsp::Position::new(2, 0),
 9418                            ),
 9419                            new_text: "\n".into(),
 9420                        },
 9421                    ]))
 9422                }
 9423            }
 9424        });
 9425
 9426    // After formatting the buffer, the trailing whitespace is stripped,
 9427    // a newline is appended, and the edits provided by the language server
 9428    // have been applied.
 9429    format.await.unwrap();
 9430    cx.assert_editor_state(
 9431        &[
 9432            "one",   //
 9433            "",      //
 9434            "twoˇ",  //
 9435            "",      //
 9436            "three", //
 9437            "four",  //
 9438            "",      //
 9439        ]
 9440        .join("\n"),
 9441    );
 9442
 9443    // Undoing the formatting undoes the trailing whitespace removal, the
 9444    // trailing newline, and the LSP edits.
 9445    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9446    cx.assert_editor_state(
 9447        &[
 9448            "one ",   //
 9449            "twoˇ",   //
 9450            "three ", //
 9451            "four",   //
 9452        ]
 9453        .join("\n"),
 9454    );
 9455}
 9456
 9457#[gpui::test]
 9458async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9459    cx: &mut TestAppContext,
 9460) {
 9461    init_test(cx, |_| {});
 9462
 9463    cx.update(|cx| {
 9464        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9465            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9466                settings.auto_signature_help = Some(true);
 9467            });
 9468        });
 9469    });
 9470
 9471    let mut cx = EditorLspTestContext::new_rust(
 9472        lsp::ServerCapabilities {
 9473            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9474                ..Default::default()
 9475            }),
 9476            ..Default::default()
 9477        },
 9478        cx,
 9479    )
 9480    .await;
 9481
 9482    let language = Language::new(
 9483        LanguageConfig {
 9484            name: "Rust".into(),
 9485            brackets: BracketPairConfig {
 9486                pairs: vec![
 9487                    BracketPair {
 9488                        start: "{".to_string(),
 9489                        end: "}".to_string(),
 9490                        close: true,
 9491                        surround: true,
 9492                        newline: true,
 9493                    },
 9494                    BracketPair {
 9495                        start: "(".to_string(),
 9496                        end: ")".to_string(),
 9497                        close: true,
 9498                        surround: true,
 9499                        newline: true,
 9500                    },
 9501                    BracketPair {
 9502                        start: "/*".to_string(),
 9503                        end: " */".to_string(),
 9504                        close: true,
 9505                        surround: true,
 9506                        newline: true,
 9507                    },
 9508                    BracketPair {
 9509                        start: "[".to_string(),
 9510                        end: "]".to_string(),
 9511                        close: false,
 9512                        surround: false,
 9513                        newline: true,
 9514                    },
 9515                    BracketPair {
 9516                        start: "\"".to_string(),
 9517                        end: "\"".to_string(),
 9518                        close: true,
 9519                        surround: true,
 9520                        newline: false,
 9521                    },
 9522                    BracketPair {
 9523                        start: "<".to_string(),
 9524                        end: ">".to_string(),
 9525                        close: false,
 9526                        surround: true,
 9527                        newline: true,
 9528                    },
 9529                ],
 9530                ..Default::default()
 9531            },
 9532            autoclose_before: "})]".to_string(),
 9533            ..Default::default()
 9534        },
 9535        Some(tree_sitter_rust::LANGUAGE.into()),
 9536    );
 9537    let language = Arc::new(language);
 9538
 9539    cx.language_registry().add(language.clone());
 9540    cx.update_buffer(|buffer, cx| {
 9541        buffer.set_language(Some(language), cx);
 9542    });
 9543
 9544    cx.set_state(
 9545        &r#"
 9546            fn main() {
 9547                sampleˇ
 9548            }
 9549        "#
 9550        .unindent(),
 9551    );
 9552
 9553    cx.update_editor(|editor, window, cx| {
 9554        editor.handle_input("(", window, cx);
 9555    });
 9556    cx.assert_editor_state(
 9557        &"
 9558            fn main() {
 9559                sample(ˇ)
 9560            }
 9561        "
 9562        .unindent(),
 9563    );
 9564
 9565    let mocked_response = lsp::SignatureHelp {
 9566        signatures: vec![lsp::SignatureInformation {
 9567            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9568            documentation: None,
 9569            parameters: Some(vec![
 9570                lsp::ParameterInformation {
 9571                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9572                    documentation: None,
 9573                },
 9574                lsp::ParameterInformation {
 9575                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9576                    documentation: None,
 9577                },
 9578            ]),
 9579            active_parameter: None,
 9580        }],
 9581        active_signature: Some(0),
 9582        active_parameter: Some(0),
 9583    };
 9584    handle_signature_help_request(&mut cx, mocked_response).await;
 9585
 9586    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9587        .await;
 9588
 9589    cx.editor(|editor, _, _| {
 9590        let signature_help_state = editor.signature_help_state.popover().cloned();
 9591        assert_eq!(
 9592            signature_help_state.unwrap().label,
 9593            "param1: u8, param2: u8"
 9594        );
 9595    });
 9596}
 9597
 9598#[gpui::test]
 9599async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9600    init_test(cx, |_| {});
 9601
 9602    cx.update(|cx| {
 9603        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9604            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9605                settings.auto_signature_help = Some(false);
 9606                settings.show_signature_help_after_edits = Some(false);
 9607            });
 9608        });
 9609    });
 9610
 9611    let mut cx = EditorLspTestContext::new_rust(
 9612        lsp::ServerCapabilities {
 9613            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9614                ..Default::default()
 9615            }),
 9616            ..Default::default()
 9617        },
 9618        cx,
 9619    )
 9620    .await;
 9621
 9622    let language = Language::new(
 9623        LanguageConfig {
 9624            name: "Rust".into(),
 9625            brackets: BracketPairConfig {
 9626                pairs: vec![
 9627                    BracketPair {
 9628                        start: "{".to_string(),
 9629                        end: "}".to_string(),
 9630                        close: true,
 9631                        surround: true,
 9632                        newline: true,
 9633                    },
 9634                    BracketPair {
 9635                        start: "(".to_string(),
 9636                        end: ")".to_string(),
 9637                        close: true,
 9638                        surround: true,
 9639                        newline: true,
 9640                    },
 9641                    BracketPair {
 9642                        start: "/*".to_string(),
 9643                        end: " */".to_string(),
 9644                        close: true,
 9645                        surround: true,
 9646                        newline: true,
 9647                    },
 9648                    BracketPair {
 9649                        start: "[".to_string(),
 9650                        end: "]".to_string(),
 9651                        close: false,
 9652                        surround: false,
 9653                        newline: true,
 9654                    },
 9655                    BracketPair {
 9656                        start: "\"".to_string(),
 9657                        end: "\"".to_string(),
 9658                        close: true,
 9659                        surround: true,
 9660                        newline: false,
 9661                    },
 9662                    BracketPair {
 9663                        start: "<".to_string(),
 9664                        end: ">".to_string(),
 9665                        close: false,
 9666                        surround: true,
 9667                        newline: true,
 9668                    },
 9669                ],
 9670                ..Default::default()
 9671            },
 9672            autoclose_before: "})]".to_string(),
 9673            ..Default::default()
 9674        },
 9675        Some(tree_sitter_rust::LANGUAGE.into()),
 9676    );
 9677    let language = Arc::new(language);
 9678
 9679    cx.language_registry().add(language.clone());
 9680    cx.update_buffer(|buffer, cx| {
 9681        buffer.set_language(Some(language), cx);
 9682    });
 9683
 9684    // Ensure that signature_help is not called when no signature help is enabled.
 9685    cx.set_state(
 9686        &r#"
 9687            fn main() {
 9688                sampleˇ
 9689            }
 9690        "#
 9691        .unindent(),
 9692    );
 9693    cx.update_editor(|editor, window, cx| {
 9694        editor.handle_input("(", window, cx);
 9695    });
 9696    cx.assert_editor_state(
 9697        &"
 9698            fn main() {
 9699                sample(ˇ)
 9700            }
 9701        "
 9702        .unindent(),
 9703    );
 9704    cx.editor(|editor, _, _| {
 9705        assert!(editor.signature_help_state.task().is_none());
 9706    });
 9707
 9708    let mocked_response = lsp::SignatureHelp {
 9709        signatures: vec![lsp::SignatureInformation {
 9710            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9711            documentation: None,
 9712            parameters: Some(vec![
 9713                lsp::ParameterInformation {
 9714                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9715                    documentation: None,
 9716                },
 9717                lsp::ParameterInformation {
 9718                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9719                    documentation: None,
 9720                },
 9721            ]),
 9722            active_parameter: None,
 9723        }],
 9724        active_signature: Some(0),
 9725        active_parameter: Some(0),
 9726    };
 9727
 9728    // Ensure that signature_help is called when enabled afte edits
 9729    cx.update(|_, cx| {
 9730        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9731            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9732                settings.auto_signature_help = Some(false);
 9733                settings.show_signature_help_after_edits = Some(true);
 9734            });
 9735        });
 9736    });
 9737    cx.set_state(
 9738        &r#"
 9739            fn main() {
 9740                sampleˇ
 9741            }
 9742        "#
 9743        .unindent(),
 9744    );
 9745    cx.update_editor(|editor, window, cx| {
 9746        editor.handle_input("(", window, cx);
 9747    });
 9748    cx.assert_editor_state(
 9749        &"
 9750            fn main() {
 9751                sample(ˇ)
 9752            }
 9753        "
 9754        .unindent(),
 9755    );
 9756    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9757    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9758        .await;
 9759    cx.update_editor(|editor, _, _| {
 9760        let signature_help_state = editor.signature_help_state.popover().cloned();
 9761        assert!(signature_help_state.is_some());
 9762        assert_eq!(
 9763            signature_help_state.unwrap().label,
 9764            "param1: u8, param2: u8"
 9765        );
 9766        editor.signature_help_state = SignatureHelpState::default();
 9767    });
 9768
 9769    // Ensure that signature_help is called when auto signature help override is enabled
 9770    cx.update(|_, cx| {
 9771        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9772            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9773                settings.auto_signature_help = Some(true);
 9774                settings.show_signature_help_after_edits = Some(false);
 9775            });
 9776        });
 9777    });
 9778    cx.set_state(
 9779        &r#"
 9780            fn main() {
 9781                sampleˇ
 9782            }
 9783        "#
 9784        .unindent(),
 9785    );
 9786    cx.update_editor(|editor, window, cx| {
 9787        editor.handle_input("(", window, cx);
 9788    });
 9789    cx.assert_editor_state(
 9790        &"
 9791            fn main() {
 9792                sample(ˇ)
 9793            }
 9794        "
 9795        .unindent(),
 9796    );
 9797    handle_signature_help_request(&mut cx, mocked_response).await;
 9798    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9799        .await;
 9800    cx.editor(|editor, _, _| {
 9801        let signature_help_state = editor.signature_help_state.popover().cloned();
 9802        assert!(signature_help_state.is_some());
 9803        assert_eq!(
 9804            signature_help_state.unwrap().label,
 9805            "param1: u8, param2: u8"
 9806        );
 9807    });
 9808}
 9809
 9810#[gpui::test]
 9811async fn test_signature_help(cx: &mut TestAppContext) {
 9812    init_test(cx, |_| {});
 9813    cx.update(|cx| {
 9814        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9815            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9816                settings.auto_signature_help = Some(true);
 9817            });
 9818        });
 9819    });
 9820
 9821    let mut cx = EditorLspTestContext::new_rust(
 9822        lsp::ServerCapabilities {
 9823            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9824                ..Default::default()
 9825            }),
 9826            ..Default::default()
 9827        },
 9828        cx,
 9829    )
 9830    .await;
 9831
 9832    // A test that directly calls `show_signature_help`
 9833    cx.update_editor(|editor, window, cx| {
 9834        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9835    });
 9836
 9837    let mocked_response = lsp::SignatureHelp {
 9838        signatures: vec![lsp::SignatureInformation {
 9839            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9840            documentation: None,
 9841            parameters: Some(vec![
 9842                lsp::ParameterInformation {
 9843                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9844                    documentation: None,
 9845                },
 9846                lsp::ParameterInformation {
 9847                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9848                    documentation: None,
 9849                },
 9850            ]),
 9851            active_parameter: None,
 9852        }],
 9853        active_signature: Some(0),
 9854        active_parameter: Some(0),
 9855    };
 9856    handle_signature_help_request(&mut cx, mocked_response).await;
 9857
 9858    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9859        .await;
 9860
 9861    cx.editor(|editor, _, _| {
 9862        let signature_help_state = editor.signature_help_state.popover().cloned();
 9863        assert!(signature_help_state.is_some());
 9864        assert_eq!(
 9865            signature_help_state.unwrap().label,
 9866            "param1: u8, param2: u8"
 9867        );
 9868    });
 9869
 9870    // When exiting outside from inside the brackets, `signature_help` is closed.
 9871    cx.set_state(indoc! {"
 9872        fn main() {
 9873            sample(ˇ);
 9874        }
 9875
 9876        fn sample(param1: u8, param2: u8) {}
 9877    "});
 9878
 9879    cx.update_editor(|editor, window, cx| {
 9880        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9881    });
 9882
 9883    let mocked_response = lsp::SignatureHelp {
 9884        signatures: Vec::new(),
 9885        active_signature: None,
 9886        active_parameter: None,
 9887    };
 9888    handle_signature_help_request(&mut cx, mocked_response).await;
 9889
 9890    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9891        .await;
 9892
 9893    cx.editor(|editor, _, _| {
 9894        assert!(!editor.signature_help_state.is_shown());
 9895    });
 9896
 9897    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9898    cx.set_state(indoc! {"
 9899        fn main() {
 9900            sample(ˇ);
 9901        }
 9902
 9903        fn sample(param1: u8, param2: u8) {}
 9904    "});
 9905
 9906    let mocked_response = lsp::SignatureHelp {
 9907        signatures: vec![lsp::SignatureInformation {
 9908            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9909            documentation: None,
 9910            parameters: Some(vec![
 9911                lsp::ParameterInformation {
 9912                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9913                    documentation: None,
 9914                },
 9915                lsp::ParameterInformation {
 9916                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9917                    documentation: None,
 9918                },
 9919            ]),
 9920            active_parameter: None,
 9921        }],
 9922        active_signature: Some(0),
 9923        active_parameter: Some(0),
 9924    };
 9925    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9926    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9927        .await;
 9928    cx.editor(|editor, _, _| {
 9929        assert!(editor.signature_help_state.is_shown());
 9930    });
 9931
 9932    // Restore the popover with more parameter input
 9933    cx.set_state(indoc! {"
 9934        fn main() {
 9935            sample(param1, param2ˇ);
 9936        }
 9937
 9938        fn sample(param1: u8, param2: u8) {}
 9939    "});
 9940
 9941    let mocked_response = lsp::SignatureHelp {
 9942        signatures: vec![lsp::SignatureInformation {
 9943            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9944            documentation: None,
 9945            parameters: Some(vec![
 9946                lsp::ParameterInformation {
 9947                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9948                    documentation: None,
 9949                },
 9950                lsp::ParameterInformation {
 9951                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9952                    documentation: None,
 9953                },
 9954            ]),
 9955            active_parameter: None,
 9956        }],
 9957        active_signature: Some(0),
 9958        active_parameter: Some(1),
 9959    };
 9960    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9961    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9962        .await;
 9963
 9964    // When selecting a range, the popover is gone.
 9965    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9966    cx.update_editor(|editor, window, cx| {
 9967        editor.change_selections(None, window, cx, |s| {
 9968            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9969        })
 9970    });
 9971    cx.assert_editor_state(indoc! {"
 9972        fn main() {
 9973            sample(param1, «ˇparam2»);
 9974        }
 9975
 9976        fn sample(param1: u8, param2: u8) {}
 9977    "});
 9978    cx.editor(|editor, _, _| {
 9979        assert!(!editor.signature_help_state.is_shown());
 9980    });
 9981
 9982    // When unselecting again, the popover is back if within the brackets.
 9983    cx.update_editor(|editor, window, cx| {
 9984        editor.change_selections(None, window, cx, |s| {
 9985            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9986        })
 9987    });
 9988    cx.assert_editor_state(indoc! {"
 9989        fn main() {
 9990            sample(param1, ˇparam2);
 9991        }
 9992
 9993        fn sample(param1: u8, param2: u8) {}
 9994    "});
 9995    handle_signature_help_request(&mut cx, mocked_response).await;
 9996    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9997        .await;
 9998    cx.editor(|editor, _, _| {
 9999        assert!(editor.signature_help_state.is_shown());
10000    });
10001
10002    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10003    cx.update_editor(|editor, window, cx| {
10004        editor.change_selections(None, window, cx, |s| {
10005            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10006            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10007        })
10008    });
10009    cx.assert_editor_state(indoc! {"
10010        fn main() {
10011            sample(param1, ˇparam2);
10012        }
10013
10014        fn sample(param1: u8, param2: u8) {}
10015    "});
10016
10017    let mocked_response = lsp::SignatureHelp {
10018        signatures: vec![lsp::SignatureInformation {
10019            label: "fn sample(param1: u8, param2: u8)".to_string(),
10020            documentation: None,
10021            parameters: Some(vec![
10022                lsp::ParameterInformation {
10023                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10024                    documentation: None,
10025                },
10026                lsp::ParameterInformation {
10027                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10028                    documentation: None,
10029                },
10030            ]),
10031            active_parameter: None,
10032        }],
10033        active_signature: Some(0),
10034        active_parameter: Some(1),
10035    };
10036    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10037    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10038        .await;
10039    cx.update_editor(|editor, _, cx| {
10040        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10041    });
10042    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10043        .await;
10044    cx.update_editor(|editor, window, cx| {
10045        editor.change_selections(None, window, cx, |s| {
10046            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10047        })
10048    });
10049    cx.assert_editor_state(indoc! {"
10050        fn main() {
10051            sample(param1, «ˇparam2»);
10052        }
10053
10054        fn sample(param1: u8, param2: u8) {}
10055    "});
10056    cx.update_editor(|editor, window, cx| {
10057        editor.change_selections(None, window, cx, |s| {
10058            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10059        })
10060    });
10061    cx.assert_editor_state(indoc! {"
10062        fn main() {
10063            sample(param1, ˇparam2);
10064        }
10065
10066        fn sample(param1: u8, param2: u8) {}
10067    "});
10068    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10069        .await;
10070}
10071
10072#[gpui::test]
10073async fn test_completion_mode(cx: &mut TestAppContext) {
10074    init_test(cx, |_| {});
10075    let mut cx = EditorLspTestContext::new_rust(
10076        lsp::ServerCapabilities {
10077            completion_provider: Some(lsp::CompletionOptions {
10078                resolve_provider: Some(true),
10079                ..Default::default()
10080            }),
10081            ..Default::default()
10082        },
10083        cx,
10084    )
10085    .await;
10086
10087    struct Run {
10088        run_description: &'static str,
10089        initial_state: String,
10090        buffer_marked_text: String,
10091        completion_text: &'static str,
10092        expected_with_insert_mode: String,
10093        expected_with_replace_mode: String,
10094        expected_with_replace_subsequence_mode: String,
10095        expected_with_replace_suffix_mode: String,
10096    }
10097
10098    let runs = [
10099        Run {
10100            run_description: "Start of word matches completion text",
10101            initial_state: "before ediˇ after".into(),
10102            buffer_marked_text: "before <edi|> after".into(),
10103            completion_text: "editor",
10104            expected_with_insert_mode: "before editorˇ after".into(),
10105            expected_with_replace_mode: "before editorˇ after".into(),
10106            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10107            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10108        },
10109        Run {
10110            run_description: "Accept same text at the middle of the word",
10111            initial_state: "before ediˇtor after".into(),
10112            buffer_marked_text: "before <edi|tor> after".into(),
10113            completion_text: "editor",
10114            expected_with_insert_mode: "before editorˇtor after".into(),
10115            expected_with_replace_mode: "before editorˇ after".into(),
10116            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10117            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10118        },
10119        Run {
10120            run_description: "End of word matches completion text -- cursor at end",
10121            initial_state: "before torˇ after".into(),
10122            buffer_marked_text: "before <tor|> after".into(),
10123            completion_text: "editor",
10124            expected_with_insert_mode: "before editorˇ after".into(),
10125            expected_with_replace_mode: "before editorˇ after".into(),
10126            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10127            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10128        },
10129        Run {
10130            run_description: "End of word matches completion text -- cursor at start",
10131            initial_state: "before ˇtor after".into(),
10132            buffer_marked_text: "before <|tor> after".into(),
10133            completion_text: "editor",
10134            expected_with_insert_mode: "before editorˇtor after".into(),
10135            expected_with_replace_mode: "before editorˇ after".into(),
10136            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10137            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10138        },
10139        Run {
10140            run_description: "Prepend text containing whitespace",
10141            initial_state: "pˇfield: bool".into(),
10142            buffer_marked_text: "<p|field>: bool".into(),
10143            completion_text: "pub ",
10144            expected_with_insert_mode: "pub ˇfield: bool".into(),
10145            expected_with_replace_mode: "pub ˇ: bool".into(),
10146            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10147            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10148        },
10149        Run {
10150            run_description: "Add element to start of list",
10151            initial_state: "[element_ˇelement_2]".into(),
10152            buffer_marked_text: "[<element_|element_2>]".into(),
10153            completion_text: "element_1",
10154            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10155            expected_with_replace_mode: "[element_1ˇ]".into(),
10156            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10157            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10158        },
10159        Run {
10160            run_description: "Add element to start of list -- first and second elements are equal",
10161            initial_state: "[elˇelement]".into(),
10162            buffer_marked_text: "[<el|element>]".into(),
10163            completion_text: "element",
10164            expected_with_insert_mode: "[elementˇelement]".into(),
10165            expected_with_replace_mode: "[elementˇ]".into(),
10166            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10167            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10168        },
10169        Run {
10170            run_description: "Ends with matching suffix",
10171            initial_state: "SubˇError".into(),
10172            buffer_marked_text: "<Sub|Error>".into(),
10173            completion_text: "SubscriptionError",
10174            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10175            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10176            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10177            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10178        },
10179        Run {
10180            run_description: "Suffix is a subsequence -- contiguous",
10181            initial_state: "SubˇErr".into(),
10182            buffer_marked_text: "<Sub|Err>".into(),
10183            completion_text: "SubscriptionError",
10184            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10185            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10186            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10187            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10188        },
10189        Run {
10190            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10191            initial_state: "Suˇscrirr".into(),
10192            buffer_marked_text: "<Su|scrirr>".into(),
10193            completion_text: "SubscriptionError",
10194            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10195            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10196            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10197            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10198        },
10199        Run {
10200            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10201            initial_state: "foo(indˇix)".into(),
10202            buffer_marked_text: "foo(<ind|ix>)".into(),
10203            completion_text: "node_index",
10204            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10205            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10206            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10207            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10208        },
10209    ];
10210
10211    for run in runs {
10212        let run_variations = [
10213            (LspInsertMode::Insert, run.expected_with_insert_mode),
10214            (LspInsertMode::Replace, run.expected_with_replace_mode),
10215            (
10216                LspInsertMode::ReplaceSubsequence,
10217                run.expected_with_replace_subsequence_mode,
10218            ),
10219            (
10220                LspInsertMode::ReplaceSuffix,
10221                run.expected_with_replace_suffix_mode,
10222            ),
10223        ];
10224
10225        for (lsp_insert_mode, expected_text) in run_variations {
10226            eprintln!(
10227                "run = {:?}, mode = {lsp_insert_mode:.?}",
10228                run.run_description,
10229            );
10230
10231            update_test_language_settings(&mut cx, |settings| {
10232                settings.defaults.completions = Some(CompletionSettings {
10233                    lsp_insert_mode,
10234                    words: WordsCompletionMode::Disabled,
10235                    lsp: true,
10236                    lsp_fetch_timeout_ms: 0,
10237                });
10238            });
10239
10240            cx.set_state(&run.initial_state);
10241            cx.update_editor(|editor, window, cx| {
10242                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10243            });
10244
10245            let counter = Arc::new(AtomicUsize::new(0));
10246            handle_completion_request_with_insert_and_replace(
10247                &mut cx,
10248                &run.buffer_marked_text,
10249                vec![run.completion_text],
10250                counter.clone(),
10251            )
10252            .await;
10253            cx.condition(|editor, _| editor.context_menu_visible())
10254                .await;
10255            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10256
10257            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10258                editor
10259                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10260                    .unwrap()
10261            });
10262            cx.assert_editor_state(&expected_text);
10263            handle_resolve_completion_request(&mut cx, None).await;
10264            apply_additional_edits.await.unwrap();
10265        }
10266    }
10267}
10268
10269#[gpui::test]
10270async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10271    init_test(cx, |_| {});
10272    let mut cx = EditorLspTestContext::new_rust(
10273        lsp::ServerCapabilities {
10274            completion_provider: Some(lsp::CompletionOptions {
10275                resolve_provider: Some(true),
10276                ..Default::default()
10277            }),
10278            ..Default::default()
10279        },
10280        cx,
10281    )
10282    .await;
10283
10284    let initial_state = "SubˇError";
10285    let buffer_marked_text = "<Sub|Error>";
10286    let completion_text = "SubscriptionError";
10287    let expected_with_insert_mode = "SubscriptionErrorˇError";
10288    let expected_with_replace_mode = "SubscriptionErrorˇ";
10289
10290    update_test_language_settings(&mut cx, |settings| {
10291        settings.defaults.completions = Some(CompletionSettings {
10292            words: WordsCompletionMode::Disabled,
10293            // set the opposite here to ensure that the action is overriding the default behavior
10294            lsp_insert_mode: LspInsertMode::Insert,
10295            lsp: true,
10296            lsp_fetch_timeout_ms: 0,
10297        });
10298    });
10299
10300    cx.set_state(initial_state);
10301    cx.update_editor(|editor, window, cx| {
10302        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10303    });
10304
10305    let counter = Arc::new(AtomicUsize::new(0));
10306    handle_completion_request_with_insert_and_replace(
10307        &mut cx,
10308        &buffer_marked_text,
10309        vec![completion_text],
10310        counter.clone(),
10311    )
10312    .await;
10313    cx.condition(|editor, _| editor.context_menu_visible())
10314        .await;
10315    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10316
10317    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10318        editor
10319            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10320            .unwrap()
10321    });
10322    cx.assert_editor_state(&expected_with_replace_mode);
10323    handle_resolve_completion_request(&mut cx, None).await;
10324    apply_additional_edits.await.unwrap();
10325
10326    update_test_language_settings(&mut cx, |settings| {
10327        settings.defaults.completions = Some(CompletionSettings {
10328            words: WordsCompletionMode::Disabled,
10329            // set the opposite here to ensure that the action is overriding the default behavior
10330            lsp_insert_mode: LspInsertMode::Replace,
10331            lsp: true,
10332            lsp_fetch_timeout_ms: 0,
10333        });
10334    });
10335
10336    cx.set_state(initial_state);
10337    cx.update_editor(|editor, window, cx| {
10338        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10339    });
10340    handle_completion_request_with_insert_and_replace(
10341        &mut cx,
10342        &buffer_marked_text,
10343        vec![completion_text],
10344        counter.clone(),
10345    )
10346    .await;
10347    cx.condition(|editor, _| editor.context_menu_visible())
10348        .await;
10349    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10350
10351    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10352        editor
10353            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10354            .unwrap()
10355    });
10356    cx.assert_editor_state(&expected_with_insert_mode);
10357    handle_resolve_completion_request(&mut cx, None).await;
10358    apply_additional_edits.await.unwrap();
10359}
10360
10361#[gpui::test]
10362async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10363    init_test(cx, |_| {});
10364    let mut cx = EditorLspTestContext::new_rust(
10365        lsp::ServerCapabilities {
10366            completion_provider: Some(lsp::CompletionOptions {
10367                resolve_provider: Some(true),
10368                ..Default::default()
10369            }),
10370            ..Default::default()
10371        },
10372        cx,
10373    )
10374    .await;
10375
10376    // scenario: surrounding text matches completion text
10377    let completion_text = "to_offset";
10378    let initial_state = indoc! {"
10379        1. buf.to_offˇsuffix
10380        2. buf.to_offˇsuf
10381        3. buf.to_offˇfix
10382        4. buf.to_offˇ
10383        5. into_offˇensive
10384        6. ˇsuffix
10385        7. let ˇ //
10386        8. aaˇzz
10387        9. buf.to_off«zzzzzˇ»suffix
10388        10. buf.«ˇzzzzz»suffix
10389        11. to_off«ˇzzzzz»
10390
10391        buf.to_offˇsuffix  // newest cursor
10392    "};
10393    let completion_marked_buffer = indoc! {"
10394        1. buf.to_offsuffix
10395        2. buf.to_offsuf
10396        3. buf.to_offfix
10397        4. buf.to_off
10398        5. into_offensive
10399        6. suffix
10400        7. let  //
10401        8. aazz
10402        9. buf.to_offzzzzzsuffix
10403        10. buf.zzzzzsuffix
10404        11. to_offzzzzz
10405
10406        buf.<to_off|suffix>  // newest cursor
10407    "};
10408    let expected = indoc! {"
10409        1. buf.to_offsetˇ
10410        2. buf.to_offsetˇsuf
10411        3. buf.to_offsetˇfix
10412        4. buf.to_offsetˇ
10413        5. into_offsetˇensive
10414        6. to_offsetˇsuffix
10415        7. let to_offsetˇ //
10416        8. aato_offsetˇzz
10417        9. buf.to_offsetˇ
10418        10. buf.to_offsetˇsuffix
10419        11. to_offsetˇ
10420
10421        buf.to_offsetˇ  // newest cursor
10422    "};
10423    cx.set_state(initial_state);
10424    cx.update_editor(|editor, window, cx| {
10425        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10426    });
10427    handle_completion_request_with_insert_and_replace(
10428        &mut cx,
10429        completion_marked_buffer,
10430        vec![completion_text],
10431        Arc::new(AtomicUsize::new(0)),
10432    )
10433    .await;
10434    cx.condition(|editor, _| editor.context_menu_visible())
10435        .await;
10436    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10437        editor
10438            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10439            .unwrap()
10440    });
10441    cx.assert_editor_state(expected);
10442    handle_resolve_completion_request(&mut cx, None).await;
10443    apply_additional_edits.await.unwrap();
10444
10445    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10446    let completion_text = "foo_and_bar";
10447    let initial_state = indoc! {"
10448        1. ooanbˇ
10449        2. zooanbˇ
10450        3. ooanbˇz
10451        4. zooanbˇz
10452        5. ooanˇ
10453        6. oanbˇ
10454
10455        ooanbˇ
10456    "};
10457    let completion_marked_buffer = indoc! {"
10458        1. ooanb
10459        2. zooanb
10460        3. ooanbz
10461        4. zooanbz
10462        5. ooan
10463        6. oanb
10464
10465        <ooanb|>
10466    "};
10467    let expected = indoc! {"
10468        1. foo_and_barˇ
10469        2. zfoo_and_barˇ
10470        3. foo_and_barˇz
10471        4. zfoo_and_barˇz
10472        5. ooanfoo_and_barˇ
10473        6. oanbfoo_and_barˇ
10474
10475        foo_and_barˇ
10476    "};
10477    cx.set_state(initial_state);
10478    cx.update_editor(|editor, window, cx| {
10479        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10480    });
10481    handle_completion_request_with_insert_and_replace(
10482        &mut cx,
10483        completion_marked_buffer,
10484        vec![completion_text],
10485        Arc::new(AtomicUsize::new(0)),
10486    )
10487    .await;
10488    cx.condition(|editor, _| editor.context_menu_visible())
10489        .await;
10490    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10491        editor
10492            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10493            .unwrap()
10494    });
10495    cx.assert_editor_state(expected);
10496    handle_resolve_completion_request(&mut cx, None).await;
10497    apply_additional_edits.await.unwrap();
10498
10499    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10500    // (expects the same as if it was inserted at the end)
10501    let completion_text = "foo_and_bar";
10502    let initial_state = indoc! {"
10503        1. ooˇanb
10504        2. zooˇanb
10505        3. ooˇanbz
10506        4. zooˇanbz
10507
10508        ooˇanb
10509    "};
10510    let completion_marked_buffer = indoc! {"
10511        1. ooanb
10512        2. zooanb
10513        3. ooanbz
10514        4. zooanbz
10515
10516        <oo|anb>
10517    "};
10518    let expected = indoc! {"
10519        1. foo_and_barˇ
10520        2. zfoo_and_barˇ
10521        3. foo_and_barˇz
10522        4. zfoo_and_barˇz
10523
10524        foo_and_barˇ
10525    "};
10526    cx.set_state(initial_state);
10527    cx.update_editor(|editor, window, cx| {
10528        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10529    });
10530    handle_completion_request_with_insert_and_replace(
10531        &mut cx,
10532        completion_marked_buffer,
10533        vec![completion_text],
10534        Arc::new(AtomicUsize::new(0)),
10535    )
10536    .await;
10537    cx.condition(|editor, _| editor.context_menu_visible())
10538        .await;
10539    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10540        editor
10541            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10542            .unwrap()
10543    });
10544    cx.assert_editor_state(expected);
10545    handle_resolve_completion_request(&mut cx, None).await;
10546    apply_additional_edits.await.unwrap();
10547}
10548
10549// This used to crash
10550#[gpui::test]
10551async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10552    init_test(cx, |_| {});
10553
10554    let buffer_text = indoc! {"
10555        fn main() {
10556            10.satu;
10557
10558            //
10559            // separate cursors so they open in different excerpts (manually reproducible)
10560            //
10561
10562            10.satu20;
10563        }
10564    "};
10565    let multibuffer_text_with_selections = indoc! {"
10566        fn main() {
10567            10.satuˇ;
10568
10569            //
10570
10571            //
10572
10573            10.satuˇ20;
10574        }
10575    "};
10576    let expected_multibuffer = indoc! {"
10577        fn main() {
10578            10.saturating_sub()ˇ;
10579
10580            //
10581
10582            //
10583
10584            10.saturating_sub()ˇ;
10585        }
10586    "};
10587
10588    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10589    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10590
10591    let fs = FakeFs::new(cx.executor());
10592    fs.insert_tree(
10593        path!("/a"),
10594        json!({
10595            "main.rs": buffer_text,
10596        }),
10597    )
10598    .await;
10599
10600    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10601    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10602    language_registry.add(rust_lang());
10603    let mut fake_servers = language_registry.register_fake_lsp(
10604        "Rust",
10605        FakeLspAdapter {
10606            capabilities: lsp::ServerCapabilities {
10607                completion_provider: Some(lsp::CompletionOptions {
10608                    resolve_provider: None,
10609                    ..lsp::CompletionOptions::default()
10610                }),
10611                ..lsp::ServerCapabilities::default()
10612            },
10613            ..FakeLspAdapter::default()
10614        },
10615    );
10616    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10617    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10618    let buffer = project
10619        .update(cx, |project, cx| {
10620            project.open_local_buffer(path!("/a/main.rs"), cx)
10621        })
10622        .await
10623        .unwrap();
10624
10625    let multi_buffer = cx.new(|cx| {
10626        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10627        multi_buffer.push_excerpts(
10628            buffer.clone(),
10629            [ExcerptRange::new(0..first_excerpt_end)],
10630            cx,
10631        );
10632        multi_buffer.push_excerpts(
10633            buffer.clone(),
10634            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10635            cx,
10636        );
10637        multi_buffer
10638    });
10639
10640    let editor = workspace
10641        .update(cx, |_, window, cx| {
10642            cx.new(|cx| {
10643                Editor::new(
10644                    EditorMode::Full {
10645                        scale_ui_elements_with_buffer_font_size: false,
10646                        show_active_line_background: false,
10647                        sized_by_content: false,
10648                    },
10649                    multi_buffer.clone(),
10650                    Some(project.clone()),
10651                    window,
10652                    cx,
10653                )
10654            })
10655        })
10656        .unwrap();
10657
10658    let pane = workspace
10659        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10660        .unwrap();
10661    pane.update_in(cx, |pane, window, cx| {
10662        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10663    });
10664
10665    let fake_server = fake_servers.next().await.unwrap();
10666
10667    editor.update_in(cx, |editor, window, cx| {
10668        editor.change_selections(None, window, cx, |s| {
10669            s.select_ranges([
10670                Point::new(1, 11)..Point::new(1, 11),
10671                Point::new(7, 11)..Point::new(7, 11),
10672            ])
10673        });
10674
10675        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10676    });
10677
10678    editor.update_in(cx, |editor, window, cx| {
10679        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10680    });
10681
10682    fake_server
10683        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10684            let completion_item = lsp::CompletionItem {
10685                label: "saturating_sub()".into(),
10686                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10687                    lsp::InsertReplaceEdit {
10688                        new_text: "saturating_sub()".to_owned(),
10689                        insert: lsp::Range::new(
10690                            lsp::Position::new(7, 7),
10691                            lsp::Position::new(7, 11),
10692                        ),
10693                        replace: lsp::Range::new(
10694                            lsp::Position::new(7, 7),
10695                            lsp::Position::new(7, 13),
10696                        ),
10697                    },
10698                )),
10699                ..lsp::CompletionItem::default()
10700            };
10701
10702            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10703        })
10704        .next()
10705        .await
10706        .unwrap();
10707
10708    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10709        .await;
10710
10711    editor
10712        .update_in(cx, |editor, window, cx| {
10713            editor
10714                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10715                .unwrap()
10716        })
10717        .await
10718        .unwrap();
10719
10720    editor.update(cx, |editor, cx| {
10721        assert_text_with_selections(editor, expected_multibuffer, cx);
10722    })
10723}
10724
10725#[gpui::test]
10726async fn test_completion(cx: &mut TestAppContext) {
10727    init_test(cx, |_| {});
10728
10729    let mut cx = EditorLspTestContext::new_rust(
10730        lsp::ServerCapabilities {
10731            completion_provider: Some(lsp::CompletionOptions {
10732                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10733                resolve_provider: Some(true),
10734                ..Default::default()
10735            }),
10736            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10737            ..Default::default()
10738        },
10739        cx,
10740    )
10741    .await;
10742    let counter = Arc::new(AtomicUsize::new(0));
10743
10744    cx.set_state(indoc! {"
10745        oneˇ
10746        two
10747        three
10748    "});
10749    cx.simulate_keystroke(".");
10750    handle_completion_request(
10751        &mut cx,
10752        indoc! {"
10753            one.|<>
10754            two
10755            three
10756        "},
10757        vec!["first_completion", "second_completion"],
10758        counter.clone(),
10759    )
10760    .await;
10761    cx.condition(|editor, _| editor.context_menu_visible())
10762        .await;
10763    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10764
10765    let _handler = handle_signature_help_request(
10766        &mut cx,
10767        lsp::SignatureHelp {
10768            signatures: vec![lsp::SignatureInformation {
10769                label: "test signature".to_string(),
10770                documentation: None,
10771                parameters: Some(vec![lsp::ParameterInformation {
10772                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10773                    documentation: None,
10774                }]),
10775                active_parameter: None,
10776            }],
10777            active_signature: None,
10778            active_parameter: None,
10779        },
10780    );
10781    cx.update_editor(|editor, window, cx| {
10782        assert!(
10783            !editor.signature_help_state.is_shown(),
10784            "No signature help was called for"
10785        );
10786        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10787    });
10788    cx.run_until_parked();
10789    cx.update_editor(|editor, _, _| {
10790        assert!(
10791            !editor.signature_help_state.is_shown(),
10792            "No signature help should be shown when completions menu is open"
10793        );
10794    });
10795
10796    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10797        editor.context_menu_next(&Default::default(), window, cx);
10798        editor
10799            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10800            .unwrap()
10801    });
10802    cx.assert_editor_state(indoc! {"
10803        one.second_completionˇ
10804        two
10805        three
10806    "});
10807
10808    handle_resolve_completion_request(
10809        &mut cx,
10810        Some(vec![
10811            (
10812                //This overlaps with the primary completion edit which is
10813                //misbehavior from the LSP spec, test that we filter it out
10814                indoc! {"
10815                    one.second_ˇcompletion
10816                    two
10817                    threeˇ
10818                "},
10819                "overlapping additional edit",
10820            ),
10821            (
10822                indoc! {"
10823                    one.second_completion
10824                    two
10825                    threeˇ
10826                "},
10827                "\nadditional edit",
10828            ),
10829        ]),
10830    )
10831    .await;
10832    apply_additional_edits.await.unwrap();
10833    cx.assert_editor_state(indoc! {"
10834        one.second_completionˇ
10835        two
10836        three
10837        additional edit
10838    "});
10839
10840    cx.set_state(indoc! {"
10841        one.second_completion
10842        twoˇ
10843        threeˇ
10844        additional edit
10845    "});
10846    cx.simulate_keystroke(" ");
10847    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10848    cx.simulate_keystroke("s");
10849    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10850
10851    cx.assert_editor_state(indoc! {"
10852        one.second_completion
10853        two sˇ
10854        three sˇ
10855        additional edit
10856    "});
10857    handle_completion_request(
10858        &mut cx,
10859        indoc! {"
10860            one.second_completion
10861            two s
10862            three <s|>
10863            additional edit
10864        "},
10865        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10866        counter.clone(),
10867    )
10868    .await;
10869    cx.condition(|editor, _| editor.context_menu_visible())
10870        .await;
10871    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10872
10873    cx.simulate_keystroke("i");
10874
10875    handle_completion_request(
10876        &mut cx,
10877        indoc! {"
10878            one.second_completion
10879            two si
10880            three <si|>
10881            additional edit
10882        "},
10883        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10884        counter.clone(),
10885    )
10886    .await;
10887    cx.condition(|editor, _| editor.context_menu_visible())
10888        .await;
10889    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10890
10891    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10892        editor
10893            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10894            .unwrap()
10895    });
10896    cx.assert_editor_state(indoc! {"
10897        one.second_completion
10898        two sixth_completionˇ
10899        three sixth_completionˇ
10900        additional edit
10901    "});
10902
10903    apply_additional_edits.await.unwrap();
10904
10905    update_test_language_settings(&mut cx, |settings| {
10906        settings.defaults.show_completions_on_input = Some(false);
10907    });
10908    cx.set_state("editorˇ");
10909    cx.simulate_keystroke(".");
10910    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10911    cx.simulate_keystrokes("c l o");
10912    cx.assert_editor_state("editor.cloˇ");
10913    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10914    cx.update_editor(|editor, window, cx| {
10915        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10916    });
10917    handle_completion_request(
10918        &mut cx,
10919        "editor.<clo|>",
10920        vec!["close", "clobber"],
10921        counter.clone(),
10922    )
10923    .await;
10924    cx.condition(|editor, _| editor.context_menu_visible())
10925        .await;
10926    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10927
10928    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10929        editor
10930            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10931            .unwrap()
10932    });
10933    cx.assert_editor_state("editor.closeˇ");
10934    handle_resolve_completion_request(&mut cx, None).await;
10935    apply_additional_edits.await.unwrap();
10936}
10937
10938#[gpui::test]
10939async fn test_word_completion(cx: &mut TestAppContext) {
10940    let lsp_fetch_timeout_ms = 10;
10941    init_test(cx, |language_settings| {
10942        language_settings.defaults.completions = Some(CompletionSettings {
10943            words: WordsCompletionMode::Fallback,
10944            lsp: true,
10945            lsp_fetch_timeout_ms: 10,
10946            lsp_insert_mode: LspInsertMode::Insert,
10947        });
10948    });
10949
10950    let mut cx = EditorLspTestContext::new_rust(
10951        lsp::ServerCapabilities {
10952            completion_provider: Some(lsp::CompletionOptions {
10953                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10954                ..lsp::CompletionOptions::default()
10955            }),
10956            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10957            ..lsp::ServerCapabilities::default()
10958        },
10959        cx,
10960    )
10961    .await;
10962
10963    let throttle_completions = Arc::new(AtomicBool::new(false));
10964
10965    let lsp_throttle_completions = throttle_completions.clone();
10966    let _completion_requests_handler =
10967        cx.lsp
10968            .server
10969            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10970                let lsp_throttle_completions = lsp_throttle_completions.clone();
10971                let cx = cx.clone();
10972                async move {
10973                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10974                        cx.background_executor()
10975                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10976                            .await;
10977                    }
10978                    Ok(Some(lsp::CompletionResponse::Array(vec![
10979                        lsp::CompletionItem {
10980                            label: "first".into(),
10981                            ..lsp::CompletionItem::default()
10982                        },
10983                        lsp::CompletionItem {
10984                            label: "last".into(),
10985                            ..lsp::CompletionItem::default()
10986                        },
10987                    ])))
10988                }
10989            });
10990
10991    cx.set_state(indoc! {"
10992        oneˇ
10993        two
10994        three
10995    "});
10996    cx.simulate_keystroke(".");
10997    cx.executor().run_until_parked();
10998    cx.condition(|editor, _| editor.context_menu_visible())
10999        .await;
11000    cx.update_editor(|editor, window, cx| {
11001        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11002        {
11003            assert_eq!(
11004                completion_menu_entries(&menu),
11005                &["first", "last"],
11006                "When LSP server is fast to reply, no fallback word completions are used"
11007            );
11008        } else {
11009            panic!("expected completion menu to be open");
11010        }
11011        editor.cancel(&Cancel, window, cx);
11012    });
11013    cx.executor().run_until_parked();
11014    cx.condition(|editor, _| !editor.context_menu_visible())
11015        .await;
11016
11017    throttle_completions.store(true, atomic::Ordering::Release);
11018    cx.simulate_keystroke(".");
11019    cx.executor()
11020        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11021    cx.executor().run_until_parked();
11022    cx.condition(|editor, _| editor.context_menu_visible())
11023        .await;
11024    cx.update_editor(|editor, _, _| {
11025        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11026        {
11027            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11028                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11029        } else {
11030            panic!("expected completion menu to be open");
11031        }
11032    });
11033}
11034
11035#[gpui::test]
11036async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11037    init_test(cx, |language_settings| {
11038        language_settings.defaults.completions = Some(CompletionSettings {
11039            words: WordsCompletionMode::Enabled,
11040            lsp: true,
11041            lsp_fetch_timeout_ms: 0,
11042            lsp_insert_mode: LspInsertMode::Insert,
11043        });
11044    });
11045
11046    let mut cx = EditorLspTestContext::new_rust(
11047        lsp::ServerCapabilities {
11048            completion_provider: Some(lsp::CompletionOptions {
11049                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11050                ..lsp::CompletionOptions::default()
11051            }),
11052            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11053            ..lsp::ServerCapabilities::default()
11054        },
11055        cx,
11056    )
11057    .await;
11058
11059    let _completion_requests_handler =
11060        cx.lsp
11061            .server
11062            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11063                Ok(Some(lsp::CompletionResponse::Array(vec![
11064                    lsp::CompletionItem {
11065                        label: "first".into(),
11066                        ..lsp::CompletionItem::default()
11067                    },
11068                    lsp::CompletionItem {
11069                        label: "last".into(),
11070                        ..lsp::CompletionItem::default()
11071                    },
11072                ])))
11073            });
11074
11075    cx.set_state(indoc! {"ˇ
11076        first
11077        last
11078        second
11079    "});
11080    cx.simulate_keystroke(".");
11081    cx.executor().run_until_parked();
11082    cx.condition(|editor, _| editor.context_menu_visible())
11083        .await;
11084    cx.update_editor(|editor, _, _| {
11085        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11086        {
11087            assert_eq!(
11088                completion_menu_entries(&menu),
11089                &["first", "last", "second"],
11090                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11091            );
11092        } else {
11093            panic!("expected completion menu to be open");
11094        }
11095    });
11096}
11097
11098#[gpui::test]
11099async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11100    init_test(cx, |language_settings| {
11101        language_settings.defaults.completions = Some(CompletionSettings {
11102            words: WordsCompletionMode::Disabled,
11103            lsp: true,
11104            lsp_fetch_timeout_ms: 0,
11105            lsp_insert_mode: LspInsertMode::Insert,
11106        });
11107    });
11108
11109    let mut cx = EditorLspTestContext::new_rust(
11110        lsp::ServerCapabilities {
11111            completion_provider: Some(lsp::CompletionOptions {
11112                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11113                ..lsp::CompletionOptions::default()
11114            }),
11115            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11116            ..lsp::ServerCapabilities::default()
11117        },
11118        cx,
11119    )
11120    .await;
11121
11122    let _completion_requests_handler =
11123        cx.lsp
11124            .server
11125            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11126                panic!("LSP completions should not be queried when dealing with word completions")
11127            });
11128
11129    cx.set_state(indoc! {"ˇ
11130        first
11131        last
11132        second
11133    "});
11134    cx.update_editor(|editor, window, cx| {
11135        editor.show_word_completions(&ShowWordCompletions, window, cx);
11136    });
11137    cx.executor().run_until_parked();
11138    cx.condition(|editor, _| editor.context_menu_visible())
11139        .await;
11140    cx.update_editor(|editor, _, _| {
11141        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11142        {
11143            assert_eq!(
11144                completion_menu_entries(&menu),
11145                &["first", "last", "second"],
11146                "`ShowWordCompletions` action should show word completions"
11147            );
11148        } else {
11149            panic!("expected completion menu to be open");
11150        }
11151    });
11152
11153    cx.simulate_keystroke("l");
11154    cx.executor().run_until_parked();
11155    cx.condition(|editor, _| editor.context_menu_visible())
11156        .await;
11157    cx.update_editor(|editor, _, _| {
11158        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11159        {
11160            assert_eq!(
11161                completion_menu_entries(&menu),
11162                &["last"],
11163                "After showing word completions, further editing should filter them and not query the LSP"
11164            );
11165        } else {
11166            panic!("expected completion menu to be open");
11167        }
11168    });
11169}
11170
11171#[gpui::test]
11172async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11173    init_test(cx, |language_settings| {
11174        language_settings.defaults.completions = Some(CompletionSettings {
11175            words: WordsCompletionMode::Fallback,
11176            lsp: false,
11177            lsp_fetch_timeout_ms: 0,
11178            lsp_insert_mode: LspInsertMode::Insert,
11179        });
11180    });
11181
11182    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11183
11184    cx.set_state(indoc! {"ˇ
11185        0_usize
11186        let
11187        33
11188        4.5f32
11189    "});
11190    cx.update_editor(|editor, window, cx| {
11191        editor.show_completions(&ShowCompletions::default(), window, cx);
11192    });
11193    cx.executor().run_until_parked();
11194    cx.condition(|editor, _| editor.context_menu_visible())
11195        .await;
11196    cx.update_editor(|editor, window, cx| {
11197        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11198        {
11199            assert_eq!(
11200                completion_menu_entries(&menu),
11201                &["let"],
11202                "With no digits in the completion query, no digits should be in the word completions"
11203            );
11204        } else {
11205            panic!("expected completion menu to be open");
11206        }
11207        editor.cancel(&Cancel, window, cx);
11208    });
11209
11210    cx.set_state(indoc! {"11211        0_usize
11212        let
11213        3
11214        33.35f32
11215    "});
11216    cx.update_editor(|editor, window, cx| {
11217        editor.show_completions(&ShowCompletions::default(), window, cx);
11218    });
11219    cx.executor().run_until_parked();
11220    cx.condition(|editor, _| editor.context_menu_visible())
11221        .await;
11222    cx.update_editor(|editor, _, _| {
11223        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11224        {
11225            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11226                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11227        } else {
11228            panic!("expected completion menu to be open");
11229        }
11230    });
11231}
11232
11233fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11234    let position = || lsp::Position {
11235        line: params.text_document_position.position.line,
11236        character: params.text_document_position.position.character,
11237    };
11238    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11239        range: lsp::Range {
11240            start: position(),
11241            end: position(),
11242        },
11243        new_text: text.to_string(),
11244    }))
11245}
11246
11247#[gpui::test]
11248async fn test_multiline_completion(cx: &mut TestAppContext) {
11249    init_test(cx, |_| {});
11250
11251    let fs = FakeFs::new(cx.executor());
11252    fs.insert_tree(
11253        path!("/a"),
11254        json!({
11255            "main.ts": "a",
11256        }),
11257    )
11258    .await;
11259
11260    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11261    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11262    let typescript_language = Arc::new(Language::new(
11263        LanguageConfig {
11264            name: "TypeScript".into(),
11265            matcher: LanguageMatcher {
11266                path_suffixes: vec!["ts".to_string()],
11267                ..LanguageMatcher::default()
11268            },
11269            line_comments: vec!["// ".into()],
11270            ..LanguageConfig::default()
11271        },
11272        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11273    ));
11274    language_registry.add(typescript_language.clone());
11275    let mut fake_servers = language_registry.register_fake_lsp(
11276        "TypeScript",
11277        FakeLspAdapter {
11278            capabilities: lsp::ServerCapabilities {
11279                completion_provider: Some(lsp::CompletionOptions {
11280                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11281                    ..lsp::CompletionOptions::default()
11282                }),
11283                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11284                ..lsp::ServerCapabilities::default()
11285            },
11286            // Emulate vtsls label generation
11287            label_for_completion: Some(Box::new(|item, _| {
11288                let text = if let Some(description) = item
11289                    .label_details
11290                    .as_ref()
11291                    .and_then(|label_details| label_details.description.as_ref())
11292                {
11293                    format!("{} {}", item.label, description)
11294                } else if let Some(detail) = &item.detail {
11295                    format!("{} {}", item.label, detail)
11296                } else {
11297                    item.label.clone()
11298                };
11299                let len = text.len();
11300                Some(language::CodeLabel {
11301                    text,
11302                    runs: Vec::new(),
11303                    filter_range: 0..len,
11304                })
11305            })),
11306            ..FakeLspAdapter::default()
11307        },
11308    );
11309    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11310    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11311    let worktree_id = workspace
11312        .update(cx, |workspace, _window, cx| {
11313            workspace.project().update(cx, |project, cx| {
11314                project.worktrees(cx).next().unwrap().read(cx).id()
11315            })
11316        })
11317        .unwrap();
11318    let _buffer = project
11319        .update(cx, |project, cx| {
11320            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11321        })
11322        .await
11323        .unwrap();
11324    let editor = workspace
11325        .update(cx, |workspace, window, cx| {
11326            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11327        })
11328        .unwrap()
11329        .await
11330        .unwrap()
11331        .downcast::<Editor>()
11332        .unwrap();
11333    let fake_server = fake_servers.next().await.unwrap();
11334
11335    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11336    let multiline_label_2 = "a\nb\nc\n";
11337    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11338    let multiline_description = "d\ne\nf\n";
11339    let multiline_detail_2 = "g\nh\ni\n";
11340
11341    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11342        move |params, _| async move {
11343            Ok(Some(lsp::CompletionResponse::Array(vec![
11344                lsp::CompletionItem {
11345                    label: multiline_label.to_string(),
11346                    text_edit: gen_text_edit(&params, "new_text_1"),
11347                    ..lsp::CompletionItem::default()
11348                },
11349                lsp::CompletionItem {
11350                    label: "single line label 1".to_string(),
11351                    detail: Some(multiline_detail.to_string()),
11352                    text_edit: gen_text_edit(&params, "new_text_2"),
11353                    ..lsp::CompletionItem::default()
11354                },
11355                lsp::CompletionItem {
11356                    label: "single line label 2".to_string(),
11357                    label_details: Some(lsp::CompletionItemLabelDetails {
11358                        description: Some(multiline_description.to_string()),
11359                        detail: None,
11360                    }),
11361                    text_edit: gen_text_edit(&params, "new_text_2"),
11362                    ..lsp::CompletionItem::default()
11363                },
11364                lsp::CompletionItem {
11365                    label: multiline_label_2.to_string(),
11366                    detail: Some(multiline_detail_2.to_string()),
11367                    text_edit: gen_text_edit(&params, "new_text_3"),
11368                    ..lsp::CompletionItem::default()
11369                },
11370                lsp::CompletionItem {
11371                    label: "Label with many     spaces and \t but without newlines".to_string(),
11372                    detail: Some(
11373                        "Details with many     spaces and \t but without newlines".to_string(),
11374                    ),
11375                    text_edit: gen_text_edit(&params, "new_text_4"),
11376                    ..lsp::CompletionItem::default()
11377                },
11378            ])))
11379        },
11380    );
11381
11382    editor.update_in(cx, |editor, window, cx| {
11383        cx.focus_self(window);
11384        editor.move_to_end(&MoveToEnd, window, cx);
11385        editor.handle_input(".", window, cx);
11386    });
11387    cx.run_until_parked();
11388    completion_handle.next().await.unwrap();
11389
11390    editor.update(cx, |editor, _| {
11391        assert!(editor.context_menu_visible());
11392        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11393        {
11394            let completion_labels = menu
11395                .completions
11396                .borrow()
11397                .iter()
11398                .map(|c| c.label.text.clone())
11399                .collect::<Vec<_>>();
11400            assert_eq!(
11401                completion_labels,
11402                &[
11403                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11404                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11405                    "single line label 2 d e f ",
11406                    "a b c g h i ",
11407                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11408                ],
11409                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11410            );
11411
11412            for completion in menu
11413                .completions
11414                .borrow()
11415                .iter() {
11416                    assert_eq!(
11417                        completion.label.filter_range,
11418                        0..completion.label.text.len(),
11419                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11420                    );
11421                }
11422        } else {
11423            panic!("expected completion menu to be open");
11424        }
11425    });
11426}
11427
11428#[gpui::test]
11429async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11430    init_test(cx, |_| {});
11431    let mut cx = EditorLspTestContext::new_rust(
11432        lsp::ServerCapabilities {
11433            completion_provider: Some(lsp::CompletionOptions {
11434                trigger_characters: Some(vec![".".to_string()]),
11435                ..Default::default()
11436            }),
11437            ..Default::default()
11438        },
11439        cx,
11440    )
11441    .await;
11442    cx.lsp
11443        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11444            Ok(Some(lsp::CompletionResponse::Array(vec![
11445                lsp::CompletionItem {
11446                    label: "first".into(),
11447                    ..Default::default()
11448                },
11449                lsp::CompletionItem {
11450                    label: "last".into(),
11451                    ..Default::default()
11452                },
11453            ])))
11454        });
11455    cx.set_state("variableˇ");
11456    cx.simulate_keystroke(".");
11457    cx.executor().run_until_parked();
11458
11459    cx.update_editor(|editor, _, _| {
11460        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11461        {
11462            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11463        } else {
11464            panic!("expected completion menu to be open");
11465        }
11466    });
11467
11468    cx.update_editor(|editor, window, cx| {
11469        editor.move_page_down(&MovePageDown::default(), window, cx);
11470        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11471        {
11472            assert!(
11473                menu.selected_item == 1,
11474                "expected PageDown to select the last item from the context menu"
11475            );
11476        } else {
11477            panic!("expected completion menu to stay open after PageDown");
11478        }
11479    });
11480
11481    cx.update_editor(|editor, window, cx| {
11482        editor.move_page_up(&MovePageUp::default(), window, cx);
11483        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11484        {
11485            assert!(
11486                menu.selected_item == 0,
11487                "expected PageUp to select the first item from the context menu"
11488            );
11489        } else {
11490            panic!("expected completion menu to stay open after PageUp");
11491        }
11492    });
11493}
11494
11495#[gpui::test]
11496async fn test_as_is_completions(cx: &mut TestAppContext) {
11497    init_test(cx, |_| {});
11498    let mut cx = EditorLspTestContext::new_rust(
11499        lsp::ServerCapabilities {
11500            completion_provider: Some(lsp::CompletionOptions {
11501                ..Default::default()
11502            }),
11503            ..Default::default()
11504        },
11505        cx,
11506    )
11507    .await;
11508    cx.lsp
11509        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11510            Ok(Some(lsp::CompletionResponse::Array(vec![
11511                lsp::CompletionItem {
11512                    label: "unsafe".into(),
11513                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11514                        range: lsp::Range {
11515                            start: lsp::Position {
11516                                line: 1,
11517                                character: 2,
11518                            },
11519                            end: lsp::Position {
11520                                line: 1,
11521                                character: 3,
11522                            },
11523                        },
11524                        new_text: "unsafe".to_string(),
11525                    })),
11526                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11527                    ..Default::default()
11528                },
11529            ])))
11530        });
11531    cx.set_state("fn a() {}\n");
11532    cx.executor().run_until_parked();
11533    cx.update_editor(|editor, window, cx| {
11534        editor.show_completions(
11535            &ShowCompletions {
11536                trigger: Some("\n".into()),
11537            },
11538            window,
11539            cx,
11540        );
11541    });
11542    cx.executor().run_until_parked();
11543
11544    cx.update_editor(|editor, window, cx| {
11545        editor.confirm_completion(&Default::default(), window, cx)
11546    });
11547    cx.executor().run_until_parked();
11548    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11549}
11550
11551#[gpui::test]
11552async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11553    init_test(cx, |_| {});
11554
11555    let mut cx = EditorLspTestContext::new_rust(
11556        lsp::ServerCapabilities {
11557            completion_provider: Some(lsp::CompletionOptions {
11558                trigger_characters: Some(vec![".".to_string()]),
11559                resolve_provider: Some(true),
11560                ..Default::default()
11561            }),
11562            ..Default::default()
11563        },
11564        cx,
11565    )
11566    .await;
11567
11568    cx.set_state("fn main() { let a = 2ˇ; }");
11569    cx.simulate_keystroke(".");
11570    let completion_item = lsp::CompletionItem {
11571        label: "Some".into(),
11572        kind: Some(lsp::CompletionItemKind::SNIPPET),
11573        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11574        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11575            kind: lsp::MarkupKind::Markdown,
11576            value: "```rust\nSome(2)\n```".to_string(),
11577        })),
11578        deprecated: Some(false),
11579        sort_text: Some("Some".to_string()),
11580        filter_text: Some("Some".to_string()),
11581        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11582        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11583            range: lsp::Range {
11584                start: lsp::Position {
11585                    line: 0,
11586                    character: 22,
11587                },
11588                end: lsp::Position {
11589                    line: 0,
11590                    character: 22,
11591                },
11592            },
11593            new_text: "Some(2)".to_string(),
11594        })),
11595        additional_text_edits: Some(vec![lsp::TextEdit {
11596            range: lsp::Range {
11597                start: lsp::Position {
11598                    line: 0,
11599                    character: 20,
11600                },
11601                end: lsp::Position {
11602                    line: 0,
11603                    character: 22,
11604                },
11605            },
11606            new_text: "".to_string(),
11607        }]),
11608        ..Default::default()
11609    };
11610
11611    let closure_completion_item = completion_item.clone();
11612    let counter = Arc::new(AtomicUsize::new(0));
11613    let counter_clone = counter.clone();
11614    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11615        let task_completion_item = closure_completion_item.clone();
11616        counter_clone.fetch_add(1, atomic::Ordering::Release);
11617        async move {
11618            Ok(Some(lsp::CompletionResponse::Array(vec![
11619                task_completion_item,
11620            ])))
11621        }
11622    });
11623
11624    cx.condition(|editor, _| editor.context_menu_visible())
11625        .await;
11626    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11627    assert!(request.next().await.is_some());
11628    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11629
11630    cx.simulate_keystrokes("S o m");
11631    cx.condition(|editor, _| editor.context_menu_visible())
11632        .await;
11633    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11634    assert!(request.next().await.is_some());
11635    assert!(request.next().await.is_some());
11636    assert!(request.next().await.is_some());
11637    request.close();
11638    assert!(request.next().await.is_none());
11639    assert_eq!(
11640        counter.load(atomic::Ordering::Acquire),
11641        4,
11642        "With the completions menu open, only one LSP request should happen per input"
11643    );
11644}
11645
11646#[gpui::test]
11647async fn test_toggle_comment(cx: &mut TestAppContext) {
11648    init_test(cx, |_| {});
11649    let mut cx = EditorTestContext::new(cx).await;
11650    let language = Arc::new(Language::new(
11651        LanguageConfig {
11652            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11653            ..Default::default()
11654        },
11655        Some(tree_sitter_rust::LANGUAGE.into()),
11656    ));
11657    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11658
11659    // If multiple selections intersect a line, the line is only toggled once.
11660    cx.set_state(indoc! {"
11661        fn a() {
11662            «//b();
11663            ˇ»// «c();
11664            //ˇ»  d();
11665        }
11666    "});
11667
11668    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11669
11670    cx.assert_editor_state(indoc! {"
11671        fn a() {
11672            «b();
11673            c();
11674            ˇ» d();
11675        }
11676    "});
11677
11678    // The comment prefix is inserted at the same column for every line in a
11679    // selection.
11680    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11681
11682    cx.assert_editor_state(indoc! {"
11683        fn a() {
11684            // «b();
11685            // c();
11686            ˇ»//  d();
11687        }
11688    "});
11689
11690    // If a selection ends at the beginning of a line, that line is not toggled.
11691    cx.set_selections_state(indoc! {"
11692        fn a() {
11693            // b();
11694            «// c();
11695        ˇ»    //  d();
11696        }
11697    "});
11698
11699    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11700
11701    cx.assert_editor_state(indoc! {"
11702        fn a() {
11703            // b();
11704            «c();
11705        ˇ»    //  d();
11706        }
11707    "});
11708
11709    // If a selection span a single line and is empty, the line is toggled.
11710    cx.set_state(indoc! {"
11711        fn a() {
11712            a();
11713            b();
11714        ˇ
11715        }
11716    "});
11717
11718    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11719
11720    cx.assert_editor_state(indoc! {"
11721        fn a() {
11722            a();
11723            b();
11724        //•ˇ
11725        }
11726    "});
11727
11728    // If a selection span multiple lines, empty lines are not toggled.
11729    cx.set_state(indoc! {"
11730        fn a() {
11731            «a();
11732
11733            c();ˇ»
11734        }
11735    "});
11736
11737    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11738
11739    cx.assert_editor_state(indoc! {"
11740        fn a() {
11741            // «a();
11742
11743            // c();ˇ»
11744        }
11745    "});
11746
11747    // If a selection includes multiple comment prefixes, all lines are uncommented.
11748    cx.set_state(indoc! {"
11749        fn a() {
11750            «// a();
11751            /// b();
11752            //! c();ˇ»
11753        }
11754    "});
11755
11756    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11757
11758    cx.assert_editor_state(indoc! {"
11759        fn a() {
11760            «a();
11761            b();
11762            c();ˇ»
11763        }
11764    "});
11765}
11766
11767#[gpui::test]
11768async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11769    init_test(cx, |_| {});
11770    let mut cx = EditorTestContext::new(cx).await;
11771    let language = Arc::new(Language::new(
11772        LanguageConfig {
11773            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11774            ..Default::default()
11775        },
11776        Some(tree_sitter_rust::LANGUAGE.into()),
11777    ));
11778    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11779
11780    let toggle_comments = &ToggleComments {
11781        advance_downwards: false,
11782        ignore_indent: true,
11783    };
11784
11785    // If multiple selections intersect a line, the line is only toggled once.
11786    cx.set_state(indoc! {"
11787        fn a() {
11788        //    «b();
11789        //    c();
11790        //    ˇ» d();
11791        }
11792    "});
11793
11794    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11795
11796    cx.assert_editor_state(indoc! {"
11797        fn a() {
11798            «b();
11799            c();
11800            ˇ» d();
11801        }
11802    "});
11803
11804    // The comment prefix is inserted at the beginning of each line
11805    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11806
11807    cx.assert_editor_state(indoc! {"
11808        fn a() {
11809        //    «b();
11810        //    c();
11811        //    ˇ» d();
11812        }
11813    "});
11814
11815    // If a selection ends at the beginning of a line, that line is not toggled.
11816    cx.set_selections_state(indoc! {"
11817        fn a() {
11818        //    b();
11819        //    «c();
11820        ˇ»//     d();
11821        }
11822    "});
11823
11824    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11825
11826    cx.assert_editor_state(indoc! {"
11827        fn a() {
11828        //    b();
11829            «c();
11830        ˇ»//     d();
11831        }
11832    "});
11833
11834    // If a selection span a single line and is empty, the line is toggled.
11835    cx.set_state(indoc! {"
11836        fn a() {
11837            a();
11838            b();
11839        ˇ
11840        }
11841    "});
11842
11843    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11844
11845    cx.assert_editor_state(indoc! {"
11846        fn a() {
11847            a();
11848            b();
11849        //ˇ
11850        }
11851    "});
11852
11853    // If a selection span multiple lines, empty lines are not toggled.
11854    cx.set_state(indoc! {"
11855        fn a() {
11856            «a();
11857
11858            c();ˇ»
11859        }
11860    "});
11861
11862    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11863
11864    cx.assert_editor_state(indoc! {"
11865        fn a() {
11866        //    «a();
11867
11868        //    c();ˇ»
11869        }
11870    "});
11871
11872    // If a selection includes multiple comment prefixes, all lines are uncommented.
11873    cx.set_state(indoc! {"
11874        fn a() {
11875        //    «a();
11876        ///    b();
11877        //!    c();ˇ»
11878        }
11879    "});
11880
11881    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11882
11883    cx.assert_editor_state(indoc! {"
11884        fn a() {
11885            «a();
11886            b();
11887            c();ˇ»
11888        }
11889    "});
11890}
11891
11892#[gpui::test]
11893async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11894    init_test(cx, |_| {});
11895
11896    let language = Arc::new(Language::new(
11897        LanguageConfig {
11898            line_comments: vec!["// ".into()],
11899            ..Default::default()
11900        },
11901        Some(tree_sitter_rust::LANGUAGE.into()),
11902    ));
11903
11904    let mut cx = EditorTestContext::new(cx).await;
11905
11906    cx.language_registry().add(language.clone());
11907    cx.update_buffer(|buffer, cx| {
11908        buffer.set_language(Some(language), cx);
11909    });
11910
11911    let toggle_comments = &ToggleComments {
11912        advance_downwards: true,
11913        ignore_indent: false,
11914    };
11915
11916    // Single cursor on one line -> advance
11917    // Cursor moves horizontally 3 characters as well on non-blank line
11918    cx.set_state(indoc!(
11919        "fn a() {
11920             ˇdog();
11921             cat();
11922        }"
11923    ));
11924    cx.update_editor(|editor, window, cx| {
11925        editor.toggle_comments(toggle_comments, window, cx);
11926    });
11927    cx.assert_editor_state(indoc!(
11928        "fn a() {
11929             // dog();
11930             catˇ();
11931        }"
11932    ));
11933
11934    // Single selection on one line -> don't advance
11935    cx.set_state(indoc!(
11936        "fn a() {
11937             «dog()ˇ»;
11938             cat();
11939        }"
11940    ));
11941    cx.update_editor(|editor, window, cx| {
11942        editor.toggle_comments(toggle_comments, window, cx);
11943    });
11944    cx.assert_editor_state(indoc!(
11945        "fn a() {
11946             // «dog()ˇ»;
11947             cat();
11948        }"
11949    ));
11950
11951    // Multiple cursors on one line -> advance
11952    cx.set_state(indoc!(
11953        "fn a() {
11954             ˇdˇog();
11955             cat();
11956        }"
11957    ));
11958    cx.update_editor(|editor, window, cx| {
11959        editor.toggle_comments(toggle_comments, window, cx);
11960    });
11961    cx.assert_editor_state(indoc!(
11962        "fn a() {
11963             // dog();
11964             catˇ(ˇ);
11965        }"
11966    ));
11967
11968    // Multiple cursors on one line, with selection -> don't advance
11969    cx.set_state(indoc!(
11970        "fn a() {
11971             ˇdˇog«()ˇ»;
11972             cat();
11973        }"
11974    ));
11975    cx.update_editor(|editor, window, cx| {
11976        editor.toggle_comments(toggle_comments, window, cx);
11977    });
11978    cx.assert_editor_state(indoc!(
11979        "fn a() {
11980             // ˇdˇog«()ˇ»;
11981             cat();
11982        }"
11983    ));
11984
11985    // Single cursor on one line -> advance
11986    // Cursor moves to column 0 on blank line
11987    cx.set_state(indoc!(
11988        "fn a() {
11989             ˇdog();
11990
11991             cat();
11992        }"
11993    ));
11994    cx.update_editor(|editor, window, cx| {
11995        editor.toggle_comments(toggle_comments, window, cx);
11996    });
11997    cx.assert_editor_state(indoc!(
11998        "fn a() {
11999             // dog();
12000        ˇ
12001             cat();
12002        }"
12003    ));
12004
12005    // Single cursor on one line -> advance
12006    // Cursor starts and ends at column 0
12007    cx.set_state(indoc!(
12008        "fn a() {
12009         ˇ    dog();
12010             cat();
12011        }"
12012    ));
12013    cx.update_editor(|editor, window, cx| {
12014        editor.toggle_comments(toggle_comments, window, cx);
12015    });
12016    cx.assert_editor_state(indoc!(
12017        "fn a() {
12018             // dog();
12019         ˇ    cat();
12020        }"
12021    ));
12022}
12023
12024#[gpui::test]
12025async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12026    init_test(cx, |_| {});
12027
12028    let mut cx = EditorTestContext::new(cx).await;
12029
12030    let html_language = Arc::new(
12031        Language::new(
12032            LanguageConfig {
12033                name: "HTML".into(),
12034                block_comment: Some(("<!-- ".into(), " -->".into())),
12035                ..Default::default()
12036            },
12037            Some(tree_sitter_html::LANGUAGE.into()),
12038        )
12039        .with_injection_query(
12040            r#"
12041            (script_element
12042                (raw_text) @injection.content
12043                (#set! injection.language "javascript"))
12044            "#,
12045        )
12046        .unwrap(),
12047    );
12048
12049    let javascript_language = Arc::new(Language::new(
12050        LanguageConfig {
12051            name: "JavaScript".into(),
12052            line_comments: vec!["// ".into()],
12053            ..Default::default()
12054        },
12055        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12056    ));
12057
12058    cx.language_registry().add(html_language.clone());
12059    cx.language_registry().add(javascript_language.clone());
12060    cx.update_buffer(|buffer, cx| {
12061        buffer.set_language(Some(html_language), cx);
12062    });
12063
12064    // Toggle comments for empty selections
12065    cx.set_state(
12066        &r#"
12067            <p>A</p>ˇ
12068            <p>B</p>ˇ
12069            <p>C</p>ˇ
12070        "#
12071        .unindent(),
12072    );
12073    cx.update_editor(|editor, window, cx| {
12074        editor.toggle_comments(&ToggleComments::default(), window, cx)
12075    });
12076    cx.assert_editor_state(
12077        &r#"
12078            <!-- <p>A</p>ˇ -->
12079            <!-- <p>B</p>ˇ -->
12080            <!-- <p>C</p>ˇ -->
12081        "#
12082        .unindent(),
12083    );
12084    cx.update_editor(|editor, window, cx| {
12085        editor.toggle_comments(&ToggleComments::default(), window, cx)
12086    });
12087    cx.assert_editor_state(
12088        &r#"
12089            <p>A</p>ˇ
12090            <p>B</p>ˇ
12091            <p>C</p>ˇ
12092        "#
12093        .unindent(),
12094    );
12095
12096    // Toggle comments for mixture of empty and non-empty selections, where
12097    // multiple selections occupy a given line.
12098    cx.set_state(
12099        &r#"
12100            <p>A«</p>
12101            <p>ˇ»B</p>ˇ
12102            <p>C«</p>
12103            <p>ˇ»D</p>ˇ
12104        "#
12105        .unindent(),
12106    );
12107
12108    cx.update_editor(|editor, window, cx| {
12109        editor.toggle_comments(&ToggleComments::default(), window, cx)
12110    });
12111    cx.assert_editor_state(
12112        &r#"
12113            <!-- <p>A«</p>
12114            <p>ˇ»B</p>ˇ -->
12115            <!-- <p>C«</p>
12116            <p>ˇ»D</p>ˇ -->
12117        "#
12118        .unindent(),
12119    );
12120    cx.update_editor(|editor, window, cx| {
12121        editor.toggle_comments(&ToggleComments::default(), window, cx)
12122    });
12123    cx.assert_editor_state(
12124        &r#"
12125            <p>A«</p>
12126            <p>ˇ»B</p>ˇ
12127            <p>C«</p>
12128            <p>ˇ»D</p>ˇ
12129        "#
12130        .unindent(),
12131    );
12132
12133    // Toggle comments when different languages are active for different
12134    // selections.
12135    cx.set_state(
12136        &r#"
12137            ˇ<script>
12138                ˇvar x = new Y();
12139            ˇ</script>
12140        "#
12141        .unindent(),
12142    );
12143    cx.executor().run_until_parked();
12144    cx.update_editor(|editor, window, cx| {
12145        editor.toggle_comments(&ToggleComments::default(), window, cx)
12146    });
12147    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12148    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12149    cx.assert_editor_state(
12150        &r#"
12151            <!-- ˇ<script> -->
12152                // ˇvar x = new Y();
12153            <!-- ˇ</script> -->
12154        "#
12155        .unindent(),
12156    );
12157}
12158
12159#[gpui::test]
12160fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12161    init_test(cx, |_| {});
12162
12163    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12164    let multibuffer = cx.new(|cx| {
12165        let mut multibuffer = MultiBuffer::new(ReadWrite);
12166        multibuffer.push_excerpts(
12167            buffer.clone(),
12168            [
12169                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12170                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12171            ],
12172            cx,
12173        );
12174        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12175        multibuffer
12176    });
12177
12178    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12179    editor.update_in(cx, |editor, window, cx| {
12180        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12181        editor.change_selections(None, window, cx, |s| {
12182            s.select_ranges([
12183                Point::new(0, 0)..Point::new(0, 0),
12184                Point::new(1, 0)..Point::new(1, 0),
12185            ])
12186        });
12187
12188        editor.handle_input("X", window, cx);
12189        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12190        assert_eq!(
12191            editor.selections.ranges(cx),
12192            [
12193                Point::new(0, 1)..Point::new(0, 1),
12194                Point::new(1, 1)..Point::new(1, 1),
12195            ]
12196        );
12197
12198        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12199        editor.change_selections(None, window, cx, |s| {
12200            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12201        });
12202        editor.backspace(&Default::default(), window, cx);
12203        assert_eq!(editor.text(cx), "Xa\nbbb");
12204        assert_eq!(
12205            editor.selections.ranges(cx),
12206            [Point::new(1, 0)..Point::new(1, 0)]
12207        );
12208
12209        editor.change_selections(None, window, cx, |s| {
12210            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12211        });
12212        editor.backspace(&Default::default(), window, cx);
12213        assert_eq!(editor.text(cx), "X\nbb");
12214        assert_eq!(
12215            editor.selections.ranges(cx),
12216            [Point::new(0, 1)..Point::new(0, 1)]
12217        );
12218    });
12219}
12220
12221#[gpui::test]
12222fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12223    init_test(cx, |_| {});
12224
12225    let markers = vec![('[', ']').into(), ('(', ')').into()];
12226    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12227        indoc! {"
12228            [aaaa
12229            (bbbb]
12230            cccc)",
12231        },
12232        markers.clone(),
12233    );
12234    let excerpt_ranges = markers.into_iter().map(|marker| {
12235        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12236        ExcerptRange::new(context.clone())
12237    });
12238    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12239    let multibuffer = cx.new(|cx| {
12240        let mut multibuffer = MultiBuffer::new(ReadWrite);
12241        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12242        multibuffer
12243    });
12244
12245    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12246    editor.update_in(cx, |editor, window, cx| {
12247        let (expected_text, selection_ranges) = marked_text_ranges(
12248            indoc! {"
12249                aaaa
12250                bˇbbb
12251                bˇbbˇb
12252                cccc"
12253            },
12254            true,
12255        );
12256        assert_eq!(editor.text(cx), expected_text);
12257        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12258
12259        editor.handle_input("X", window, cx);
12260
12261        let (expected_text, expected_selections) = marked_text_ranges(
12262            indoc! {"
12263                aaaa
12264                bXˇbbXb
12265                bXˇbbXˇb
12266                cccc"
12267            },
12268            false,
12269        );
12270        assert_eq!(editor.text(cx), expected_text);
12271        assert_eq!(editor.selections.ranges(cx), expected_selections);
12272
12273        editor.newline(&Newline, window, cx);
12274        let (expected_text, expected_selections) = marked_text_ranges(
12275            indoc! {"
12276                aaaa
12277                bX
12278                ˇbbX
12279                b
12280                bX
12281                ˇbbX
12282                ˇb
12283                cccc"
12284            },
12285            false,
12286        );
12287        assert_eq!(editor.text(cx), expected_text);
12288        assert_eq!(editor.selections.ranges(cx), expected_selections);
12289    });
12290}
12291
12292#[gpui::test]
12293fn test_refresh_selections(cx: &mut TestAppContext) {
12294    init_test(cx, |_| {});
12295
12296    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12297    let mut excerpt1_id = None;
12298    let multibuffer = cx.new(|cx| {
12299        let mut multibuffer = MultiBuffer::new(ReadWrite);
12300        excerpt1_id = multibuffer
12301            .push_excerpts(
12302                buffer.clone(),
12303                [
12304                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12305                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12306                ],
12307                cx,
12308            )
12309            .into_iter()
12310            .next();
12311        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12312        multibuffer
12313    });
12314
12315    let editor = cx.add_window(|window, cx| {
12316        let mut editor = build_editor(multibuffer.clone(), window, cx);
12317        let snapshot = editor.snapshot(window, cx);
12318        editor.change_selections(None, window, cx, |s| {
12319            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12320        });
12321        editor.begin_selection(
12322            Point::new(2, 1).to_display_point(&snapshot),
12323            true,
12324            1,
12325            window,
12326            cx,
12327        );
12328        assert_eq!(
12329            editor.selections.ranges(cx),
12330            [
12331                Point::new(1, 3)..Point::new(1, 3),
12332                Point::new(2, 1)..Point::new(2, 1),
12333            ]
12334        );
12335        editor
12336    });
12337
12338    // Refreshing selections is a no-op when excerpts haven't changed.
12339    _ = editor.update(cx, |editor, window, cx| {
12340        editor.change_selections(None, window, cx, |s| s.refresh());
12341        assert_eq!(
12342            editor.selections.ranges(cx),
12343            [
12344                Point::new(1, 3)..Point::new(1, 3),
12345                Point::new(2, 1)..Point::new(2, 1),
12346            ]
12347        );
12348    });
12349
12350    multibuffer.update(cx, |multibuffer, cx| {
12351        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12352    });
12353    _ = editor.update(cx, |editor, window, cx| {
12354        // Removing an excerpt causes the first selection to become degenerate.
12355        assert_eq!(
12356            editor.selections.ranges(cx),
12357            [
12358                Point::new(0, 0)..Point::new(0, 0),
12359                Point::new(0, 1)..Point::new(0, 1)
12360            ]
12361        );
12362
12363        // Refreshing selections will relocate the first selection to the original buffer
12364        // location.
12365        editor.change_selections(None, window, cx, |s| s.refresh());
12366        assert_eq!(
12367            editor.selections.ranges(cx),
12368            [
12369                Point::new(0, 1)..Point::new(0, 1),
12370                Point::new(0, 3)..Point::new(0, 3)
12371            ]
12372        );
12373        assert!(editor.selections.pending_anchor().is_some());
12374    });
12375}
12376
12377#[gpui::test]
12378fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12379    init_test(cx, |_| {});
12380
12381    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12382    let mut excerpt1_id = None;
12383    let multibuffer = cx.new(|cx| {
12384        let mut multibuffer = MultiBuffer::new(ReadWrite);
12385        excerpt1_id = multibuffer
12386            .push_excerpts(
12387                buffer.clone(),
12388                [
12389                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12390                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12391                ],
12392                cx,
12393            )
12394            .into_iter()
12395            .next();
12396        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12397        multibuffer
12398    });
12399
12400    let editor = cx.add_window(|window, cx| {
12401        let mut editor = build_editor(multibuffer.clone(), window, cx);
12402        let snapshot = editor.snapshot(window, cx);
12403        editor.begin_selection(
12404            Point::new(1, 3).to_display_point(&snapshot),
12405            false,
12406            1,
12407            window,
12408            cx,
12409        );
12410        assert_eq!(
12411            editor.selections.ranges(cx),
12412            [Point::new(1, 3)..Point::new(1, 3)]
12413        );
12414        editor
12415    });
12416
12417    multibuffer.update(cx, |multibuffer, cx| {
12418        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12419    });
12420    _ = editor.update(cx, |editor, window, cx| {
12421        assert_eq!(
12422            editor.selections.ranges(cx),
12423            [Point::new(0, 0)..Point::new(0, 0)]
12424        );
12425
12426        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12427        editor.change_selections(None, window, cx, |s| s.refresh());
12428        assert_eq!(
12429            editor.selections.ranges(cx),
12430            [Point::new(0, 3)..Point::new(0, 3)]
12431        );
12432        assert!(editor.selections.pending_anchor().is_some());
12433    });
12434}
12435
12436#[gpui::test]
12437async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12438    init_test(cx, |_| {});
12439
12440    let language = Arc::new(
12441        Language::new(
12442            LanguageConfig {
12443                brackets: BracketPairConfig {
12444                    pairs: vec![
12445                        BracketPair {
12446                            start: "{".to_string(),
12447                            end: "}".to_string(),
12448                            close: true,
12449                            surround: true,
12450                            newline: true,
12451                        },
12452                        BracketPair {
12453                            start: "/* ".to_string(),
12454                            end: " */".to_string(),
12455                            close: true,
12456                            surround: true,
12457                            newline: true,
12458                        },
12459                    ],
12460                    ..Default::default()
12461                },
12462                ..Default::default()
12463            },
12464            Some(tree_sitter_rust::LANGUAGE.into()),
12465        )
12466        .with_indents_query("")
12467        .unwrap(),
12468    );
12469
12470    let text = concat!(
12471        "{   }\n",     //
12472        "  x\n",       //
12473        "  /*   */\n", //
12474        "x\n",         //
12475        "{{} }\n",     //
12476    );
12477
12478    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12479    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12480    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12481    editor
12482        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12483        .await;
12484
12485    editor.update_in(cx, |editor, window, cx| {
12486        editor.change_selections(None, window, cx, |s| {
12487            s.select_display_ranges([
12488                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12489                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12490                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12491            ])
12492        });
12493        editor.newline(&Newline, window, cx);
12494
12495        assert_eq!(
12496            editor.buffer().read(cx).read(cx).text(),
12497            concat!(
12498                "{ \n",    // Suppress rustfmt
12499                "\n",      //
12500                "}\n",     //
12501                "  x\n",   //
12502                "  /* \n", //
12503                "  \n",    //
12504                "  */\n",  //
12505                "x\n",     //
12506                "{{} \n",  //
12507                "}\n",     //
12508            )
12509        );
12510    });
12511}
12512
12513#[gpui::test]
12514fn test_highlighted_ranges(cx: &mut TestAppContext) {
12515    init_test(cx, |_| {});
12516
12517    let editor = cx.add_window(|window, cx| {
12518        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12519        build_editor(buffer.clone(), window, cx)
12520    });
12521
12522    _ = editor.update(cx, |editor, window, cx| {
12523        struct Type1;
12524        struct Type2;
12525
12526        let buffer = editor.buffer.read(cx).snapshot(cx);
12527
12528        let anchor_range =
12529            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12530
12531        editor.highlight_background::<Type1>(
12532            &[
12533                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12534                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12535                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12536                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12537            ],
12538            |_| Hsla::red(),
12539            cx,
12540        );
12541        editor.highlight_background::<Type2>(
12542            &[
12543                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12544                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12545                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12546                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12547            ],
12548            |_| Hsla::green(),
12549            cx,
12550        );
12551
12552        let snapshot = editor.snapshot(window, cx);
12553        let mut highlighted_ranges = editor.background_highlights_in_range(
12554            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12555            &snapshot,
12556            cx.theme().colors(),
12557        );
12558        // Enforce a consistent ordering based on color without relying on the ordering of the
12559        // highlight's `TypeId` which is non-executor.
12560        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12561        assert_eq!(
12562            highlighted_ranges,
12563            &[
12564                (
12565                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12566                    Hsla::red(),
12567                ),
12568                (
12569                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12570                    Hsla::red(),
12571                ),
12572                (
12573                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12574                    Hsla::green(),
12575                ),
12576                (
12577                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12578                    Hsla::green(),
12579                ),
12580            ]
12581        );
12582        assert_eq!(
12583            editor.background_highlights_in_range(
12584                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12585                &snapshot,
12586                cx.theme().colors(),
12587            ),
12588            &[(
12589                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12590                Hsla::red(),
12591            )]
12592        );
12593    });
12594}
12595
12596#[gpui::test]
12597async fn test_following(cx: &mut TestAppContext) {
12598    init_test(cx, |_| {});
12599
12600    let fs = FakeFs::new(cx.executor());
12601    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12602
12603    let buffer = project.update(cx, |project, cx| {
12604        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12605        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12606    });
12607    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12608    let follower = cx.update(|cx| {
12609        cx.open_window(
12610            WindowOptions {
12611                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12612                    gpui::Point::new(px(0.), px(0.)),
12613                    gpui::Point::new(px(10.), px(80.)),
12614                ))),
12615                ..Default::default()
12616            },
12617            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12618        )
12619        .unwrap()
12620    });
12621
12622    let is_still_following = Rc::new(RefCell::new(true));
12623    let follower_edit_event_count = Rc::new(RefCell::new(0));
12624    let pending_update = Rc::new(RefCell::new(None));
12625    let leader_entity = leader.root(cx).unwrap();
12626    let follower_entity = follower.root(cx).unwrap();
12627    _ = follower.update(cx, {
12628        let update = pending_update.clone();
12629        let is_still_following = is_still_following.clone();
12630        let follower_edit_event_count = follower_edit_event_count.clone();
12631        |_, window, cx| {
12632            cx.subscribe_in(
12633                &leader_entity,
12634                window,
12635                move |_, leader, event, window, cx| {
12636                    leader.read(cx).add_event_to_update_proto(
12637                        event,
12638                        &mut update.borrow_mut(),
12639                        window,
12640                        cx,
12641                    );
12642                },
12643            )
12644            .detach();
12645
12646            cx.subscribe_in(
12647                &follower_entity,
12648                window,
12649                move |_, _, event: &EditorEvent, _window, _cx| {
12650                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12651                        *is_still_following.borrow_mut() = false;
12652                    }
12653
12654                    if let EditorEvent::BufferEdited = event {
12655                        *follower_edit_event_count.borrow_mut() += 1;
12656                    }
12657                },
12658            )
12659            .detach();
12660        }
12661    });
12662
12663    // Update the selections only
12664    _ = leader.update(cx, |leader, window, cx| {
12665        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12666    });
12667    follower
12668        .update(cx, |follower, window, cx| {
12669            follower.apply_update_proto(
12670                &project,
12671                pending_update.borrow_mut().take().unwrap(),
12672                window,
12673                cx,
12674            )
12675        })
12676        .unwrap()
12677        .await
12678        .unwrap();
12679    _ = follower.update(cx, |follower, _, cx| {
12680        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12681    });
12682    assert!(*is_still_following.borrow());
12683    assert_eq!(*follower_edit_event_count.borrow(), 0);
12684
12685    // Update the scroll position only
12686    _ = leader.update(cx, |leader, window, cx| {
12687        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12688    });
12689    follower
12690        .update(cx, |follower, window, cx| {
12691            follower.apply_update_proto(
12692                &project,
12693                pending_update.borrow_mut().take().unwrap(),
12694                window,
12695                cx,
12696            )
12697        })
12698        .unwrap()
12699        .await
12700        .unwrap();
12701    assert_eq!(
12702        follower
12703            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12704            .unwrap(),
12705        gpui::Point::new(1.5, 3.5)
12706    );
12707    assert!(*is_still_following.borrow());
12708    assert_eq!(*follower_edit_event_count.borrow(), 0);
12709
12710    // Update the selections and scroll position. The follower's scroll position is updated
12711    // via autoscroll, not via the leader's exact scroll position.
12712    _ = leader.update(cx, |leader, window, cx| {
12713        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12714        leader.request_autoscroll(Autoscroll::newest(), cx);
12715        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12716    });
12717    follower
12718        .update(cx, |follower, window, cx| {
12719            follower.apply_update_proto(
12720                &project,
12721                pending_update.borrow_mut().take().unwrap(),
12722                window,
12723                cx,
12724            )
12725        })
12726        .unwrap()
12727        .await
12728        .unwrap();
12729    _ = follower.update(cx, |follower, _, cx| {
12730        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12731        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12732    });
12733    assert!(*is_still_following.borrow());
12734
12735    // Creating a pending selection that precedes another selection
12736    _ = leader.update(cx, |leader, window, cx| {
12737        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12738        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12739    });
12740    follower
12741        .update(cx, |follower, window, cx| {
12742            follower.apply_update_proto(
12743                &project,
12744                pending_update.borrow_mut().take().unwrap(),
12745                window,
12746                cx,
12747            )
12748        })
12749        .unwrap()
12750        .await
12751        .unwrap();
12752    _ = follower.update(cx, |follower, _, cx| {
12753        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12754    });
12755    assert!(*is_still_following.borrow());
12756
12757    // Extend the pending selection so that it surrounds another selection
12758    _ = leader.update(cx, |leader, window, cx| {
12759        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12760    });
12761    follower
12762        .update(cx, |follower, window, cx| {
12763            follower.apply_update_proto(
12764                &project,
12765                pending_update.borrow_mut().take().unwrap(),
12766                window,
12767                cx,
12768            )
12769        })
12770        .unwrap()
12771        .await
12772        .unwrap();
12773    _ = follower.update(cx, |follower, _, cx| {
12774        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12775    });
12776
12777    // Scrolling locally breaks the follow
12778    _ = follower.update(cx, |follower, window, cx| {
12779        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12780        follower.set_scroll_anchor(
12781            ScrollAnchor {
12782                anchor: top_anchor,
12783                offset: gpui::Point::new(0.0, 0.5),
12784            },
12785            window,
12786            cx,
12787        );
12788    });
12789    assert!(!(*is_still_following.borrow()));
12790}
12791
12792#[gpui::test]
12793async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12794    init_test(cx, |_| {});
12795
12796    let fs = FakeFs::new(cx.executor());
12797    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12798    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12799    let pane = workspace
12800        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12801        .unwrap();
12802
12803    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12804
12805    let leader = pane.update_in(cx, |_, window, cx| {
12806        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12807        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12808    });
12809
12810    // Start following the editor when it has no excerpts.
12811    let mut state_message =
12812        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12813    let workspace_entity = workspace.root(cx).unwrap();
12814    let follower_1 = cx
12815        .update_window(*workspace.deref(), |_, window, cx| {
12816            Editor::from_state_proto(
12817                workspace_entity,
12818                ViewId {
12819                    creator: CollaboratorId::PeerId(PeerId::default()),
12820                    id: 0,
12821                },
12822                &mut state_message,
12823                window,
12824                cx,
12825            )
12826        })
12827        .unwrap()
12828        .unwrap()
12829        .await
12830        .unwrap();
12831
12832    let update_message = Rc::new(RefCell::new(None));
12833    follower_1.update_in(cx, {
12834        let update = update_message.clone();
12835        |_, window, cx| {
12836            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12837                leader.read(cx).add_event_to_update_proto(
12838                    event,
12839                    &mut update.borrow_mut(),
12840                    window,
12841                    cx,
12842                );
12843            })
12844            .detach();
12845        }
12846    });
12847
12848    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12849        (
12850            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12851            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12852        )
12853    });
12854
12855    // Insert some excerpts.
12856    leader.update(cx, |leader, cx| {
12857        leader.buffer.update(cx, |multibuffer, cx| {
12858            multibuffer.set_excerpts_for_path(
12859                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12860                buffer_1.clone(),
12861                vec![
12862                    Point::row_range(0..3),
12863                    Point::row_range(1..6),
12864                    Point::row_range(12..15),
12865                ],
12866                0,
12867                cx,
12868            );
12869            multibuffer.set_excerpts_for_path(
12870                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12871                buffer_2.clone(),
12872                vec![Point::row_range(0..6), Point::row_range(8..12)],
12873                0,
12874                cx,
12875            );
12876        });
12877    });
12878
12879    // Apply the update of adding the excerpts.
12880    follower_1
12881        .update_in(cx, |follower, window, cx| {
12882            follower.apply_update_proto(
12883                &project,
12884                update_message.borrow().clone().unwrap(),
12885                window,
12886                cx,
12887            )
12888        })
12889        .await
12890        .unwrap();
12891    assert_eq!(
12892        follower_1.update(cx, |editor, cx| editor.text(cx)),
12893        leader.update(cx, |editor, cx| editor.text(cx))
12894    );
12895    update_message.borrow_mut().take();
12896
12897    // Start following separately after it already has excerpts.
12898    let mut state_message =
12899        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12900    let workspace_entity = workspace.root(cx).unwrap();
12901    let follower_2 = cx
12902        .update_window(*workspace.deref(), |_, window, cx| {
12903            Editor::from_state_proto(
12904                workspace_entity,
12905                ViewId {
12906                    creator: CollaboratorId::PeerId(PeerId::default()),
12907                    id: 0,
12908                },
12909                &mut state_message,
12910                window,
12911                cx,
12912            )
12913        })
12914        .unwrap()
12915        .unwrap()
12916        .await
12917        .unwrap();
12918    assert_eq!(
12919        follower_2.update(cx, |editor, cx| editor.text(cx)),
12920        leader.update(cx, |editor, cx| editor.text(cx))
12921    );
12922
12923    // Remove some excerpts.
12924    leader.update(cx, |leader, cx| {
12925        leader.buffer.update(cx, |multibuffer, cx| {
12926            let excerpt_ids = multibuffer.excerpt_ids();
12927            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12928            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12929        });
12930    });
12931
12932    // Apply the update of removing the excerpts.
12933    follower_1
12934        .update_in(cx, |follower, window, cx| {
12935            follower.apply_update_proto(
12936                &project,
12937                update_message.borrow().clone().unwrap(),
12938                window,
12939                cx,
12940            )
12941        })
12942        .await
12943        .unwrap();
12944    follower_2
12945        .update_in(cx, |follower, window, cx| {
12946            follower.apply_update_proto(
12947                &project,
12948                update_message.borrow().clone().unwrap(),
12949                window,
12950                cx,
12951            )
12952        })
12953        .await
12954        .unwrap();
12955    update_message.borrow_mut().take();
12956    assert_eq!(
12957        follower_1.update(cx, |editor, cx| editor.text(cx)),
12958        leader.update(cx, |editor, cx| editor.text(cx))
12959    );
12960}
12961
12962#[gpui::test]
12963async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12964    init_test(cx, |_| {});
12965
12966    let mut cx = EditorTestContext::new(cx).await;
12967    let lsp_store =
12968        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12969
12970    cx.set_state(indoc! {"
12971        ˇfn func(abc def: i32) -> u32 {
12972        }
12973    "});
12974
12975    cx.update(|_, cx| {
12976        lsp_store.update(cx, |lsp_store, cx| {
12977            lsp_store
12978                .update_diagnostics(
12979                    LanguageServerId(0),
12980                    lsp::PublishDiagnosticsParams {
12981                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12982                        version: None,
12983                        diagnostics: vec![
12984                            lsp::Diagnostic {
12985                                range: lsp::Range::new(
12986                                    lsp::Position::new(0, 11),
12987                                    lsp::Position::new(0, 12),
12988                                ),
12989                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12990                                ..Default::default()
12991                            },
12992                            lsp::Diagnostic {
12993                                range: lsp::Range::new(
12994                                    lsp::Position::new(0, 12),
12995                                    lsp::Position::new(0, 15),
12996                                ),
12997                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12998                                ..Default::default()
12999                            },
13000                            lsp::Diagnostic {
13001                                range: lsp::Range::new(
13002                                    lsp::Position::new(0, 25),
13003                                    lsp::Position::new(0, 28),
13004                                ),
13005                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13006                                ..Default::default()
13007                            },
13008                        ],
13009                    },
13010                    &[],
13011                    cx,
13012                )
13013                .unwrap()
13014        });
13015    });
13016
13017    executor.run_until_parked();
13018
13019    cx.update_editor(|editor, window, cx| {
13020        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13021    });
13022
13023    cx.assert_editor_state(indoc! {"
13024        fn func(abc def: i32) -> ˇu32 {
13025        }
13026    "});
13027
13028    cx.update_editor(|editor, window, cx| {
13029        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13030    });
13031
13032    cx.assert_editor_state(indoc! {"
13033        fn func(abc ˇdef: i32) -> u32 {
13034        }
13035    "});
13036
13037    cx.update_editor(|editor, window, cx| {
13038        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13039    });
13040
13041    cx.assert_editor_state(indoc! {"
13042        fn func(abcˇ def: i32) -> u32 {
13043        }
13044    "});
13045
13046    cx.update_editor(|editor, window, cx| {
13047        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13048    });
13049
13050    cx.assert_editor_state(indoc! {"
13051        fn func(abc def: i32) -> ˇu32 {
13052        }
13053    "});
13054}
13055
13056#[gpui::test]
13057async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13058    init_test(cx, |_| {});
13059
13060    let mut cx = EditorTestContext::new(cx).await;
13061
13062    let diff_base = r#"
13063        use some::mod;
13064
13065        const A: u32 = 42;
13066
13067        fn main() {
13068            println!("hello");
13069
13070            println!("world");
13071        }
13072        "#
13073    .unindent();
13074
13075    // Edits are modified, removed, modified, added
13076    cx.set_state(
13077        &r#"
13078        use some::modified;
13079
13080        ˇ
13081        fn main() {
13082            println!("hello there");
13083
13084            println!("around the");
13085            println!("world");
13086        }
13087        "#
13088        .unindent(),
13089    );
13090
13091    cx.set_head_text(&diff_base);
13092    executor.run_until_parked();
13093
13094    cx.update_editor(|editor, window, cx| {
13095        //Wrap around the bottom of the buffer
13096        for _ in 0..3 {
13097            editor.go_to_next_hunk(&GoToHunk, window, cx);
13098        }
13099    });
13100
13101    cx.assert_editor_state(
13102        &r#"
13103        ˇuse some::modified;
13104
13105
13106        fn main() {
13107            println!("hello there");
13108
13109            println!("around the");
13110            println!("world");
13111        }
13112        "#
13113        .unindent(),
13114    );
13115
13116    cx.update_editor(|editor, window, cx| {
13117        //Wrap around the top of the buffer
13118        for _ in 0..2 {
13119            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13120        }
13121    });
13122
13123    cx.assert_editor_state(
13124        &r#"
13125        use some::modified;
13126
13127
13128        fn main() {
13129        ˇ    println!("hello there");
13130
13131            println!("around the");
13132            println!("world");
13133        }
13134        "#
13135        .unindent(),
13136    );
13137
13138    cx.update_editor(|editor, window, cx| {
13139        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13140    });
13141
13142    cx.assert_editor_state(
13143        &r#"
13144        use some::modified;
13145
13146        ˇ
13147        fn main() {
13148            println!("hello there");
13149
13150            println!("around the");
13151            println!("world");
13152        }
13153        "#
13154        .unindent(),
13155    );
13156
13157    cx.update_editor(|editor, window, cx| {
13158        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13159    });
13160
13161    cx.assert_editor_state(
13162        &r#"
13163        ˇuse some::modified;
13164
13165
13166        fn main() {
13167            println!("hello there");
13168
13169            println!("around the");
13170            println!("world");
13171        }
13172        "#
13173        .unindent(),
13174    );
13175
13176    cx.update_editor(|editor, window, cx| {
13177        for _ in 0..2 {
13178            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13179        }
13180    });
13181
13182    cx.assert_editor_state(
13183        &r#"
13184        use some::modified;
13185
13186
13187        fn main() {
13188        ˇ    println!("hello there");
13189
13190            println!("around the");
13191            println!("world");
13192        }
13193        "#
13194        .unindent(),
13195    );
13196
13197    cx.update_editor(|editor, window, cx| {
13198        editor.fold(&Fold, window, cx);
13199    });
13200
13201    cx.update_editor(|editor, window, cx| {
13202        editor.go_to_next_hunk(&GoToHunk, window, cx);
13203    });
13204
13205    cx.assert_editor_state(
13206        &r#"
13207        ˇuse some::modified;
13208
13209
13210        fn main() {
13211            println!("hello there");
13212
13213            println!("around the");
13214            println!("world");
13215        }
13216        "#
13217        .unindent(),
13218    );
13219}
13220
13221#[test]
13222fn test_split_words() {
13223    fn split(text: &str) -> Vec<&str> {
13224        split_words(text).collect()
13225    }
13226
13227    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13228    assert_eq!(split("hello_world"), &["hello_", "world"]);
13229    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13230    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13231    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13232    assert_eq!(split("helloworld"), &["helloworld"]);
13233
13234    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13235}
13236
13237#[gpui::test]
13238async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13239    init_test(cx, |_| {});
13240
13241    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13242    let mut assert = |before, after| {
13243        let _state_context = cx.set_state(before);
13244        cx.run_until_parked();
13245        cx.update_editor(|editor, window, cx| {
13246            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13247        });
13248        cx.run_until_parked();
13249        cx.assert_editor_state(after);
13250    };
13251
13252    // Outside bracket jumps to outside of matching bracket
13253    assert("console.logˇ(var);", "console.log(var)ˇ;");
13254    assert("console.log(var)ˇ;", "console.logˇ(var);");
13255
13256    // Inside bracket jumps to inside of matching bracket
13257    assert("console.log(ˇvar);", "console.log(varˇ);");
13258    assert("console.log(varˇ);", "console.log(ˇvar);");
13259
13260    // When outside a bracket and inside, favor jumping to the inside bracket
13261    assert(
13262        "console.log('foo', [1, 2, 3]ˇ);",
13263        "console.log(ˇ'foo', [1, 2, 3]);",
13264    );
13265    assert(
13266        "console.log(ˇ'foo', [1, 2, 3]);",
13267        "console.log('foo', [1, 2, 3]ˇ);",
13268    );
13269
13270    // Bias forward if two options are equally likely
13271    assert(
13272        "let result = curried_fun()ˇ();",
13273        "let result = curried_fun()()ˇ;",
13274    );
13275
13276    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13277    assert(
13278        indoc! {"
13279            function test() {
13280                console.log('test')ˇ
13281            }"},
13282        indoc! {"
13283            function test() {
13284                console.logˇ('test')
13285            }"},
13286    );
13287}
13288
13289#[gpui::test]
13290async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13291    init_test(cx, |_| {});
13292
13293    let fs = FakeFs::new(cx.executor());
13294    fs.insert_tree(
13295        path!("/a"),
13296        json!({
13297            "main.rs": "fn main() { let a = 5; }",
13298            "other.rs": "// Test file",
13299        }),
13300    )
13301    .await;
13302    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13303
13304    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13305    language_registry.add(Arc::new(Language::new(
13306        LanguageConfig {
13307            name: "Rust".into(),
13308            matcher: LanguageMatcher {
13309                path_suffixes: vec!["rs".to_string()],
13310                ..Default::default()
13311            },
13312            brackets: BracketPairConfig {
13313                pairs: vec![BracketPair {
13314                    start: "{".to_string(),
13315                    end: "}".to_string(),
13316                    close: true,
13317                    surround: true,
13318                    newline: true,
13319                }],
13320                disabled_scopes_by_bracket_ix: Vec::new(),
13321            },
13322            ..Default::default()
13323        },
13324        Some(tree_sitter_rust::LANGUAGE.into()),
13325    )));
13326    let mut fake_servers = language_registry.register_fake_lsp(
13327        "Rust",
13328        FakeLspAdapter {
13329            capabilities: lsp::ServerCapabilities {
13330                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13331                    first_trigger_character: "{".to_string(),
13332                    more_trigger_character: None,
13333                }),
13334                ..Default::default()
13335            },
13336            ..Default::default()
13337        },
13338    );
13339
13340    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13341
13342    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13343
13344    let worktree_id = workspace
13345        .update(cx, |workspace, _, cx| {
13346            workspace.project().update(cx, |project, cx| {
13347                project.worktrees(cx).next().unwrap().read(cx).id()
13348            })
13349        })
13350        .unwrap();
13351
13352    let buffer = project
13353        .update(cx, |project, cx| {
13354            project.open_local_buffer(path!("/a/main.rs"), cx)
13355        })
13356        .await
13357        .unwrap();
13358    let editor_handle = workspace
13359        .update(cx, |workspace, window, cx| {
13360            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13361        })
13362        .unwrap()
13363        .await
13364        .unwrap()
13365        .downcast::<Editor>()
13366        .unwrap();
13367
13368    cx.executor().start_waiting();
13369    let fake_server = fake_servers.next().await.unwrap();
13370
13371    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13372        |params, _| async move {
13373            assert_eq!(
13374                params.text_document_position.text_document.uri,
13375                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13376            );
13377            assert_eq!(
13378                params.text_document_position.position,
13379                lsp::Position::new(0, 21),
13380            );
13381
13382            Ok(Some(vec![lsp::TextEdit {
13383                new_text: "]".to_string(),
13384                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13385            }]))
13386        },
13387    );
13388
13389    editor_handle.update_in(cx, |editor, window, cx| {
13390        window.focus(&editor.focus_handle(cx));
13391        editor.change_selections(None, window, cx, |s| {
13392            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13393        });
13394        editor.handle_input("{", window, cx);
13395    });
13396
13397    cx.executor().run_until_parked();
13398
13399    buffer.update(cx, |buffer, _| {
13400        assert_eq!(
13401            buffer.text(),
13402            "fn main() { let a = {5}; }",
13403            "No extra braces from on type formatting should appear in the buffer"
13404        )
13405    });
13406}
13407
13408#[gpui::test]
13409async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13410    init_test(cx, |_| {});
13411
13412    let fs = FakeFs::new(cx.executor());
13413    fs.insert_tree(
13414        path!("/a"),
13415        json!({
13416            "main.rs": "fn main() { let a = 5; }",
13417            "other.rs": "// Test file",
13418        }),
13419    )
13420    .await;
13421
13422    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13423
13424    let server_restarts = Arc::new(AtomicUsize::new(0));
13425    let closure_restarts = Arc::clone(&server_restarts);
13426    let language_server_name = "test language server";
13427    let language_name: LanguageName = "Rust".into();
13428
13429    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13430    language_registry.add(Arc::new(Language::new(
13431        LanguageConfig {
13432            name: language_name.clone(),
13433            matcher: LanguageMatcher {
13434                path_suffixes: vec!["rs".to_string()],
13435                ..Default::default()
13436            },
13437            ..Default::default()
13438        },
13439        Some(tree_sitter_rust::LANGUAGE.into()),
13440    )));
13441    let mut fake_servers = language_registry.register_fake_lsp(
13442        "Rust",
13443        FakeLspAdapter {
13444            name: language_server_name,
13445            initialization_options: Some(json!({
13446                "testOptionValue": true
13447            })),
13448            initializer: Some(Box::new(move |fake_server| {
13449                let task_restarts = Arc::clone(&closure_restarts);
13450                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13451                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13452                    futures::future::ready(Ok(()))
13453                });
13454            })),
13455            ..Default::default()
13456        },
13457    );
13458
13459    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13460    let _buffer = project
13461        .update(cx, |project, cx| {
13462            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13463        })
13464        .await
13465        .unwrap();
13466    let _fake_server = fake_servers.next().await.unwrap();
13467    update_test_language_settings(cx, |language_settings| {
13468        language_settings.languages.insert(
13469            language_name.clone(),
13470            LanguageSettingsContent {
13471                tab_size: NonZeroU32::new(8),
13472                ..Default::default()
13473            },
13474        );
13475    });
13476    cx.executor().run_until_parked();
13477    assert_eq!(
13478        server_restarts.load(atomic::Ordering::Acquire),
13479        0,
13480        "Should not restart LSP server on an unrelated change"
13481    );
13482
13483    update_test_project_settings(cx, |project_settings| {
13484        project_settings.lsp.insert(
13485            "Some other server name".into(),
13486            LspSettings {
13487                binary: None,
13488                settings: None,
13489                initialization_options: Some(json!({
13490                    "some other init value": false
13491                })),
13492                enable_lsp_tasks: false,
13493            },
13494        );
13495    });
13496    cx.executor().run_until_parked();
13497    assert_eq!(
13498        server_restarts.load(atomic::Ordering::Acquire),
13499        0,
13500        "Should not restart LSP server on an unrelated LSP settings change"
13501    );
13502
13503    update_test_project_settings(cx, |project_settings| {
13504        project_settings.lsp.insert(
13505            language_server_name.into(),
13506            LspSettings {
13507                binary: None,
13508                settings: None,
13509                initialization_options: Some(json!({
13510                    "anotherInitValue": false
13511                })),
13512                enable_lsp_tasks: false,
13513            },
13514        );
13515    });
13516    cx.executor().run_until_parked();
13517    assert_eq!(
13518        server_restarts.load(atomic::Ordering::Acquire),
13519        1,
13520        "Should restart LSP server on a related LSP settings change"
13521    );
13522
13523    update_test_project_settings(cx, |project_settings| {
13524        project_settings.lsp.insert(
13525            language_server_name.into(),
13526            LspSettings {
13527                binary: None,
13528                settings: None,
13529                initialization_options: Some(json!({
13530                    "anotherInitValue": false
13531                })),
13532                enable_lsp_tasks: false,
13533            },
13534        );
13535    });
13536    cx.executor().run_until_parked();
13537    assert_eq!(
13538        server_restarts.load(atomic::Ordering::Acquire),
13539        1,
13540        "Should not restart LSP server on a related LSP settings change that is the same"
13541    );
13542
13543    update_test_project_settings(cx, |project_settings| {
13544        project_settings.lsp.insert(
13545            language_server_name.into(),
13546            LspSettings {
13547                binary: None,
13548                settings: None,
13549                initialization_options: None,
13550                enable_lsp_tasks: false,
13551            },
13552        );
13553    });
13554    cx.executor().run_until_parked();
13555    assert_eq!(
13556        server_restarts.load(atomic::Ordering::Acquire),
13557        2,
13558        "Should restart LSP server on another related LSP settings change"
13559    );
13560}
13561
13562#[gpui::test]
13563async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13564    init_test(cx, |_| {});
13565
13566    let mut cx = EditorLspTestContext::new_rust(
13567        lsp::ServerCapabilities {
13568            completion_provider: Some(lsp::CompletionOptions {
13569                trigger_characters: Some(vec![".".to_string()]),
13570                resolve_provider: Some(true),
13571                ..Default::default()
13572            }),
13573            ..Default::default()
13574        },
13575        cx,
13576    )
13577    .await;
13578
13579    cx.set_state("fn main() { let a = 2ˇ; }");
13580    cx.simulate_keystroke(".");
13581    let completion_item = lsp::CompletionItem {
13582        label: "some".into(),
13583        kind: Some(lsp::CompletionItemKind::SNIPPET),
13584        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13585        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13586            kind: lsp::MarkupKind::Markdown,
13587            value: "```rust\nSome(2)\n```".to_string(),
13588        })),
13589        deprecated: Some(false),
13590        sort_text: Some("fffffff2".to_string()),
13591        filter_text: Some("some".to_string()),
13592        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13593        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13594            range: lsp::Range {
13595                start: lsp::Position {
13596                    line: 0,
13597                    character: 22,
13598                },
13599                end: lsp::Position {
13600                    line: 0,
13601                    character: 22,
13602                },
13603            },
13604            new_text: "Some(2)".to_string(),
13605        })),
13606        additional_text_edits: Some(vec![lsp::TextEdit {
13607            range: lsp::Range {
13608                start: lsp::Position {
13609                    line: 0,
13610                    character: 20,
13611                },
13612                end: lsp::Position {
13613                    line: 0,
13614                    character: 22,
13615                },
13616            },
13617            new_text: "".to_string(),
13618        }]),
13619        ..Default::default()
13620    };
13621
13622    let closure_completion_item = completion_item.clone();
13623    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13624        let task_completion_item = closure_completion_item.clone();
13625        async move {
13626            Ok(Some(lsp::CompletionResponse::Array(vec![
13627                task_completion_item,
13628            ])))
13629        }
13630    });
13631
13632    request.next().await;
13633
13634    cx.condition(|editor, _| editor.context_menu_visible())
13635        .await;
13636    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13637        editor
13638            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13639            .unwrap()
13640    });
13641    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13642
13643    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13644        let task_completion_item = completion_item.clone();
13645        async move { Ok(task_completion_item) }
13646    })
13647    .next()
13648    .await
13649    .unwrap();
13650    apply_additional_edits.await.unwrap();
13651    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13652}
13653
13654#[gpui::test]
13655async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13656    init_test(cx, |_| {});
13657
13658    let mut cx = EditorLspTestContext::new_rust(
13659        lsp::ServerCapabilities {
13660            completion_provider: Some(lsp::CompletionOptions {
13661                trigger_characters: Some(vec![".".to_string()]),
13662                resolve_provider: Some(true),
13663                ..Default::default()
13664            }),
13665            ..Default::default()
13666        },
13667        cx,
13668    )
13669    .await;
13670
13671    cx.set_state("fn main() { let a = 2ˇ; }");
13672    cx.simulate_keystroke(".");
13673
13674    let item1 = lsp::CompletionItem {
13675        label: "method id()".to_string(),
13676        filter_text: Some("id".to_string()),
13677        detail: None,
13678        documentation: None,
13679        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13680            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13681            new_text: ".id".to_string(),
13682        })),
13683        ..lsp::CompletionItem::default()
13684    };
13685
13686    let item2 = lsp::CompletionItem {
13687        label: "other".to_string(),
13688        filter_text: Some("other".to_string()),
13689        detail: None,
13690        documentation: None,
13691        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13692            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13693            new_text: ".other".to_string(),
13694        })),
13695        ..lsp::CompletionItem::default()
13696    };
13697
13698    let item1 = item1.clone();
13699    cx.set_request_handler::<lsp::request::Completion, _, _>({
13700        let item1 = item1.clone();
13701        move |_, _, _| {
13702            let item1 = item1.clone();
13703            let item2 = item2.clone();
13704            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13705        }
13706    })
13707    .next()
13708    .await;
13709
13710    cx.condition(|editor, _| editor.context_menu_visible())
13711        .await;
13712    cx.update_editor(|editor, _, _| {
13713        let context_menu = editor.context_menu.borrow_mut();
13714        let context_menu = context_menu
13715            .as_ref()
13716            .expect("Should have the context menu deployed");
13717        match context_menu {
13718            CodeContextMenu::Completions(completions_menu) => {
13719                let completions = completions_menu.completions.borrow_mut();
13720                assert_eq!(
13721                    completions
13722                        .iter()
13723                        .map(|completion| &completion.label.text)
13724                        .collect::<Vec<_>>(),
13725                    vec!["method id()", "other"]
13726                )
13727            }
13728            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13729        }
13730    });
13731
13732    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13733        let item1 = item1.clone();
13734        move |_, item_to_resolve, _| {
13735            let item1 = item1.clone();
13736            async move {
13737                if item1 == item_to_resolve {
13738                    Ok(lsp::CompletionItem {
13739                        label: "method id()".to_string(),
13740                        filter_text: Some("id".to_string()),
13741                        detail: Some("Now resolved!".to_string()),
13742                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13743                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13744                            range: lsp::Range::new(
13745                                lsp::Position::new(0, 22),
13746                                lsp::Position::new(0, 22),
13747                            ),
13748                            new_text: ".id".to_string(),
13749                        })),
13750                        ..lsp::CompletionItem::default()
13751                    })
13752                } else {
13753                    Ok(item_to_resolve)
13754                }
13755            }
13756        }
13757    })
13758    .next()
13759    .await
13760    .unwrap();
13761    cx.run_until_parked();
13762
13763    cx.update_editor(|editor, window, cx| {
13764        editor.context_menu_next(&Default::default(), window, cx);
13765    });
13766
13767    cx.update_editor(|editor, _, _| {
13768        let context_menu = editor.context_menu.borrow_mut();
13769        let context_menu = context_menu
13770            .as_ref()
13771            .expect("Should have the context menu deployed");
13772        match context_menu {
13773            CodeContextMenu::Completions(completions_menu) => {
13774                let completions = completions_menu.completions.borrow_mut();
13775                assert_eq!(
13776                    completions
13777                        .iter()
13778                        .map(|completion| &completion.label.text)
13779                        .collect::<Vec<_>>(),
13780                    vec!["method id() Now resolved!", "other"],
13781                    "Should update first completion label, but not second as the filter text did not match."
13782                );
13783            }
13784            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13785        }
13786    });
13787}
13788
13789#[gpui::test]
13790async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13791    init_test(cx, |_| {});
13792
13793    let mut cx = EditorLspTestContext::new_rust(
13794        lsp::ServerCapabilities {
13795            completion_provider: Some(lsp::CompletionOptions {
13796                trigger_characters: Some(vec![".".to_string()]),
13797                resolve_provider: Some(true),
13798                ..Default::default()
13799            }),
13800            ..Default::default()
13801        },
13802        cx,
13803    )
13804    .await;
13805
13806    cx.set_state("fn main() { let a = 2ˇ; }");
13807    cx.simulate_keystroke(".");
13808
13809    let unresolved_item_1 = lsp::CompletionItem {
13810        label: "id".to_string(),
13811        filter_text: Some("id".to_string()),
13812        detail: None,
13813        documentation: None,
13814        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13815            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13816            new_text: ".id".to_string(),
13817        })),
13818        ..lsp::CompletionItem::default()
13819    };
13820    let resolved_item_1 = lsp::CompletionItem {
13821        additional_text_edits: Some(vec![lsp::TextEdit {
13822            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13823            new_text: "!!".to_string(),
13824        }]),
13825        ..unresolved_item_1.clone()
13826    };
13827    let unresolved_item_2 = lsp::CompletionItem {
13828        label: "other".to_string(),
13829        filter_text: Some("other".to_string()),
13830        detail: None,
13831        documentation: None,
13832        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13833            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13834            new_text: ".other".to_string(),
13835        })),
13836        ..lsp::CompletionItem::default()
13837    };
13838    let resolved_item_2 = lsp::CompletionItem {
13839        additional_text_edits: Some(vec![lsp::TextEdit {
13840            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13841            new_text: "??".to_string(),
13842        }]),
13843        ..unresolved_item_2.clone()
13844    };
13845
13846    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13847    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13848    cx.lsp
13849        .server
13850        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13851            let unresolved_item_1 = unresolved_item_1.clone();
13852            let resolved_item_1 = resolved_item_1.clone();
13853            let unresolved_item_2 = unresolved_item_2.clone();
13854            let resolved_item_2 = resolved_item_2.clone();
13855            let resolve_requests_1 = resolve_requests_1.clone();
13856            let resolve_requests_2 = resolve_requests_2.clone();
13857            move |unresolved_request, _| {
13858                let unresolved_item_1 = unresolved_item_1.clone();
13859                let resolved_item_1 = resolved_item_1.clone();
13860                let unresolved_item_2 = unresolved_item_2.clone();
13861                let resolved_item_2 = resolved_item_2.clone();
13862                let resolve_requests_1 = resolve_requests_1.clone();
13863                let resolve_requests_2 = resolve_requests_2.clone();
13864                async move {
13865                    if unresolved_request == unresolved_item_1 {
13866                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13867                        Ok(resolved_item_1.clone())
13868                    } else if unresolved_request == unresolved_item_2 {
13869                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13870                        Ok(resolved_item_2.clone())
13871                    } else {
13872                        panic!("Unexpected completion item {unresolved_request:?}")
13873                    }
13874                }
13875            }
13876        })
13877        .detach();
13878
13879    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13880        let unresolved_item_1 = unresolved_item_1.clone();
13881        let unresolved_item_2 = unresolved_item_2.clone();
13882        async move {
13883            Ok(Some(lsp::CompletionResponse::Array(vec![
13884                unresolved_item_1,
13885                unresolved_item_2,
13886            ])))
13887        }
13888    })
13889    .next()
13890    .await;
13891
13892    cx.condition(|editor, _| editor.context_menu_visible())
13893        .await;
13894    cx.update_editor(|editor, _, _| {
13895        let context_menu = editor.context_menu.borrow_mut();
13896        let context_menu = context_menu
13897            .as_ref()
13898            .expect("Should have the context menu deployed");
13899        match context_menu {
13900            CodeContextMenu::Completions(completions_menu) => {
13901                let completions = completions_menu.completions.borrow_mut();
13902                assert_eq!(
13903                    completions
13904                        .iter()
13905                        .map(|completion| &completion.label.text)
13906                        .collect::<Vec<_>>(),
13907                    vec!["id", "other"]
13908                )
13909            }
13910            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13911        }
13912    });
13913    cx.run_until_parked();
13914
13915    cx.update_editor(|editor, window, cx| {
13916        editor.context_menu_next(&ContextMenuNext, window, cx);
13917    });
13918    cx.run_until_parked();
13919    cx.update_editor(|editor, window, cx| {
13920        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13921    });
13922    cx.run_until_parked();
13923    cx.update_editor(|editor, window, cx| {
13924        editor.context_menu_next(&ContextMenuNext, window, cx);
13925    });
13926    cx.run_until_parked();
13927    cx.update_editor(|editor, window, cx| {
13928        editor
13929            .compose_completion(&ComposeCompletion::default(), window, cx)
13930            .expect("No task returned")
13931    })
13932    .await
13933    .expect("Completion failed");
13934    cx.run_until_parked();
13935
13936    cx.update_editor(|editor, _, cx| {
13937        assert_eq!(
13938            resolve_requests_1.load(atomic::Ordering::Acquire),
13939            1,
13940            "Should always resolve once despite multiple selections"
13941        );
13942        assert_eq!(
13943            resolve_requests_2.load(atomic::Ordering::Acquire),
13944            1,
13945            "Should always resolve once after multiple selections and applying the completion"
13946        );
13947        assert_eq!(
13948            editor.text(cx),
13949            "fn main() { let a = ??.other; }",
13950            "Should use resolved data when applying the completion"
13951        );
13952    });
13953}
13954
13955#[gpui::test]
13956async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13957    init_test(cx, |_| {});
13958
13959    let item_0 = lsp::CompletionItem {
13960        label: "abs".into(),
13961        insert_text: Some("abs".into()),
13962        data: Some(json!({ "very": "special"})),
13963        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13964        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13965            lsp::InsertReplaceEdit {
13966                new_text: "abs".to_string(),
13967                insert: lsp::Range::default(),
13968                replace: lsp::Range::default(),
13969            },
13970        )),
13971        ..lsp::CompletionItem::default()
13972    };
13973    let items = iter::once(item_0.clone())
13974        .chain((11..51).map(|i| lsp::CompletionItem {
13975            label: format!("item_{}", i),
13976            insert_text: Some(format!("item_{}", i)),
13977            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13978            ..lsp::CompletionItem::default()
13979        }))
13980        .collect::<Vec<_>>();
13981
13982    let default_commit_characters = vec!["?".to_string()];
13983    let default_data = json!({ "default": "data"});
13984    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13985    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13986    let default_edit_range = lsp::Range {
13987        start: lsp::Position {
13988            line: 0,
13989            character: 5,
13990        },
13991        end: lsp::Position {
13992            line: 0,
13993            character: 5,
13994        },
13995    };
13996
13997    let mut cx = EditorLspTestContext::new_rust(
13998        lsp::ServerCapabilities {
13999            completion_provider: Some(lsp::CompletionOptions {
14000                trigger_characters: Some(vec![".".to_string()]),
14001                resolve_provider: Some(true),
14002                ..Default::default()
14003            }),
14004            ..Default::default()
14005        },
14006        cx,
14007    )
14008    .await;
14009
14010    cx.set_state("fn main() { let a = 2ˇ; }");
14011    cx.simulate_keystroke(".");
14012
14013    let completion_data = default_data.clone();
14014    let completion_characters = default_commit_characters.clone();
14015    let completion_items = items.clone();
14016    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14017        let default_data = completion_data.clone();
14018        let default_commit_characters = completion_characters.clone();
14019        let items = completion_items.clone();
14020        async move {
14021            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14022                items,
14023                item_defaults: Some(lsp::CompletionListItemDefaults {
14024                    data: Some(default_data.clone()),
14025                    commit_characters: Some(default_commit_characters.clone()),
14026                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14027                        default_edit_range,
14028                    )),
14029                    insert_text_format: Some(default_insert_text_format),
14030                    insert_text_mode: Some(default_insert_text_mode),
14031                }),
14032                ..lsp::CompletionList::default()
14033            })))
14034        }
14035    })
14036    .next()
14037    .await;
14038
14039    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14040    cx.lsp
14041        .server
14042        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14043            let closure_resolved_items = resolved_items.clone();
14044            move |item_to_resolve, _| {
14045                let closure_resolved_items = closure_resolved_items.clone();
14046                async move {
14047                    closure_resolved_items.lock().push(item_to_resolve.clone());
14048                    Ok(item_to_resolve)
14049                }
14050            }
14051        })
14052        .detach();
14053
14054    cx.condition(|editor, _| editor.context_menu_visible())
14055        .await;
14056    cx.run_until_parked();
14057    cx.update_editor(|editor, _, _| {
14058        let menu = editor.context_menu.borrow_mut();
14059        match menu.as_ref().expect("should have the completions menu") {
14060            CodeContextMenu::Completions(completions_menu) => {
14061                assert_eq!(
14062                    completions_menu
14063                        .entries
14064                        .borrow()
14065                        .iter()
14066                        .map(|mat| mat.string.clone())
14067                        .collect::<Vec<String>>(),
14068                    items
14069                        .iter()
14070                        .map(|completion| completion.label.clone())
14071                        .collect::<Vec<String>>()
14072                );
14073            }
14074            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14075        }
14076    });
14077    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14078    // with 4 from the end.
14079    assert_eq!(
14080        *resolved_items.lock(),
14081        [&items[0..16], &items[items.len() - 4..items.len()]]
14082            .concat()
14083            .iter()
14084            .cloned()
14085            .map(|mut item| {
14086                if item.data.is_none() {
14087                    item.data = Some(default_data.clone());
14088                }
14089                item
14090            })
14091            .collect::<Vec<lsp::CompletionItem>>(),
14092        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14093    );
14094    resolved_items.lock().clear();
14095
14096    cx.update_editor(|editor, window, cx| {
14097        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14098    });
14099    cx.run_until_parked();
14100    // Completions that have already been resolved are skipped.
14101    assert_eq!(
14102        *resolved_items.lock(),
14103        items[items.len() - 16..items.len() - 4]
14104            .iter()
14105            .cloned()
14106            .map(|mut item| {
14107                if item.data.is_none() {
14108                    item.data = Some(default_data.clone());
14109                }
14110                item
14111            })
14112            .collect::<Vec<lsp::CompletionItem>>()
14113    );
14114    resolved_items.lock().clear();
14115}
14116
14117#[gpui::test]
14118async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14119    init_test(cx, |_| {});
14120
14121    let mut cx = EditorLspTestContext::new(
14122        Language::new(
14123            LanguageConfig {
14124                matcher: LanguageMatcher {
14125                    path_suffixes: vec!["jsx".into()],
14126                    ..Default::default()
14127                },
14128                overrides: [(
14129                    "element".into(),
14130                    LanguageConfigOverride {
14131                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14132                        ..Default::default()
14133                    },
14134                )]
14135                .into_iter()
14136                .collect(),
14137                ..Default::default()
14138            },
14139            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14140        )
14141        .with_override_query("(jsx_self_closing_element) @element")
14142        .unwrap(),
14143        lsp::ServerCapabilities {
14144            completion_provider: Some(lsp::CompletionOptions {
14145                trigger_characters: Some(vec![":".to_string()]),
14146                ..Default::default()
14147            }),
14148            ..Default::default()
14149        },
14150        cx,
14151    )
14152    .await;
14153
14154    cx.lsp
14155        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14156            Ok(Some(lsp::CompletionResponse::Array(vec![
14157                lsp::CompletionItem {
14158                    label: "bg-blue".into(),
14159                    ..Default::default()
14160                },
14161                lsp::CompletionItem {
14162                    label: "bg-red".into(),
14163                    ..Default::default()
14164                },
14165                lsp::CompletionItem {
14166                    label: "bg-yellow".into(),
14167                    ..Default::default()
14168                },
14169            ])))
14170        });
14171
14172    cx.set_state(r#"<p class="bgˇ" />"#);
14173
14174    // Trigger completion when typing a dash, because the dash is an extra
14175    // word character in the 'element' scope, which contains the cursor.
14176    cx.simulate_keystroke("-");
14177    cx.executor().run_until_parked();
14178    cx.update_editor(|editor, _, _| {
14179        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14180        {
14181            assert_eq!(
14182                completion_menu_entries(&menu),
14183                &["bg-red", "bg-blue", "bg-yellow"]
14184            );
14185        } else {
14186            panic!("expected completion menu to be open");
14187        }
14188    });
14189
14190    cx.simulate_keystroke("l");
14191    cx.executor().run_until_parked();
14192    cx.update_editor(|editor, _, _| {
14193        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14194        {
14195            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14196        } else {
14197            panic!("expected completion menu to be open");
14198        }
14199    });
14200
14201    // When filtering completions, consider the character after the '-' to
14202    // be the start of a subword.
14203    cx.set_state(r#"<p class="yelˇ" />"#);
14204    cx.simulate_keystroke("l");
14205    cx.executor().run_until_parked();
14206    cx.update_editor(|editor, _, _| {
14207        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14208        {
14209            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14210        } else {
14211            panic!("expected completion menu to be open");
14212        }
14213    });
14214}
14215
14216fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14217    let entries = menu.entries.borrow();
14218    entries.iter().map(|mat| mat.string.clone()).collect()
14219}
14220
14221#[gpui::test]
14222async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14223    init_test(cx, |settings| {
14224        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14225            FormatterList(vec![Formatter::Prettier].into()),
14226        ))
14227    });
14228
14229    let fs = FakeFs::new(cx.executor());
14230    fs.insert_file(path!("/file.ts"), Default::default()).await;
14231
14232    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14233    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14234
14235    language_registry.add(Arc::new(Language::new(
14236        LanguageConfig {
14237            name: "TypeScript".into(),
14238            matcher: LanguageMatcher {
14239                path_suffixes: vec!["ts".to_string()],
14240                ..Default::default()
14241            },
14242            ..Default::default()
14243        },
14244        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14245    )));
14246    update_test_language_settings(cx, |settings| {
14247        settings.defaults.prettier = Some(PrettierSettings {
14248            allowed: true,
14249            ..PrettierSettings::default()
14250        });
14251    });
14252
14253    let test_plugin = "test_plugin";
14254    let _ = language_registry.register_fake_lsp(
14255        "TypeScript",
14256        FakeLspAdapter {
14257            prettier_plugins: vec![test_plugin],
14258            ..Default::default()
14259        },
14260    );
14261
14262    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14263    let buffer = project
14264        .update(cx, |project, cx| {
14265            project.open_local_buffer(path!("/file.ts"), cx)
14266        })
14267        .await
14268        .unwrap();
14269
14270    let buffer_text = "one\ntwo\nthree\n";
14271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14272    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14273    editor.update_in(cx, |editor, window, cx| {
14274        editor.set_text(buffer_text, window, cx)
14275    });
14276
14277    editor
14278        .update_in(cx, |editor, window, cx| {
14279            editor.perform_format(
14280                project.clone(),
14281                FormatTrigger::Manual,
14282                FormatTarget::Buffers,
14283                window,
14284                cx,
14285            )
14286        })
14287        .unwrap()
14288        .await;
14289    assert_eq!(
14290        editor.update(cx, |editor, cx| editor.text(cx)),
14291        buffer_text.to_string() + prettier_format_suffix,
14292        "Test prettier formatting was not applied to the original buffer text",
14293    );
14294
14295    update_test_language_settings(cx, |settings| {
14296        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14297    });
14298    let format = editor.update_in(cx, |editor, window, cx| {
14299        editor.perform_format(
14300            project.clone(),
14301            FormatTrigger::Manual,
14302            FormatTarget::Buffers,
14303            window,
14304            cx,
14305        )
14306    });
14307    format.await.unwrap();
14308    assert_eq!(
14309        editor.update(cx, |editor, cx| editor.text(cx)),
14310        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14311        "Autoformatting (via test prettier) was not applied to the original buffer text",
14312    );
14313}
14314
14315#[gpui::test]
14316async fn test_addition_reverts(cx: &mut TestAppContext) {
14317    init_test(cx, |_| {});
14318    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14319    let base_text = indoc! {r#"
14320        struct Row;
14321        struct Row1;
14322        struct Row2;
14323
14324        struct Row4;
14325        struct Row5;
14326        struct Row6;
14327
14328        struct Row8;
14329        struct Row9;
14330        struct Row10;"#};
14331
14332    // When addition hunks are not adjacent to carets, no hunk revert is performed
14333    assert_hunk_revert(
14334        indoc! {r#"struct Row;
14335                   struct Row1;
14336                   struct Row1.1;
14337                   struct Row1.2;
14338                   struct Row2;ˇ
14339
14340                   struct Row4;
14341                   struct Row5;
14342                   struct Row6;
14343
14344                   struct Row8;
14345                   ˇstruct Row9;
14346                   struct Row9.1;
14347                   struct Row9.2;
14348                   struct Row9.3;
14349                   struct Row10;"#},
14350        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14351        indoc! {r#"struct Row;
14352                   struct Row1;
14353                   struct Row1.1;
14354                   struct Row1.2;
14355                   struct Row2;ˇ
14356
14357                   struct Row4;
14358                   struct Row5;
14359                   struct Row6;
14360
14361                   struct Row8;
14362                   ˇstruct Row9;
14363                   struct Row9.1;
14364                   struct Row9.2;
14365                   struct Row9.3;
14366                   struct Row10;"#},
14367        base_text,
14368        &mut cx,
14369    );
14370    // Same for selections
14371    assert_hunk_revert(
14372        indoc! {r#"struct Row;
14373                   struct Row1;
14374                   struct Row2;
14375                   struct Row2.1;
14376                   struct Row2.2;
14377                   «ˇ
14378                   struct Row4;
14379                   struct» Row5;
14380                   «struct Row6;
14381                   ˇ»
14382                   struct Row9.1;
14383                   struct Row9.2;
14384                   struct Row9.3;
14385                   struct Row8;
14386                   struct Row9;
14387                   struct Row10;"#},
14388        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14389        indoc! {r#"struct Row;
14390                   struct Row1;
14391                   struct Row2;
14392                   struct Row2.1;
14393                   struct Row2.2;
14394                   «ˇ
14395                   struct Row4;
14396                   struct» Row5;
14397                   «struct Row6;
14398                   ˇ»
14399                   struct Row9.1;
14400                   struct Row9.2;
14401                   struct Row9.3;
14402                   struct Row8;
14403                   struct Row9;
14404                   struct Row10;"#},
14405        base_text,
14406        &mut cx,
14407    );
14408
14409    // When carets and selections intersect the addition hunks, those are reverted.
14410    // Adjacent carets got merged.
14411    assert_hunk_revert(
14412        indoc! {r#"struct Row;
14413                   ˇ// something on the top
14414                   struct Row1;
14415                   struct Row2;
14416                   struct Roˇw3.1;
14417                   struct Row2.2;
14418                   struct Row2.3;ˇ
14419
14420                   struct Row4;
14421                   struct ˇRow5.1;
14422                   struct Row5.2;
14423                   struct «Rowˇ»5.3;
14424                   struct Row5;
14425                   struct Row6;
14426                   ˇ
14427                   struct Row9.1;
14428                   struct «Rowˇ»9.2;
14429                   struct «ˇRow»9.3;
14430                   struct Row8;
14431                   struct Row9;
14432                   «ˇ// something on bottom»
14433                   struct Row10;"#},
14434        vec![
14435            DiffHunkStatusKind::Added,
14436            DiffHunkStatusKind::Added,
14437            DiffHunkStatusKind::Added,
14438            DiffHunkStatusKind::Added,
14439            DiffHunkStatusKind::Added,
14440        ],
14441        indoc! {r#"struct Row;
14442                   ˇstruct Row1;
14443                   struct Row2;
14444                   ˇ
14445                   struct Row4;
14446                   ˇstruct Row5;
14447                   struct Row6;
14448                   ˇ
14449                   ˇstruct Row8;
14450                   struct Row9;
14451                   ˇstruct Row10;"#},
14452        base_text,
14453        &mut cx,
14454    );
14455}
14456
14457#[gpui::test]
14458async fn test_modification_reverts(cx: &mut TestAppContext) {
14459    init_test(cx, |_| {});
14460    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14461    let base_text = indoc! {r#"
14462        struct Row;
14463        struct Row1;
14464        struct Row2;
14465
14466        struct Row4;
14467        struct Row5;
14468        struct Row6;
14469
14470        struct Row8;
14471        struct Row9;
14472        struct Row10;"#};
14473
14474    // Modification hunks behave the same as the addition ones.
14475    assert_hunk_revert(
14476        indoc! {r#"struct Row;
14477                   struct Row1;
14478                   struct Row33;
14479                   ˇ
14480                   struct Row4;
14481                   struct Row5;
14482                   struct Row6;
14483                   ˇ
14484                   struct Row99;
14485                   struct Row9;
14486                   struct Row10;"#},
14487        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14488        indoc! {r#"struct Row;
14489                   struct Row1;
14490                   struct Row33;
14491                   ˇ
14492                   struct Row4;
14493                   struct Row5;
14494                   struct Row6;
14495                   ˇ
14496                   struct Row99;
14497                   struct Row9;
14498                   struct Row10;"#},
14499        base_text,
14500        &mut cx,
14501    );
14502    assert_hunk_revert(
14503        indoc! {r#"struct Row;
14504                   struct Row1;
14505                   struct Row33;
14506                   «ˇ
14507                   struct Row4;
14508                   struct» Row5;
14509                   «struct Row6;
14510                   ˇ»
14511                   struct Row99;
14512                   struct Row9;
14513                   struct Row10;"#},
14514        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14515        indoc! {r#"struct Row;
14516                   struct Row1;
14517                   struct Row33;
14518                   «ˇ
14519                   struct Row4;
14520                   struct» Row5;
14521                   «struct Row6;
14522                   ˇ»
14523                   struct Row99;
14524                   struct Row9;
14525                   struct Row10;"#},
14526        base_text,
14527        &mut cx,
14528    );
14529
14530    assert_hunk_revert(
14531        indoc! {r#"ˇstruct Row1.1;
14532                   struct Row1;
14533                   «ˇstr»uct Row22;
14534
14535                   struct ˇRow44;
14536                   struct Row5;
14537                   struct «Rˇ»ow66;ˇ
14538
14539                   «struˇ»ct Row88;
14540                   struct Row9;
14541                   struct Row1011;ˇ"#},
14542        vec![
14543            DiffHunkStatusKind::Modified,
14544            DiffHunkStatusKind::Modified,
14545            DiffHunkStatusKind::Modified,
14546            DiffHunkStatusKind::Modified,
14547            DiffHunkStatusKind::Modified,
14548            DiffHunkStatusKind::Modified,
14549        ],
14550        indoc! {r#"struct Row;
14551                   ˇstruct Row1;
14552                   struct Row2;
14553                   ˇ
14554                   struct Row4;
14555                   ˇstruct Row5;
14556                   struct Row6;
14557                   ˇ
14558                   struct Row8;
14559                   ˇstruct Row9;
14560                   struct Row10;ˇ"#},
14561        base_text,
14562        &mut cx,
14563    );
14564}
14565
14566#[gpui::test]
14567async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14568    init_test(cx, |_| {});
14569    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14570    let base_text = indoc! {r#"
14571        one
14572
14573        two
14574        three
14575        "#};
14576
14577    cx.set_head_text(base_text);
14578    cx.set_state("\nˇ\n");
14579    cx.executor().run_until_parked();
14580    cx.update_editor(|editor, _window, cx| {
14581        editor.expand_selected_diff_hunks(cx);
14582    });
14583    cx.executor().run_until_parked();
14584    cx.update_editor(|editor, window, cx| {
14585        editor.backspace(&Default::default(), window, cx);
14586    });
14587    cx.run_until_parked();
14588    cx.assert_state_with_diff(
14589        indoc! {r#"
14590
14591        - two
14592        - threeˇ
14593        +
14594        "#}
14595        .to_string(),
14596    );
14597}
14598
14599#[gpui::test]
14600async fn test_deletion_reverts(cx: &mut TestAppContext) {
14601    init_test(cx, |_| {});
14602    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14603    let base_text = indoc! {r#"struct Row;
14604struct Row1;
14605struct Row2;
14606
14607struct Row4;
14608struct Row5;
14609struct Row6;
14610
14611struct Row8;
14612struct Row9;
14613struct Row10;"#};
14614
14615    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14616    assert_hunk_revert(
14617        indoc! {r#"struct Row;
14618                   struct Row2;
14619
14620                   ˇstruct Row4;
14621                   struct Row5;
14622                   struct Row6;
14623                   ˇ
14624                   struct Row8;
14625                   struct Row10;"#},
14626        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14627        indoc! {r#"struct Row;
14628                   struct Row2;
14629
14630                   ˇstruct Row4;
14631                   struct Row5;
14632                   struct Row6;
14633                   ˇ
14634                   struct Row8;
14635                   struct Row10;"#},
14636        base_text,
14637        &mut cx,
14638    );
14639    assert_hunk_revert(
14640        indoc! {r#"struct Row;
14641                   struct Row2;
14642
14643                   «ˇstruct Row4;
14644                   struct» Row5;
14645                   «struct Row6;
14646                   ˇ»
14647                   struct Row8;
14648                   struct Row10;"#},
14649        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14650        indoc! {r#"struct Row;
14651                   struct Row2;
14652
14653                   «ˇstruct Row4;
14654                   struct» Row5;
14655                   «struct Row6;
14656                   ˇ»
14657                   struct Row8;
14658                   struct Row10;"#},
14659        base_text,
14660        &mut cx,
14661    );
14662
14663    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14664    assert_hunk_revert(
14665        indoc! {r#"struct Row;
14666                   ˇstruct Row2;
14667
14668                   struct Row4;
14669                   struct Row5;
14670                   struct Row6;
14671
14672                   struct Row8;ˇ
14673                   struct Row10;"#},
14674        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14675        indoc! {r#"struct Row;
14676                   struct Row1;
14677                   ˇstruct Row2;
14678
14679                   struct Row4;
14680                   struct Row5;
14681                   struct Row6;
14682
14683                   struct Row8;ˇ
14684                   struct Row9;
14685                   struct Row10;"#},
14686        base_text,
14687        &mut cx,
14688    );
14689    assert_hunk_revert(
14690        indoc! {r#"struct Row;
14691                   struct Row2«ˇ;
14692                   struct Row4;
14693                   struct» Row5;
14694                   «struct Row6;
14695
14696                   struct Row8;ˇ»
14697                   struct Row10;"#},
14698        vec![
14699            DiffHunkStatusKind::Deleted,
14700            DiffHunkStatusKind::Deleted,
14701            DiffHunkStatusKind::Deleted,
14702        ],
14703        indoc! {r#"struct Row;
14704                   struct Row1;
14705                   struct Row2«ˇ;
14706
14707                   struct Row4;
14708                   struct» Row5;
14709                   «struct Row6;
14710
14711                   struct Row8;ˇ»
14712                   struct Row9;
14713                   struct Row10;"#},
14714        base_text,
14715        &mut cx,
14716    );
14717}
14718
14719#[gpui::test]
14720async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14721    init_test(cx, |_| {});
14722
14723    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14724    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14725    let base_text_3 =
14726        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14727
14728    let text_1 = edit_first_char_of_every_line(base_text_1);
14729    let text_2 = edit_first_char_of_every_line(base_text_2);
14730    let text_3 = edit_first_char_of_every_line(base_text_3);
14731
14732    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14733    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14734    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14735
14736    let multibuffer = cx.new(|cx| {
14737        let mut multibuffer = MultiBuffer::new(ReadWrite);
14738        multibuffer.push_excerpts(
14739            buffer_1.clone(),
14740            [
14741                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14742                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14743                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14744            ],
14745            cx,
14746        );
14747        multibuffer.push_excerpts(
14748            buffer_2.clone(),
14749            [
14750                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14751                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14752                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14753            ],
14754            cx,
14755        );
14756        multibuffer.push_excerpts(
14757            buffer_3.clone(),
14758            [
14759                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14760                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14761                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14762            ],
14763            cx,
14764        );
14765        multibuffer
14766    });
14767
14768    let fs = FakeFs::new(cx.executor());
14769    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14770    let (editor, cx) = cx
14771        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14772    editor.update_in(cx, |editor, _window, cx| {
14773        for (buffer, diff_base) in [
14774            (buffer_1.clone(), base_text_1),
14775            (buffer_2.clone(), base_text_2),
14776            (buffer_3.clone(), base_text_3),
14777        ] {
14778            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14779            editor
14780                .buffer
14781                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14782        }
14783    });
14784    cx.executor().run_until_parked();
14785
14786    editor.update_in(cx, |editor, window, cx| {
14787        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}");
14788        editor.select_all(&SelectAll, window, cx);
14789        editor.git_restore(&Default::default(), window, cx);
14790    });
14791    cx.executor().run_until_parked();
14792
14793    // When all ranges are selected, all buffer hunks are reverted.
14794    editor.update(cx, |editor, cx| {
14795        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");
14796    });
14797    buffer_1.update(cx, |buffer, _| {
14798        assert_eq!(buffer.text(), base_text_1);
14799    });
14800    buffer_2.update(cx, |buffer, _| {
14801        assert_eq!(buffer.text(), base_text_2);
14802    });
14803    buffer_3.update(cx, |buffer, _| {
14804        assert_eq!(buffer.text(), base_text_3);
14805    });
14806
14807    editor.update_in(cx, |editor, window, cx| {
14808        editor.undo(&Default::default(), window, cx);
14809    });
14810
14811    editor.update_in(cx, |editor, window, cx| {
14812        editor.change_selections(None, window, cx, |s| {
14813            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14814        });
14815        editor.git_restore(&Default::default(), window, cx);
14816    });
14817
14818    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14819    // but not affect buffer_2 and its related excerpts.
14820    editor.update(cx, |editor, cx| {
14821        assert_eq!(
14822            editor.text(cx),
14823            "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}"
14824        );
14825    });
14826    buffer_1.update(cx, |buffer, _| {
14827        assert_eq!(buffer.text(), base_text_1);
14828    });
14829    buffer_2.update(cx, |buffer, _| {
14830        assert_eq!(
14831            buffer.text(),
14832            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14833        );
14834    });
14835    buffer_3.update(cx, |buffer, _| {
14836        assert_eq!(
14837            buffer.text(),
14838            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14839        );
14840    });
14841
14842    fn edit_first_char_of_every_line(text: &str) -> String {
14843        text.split('\n')
14844            .map(|line| format!("X{}", &line[1..]))
14845            .collect::<Vec<_>>()
14846            .join("\n")
14847    }
14848}
14849
14850#[gpui::test]
14851async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14852    init_test(cx, |_| {});
14853
14854    let cols = 4;
14855    let rows = 10;
14856    let sample_text_1 = sample_text(rows, cols, 'a');
14857    assert_eq!(
14858        sample_text_1,
14859        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14860    );
14861    let sample_text_2 = sample_text(rows, cols, 'l');
14862    assert_eq!(
14863        sample_text_2,
14864        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14865    );
14866    let sample_text_3 = sample_text(rows, cols, 'v');
14867    assert_eq!(
14868        sample_text_3,
14869        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14870    );
14871
14872    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14873    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14874    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14875
14876    let multi_buffer = cx.new(|cx| {
14877        let mut multibuffer = MultiBuffer::new(ReadWrite);
14878        multibuffer.push_excerpts(
14879            buffer_1.clone(),
14880            [
14881                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14882                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14883                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14884            ],
14885            cx,
14886        );
14887        multibuffer.push_excerpts(
14888            buffer_2.clone(),
14889            [
14890                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14891                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14892                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14893            ],
14894            cx,
14895        );
14896        multibuffer.push_excerpts(
14897            buffer_3.clone(),
14898            [
14899                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14900                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14901                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14902            ],
14903            cx,
14904        );
14905        multibuffer
14906    });
14907
14908    let fs = FakeFs::new(cx.executor());
14909    fs.insert_tree(
14910        "/a",
14911        json!({
14912            "main.rs": sample_text_1,
14913            "other.rs": sample_text_2,
14914            "lib.rs": sample_text_3,
14915        }),
14916    )
14917    .await;
14918    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14919    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14920    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14921    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14922        Editor::new(
14923            EditorMode::full(),
14924            multi_buffer,
14925            Some(project.clone()),
14926            window,
14927            cx,
14928        )
14929    });
14930    let multibuffer_item_id = workspace
14931        .update(cx, |workspace, window, cx| {
14932            assert!(
14933                workspace.active_item(cx).is_none(),
14934                "active item should be None before the first item is added"
14935            );
14936            workspace.add_item_to_active_pane(
14937                Box::new(multi_buffer_editor.clone()),
14938                None,
14939                true,
14940                window,
14941                cx,
14942            );
14943            let active_item = workspace
14944                .active_item(cx)
14945                .expect("should have an active item after adding the multi buffer");
14946            assert!(
14947                !active_item.is_singleton(cx),
14948                "A multi buffer was expected to active after adding"
14949            );
14950            active_item.item_id()
14951        })
14952        .unwrap();
14953    cx.executor().run_until_parked();
14954
14955    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14956        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14957            s.select_ranges(Some(1..2))
14958        });
14959        editor.open_excerpts(&OpenExcerpts, window, cx);
14960    });
14961    cx.executor().run_until_parked();
14962    let first_item_id = workspace
14963        .update(cx, |workspace, window, cx| {
14964            let active_item = workspace
14965                .active_item(cx)
14966                .expect("should have an active item after navigating into the 1st buffer");
14967            let first_item_id = active_item.item_id();
14968            assert_ne!(
14969                first_item_id, multibuffer_item_id,
14970                "Should navigate into the 1st buffer and activate it"
14971            );
14972            assert!(
14973                active_item.is_singleton(cx),
14974                "New active item should be a singleton buffer"
14975            );
14976            assert_eq!(
14977                active_item
14978                    .act_as::<Editor>(cx)
14979                    .expect("should have navigated into an editor for the 1st buffer")
14980                    .read(cx)
14981                    .text(cx),
14982                sample_text_1
14983            );
14984
14985            workspace
14986                .go_back(workspace.active_pane().downgrade(), window, cx)
14987                .detach_and_log_err(cx);
14988
14989            first_item_id
14990        })
14991        .unwrap();
14992    cx.executor().run_until_parked();
14993    workspace
14994        .update(cx, |workspace, _, cx| {
14995            let active_item = workspace
14996                .active_item(cx)
14997                .expect("should have an active item after navigating back");
14998            assert_eq!(
14999                active_item.item_id(),
15000                multibuffer_item_id,
15001                "Should navigate back to the multi buffer"
15002            );
15003            assert!(!active_item.is_singleton(cx));
15004        })
15005        .unwrap();
15006
15007    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15008        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15009            s.select_ranges(Some(39..40))
15010        });
15011        editor.open_excerpts(&OpenExcerpts, window, cx);
15012    });
15013    cx.executor().run_until_parked();
15014    let second_item_id = workspace
15015        .update(cx, |workspace, window, cx| {
15016            let active_item = workspace
15017                .active_item(cx)
15018                .expect("should have an active item after navigating into the 2nd buffer");
15019            let second_item_id = active_item.item_id();
15020            assert_ne!(
15021                second_item_id, multibuffer_item_id,
15022                "Should navigate away from the multibuffer"
15023            );
15024            assert_ne!(
15025                second_item_id, first_item_id,
15026                "Should navigate into the 2nd buffer and activate it"
15027            );
15028            assert!(
15029                active_item.is_singleton(cx),
15030                "New active item should be a singleton buffer"
15031            );
15032            assert_eq!(
15033                active_item
15034                    .act_as::<Editor>(cx)
15035                    .expect("should have navigated into an editor")
15036                    .read(cx)
15037                    .text(cx),
15038                sample_text_2
15039            );
15040
15041            workspace
15042                .go_back(workspace.active_pane().downgrade(), window, cx)
15043                .detach_and_log_err(cx);
15044
15045            second_item_id
15046        })
15047        .unwrap();
15048    cx.executor().run_until_parked();
15049    workspace
15050        .update(cx, |workspace, _, cx| {
15051            let active_item = workspace
15052                .active_item(cx)
15053                .expect("should have an active item after navigating back from the 2nd buffer");
15054            assert_eq!(
15055                active_item.item_id(),
15056                multibuffer_item_id,
15057                "Should navigate back from the 2nd buffer to the multi buffer"
15058            );
15059            assert!(!active_item.is_singleton(cx));
15060        })
15061        .unwrap();
15062
15063    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15064        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15065            s.select_ranges(Some(70..70))
15066        });
15067        editor.open_excerpts(&OpenExcerpts, window, cx);
15068    });
15069    cx.executor().run_until_parked();
15070    workspace
15071        .update(cx, |workspace, window, cx| {
15072            let active_item = workspace
15073                .active_item(cx)
15074                .expect("should have an active item after navigating into the 3rd buffer");
15075            let third_item_id = active_item.item_id();
15076            assert_ne!(
15077                third_item_id, multibuffer_item_id,
15078                "Should navigate into the 3rd buffer and activate it"
15079            );
15080            assert_ne!(third_item_id, first_item_id);
15081            assert_ne!(third_item_id, second_item_id);
15082            assert!(
15083                active_item.is_singleton(cx),
15084                "New active item should be a singleton buffer"
15085            );
15086            assert_eq!(
15087                active_item
15088                    .act_as::<Editor>(cx)
15089                    .expect("should have navigated into an editor")
15090                    .read(cx)
15091                    .text(cx),
15092                sample_text_3
15093            );
15094
15095            workspace
15096                .go_back(workspace.active_pane().downgrade(), window, cx)
15097                .detach_and_log_err(cx);
15098        })
15099        .unwrap();
15100    cx.executor().run_until_parked();
15101    workspace
15102        .update(cx, |workspace, _, cx| {
15103            let active_item = workspace
15104                .active_item(cx)
15105                .expect("should have an active item after navigating back from the 3rd buffer");
15106            assert_eq!(
15107                active_item.item_id(),
15108                multibuffer_item_id,
15109                "Should navigate back from the 3rd buffer to the multi buffer"
15110            );
15111            assert!(!active_item.is_singleton(cx));
15112        })
15113        .unwrap();
15114}
15115
15116#[gpui::test]
15117async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15118    init_test(cx, |_| {});
15119
15120    let mut cx = EditorTestContext::new(cx).await;
15121
15122    let diff_base = r#"
15123        use some::mod;
15124
15125        const A: u32 = 42;
15126
15127        fn main() {
15128            println!("hello");
15129
15130            println!("world");
15131        }
15132        "#
15133    .unindent();
15134
15135    cx.set_state(
15136        &r#"
15137        use some::modified;
15138
15139        ˇ
15140        fn main() {
15141            println!("hello there");
15142
15143            println!("around the");
15144            println!("world");
15145        }
15146        "#
15147        .unindent(),
15148    );
15149
15150    cx.set_head_text(&diff_base);
15151    executor.run_until_parked();
15152
15153    cx.update_editor(|editor, window, cx| {
15154        editor.go_to_next_hunk(&GoToHunk, window, cx);
15155        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15156    });
15157    executor.run_until_parked();
15158    cx.assert_state_with_diff(
15159        r#"
15160          use some::modified;
15161
15162
15163          fn main() {
15164        -     println!("hello");
15165        + ˇ    println!("hello there");
15166
15167              println!("around the");
15168              println!("world");
15169          }
15170        "#
15171        .unindent(),
15172    );
15173
15174    cx.update_editor(|editor, window, cx| {
15175        for _ in 0..2 {
15176            editor.go_to_next_hunk(&GoToHunk, window, cx);
15177            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15178        }
15179    });
15180    executor.run_until_parked();
15181    cx.assert_state_with_diff(
15182        r#"
15183        - use some::mod;
15184        + ˇuse some::modified;
15185
15186
15187          fn main() {
15188        -     println!("hello");
15189        +     println!("hello there");
15190
15191        +     println!("around the");
15192              println!("world");
15193          }
15194        "#
15195        .unindent(),
15196    );
15197
15198    cx.update_editor(|editor, window, cx| {
15199        editor.go_to_next_hunk(&GoToHunk, window, cx);
15200        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15201    });
15202    executor.run_until_parked();
15203    cx.assert_state_with_diff(
15204        r#"
15205        - use some::mod;
15206        + use some::modified;
15207
15208        - const A: u32 = 42;
15209          ˇ
15210          fn main() {
15211        -     println!("hello");
15212        +     println!("hello there");
15213
15214        +     println!("around the");
15215              println!("world");
15216          }
15217        "#
15218        .unindent(),
15219    );
15220
15221    cx.update_editor(|editor, window, cx| {
15222        editor.cancel(&Cancel, window, cx);
15223    });
15224
15225    cx.assert_state_with_diff(
15226        r#"
15227          use some::modified;
15228
15229          ˇ
15230          fn main() {
15231              println!("hello there");
15232
15233              println!("around the");
15234              println!("world");
15235          }
15236        "#
15237        .unindent(),
15238    );
15239}
15240
15241#[gpui::test]
15242async fn test_diff_base_change_with_expanded_diff_hunks(
15243    executor: BackgroundExecutor,
15244    cx: &mut TestAppContext,
15245) {
15246    init_test(cx, |_| {});
15247
15248    let mut cx = EditorTestContext::new(cx).await;
15249
15250    let diff_base = r#"
15251        use some::mod1;
15252        use some::mod2;
15253
15254        const A: u32 = 42;
15255        const B: u32 = 42;
15256        const C: u32 = 42;
15257
15258        fn main() {
15259            println!("hello");
15260
15261            println!("world");
15262        }
15263        "#
15264    .unindent();
15265
15266    cx.set_state(
15267        &r#"
15268        use some::mod2;
15269
15270        const A: u32 = 42;
15271        const C: u32 = 42;
15272
15273        fn main(ˇ) {
15274            //println!("hello");
15275
15276            println!("world");
15277            //
15278            //
15279        }
15280        "#
15281        .unindent(),
15282    );
15283
15284    cx.set_head_text(&diff_base);
15285    executor.run_until_parked();
15286
15287    cx.update_editor(|editor, window, cx| {
15288        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15289    });
15290    executor.run_until_parked();
15291    cx.assert_state_with_diff(
15292        r#"
15293        - use some::mod1;
15294          use some::mod2;
15295
15296          const A: u32 = 42;
15297        - const B: u32 = 42;
15298          const C: u32 = 42;
15299
15300          fn main(ˇ) {
15301        -     println!("hello");
15302        +     //println!("hello");
15303
15304              println!("world");
15305        +     //
15306        +     //
15307          }
15308        "#
15309        .unindent(),
15310    );
15311
15312    cx.set_head_text("new diff base!");
15313    executor.run_until_parked();
15314    cx.assert_state_with_diff(
15315        r#"
15316        - new diff base!
15317        + use some::mod2;
15318        +
15319        + const A: u32 = 42;
15320        + const C: u32 = 42;
15321        +
15322        + fn main(ˇ) {
15323        +     //println!("hello");
15324        +
15325        +     println!("world");
15326        +     //
15327        +     //
15328        + }
15329        "#
15330        .unindent(),
15331    );
15332}
15333
15334#[gpui::test]
15335async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15336    init_test(cx, |_| {});
15337
15338    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15339    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15340    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15341    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15342    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15343    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15344
15345    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15346    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15347    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15348
15349    let multi_buffer = cx.new(|cx| {
15350        let mut multibuffer = MultiBuffer::new(ReadWrite);
15351        multibuffer.push_excerpts(
15352            buffer_1.clone(),
15353            [
15354                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15355                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15356                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15357            ],
15358            cx,
15359        );
15360        multibuffer.push_excerpts(
15361            buffer_2.clone(),
15362            [
15363                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15364                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15365                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15366            ],
15367            cx,
15368        );
15369        multibuffer.push_excerpts(
15370            buffer_3.clone(),
15371            [
15372                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15373                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15374                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15375            ],
15376            cx,
15377        );
15378        multibuffer
15379    });
15380
15381    let editor =
15382        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15383    editor
15384        .update(cx, |editor, _window, cx| {
15385            for (buffer, diff_base) in [
15386                (buffer_1.clone(), file_1_old),
15387                (buffer_2.clone(), file_2_old),
15388                (buffer_3.clone(), file_3_old),
15389            ] {
15390                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15391                editor
15392                    .buffer
15393                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15394            }
15395        })
15396        .unwrap();
15397
15398    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15399    cx.run_until_parked();
15400
15401    cx.assert_editor_state(
15402        &"
15403            ˇaaa
15404            ccc
15405            ddd
15406
15407            ggg
15408            hhh
15409
15410
15411            lll
15412            mmm
15413            NNN
15414
15415            qqq
15416            rrr
15417
15418            uuu
15419            111
15420            222
15421            333
15422
15423            666
15424            777
15425
15426            000
15427            !!!"
15428        .unindent(),
15429    );
15430
15431    cx.update_editor(|editor, window, cx| {
15432        editor.select_all(&SelectAll, window, cx);
15433        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15434    });
15435    cx.executor().run_until_parked();
15436
15437    cx.assert_state_with_diff(
15438        "
15439            «aaa
15440          - bbb
15441            ccc
15442            ddd
15443
15444            ggg
15445            hhh
15446
15447
15448            lll
15449            mmm
15450          - nnn
15451          + NNN
15452
15453            qqq
15454            rrr
15455
15456            uuu
15457            111
15458            222
15459            333
15460
15461          + 666
15462            777
15463
15464            000
15465            !!!ˇ»"
15466            .unindent(),
15467    );
15468}
15469
15470#[gpui::test]
15471async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15472    init_test(cx, |_| {});
15473
15474    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15475    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15476
15477    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15478    let multi_buffer = cx.new(|cx| {
15479        let mut multibuffer = MultiBuffer::new(ReadWrite);
15480        multibuffer.push_excerpts(
15481            buffer.clone(),
15482            [
15483                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15484                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15485                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15486            ],
15487            cx,
15488        );
15489        multibuffer
15490    });
15491
15492    let editor =
15493        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15494    editor
15495        .update(cx, |editor, _window, cx| {
15496            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15497            editor
15498                .buffer
15499                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15500        })
15501        .unwrap();
15502
15503    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15504    cx.run_until_parked();
15505
15506    cx.update_editor(|editor, window, cx| {
15507        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15508    });
15509    cx.executor().run_until_parked();
15510
15511    // When the start of a hunk coincides with the start of its excerpt,
15512    // the hunk is expanded. When the start of a a hunk is earlier than
15513    // the start of its excerpt, the hunk is not expanded.
15514    cx.assert_state_with_diff(
15515        "
15516            ˇaaa
15517          - bbb
15518          + BBB
15519
15520          - ddd
15521          - eee
15522          + DDD
15523          + EEE
15524            fff
15525
15526            iii
15527        "
15528        .unindent(),
15529    );
15530}
15531
15532#[gpui::test]
15533async fn test_edits_around_expanded_insertion_hunks(
15534    executor: BackgroundExecutor,
15535    cx: &mut TestAppContext,
15536) {
15537    init_test(cx, |_| {});
15538
15539    let mut cx = EditorTestContext::new(cx).await;
15540
15541    let diff_base = r#"
15542        use some::mod1;
15543        use some::mod2;
15544
15545        const A: u32 = 42;
15546
15547        fn main() {
15548            println!("hello");
15549
15550            println!("world");
15551        }
15552        "#
15553    .unindent();
15554    executor.run_until_parked();
15555    cx.set_state(
15556        &r#"
15557        use some::mod1;
15558        use some::mod2;
15559
15560        const A: u32 = 42;
15561        const B: u32 = 42;
15562        const C: u32 = 42;
15563        ˇ
15564
15565        fn main() {
15566            println!("hello");
15567
15568            println!("world");
15569        }
15570        "#
15571        .unindent(),
15572    );
15573
15574    cx.set_head_text(&diff_base);
15575    executor.run_until_parked();
15576
15577    cx.update_editor(|editor, window, cx| {
15578        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15579    });
15580    executor.run_until_parked();
15581
15582    cx.assert_state_with_diff(
15583        r#"
15584        use some::mod1;
15585        use some::mod2;
15586
15587        const A: u32 = 42;
15588      + const B: u32 = 42;
15589      + const C: u32 = 42;
15590      + ˇ
15591
15592        fn main() {
15593            println!("hello");
15594
15595            println!("world");
15596        }
15597      "#
15598        .unindent(),
15599    );
15600
15601    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15602    executor.run_until_parked();
15603
15604    cx.assert_state_with_diff(
15605        r#"
15606        use some::mod1;
15607        use some::mod2;
15608
15609        const A: u32 = 42;
15610      + const B: u32 = 42;
15611      + const C: u32 = 42;
15612      + const D: u32 = 42;
15613      + ˇ
15614
15615        fn main() {
15616            println!("hello");
15617
15618            println!("world");
15619        }
15620      "#
15621        .unindent(),
15622    );
15623
15624    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15625    executor.run_until_parked();
15626
15627    cx.assert_state_with_diff(
15628        r#"
15629        use some::mod1;
15630        use some::mod2;
15631
15632        const A: u32 = 42;
15633      + const B: u32 = 42;
15634      + const C: u32 = 42;
15635      + const D: u32 = 42;
15636      + const E: u32 = 42;
15637      + ˇ
15638
15639        fn main() {
15640            println!("hello");
15641
15642            println!("world");
15643        }
15644      "#
15645        .unindent(),
15646    );
15647
15648    cx.update_editor(|editor, window, cx| {
15649        editor.delete_line(&DeleteLine, window, cx);
15650    });
15651    executor.run_until_parked();
15652
15653    cx.assert_state_with_diff(
15654        r#"
15655        use some::mod1;
15656        use some::mod2;
15657
15658        const A: u32 = 42;
15659      + const B: u32 = 42;
15660      + const C: u32 = 42;
15661      + const D: u32 = 42;
15662      + const E: u32 = 42;
15663        ˇ
15664        fn main() {
15665            println!("hello");
15666
15667            println!("world");
15668        }
15669      "#
15670        .unindent(),
15671    );
15672
15673    cx.update_editor(|editor, window, cx| {
15674        editor.move_up(&MoveUp, window, cx);
15675        editor.delete_line(&DeleteLine, window, cx);
15676        editor.move_up(&MoveUp, window, cx);
15677        editor.delete_line(&DeleteLine, window, cx);
15678        editor.move_up(&MoveUp, window, cx);
15679        editor.delete_line(&DeleteLine, window, cx);
15680    });
15681    executor.run_until_parked();
15682    cx.assert_state_with_diff(
15683        r#"
15684        use some::mod1;
15685        use some::mod2;
15686
15687        const A: u32 = 42;
15688      + const B: u32 = 42;
15689        ˇ
15690        fn main() {
15691            println!("hello");
15692
15693            println!("world");
15694        }
15695      "#
15696        .unindent(),
15697    );
15698
15699    cx.update_editor(|editor, window, cx| {
15700        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15701        editor.delete_line(&DeleteLine, window, cx);
15702    });
15703    executor.run_until_parked();
15704    cx.assert_state_with_diff(
15705        r#"
15706        ˇ
15707        fn main() {
15708            println!("hello");
15709
15710            println!("world");
15711        }
15712      "#
15713        .unindent(),
15714    );
15715}
15716
15717#[gpui::test]
15718async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15719    init_test(cx, |_| {});
15720
15721    let mut cx = EditorTestContext::new(cx).await;
15722    cx.set_head_text(indoc! { "
15723        one
15724        two
15725        three
15726        four
15727        five
15728        "
15729    });
15730    cx.set_state(indoc! { "
15731        one
15732        ˇthree
15733        five
15734    "});
15735    cx.run_until_parked();
15736    cx.update_editor(|editor, window, cx| {
15737        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15738    });
15739    cx.assert_state_with_diff(
15740        indoc! { "
15741        one
15742      - two
15743        ˇthree
15744      - four
15745        five
15746    "}
15747        .to_string(),
15748    );
15749    cx.update_editor(|editor, window, cx| {
15750        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15751    });
15752
15753    cx.assert_state_with_diff(
15754        indoc! { "
15755        one
15756        ˇthree
15757        five
15758    "}
15759        .to_string(),
15760    );
15761
15762    cx.set_state(indoc! { "
15763        one
15764        ˇTWO
15765        three
15766        four
15767        five
15768    "});
15769    cx.run_until_parked();
15770    cx.update_editor(|editor, window, cx| {
15771        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15772    });
15773
15774    cx.assert_state_with_diff(
15775        indoc! { "
15776            one
15777          - two
15778          + ˇTWO
15779            three
15780            four
15781            five
15782        "}
15783        .to_string(),
15784    );
15785    cx.update_editor(|editor, window, cx| {
15786        editor.move_up(&Default::default(), window, cx);
15787        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15788    });
15789    cx.assert_state_with_diff(
15790        indoc! { "
15791            one
15792            ˇTWO
15793            three
15794            four
15795            five
15796        "}
15797        .to_string(),
15798    );
15799}
15800
15801#[gpui::test]
15802async fn test_edits_around_expanded_deletion_hunks(
15803    executor: BackgroundExecutor,
15804    cx: &mut TestAppContext,
15805) {
15806    init_test(cx, |_| {});
15807
15808    let mut cx = EditorTestContext::new(cx).await;
15809
15810    let diff_base = r#"
15811        use some::mod1;
15812        use some::mod2;
15813
15814        const A: u32 = 42;
15815        const B: u32 = 42;
15816        const C: u32 = 42;
15817
15818
15819        fn main() {
15820            println!("hello");
15821
15822            println!("world");
15823        }
15824    "#
15825    .unindent();
15826    executor.run_until_parked();
15827    cx.set_state(
15828        &r#"
15829        use some::mod1;
15830        use some::mod2;
15831
15832        ˇconst B: u32 = 42;
15833        const C: u32 = 42;
15834
15835
15836        fn main() {
15837            println!("hello");
15838
15839            println!("world");
15840        }
15841        "#
15842        .unindent(),
15843    );
15844
15845    cx.set_head_text(&diff_base);
15846    executor.run_until_parked();
15847
15848    cx.update_editor(|editor, window, cx| {
15849        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15850    });
15851    executor.run_until_parked();
15852
15853    cx.assert_state_with_diff(
15854        r#"
15855        use some::mod1;
15856        use some::mod2;
15857
15858      - const A: u32 = 42;
15859        ˇconst B: u32 = 42;
15860        const C: u32 = 42;
15861
15862
15863        fn main() {
15864            println!("hello");
15865
15866            println!("world");
15867        }
15868      "#
15869        .unindent(),
15870    );
15871
15872    cx.update_editor(|editor, window, cx| {
15873        editor.delete_line(&DeleteLine, window, cx);
15874    });
15875    executor.run_until_parked();
15876    cx.assert_state_with_diff(
15877        r#"
15878        use some::mod1;
15879        use some::mod2;
15880
15881      - const A: u32 = 42;
15882      - const B: u32 = 42;
15883        ˇconst C: u32 = 42;
15884
15885
15886        fn main() {
15887            println!("hello");
15888
15889            println!("world");
15890        }
15891      "#
15892        .unindent(),
15893    );
15894
15895    cx.update_editor(|editor, window, cx| {
15896        editor.delete_line(&DeleteLine, window, cx);
15897    });
15898    executor.run_until_parked();
15899    cx.assert_state_with_diff(
15900        r#"
15901        use some::mod1;
15902        use some::mod2;
15903
15904      - const A: u32 = 42;
15905      - const B: u32 = 42;
15906      - const C: u32 = 42;
15907        ˇ
15908
15909        fn main() {
15910            println!("hello");
15911
15912            println!("world");
15913        }
15914      "#
15915        .unindent(),
15916    );
15917
15918    cx.update_editor(|editor, window, cx| {
15919        editor.handle_input("replacement", window, cx);
15920    });
15921    executor.run_until_parked();
15922    cx.assert_state_with_diff(
15923        r#"
15924        use some::mod1;
15925        use some::mod2;
15926
15927      - const A: u32 = 42;
15928      - const B: u32 = 42;
15929      - const C: u32 = 42;
15930      -
15931      + replacementˇ
15932
15933        fn main() {
15934            println!("hello");
15935
15936            println!("world");
15937        }
15938      "#
15939        .unindent(),
15940    );
15941}
15942
15943#[gpui::test]
15944async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15945    init_test(cx, |_| {});
15946
15947    let mut cx = EditorTestContext::new(cx).await;
15948
15949    let base_text = r#"
15950        one
15951        two
15952        three
15953        four
15954        five
15955    "#
15956    .unindent();
15957    executor.run_until_parked();
15958    cx.set_state(
15959        &r#"
15960        one
15961        two
15962        fˇour
15963        five
15964        "#
15965        .unindent(),
15966    );
15967
15968    cx.set_head_text(&base_text);
15969    executor.run_until_parked();
15970
15971    cx.update_editor(|editor, window, cx| {
15972        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15973    });
15974    executor.run_until_parked();
15975
15976    cx.assert_state_with_diff(
15977        r#"
15978          one
15979          two
15980        - three
15981          fˇour
15982          five
15983        "#
15984        .unindent(),
15985    );
15986
15987    cx.update_editor(|editor, window, cx| {
15988        editor.backspace(&Backspace, window, cx);
15989        editor.backspace(&Backspace, window, cx);
15990    });
15991    executor.run_until_parked();
15992    cx.assert_state_with_diff(
15993        r#"
15994          one
15995          two
15996        - threeˇ
15997        - four
15998        + our
15999          five
16000        "#
16001        .unindent(),
16002    );
16003}
16004
16005#[gpui::test]
16006async fn test_edit_after_expanded_modification_hunk(
16007    executor: BackgroundExecutor,
16008    cx: &mut TestAppContext,
16009) {
16010    init_test(cx, |_| {});
16011
16012    let mut cx = EditorTestContext::new(cx).await;
16013
16014    let diff_base = r#"
16015        use some::mod1;
16016        use some::mod2;
16017
16018        const A: u32 = 42;
16019        const B: u32 = 42;
16020        const C: u32 = 42;
16021        const D: u32 = 42;
16022
16023
16024        fn main() {
16025            println!("hello");
16026
16027            println!("world");
16028        }"#
16029    .unindent();
16030
16031    cx.set_state(
16032        &r#"
16033        use some::mod1;
16034        use some::mod2;
16035
16036        const A: u32 = 42;
16037        const B: u32 = 42;
16038        const C: u32 = 43ˇ
16039        const D: u32 = 42;
16040
16041
16042        fn main() {
16043            println!("hello");
16044
16045            println!("world");
16046        }"#
16047        .unindent(),
16048    );
16049
16050    cx.set_head_text(&diff_base);
16051    executor.run_until_parked();
16052    cx.update_editor(|editor, window, cx| {
16053        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16054    });
16055    executor.run_until_parked();
16056
16057    cx.assert_state_with_diff(
16058        r#"
16059        use some::mod1;
16060        use some::mod2;
16061
16062        const A: u32 = 42;
16063        const B: u32 = 42;
16064      - const C: u32 = 42;
16065      + const C: u32 = 43ˇ
16066        const D: u32 = 42;
16067
16068
16069        fn main() {
16070            println!("hello");
16071
16072            println!("world");
16073        }"#
16074        .unindent(),
16075    );
16076
16077    cx.update_editor(|editor, window, cx| {
16078        editor.handle_input("\nnew_line\n", window, cx);
16079    });
16080    executor.run_until_parked();
16081
16082    cx.assert_state_with_diff(
16083        r#"
16084        use some::mod1;
16085        use some::mod2;
16086
16087        const A: u32 = 42;
16088        const B: u32 = 42;
16089      - const C: u32 = 42;
16090      + const C: u32 = 43
16091      + new_line
16092      + ˇ
16093        const D: u32 = 42;
16094
16095
16096        fn main() {
16097            println!("hello");
16098
16099            println!("world");
16100        }"#
16101        .unindent(),
16102    );
16103}
16104
16105#[gpui::test]
16106async fn test_stage_and_unstage_added_file_hunk(
16107    executor: BackgroundExecutor,
16108    cx: &mut TestAppContext,
16109) {
16110    init_test(cx, |_| {});
16111
16112    let mut cx = EditorTestContext::new(cx).await;
16113    cx.update_editor(|editor, _, cx| {
16114        editor.set_expand_all_diff_hunks(cx);
16115    });
16116
16117    let working_copy = r#"
16118            ˇfn main() {
16119                println!("hello, world!");
16120            }
16121        "#
16122    .unindent();
16123
16124    cx.set_state(&working_copy);
16125    executor.run_until_parked();
16126
16127    cx.assert_state_with_diff(
16128        r#"
16129            + ˇfn main() {
16130            +     println!("hello, world!");
16131            + }
16132        "#
16133        .unindent(),
16134    );
16135    cx.assert_index_text(None);
16136
16137    cx.update_editor(|editor, window, cx| {
16138        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16139    });
16140    executor.run_until_parked();
16141    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16142    cx.assert_state_with_diff(
16143        r#"
16144            + ˇfn main() {
16145            +     println!("hello, world!");
16146            + }
16147        "#
16148        .unindent(),
16149    );
16150
16151    cx.update_editor(|editor, window, cx| {
16152        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16153    });
16154    executor.run_until_parked();
16155    cx.assert_index_text(None);
16156}
16157
16158async fn setup_indent_guides_editor(
16159    text: &str,
16160    cx: &mut TestAppContext,
16161) -> (BufferId, EditorTestContext) {
16162    init_test(cx, |_| {});
16163
16164    let mut cx = EditorTestContext::new(cx).await;
16165
16166    let buffer_id = cx.update_editor(|editor, window, cx| {
16167        editor.set_text(text, window, cx);
16168        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16169
16170        buffer_ids[0]
16171    });
16172
16173    (buffer_id, cx)
16174}
16175
16176fn assert_indent_guides(
16177    range: Range<u32>,
16178    expected: Vec<IndentGuide>,
16179    active_indices: Option<Vec<usize>>,
16180    cx: &mut EditorTestContext,
16181) {
16182    let indent_guides = cx.update_editor(|editor, window, cx| {
16183        let snapshot = editor.snapshot(window, cx).display_snapshot;
16184        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16185            editor,
16186            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16187            true,
16188            &snapshot,
16189            cx,
16190        );
16191
16192        indent_guides.sort_by(|a, b| {
16193            a.depth.cmp(&b.depth).then(
16194                a.start_row
16195                    .cmp(&b.start_row)
16196                    .then(a.end_row.cmp(&b.end_row)),
16197            )
16198        });
16199        indent_guides
16200    });
16201
16202    if let Some(expected) = active_indices {
16203        let active_indices = cx.update_editor(|editor, window, cx| {
16204            let snapshot = editor.snapshot(window, cx).display_snapshot;
16205            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16206        });
16207
16208        assert_eq!(
16209            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16210            expected,
16211            "Active indent guide indices do not match"
16212        );
16213    }
16214
16215    assert_eq!(indent_guides, expected, "Indent guides do not match");
16216}
16217
16218fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16219    IndentGuide {
16220        buffer_id,
16221        start_row: MultiBufferRow(start_row),
16222        end_row: MultiBufferRow(end_row),
16223        depth,
16224        tab_size: 4,
16225        settings: IndentGuideSettings {
16226            enabled: true,
16227            line_width: 1,
16228            active_line_width: 1,
16229            ..Default::default()
16230        },
16231    }
16232}
16233
16234#[gpui::test]
16235async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16236    let (buffer_id, mut cx) = setup_indent_guides_editor(
16237        &"
16238    fn main() {
16239        let a = 1;
16240    }"
16241        .unindent(),
16242        cx,
16243    )
16244    .await;
16245
16246    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16247}
16248
16249#[gpui::test]
16250async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16251    let (buffer_id, mut cx) = setup_indent_guides_editor(
16252        &"
16253    fn main() {
16254        let a = 1;
16255        let b = 2;
16256    }"
16257        .unindent(),
16258        cx,
16259    )
16260    .await;
16261
16262    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16263}
16264
16265#[gpui::test]
16266async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16267    let (buffer_id, mut cx) = setup_indent_guides_editor(
16268        &"
16269    fn main() {
16270        let a = 1;
16271        if a == 3 {
16272            let b = 2;
16273        } else {
16274            let c = 3;
16275        }
16276    }"
16277        .unindent(),
16278        cx,
16279    )
16280    .await;
16281
16282    assert_indent_guides(
16283        0..8,
16284        vec![
16285            indent_guide(buffer_id, 1, 6, 0),
16286            indent_guide(buffer_id, 3, 3, 1),
16287            indent_guide(buffer_id, 5, 5, 1),
16288        ],
16289        None,
16290        &mut cx,
16291    );
16292}
16293
16294#[gpui::test]
16295async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16296    let (buffer_id, mut cx) = setup_indent_guides_editor(
16297        &"
16298    fn main() {
16299        let a = 1;
16300            let b = 2;
16301        let c = 3;
16302    }"
16303        .unindent(),
16304        cx,
16305    )
16306    .await;
16307
16308    assert_indent_guides(
16309        0..5,
16310        vec![
16311            indent_guide(buffer_id, 1, 3, 0),
16312            indent_guide(buffer_id, 2, 2, 1),
16313        ],
16314        None,
16315        &mut cx,
16316    );
16317}
16318
16319#[gpui::test]
16320async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16321    let (buffer_id, mut cx) = setup_indent_guides_editor(
16322        &"
16323        fn main() {
16324            let a = 1;
16325
16326            let c = 3;
16327        }"
16328        .unindent(),
16329        cx,
16330    )
16331    .await;
16332
16333    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16334}
16335
16336#[gpui::test]
16337async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16338    let (buffer_id, mut cx) = setup_indent_guides_editor(
16339        &"
16340        fn main() {
16341            let a = 1;
16342
16343            let c = 3;
16344
16345            if a == 3 {
16346                let b = 2;
16347            } else {
16348                let c = 3;
16349            }
16350        }"
16351        .unindent(),
16352        cx,
16353    )
16354    .await;
16355
16356    assert_indent_guides(
16357        0..11,
16358        vec![
16359            indent_guide(buffer_id, 1, 9, 0),
16360            indent_guide(buffer_id, 6, 6, 1),
16361            indent_guide(buffer_id, 8, 8, 1),
16362        ],
16363        None,
16364        &mut cx,
16365    );
16366}
16367
16368#[gpui::test]
16369async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16370    let (buffer_id, mut cx) = setup_indent_guides_editor(
16371        &"
16372        fn main() {
16373            let a = 1;
16374
16375            let c = 3;
16376
16377            if a == 3 {
16378                let b = 2;
16379            } else {
16380                let c = 3;
16381            }
16382        }"
16383        .unindent(),
16384        cx,
16385    )
16386    .await;
16387
16388    assert_indent_guides(
16389        1..11,
16390        vec![
16391            indent_guide(buffer_id, 1, 9, 0),
16392            indent_guide(buffer_id, 6, 6, 1),
16393            indent_guide(buffer_id, 8, 8, 1),
16394        ],
16395        None,
16396        &mut cx,
16397    );
16398}
16399
16400#[gpui::test]
16401async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16402    let (buffer_id, mut cx) = setup_indent_guides_editor(
16403        &"
16404        fn main() {
16405            let a = 1;
16406
16407            let c = 3;
16408
16409            if a == 3 {
16410                let b = 2;
16411            } else {
16412                let c = 3;
16413            }
16414        }"
16415        .unindent(),
16416        cx,
16417    )
16418    .await;
16419
16420    assert_indent_guides(
16421        1..10,
16422        vec![
16423            indent_guide(buffer_id, 1, 9, 0),
16424            indent_guide(buffer_id, 6, 6, 1),
16425            indent_guide(buffer_id, 8, 8, 1),
16426        ],
16427        None,
16428        &mut cx,
16429    );
16430}
16431
16432#[gpui::test]
16433async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16434    let (buffer_id, mut cx) = setup_indent_guides_editor(
16435        &"
16436        block1
16437            block2
16438                block3
16439                    block4
16440            block2
16441        block1
16442        block1"
16443            .unindent(),
16444        cx,
16445    )
16446    .await;
16447
16448    assert_indent_guides(
16449        1..10,
16450        vec![
16451            indent_guide(buffer_id, 1, 4, 0),
16452            indent_guide(buffer_id, 2, 3, 1),
16453            indent_guide(buffer_id, 3, 3, 2),
16454        ],
16455        None,
16456        &mut cx,
16457    );
16458}
16459
16460#[gpui::test]
16461async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16462    let (buffer_id, mut cx) = setup_indent_guides_editor(
16463        &"
16464        block1
16465            block2
16466                block3
16467
16468        block1
16469        block1"
16470            .unindent(),
16471        cx,
16472    )
16473    .await;
16474
16475    assert_indent_guides(
16476        0..6,
16477        vec![
16478            indent_guide(buffer_id, 1, 2, 0),
16479            indent_guide(buffer_id, 2, 2, 1),
16480        ],
16481        None,
16482        &mut cx,
16483    );
16484}
16485
16486#[gpui::test]
16487async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16488    let (buffer_id, mut cx) = setup_indent_guides_editor(
16489        &"
16490        block1
16491
16492
16493
16494            block2
16495        "
16496        .unindent(),
16497        cx,
16498    )
16499    .await;
16500
16501    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16502}
16503
16504#[gpui::test]
16505async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16506    let (buffer_id, mut cx) = setup_indent_guides_editor(
16507        &"
16508        def a:
16509        \tb = 3
16510        \tif True:
16511        \t\tc = 4
16512        \t\td = 5
16513        \tprint(b)
16514        "
16515        .unindent(),
16516        cx,
16517    )
16518    .await;
16519
16520    assert_indent_guides(
16521        0..6,
16522        vec![
16523            indent_guide(buffer_id, 1, 6, 0),
16524            indent_guide(buffer_id, 3, 4, 1),
16525        ],
16526        None,
16527        &mut cx,
16528    );
16529}
16530
16531#[gpui::test]
16532async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16533    let (buffer_id, mut cx) = setup_indent_guides_editor(
16534        &"
16535    fn main() {
16536        let a = 1;
16537    }"
16538        .unindent(),
16539        cx,
16540    )
16541    .await;
16542
16543    cx.update_editor(|editor, window, cx| {
16544        editor.change_selections(None, window, cx, |s| {
16545            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16546        });
16547    });
16548
16549    assert_indent_guides(
16550        0..3,
16551        vec![indent_guide(buffer_id, 1, 1, 0)],
16552        Some(vec![0]),
16553        &mut cx,
16554    );
16555}
16556
16557#[gpui::test]
16558async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16559    let (buffer_id, mut cx) = setup_indent_guides_editor(
16560        &"
16561    fn main() {
16562        if 1 == 2 {
16563            let a = 1;
16564        }
16565    }"
16566        .unindent(),
16567        cx,
16568    )
16569    .await;
16570
16571    cx.update_editor(|editor, window, cx| {
16572        editor.change_selections(None, window, cx, |s| {
16573            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16574        });
16575    });
16576
16577    assert_indent_guides(
16578        0..4,
16579        vec![
16580            indent_guide(buffer_id, 1, 3, 0),
16581            indent_guide(buffer_id, 2, 2, 1),
16582        ],
16583        Some(vec![1]),
16584        &mut cx,
16585    );
16586
16587    cx.update_editor(|editor, window, cx| {
16588        editor.change_selections(None, window, cx, |s| {
16589            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16590        });
16591    });
16592
16593    assert_indent_guides(
16594        0..4,
16595        vec![
16596            indent_guide(buffer_id, 1, 3, 0),
16597            indent_guide(buffer_id, 2, 2, 1),
16598        ],
16599        Some(vec![1]),
16600        &mut cx,
16601    );
16602
16603    cx.update_editor(|editor, window, cx| {
16604        editor.change_selections(None, window, cx, |s| {
16605            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16606        });
16607    });
16608
16609    assert_indent_guides(
16610        0..4,
16611        vec![
16612            indent_guide(buffer_id, 1, 3, 0),
16613            indent_guide(buffer_id, 2, 2, 1),
16614        ],
16615        Some(vec![0]),
16616        &mut cx,
16617    );
16618}
16619
16620#[gpui::test]
16621async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16622    let (buffer_id, mut cx) = setup_indent_guides_editor(
16623        &"
16624    fn main() {
16625        let a = 1;
16626
16627        let b = 2;
16628    }"
16629        .unindent(),
16630        cx,
16631    )
16632    .await;
16633
16634    cx.update_editor(|editor, window, cx| {
16635        editor.change_selections(None, window, cx, |s| {
16636            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16637        });
16638    });
16639
16640    assert_indent_guides(
16641        0..5,
16642        vec![indent_guide(buffer_id, 1, 3, 0)],
16643        Some(vec![0]),
16644        &mut cx,
16645    );
16646}
16647
16648#[gpui::test]
16649async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16650    let (buffer_id, mut cx) = setup_indent_guides_editor(
16651        &"
16652    def m:
16653        a = 1
16654        pass"
16655            .unindent(),
16656        cx,
16657    )
16658    .await;
16659
16660    cx.update_editor(|editor, window, cx| {
16661        editor.change_selections(None, window, cx, |s| {
16662            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16663        });
16664    });
16665
16666    assert_indent_guides(
16667        0..3,
16668        vec![indent_guide(buffer_id, 1, 2, 0)],
16669        Some(vec![0]),
16670        &mut cx,
16671    );
16672}
16673
16674#[gpui::test]
16675async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16676    init_test(cx, |_| {});
16677    let mut cx = EditorTestContext::new(cx).await;
16678    let text = indoc! {
16679        "
16680        impl A {
16681            fn b() {
16682                0;
16683                3;
16684                5;
16685                6;
16686                7;
16687            }
16688        }
16689        "
16690    };
16691    let base_text = indoc! {
16692        "
16693        impl A {
16694            fn b() {
16695                0;
16696                1;
16697                2;
16698                3;
16699                4;
16700            }
16701            fn c() {
16702                5;
16703                6;
16704                7;
16705            }
16706        }
16707        "
16708    };
16709
16710    cx.update_editor(|editor, window, cx| {
16711        editor.set_text(text, window, cx);
16712
16713        editor.buffer().update(cx, |multibuffer, cx| {
16714            let buffer = multibuffer.as_singleton().unwrap();
16715            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16716
16717            multibuffer.set_all_diff_hunks_expanded(cx);
16718            multibuffer.add_diff(diff, cx);
16719
16720            buffer.read(cx).remote_id()
16721        })
16722    });
16723    cx.run_until_parked();
16724
16725    cx.assert_state_with_diff(
16726        indoc! { "
16727          impl A {
16728              fn b() {
16729                  0;
16730        -         1;
16731        -         2;
16732                  3;
16733        -         4;
16734        -     }
16735        -     fn c() {
16736                  5;
16737                  6;
16738                  7;
16739              }
16740          }
16741          ˇ"
16742        }
16743        .to_string(),
16744    );
16745
16746    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16747        editor
16748            .snapshot(window, cx)
16749            .buffer_snapshot
16750            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16751            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16752            .collect::<Vec<_>>()
16753    });
16754    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16755    assert_eq!(
16756        actual_guides,
16757        vec![
16758            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16759            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16760            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16761        ]
16762    );
16763}
16764
16765#[gpui::test]
16766async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16767    init_test(cx, |_| {});
16768    let mut cx = EditorTestContext::new(cx).await;
16769
16770    let diff_base = r#"
16771        a
16772        b
16773        c
16774        "#
16775    .unindent();
16776
16777    cx.set_state(
16778        &r#"
16779        ˇA
16780        b
16781        C
16782        "#
16783        .unindent(),
16784    );
16785    cx.set_head_text(&diff_base);
16786    cx.update_editor(|editor, window, cx| {
16787        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16788    });
16789    executor.run_until_parked();
16790
16791    let both_hunks_expanded = r#"
16792        - a
16793        + ˇA
16794          b
16795        - c
16796        + C
16797        "#
16798    .unindent();
16799
16800    cx.assert_state_with_diff(both_hunks_expanded.clone());
16801
16802    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16803        let snapshot = editor.snapshot(window, cx);
16804        let hunks = editor
16805            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16806            .collect::<Vec<_>>();
16807        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16808        let buffer_id = hunks[0].buffer_id;
16809        hunks
16810            .into_iter()
16811            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16812            .collect::<Vec<_>>()
16813    });
16814    assert_eq!(hunk_ranges.len(), 2);
16815
16816    cx.update_editor(|editor, _, cx| {
16817        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16818    });
16819    executor.run_until_parked();
16820
16821    let second_hunk_expanded = r#"
16822          ˇA
16823          b
16824        - c
16825        + C
16826        "#
16827    .unindent();
16828
16829    cx.assert_state_with_diff(second_hunk_expanded);
16830
16831    cx.update_editor(|editor, _, cx| {
16832        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16833    });
16834    executor.run_until_parked();
16835
16836    cx.assert_state_with_diff(both_hunks_expanded.clone());
16837
16838    cx.update_editor(|editor, _, cx| {
16839        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16840    });
16841    executor.run_until_parked();
16842
16843    let first_hunk_expanded = r#"
16844        - a
16845        + ˇA
16846          b
16847          C
16848        "#
16849    .unindent();
16850
16851    cx.assert_state_with_diff(first_hunk_expanded);
16852
16853    cx.update_editor(|editor, _, cx| {
16854        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16855    });
16856    executor.run_until_parked();
16857
16858    cx.assert_state_with_diff(both_hunks_expanded);
16859
16860    cx.set_state(
16861        &r#"
16862        ˇA
16863        b
16864        "#
16865        .unindent(),
16866    );
16867    cx.run_until_parked();
16868
16869    // TODO this cursor position seems bad
16870    cx.assert_state_with_diff(
16871        r#"
16872        - ˇa
16873        + A
16874          b
16875        "#
16876        .unindent(),
16877    );
16878
16879    cx.update_editor(|editor, window, cx| {
16880        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16881    });
16882
16883    cx.assert_state_with_diff(
16884        r#"
16885            - ˇa
16886            + A
16887              b
16888            - c
16889            "#
16890        .unindent(),
16891    );
16892
16893    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16894        let snapshot = editor.snapshot(window, cx);
16895        let hunks = editor
16896            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16897            .collect::<Vec<_>>();
16898        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16899        let buffer_id = hunks[0].buffer_id;
16900        hunks
16901            .into_iter()
16902            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16903            .collect::<Vec<_>>()
16904    });
16905    assert_eq!(hunk_ranges.len(), 2);
16906
16907    cx.update_editor(|editor, _, cx| {
16908        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16909    });
16910    executor.run_until_parked();
16911
16912    cx.assert_state_with_diff(
16913        r#"
16914        - ˇa
16915        + A
16916          b
16917        "#
16918        .unindent(),
16919    );
16920}
16921
16922#[gpui::test]
16923async fn test_toggle_deletion_hunk_at_start_of_file(
16924    executor: BackgroundExecutor,
16925    cx: &mut TestAppContext,
16926) {
16927    init_test(cx, |_| {});
16928    let mut cx = EditorTestContext::new(cx).await;
16929
16930    let diff_base = r#"
16931        a
16932        b
16933        c
16934        "#
16935    .unindent();
16936
16937    cx.set_state(
16938        &r#"
16939        ˇb
16940        c
16941        "#
16942        .unindent(),
16943    );
16944    cx.set_head_text(&diff_base);
16945    cx.update_editor(|editor, window, cx| {
16946        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16947    });
16948    executor.run_until_parked();
16949
16950    let hunk_expanded = r#"
16951        - a
16952          ˇb
16953          c
16954        "#
16955    .unindent();
16956
16957    cx.assert_state_with_diff(hunk_expanded.clone());
16958
16959    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16960        let snapshot = editor.snapshot(window, cx);
16961        let hunks = editor
16962            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16963            .collect::<Vec<_>>();
16964        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16965        let buffer_id = hunks[0].buffer_id;
16966        hunks
16967            .into_iter()
16968            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16969            .collect::<Vec<_>>()
16970    });
16971    assert_eq!(hunk_ranges.len(), 1);
16972
16973    cx.update_editor(|editor, _, cx| {
16974        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16975    });
16976    executor.run_until_parked();
16977
16978    let hunk_collapsed = r#"
16979          ˇb
16980          c
16981        "#
16982    .unindent();
16983
16984    cx.assert_state_with_diff(hunk_collapsed);
16985
16986    cx.update_editor(|editor, _, cx| {
16987        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16988    });
16989    executor.run_until_parked();
16990
16991    cx.assert_state_with_diff(hunk_expanded.clone());
16992}
16993
16994#[gpui::test]
16995async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16996    init_test(cx, |_| {});
16997
16998    let fs = FakeFs::new(cx.executor());
16999    fs.insert_tree(
17000        path!("/test"),
17001        json!({
17002            ".git": {},
17003            "file-1": "ONE\n",
17004            "file-2": "TWO\n",
17005            "file-3": "THREE\n",
17006        }),
17007    )
17008    .await;
17009
17010    fs.set_head_for_repo(
17011        path!("/test/.git").as_ref(),
17012        &[
17013            ("file-1".into(), "one\n".into()),
17014            ("file-2".into(), "two\n".into()),
17015            ("file-3".into(), "three\n".into()),
17016        ],
17017    );
17018
17019    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17020    let mut buffers = vec![];
17021    for i in 1..=3 {
17022        let buffer = project
17023            .update(cx, |project, cx| {
17024                let path = format!(path!("/test/file-{}"), i);
17025                project.open_local_buffer(path, cx)
17026            })
17027            .await
17028            .unwrap();
17029        buffers.push(buffer);
17030    }
17031
17032    let multibuffer = cx.new(|cx| {
17033        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17034        multibuffer.set_all_diff_hunks_expanded(cx);
17035        for buffer in &buffers {
17036            let snapshot = buffer.read(cx).snapshot();
17037            multibuffer.set_excerpts_for_path(
17038                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17039                buffer.clone(),
17040                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17041                DEFAULT_MULTIBUFFER_CONTEXT,
17042                cx,
17043            );
17044        }
17045        multibuffer
17046    });
17047
17048    let editor = cx.add_window(|window, cx| {
17049        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17050    });
17051    cx.run_until_parked();
17052
17053    let snapshot = editor
17054        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17055        .unwrap();
17056    let hunks = snapshot
17057        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17058        .map(|hunk| match hunk {
17059            DisplayDiffHunk::Unfolded {
17060                display_row_range, ..
17061            } => display_row_range,
17062            DisplayDiffHunk::Folded { .. } => unreachable!(),
17063        })
17064        .collect::<Vec<_>>();
17065    assert_eq!(
17066        hunks,
17067        [
17068            DisplayRow(2)..DisplayRow(4),
17069            DisplayRow(7)..DisplayRow(9),
17070            DisplayRow(12)..DisplayRow(14),
17071        ]
17072    );
17073}
17074
17075#[gpui::test]
17076async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17077    init_test(cx, |_| {});
17078
17079    let mut cx = EditorTestContext::new(cx).await;
17080    cx.set_head_text(indoc! { "
17081        one
17082        two
17083        three
17084        four
17085        five
17086        "
17087    });
17088    cx.set_index_text(indoc! { "
17089        one
17090        two
17091        three
17092        four
17093        five
17094        "
17095    });
17096    cx.set_state(indoc! {"
17097        one
17098        TWO
17099        ˇTHREE
17100        FOUR
17101        five
17102    "});
17103    cx.run_until_parked();
17104    cx.update_editor(|editor, window, cx| {
17105        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17106    });
17107    cx.run_until_parked();
17108    cx.assert_index_text(Some(indoc! {"
17109        one
17110        TWO
17111        THREE
17112        FOUR
17113        five
17114    "}));
17115    cx.set_state(indoc! { "
17116        one
17117        TWO
17118        ˇTHREE-HUNDRED
17119        FOUR
17120        five
17121    "});
17122    cx.run_until_parked();
17123    cx.update_editor(|editor, window, cx| {
17124        let snapshot = editor.snapshot(window, cx);
17125        let hunks = editor
17126            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17127            .collect::<Vec<_>>();
17128        assert_eq!(hunks.len(), 1);
17129        assert_eq!(
17130            hunks[0].status(),
17131            DiffHunkStatus {
17132                kind: DiffHunkStatusKind::Modified,
17133                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17134            }
17135        );
17136
17137        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17138    });
17139    cx.run_until_parked();
17140    cx.assert_index_text(Some(indoc! {"
17141        one
17142        TWO
17143        THREE-HUNDRED
17144        FOUR
17145        five
17146    "}));
17147}
17148
17149#[gpui::test]
17150fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17151    init_test(cx, |_| {});
17152
17153    let editor = cx.add_window(|window, cx| {
17154        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17155        build_editor(buffer, window, cx)
17156    });
17157
17158    let render_args = Arc::new(Mutex::new(None));
17159    let snapshot = editor
17160        .update(cx, |editor, window, cx| {
17161            let snapshot = editor.buffer().read(cx).snapshot(cx);
17162            let range =
17163                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17164
17165            struct RenderArgs {
17166                row: MultiBufferRow,
17167                folded: bool,
17168                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17169            }
17170
17171            let crease = Crease::inline(
17172                range,
17173                FoldPlaceholder::test(),
17174                {
17175                    let toggle_callback = render_args.clone();
17176                    move |row, folded, callback, _window, _cx| {
17177                        *toggle_callback.lock() = Some(RenderArgs {
17178                            row,
17179                            folded,
17180                            callback,
17181                        });
17182                        div()
17183                    }
17184                },
17185                |_row, _folded, _window, _cx| div(),
17186            );
17187
17188            editor.insert_creases(Some(crease), cx);
17189            let snapshot = editor.snapshot(window, cx);
17190            let _div = snapshot.render_crease_toggle(
17191                MultiBufferRow(1),
17192                false,
17193                cx.entity().clone(),
17194                window,
17195                cx,
17196            );
17197            snapshot
17198        })
17199        .unwrap();
17200
17201    let render_args = render_args.lock().take().unwrap();
17202    assert_eq!(render_args.row, MultiBufferRow(1));
17203    assert!(!render_args.folded);
17204    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17205
17206    cx.update_window(*editor, |_, window, cx| {
17207        (render_args.callback)(true, window, cx)
17208    })
17209    .unwrap();
17210    let snapshot = editor
17211        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17212        .unwrap();
17213    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17214
17215    cx.update_window(*editor, |_, window, cx| {
17216        (render_args.callback)(false, window, cx)
17217    })
17218    .unwrap();
17219    let snapshot = editor
17220        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17221        .unwrap();
17222    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17223}
17224
17225#[gpui::test]
17226async fn test_input_text(cx: &mut TestAppContext) {
17227    init_test(cx, |_| {});
17228    let mut cx = EditorTestContext::new(cx).await;
17229
17230    cx.set_state(
17231        &r#"ˇone
17232        two
17233
17234        three
17235        fourˇ
17236        five
17237
17238        siˇx"#
17239            .unindent(),
17240    );
17241
17242    cx.dispatch_action(HandleInput(String::new()));
17243    cx.assert_editor_state(
17244        &r#"ˇone
17245        two
17246
17247        three
17248        fourˇ
17249        five
17250
17251        siˇx"#
17252            .unindent(),
17253    );
17254
17255    cx.dispatch_action(HandleInput("AAAA".to_string()));
17256    cx.assert_editor_state(
17257        &r#"AAAAˇone
17258        two
17259
17260        three
17261        fourAAAAˇ
17262        five
17263
17264        siAAAAˇx"#
17265            .unindent(),
17266    );
17267}
17268
17269#[gpui::test]
17270async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17271    init_test(cx, |_| {});
17272
17273    let mut cx = EditorTestContext::new(cx).await;
17274    cx.set_state(
17275        r#"let foo = 1;
17276let foo = 2;
17277let foo = 3;
17278let fooˇ = 4;
17279let foo = 5;
17280let foo = 6;
17281let foo = 7;
17282let foo = 8;
17283let foo = 9;
17284let foo = 10;
17285let foo = 11;
17286let foo = 12;
17287let foo = 13;
17288let foo = 14;
17289let foo = 15;"#,
17290    );
17291
17292    cx.update_editor(|e, window, cx| {
17293        assert_eq!(
17294            e.next_scroll_position,
17295            NextScrollCursorCenterTopBottom::Center,
17296            "Default next scroll direction is center",
17297        );
17298
17299        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17300        assert_eq!(
17301            e.next_scroll_position,
17302            NextScrollCursorCenterTopBottom::Top,
17303            "After center, next scroll direction should be top",
17304        );
17305
17306        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17307        assert_eq!(
17308            e.next_scroll_position,
17309            NextScrollCursorCenterTopBottom::Bottom,
17310            "After top, next scroll direction should be bottom",
17311        );
17312
17313        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17314        assert_eq!(
17315            e.next_scroll_position,
17316            NextScrollCursorCenterTopBottom::Center,
17317            "After bottom, scrolling should start over",
17318        );
17319
17320        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17321        assert_eq!(
17322            e.next_scroll_position,
17323            NextScrollCursorCenterTopBottom::Top,
17324            "Scrolling continues if retriggered fast enough"
17325        );
17326    });
17327
17328    cx.executor()
17329        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17330    cx.executor().run_until_parked();
17331    cx.update_editor(|e, _, _| {
17332        assert_eq!(
17333            e.next_scroll_position,
17334            NextScrollCursorCenterTopBottom::Center,
17335            "If scrolling is not triggered fast enough, it should reset"
17336        );
17337    });
17338}
17339
17340#[gpui::test]
17341async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17342    init_test(cx, |_| {});
17343    let mut cx = EditorLspTestContext::new_rust(
17344        lsp::ServerCapabilities {
17345            definition_provider: Some(lsp::OneOf::Left(true)),
17346            references_provider: Some(lsp::OneOf::Left(true)),
17347            ..lsp::ServerCapabilities::default()
17348        },
17349        cx,
17350    )
17351    .await;
17352
17353    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17354        let go_to_definition = cx
17355            .lsp
17356            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17357                move |params, _| async move {
17358                    if empty_go_to_definition {
17359                        Ok(None)
17360                    } else {
17361                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17362                            uri: params.text_document_position_params.text_document.uri,
17363                            range: lsp::Range::new(
17364                                lsp::Position::new(4, 3),
17365                                lsp::Position::new(4, 6),
17366                            ),
17367                        })))
17368                    }
17369                },
17370            );
17371        let references = cx
17372            .lsp
17373            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17374                Ok(Some(vec![lsp::Location {
17375                    uri: params.text_document_position.text_document.uri,
17376                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17377                }]))
17378            });
17379        (go_to_definition, references)
17380    };
17381
17382    cx.set_state(
17383        &r#"fn one() {
17384            let mut a = ˇtwo();
17385        }
17386
17387        fn two() {}"#
17388            .unindent(),
17389    );
17390    set_up_lsp_handlers(false, &mut cx);
17391    let navigated = cx
17392        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17393        .await
17394        .expect("Failed to navigate to definition");
17395    assert_eq!(
17396        navigated,
17397        Navigated::Yes,
17398        "Should have navigated to definition from the GetDefinition response"
17399    );
17400    cx.assert_editor_state(
17401        &r#"fn one() {
17402            let mut a = two();
17403        }
17404
17405        fn «twoˇ»() {}"#
17406            .unindent(),
17407    );
17408
17409    let editors = cx.update_workspace(|workspace, _, cx| {
17410        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17411    });
17412    cx.update_editor(|_, _, test_editor_cx| {
17413        assert_eq!(
17414            editors.len(),
17415            1,
17416            "Initially, only one, test, editor should be open in the workspace"
17417        );
17418        assert_eq!(
17419            test_editor_cx.entity(),
17420            editors.last().expect("Asserted len is 1").clone()
17421        );
17422    });
17423
17424    set_up_lsp_handlers(true, &mut cx);
17425    let navigated = cx
17426        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17427        .await
17428        .expect("Failed to navigate to lookup references");
17429    assert_eq!(
17430        navigated,
17431        Navigated::Yes,
17432        "Should have navigated to references as a fallback after empty GoToDefinition response"
17433    );
17434    // We should not change the selections in the existing file,
17435    // if opening another milti buffer with the references
17436    cx.assert_editor_state(
17437        &r#"fn one() {
17438            let mut a = two();
17439        }
17440
17441        fn «twoˇ»() {}"#
17442            .unindent(),
17443    );
17444    let editors = cx.update_workspace(|workspace, _, cx| {
17445        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17446    });
17447    cx.update_editor(|_, _, test_editor_cx| {
17448        assert_eq!(
17449            editors.len(),
17450            2,
17451            "After falling back to references search, we open a new editor with the results"
17452        );
17453        let references_fallback_text = editors
17454            .into_iter()
17455            .find(|new_editor| *new_editor != test_editor_cx.entity())
17456            .expect("Should have one non-test editor now")
17457            .read(test_editor_cx)
17458            .text(test_editor_cx);
17459        assert_eq!(
17460            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17461            "Should use the range from the references response and not the GoToDefinition one"
17462        );
17463    });
17464}
17465
17466#[gpui::test]
17467async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17468    init_test(cx, |_| {});
17469    cx.update(|cx| {
17470        let mut editor_settings = EditorSettings::get_global(cx).clone();
17471        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17472        EditorSettings::override_global(editor_settings, cx);
17473    });
17474    let mut cx = EditorLspTestContext::new_rust(
17475        lsp::ServerCapabilities {
17476            definition_provider: Some(lsp::OneOf::Left(true)),
17477            references_provider: Some(lsp::OneOf::Left(true)),
17478            ..lsp::ServerCapabilities::default()
17479        },
17480        cx,
17481    )
17482    .await;
17483    let original_state = r#"fn one() {
17484        let mut a = ˇtwo();
17485    }
17486
17487    fn two() {}"#
17488        .unindent();
17489    cx.set_state(&original_state);
17490
17491    let mut go_to_definition = cx
17492        .lsp
17493        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17494            move |_, _| async move { Ok(None) },
17495        );
17496    let _references = cx
17497        .lsp
17498        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17499            panic!("Should not call for references with no go to definition fallback")
17500        });
17501
17502    let navigated = cx
17503        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17504        .await
17505        .expect("Failed to navigate to lookup references");
17506    go_to_definition
17507        .next()
17508        .await
17509        .expect("Should have called the go_to_definition handler");
17510
17511    assert_eq!(
17512        navigated,
17513        Navigated::No,
17514        "Should have navigated to references as a fallback after empty GoToDefinition response"
17515    );
17516    cx.assert_editor_state(&original_state);
17517    let editors = cx.update_workspace(|workspace, _, cx| {
17518        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17519    });
17520    cx.update_editor(|_, _, _| {
17521        assert_eq!(
17522            editors.len(),
17523            1,
17524            "After unsuccessful fallback, no other editor should have been opened"
17525        );
17526    });
17527}
17528
17529#[gpui::test]
17530async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17531    init_test(cx, |_| {});
17532
17533    let language = Arc::new(Language::new(
17534        LanguageConfig::default(),
17535        Some(tree_sitter_rust::LANGUAGE.into()),
17536    ));
17537
17538    let text = r#"
17539        #[cfg(test)]
17540        mod tests() {
17541            #[test]
17542            fn runnable_1() {
17543                let a = 1;
17544            }
17545
17546            #[test]
17547            fn runnable_2() {
17548                let a = 1;
17549                let b = 2;
17550            }
17551        }
17552    "#
17553    .unindent();
17554
17555    let fs = FakeFs::new(cx.executor());
17556    fs.insert_file("/file.rs", Default::default()).await;
17557
17558    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17559    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17560    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17561    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17562    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17563
17564    let editor = cx.new_window_entity(|window, cx| {
17565        Editor::new(
17566            EditorMode::full(),
17567            multi_buffer,
17568            Some(project.clone()),
17569            window,
17570            cx,
17571        )
17572    });
17573
17574    editor.update_in(cx, |editor, window, cx| {
17575        let snapshot = editor.buffer().read(cx).snapshot(cx);
17576        editor.tasks.insert(
17577            (buffer.read(cx).remote_id(), 3),
17578            RunnableTasks {
17579                templates: vec![],
17580                offset: snapshot.anchor_before(43),
17581                column: 0,
17582                extra_variables: HashMap::default(),
17583                context_range: BufferOffset(43)..BufferOffset(85),
17584            },
17585        );
17586        editor.tasks.insert(
17587            (buffer.read(cx).remote_id(), 8),
17588            RunnableTasks {
17589                templates: vec![],
17590                offset: snapshot.anchor_before(86),
17591                column: 0,
17592                extra_variables: HashMap::default(),
17593                context_range: BufferOffset(86)..BufferOffset(191),
17594            },
17595        );
17596
17597        // Test finding task when cursor is inside function body
17598        editor.change_selections(None, window, cx, |s| {
17599            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17600        });
17601        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17602        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17603
17604        // Test finding task when cursor is on function name
17605        editor.change_selections(None, window, cx, |s| {
17606            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17607        });
17608        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17609        assert_eq!(row, 8, "Should find task when cursor is on function name");
17610    });
17611}
17612
17613#[gpui::test]
17614async fn test_folding_buffers(cx: &mut TestAppContext) {
17615    init_test(cx, |_| {});
17616
17617    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17618    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17619    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17620
17621    let fs = FakeFs::new(cx.executor());
17622    fs.insert_tree(
17623        path!("/a"),
17624        json!({
17625            "first.rs": sample_text_1,
17626            "second.rs": sample_text_2,
17627            "third.rs": sample_text_3,
17628        }),
17629    )
17630    .await;
17631    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17632    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17633    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17634    let worktree = project.update(cx, |project, cx| {
17635        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17636        assert_eq!(worktrees.len(), 1);
17637        worktrees.pop().unwrap()
17638    });
17639    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17640
17641    let buffer_1 = project
17642        .update(cx, |project, cx| {
17643            project.open_buffer((worktree_id, "first.rs"), cx)
17644        })
17645        .await
17646        .unwrap();
17647    let buffer_2 = project
17648        .update(cx, |project, cx| {
17649            project.open_buffer((worktree_id, "second.rs"), cx)
17650        })
17651        .await
17652        .unwrap();
17653    let buffer_3 = project
17654        .update(cx, |project, cx| {
17655            project.open_buffer((worktree_id, "third.rs"), cx)
17656        })
17657        .await
17658        .unwrap();
17659
17660    let multi_buffer = cx.new(|cx| {
17661        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17662        multi_buffer.push_excerpts(
17663            buffer_1.clone(),
17664            [
17665                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17666                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17667                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17668            ],
17669            cx,
17670        );
17671        multi_buffer.push_excerpts(
17672            buffer_2.clone(),
17673            [
17674                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17675                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17676                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17677            ],
17678            cx,
17679        );
17680        multi_buffer.push_excerpts(
17681            buffer_3.clone(),
17682            [
17683                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17684                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17685                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17686            ],
17687            cx,
17688        );
17689        multi_buffer
17690    });
17691    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17692        Editor::new(
17693            EditorMode::full(),
17694            multi_buffer.clone(),
17695            Some(project.clone()),
17696            window,
17697            cx,
17698        )
17699    });
17700
17701    assert_eq!(
17702        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17703        "\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",
17704    );
17705
17706    multi_buffer_editor.update(cx, |editor, cx| {
17707        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17708    });
17709    assert_eq!(
17710        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17711        "\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",
17712        "After folding the first buffer, its text should not be displayed"
17713    );
17714
17715    multi_buffer_editor.update(cx, |editor, cx| {
17716        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17717    });
17718    assert_eq!(
17719        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17720        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17721        "After folding the second buffer, its text should not be displayed"
17722    );
17723
17724    multi_buffer_editor.update(cx, |editor, cx| {
17725        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17726    });
17727    assert_eq!(
17728        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17729        "\n\n\n\n\n",
17730        "After folding the third buffer, its text should not be displayed"
17731    );
17732
17733    // Emulate selection inside the fold logic, that should work
17734    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17735        editor
17736            .snapshot(window, cx)
17737            .next_line_boundary(Point::new(0, 4));
17738    });
17739
17740    multi_buffer_editor.update(cx, |editor, cx| {
17741        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17742    });
17743    assert_eq!(
17744        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17745        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17746        "After unfolding the second buffer, its text should be displayed"
17747    );
17748
17749    // Typing inside of buffer 1 causes that buffer to be unfolded.
17750    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17751        assert_eq!(
17752            multi_buffer
17753                .read(cx)
17754                .snapshot(cx)
17755                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17756                .collect::<String>(),
17757            "bbbb"
17758        );
17759        editor.change_selections(None, window, cx, |selections| {
17760            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17761        });
17762        editor.handle_input("B", window, cx);
17763    });
17764
17765    assert_eq!(
17766        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17767        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17768        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17769    );
17770
17771    multi_buffer_editor.update(cx, |editor, cx| {
17772        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17773    });
17774    assert_eq!(
17775        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17776        "\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",
17777        "After unfolding the all buffers, all original text should be displayed"
17778    );
17779}
17780
17781#[gpui::test]
17782async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17783    init_test(cx, |_| {});
17784
17785    let sample_text_1 = "1111\n2222\n3333".to_string();
17786    let sample_text_2 = "4444\n5555\n6666".to_string();
17787    let sample_text_3 = "7777\n8888\n9999".to_string();
17788
17789    let fs = FakeFs::new(cx.executor());
17790    fs.insert_tree(
17791        path!("/a"),
17792        json!({
17793            "first.rs": sample_text_1,
17794            "second.rs": sample_text_2,
17795            "third.rs": sample_text_3,
17796        }),
17797    )
17798    .await;
17799    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17800    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17801    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17802    let worktree = project.update(cx, |project, cx| {
17803        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17804        assert_eq!(worktrees.len(), 1);
17805        worktrees.pop().unwrap()
17806    });
17807    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17808
17809    let buffer_1 = project
17810        .update(cx, |project, cx| {
17811            project.open_buffer((worktree_id, "first.rs"), cx)
17812        })
17813        .await
17814        .unwrap();
17815    let buffer_2 = project
17816        .update(cx, |project, cx| {
17817            project.open_buffer((worktree_id, "second.rs"), cx)
17818        })
17819        .await
17820        .unwrap();
17821    let buffer_3 = project
17822        .update(cx, |project, cx| {
17823            project.open_buffer((worktree_id, "third.rs"), cx)
17824        })
17825        .await
17826        .unwrap();
17827
17828    let multi_buffer = cx.new(|cx| {
17829        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17830        multi_buffer.push_excerpts(
17831            buffer_1.clone(),
17832            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17833            cx,
17834        );
17835        multi_buffer.push_excerpts(
17836            buffer_2.clone(),
17837            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17838            cx,
17839        );
17840        multi_buffer.push_excerpts(
17841            buffer_3.clone(),
17842            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17843            cx,
17844        );
17845        multi_buffer
17846    });
17847
17848    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17849        Editor::new(
17850            EditorMode::full(),
17851            multi_buffer,
17852            Some(project.clone()),
17853            window,
17854            cx,
17855        )
17856    });
17857
17858    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17859    assert_eq!(
17860        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17861        full_text,
17862    );
17863
17864    multi_buffer_editor.update(cx, |editor, cx| {
17865        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17866    });
17867    assert_eq!(
17868        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17869        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17870        "After folding the first buffer, its text should not be displayed"
17871    );
17872
17873    multi_buffer_editor.update(cx, |editor, cx| {
17874        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17875    });
17876
17877    assert_eq!(
17878        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17879        "\n\n\n\n\n\n7777\n8888\n9999",
17880        "After folding the second buffer, its text should not be displayed"
17881    );
17882
17883    multi_buffer_editor.update(cx, |editor, cx| {
17884        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17885    });
17886    assert_eq!(
17887        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17888        "\n\n\n\n\n",
17889        "After folding the third buffer, its text should not be displayed"
17890    );
17891
17892    multi_buffer_editor.update(cx, |editor, cx| {
17893        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17894    });
17895    assert_eq!(
17896        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17897        "\n\n\n\n4444\n5555\n6666\n\n",
17898        "After unfolding the second buffer, its text should be displayed"
17899    );
17900
17901    multi_buffer_editor.update(cx, |editor, cx| {
17902        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17903    });
17904    assert_eq!(
17905        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17906        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17907        "After unfolding the first buffer, its text should be displayed"
17908    );
17909
17910    multi_buffer_editor.update(cx, |editor, cx| {
17911        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17912    });
17913    assert_eq!(
17914        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17915        full_text,
17916        "After unfolding all buffers, all original text should be displayed"
17917    );
17918}
17919
17920#[gpui::test]
17921async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17922    init_test(cx, |_| {});
17923
17924    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17925
17926    let fs = FakeFs::new(cx.executor());
17927    fs.insert_tree(
17928        path!("/a"),
17929        json!({
17930            "main.rs": sample_text,
17931        }),
17932    )
17933    .await;
17934    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17935    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17936    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17937    let worktree = project.update(cx, |project, cx| {
17938        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17939        assert_eq!(worktrees.len(), 1);
17940        worktrees.pop().unwrap()
17941    });
17942    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17943
17944    let buffer_1 = project
17945        .update(cx, |project, cx| {
17946            project.open_buffer((worktree_id, "main.rs"), cx)
17947        })
17948        .await
17949        .unwrap();
17950
17951    let multi_buffer = cx.new(|cx| {
17952        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17953        multi_buffer.push_excerpts(
17954            buffer_1.clone(),
17955            [ExcerptRange::new(
17956                Point::new(0, 0)
17957                    ..Point::new(
17958                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17959                        0,
17960                    ),
17961            )],
17962            cx,
17963        );
17964        multi_buffer
17965    });
17966    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17967        Editor::new(
17968            EditorMode::full(),
17969            multi_buffer,
17970            Some(project.clone()),
17971            window,
17972            cx,
17973        )
17974    });
17975
17976    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17977    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17978        enum TestHighlight {}
17979        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17980        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17981        editor.highlight_text::<TestHighlight>(
17982            vec![highlight_range.clone()],
17983            HighlightStyle::color(Hsla::green()),
17984            cx,
17985        );
17986        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17987    });
17988
17989    let full_text = format!("\n\n{sample_text}");
17990    assert_eq!(
17991        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17992        full_text,
17993    );
17994}
17995
17996#[gpui::test]
17997async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17998    init_test(cx, |_| {});
17999    cx.update(|cx| {
18000        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18001            "keymaps/default-linux.json",
18002            cx,
18003        )
18004        .unwrap();
18005        cx.bind_keys(default_key_bindings);
18006    });
18007
18008    let (editor, cx) = cx.add_window_view(|window, cx| {
18009        let multi_buffer = MultiBuffer::build_multi(
18010            [
18011                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18012                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18013                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18014                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18015            ],
18016            cx,
18017        );
18018        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18019
18020        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18021        // fold all but the second buffer, so that we test navigating between two
18022        // adjacent folded buffers, as well as folded buffers at the start and
18023        // end the multibuffer
18024        editor.fold_buffer(buffer_ids[0], cx);
18025        editor.fold_buffer(buffer_ids[2], cx);
18026        editor.fold_buffer(buffer_ids[3], cx);
18027
18028        editor
18029    });
18030    cx.simulate_resize(size(px(1000.), px(1000.)));
18031
18032    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18033    cx.assert_excerpts_with_selections(indoc! {"
18034        [EXCERPT]
18035        ˇ[FOLDED]
18036        [EXCERPT]
18037        a1
18038        b1
18039        [EXCERPT]
18040        [FOLDED]
18041        [EXCERPT]
18042        [FOLDED]
18043        "
18044    });
18045    cx.simulate_keystroke("down");
18046    cx.assert_excerpts_with_selections(indoc! {"
18047        [EXCERPT]
18048        [FOLDED]
18049        [EXCERPT]
18050        ˇa1
18051        b1
18052        [EXCERPT]
18053        [FOLDED]
18054        [EXCERPT]
18055        [FOLDED]
18056        "
18057    });
18058    cx.simulate_keystroke("down");
18059    cx.assert_excerpts_with_selections(indoc! {"
18060        [EXCERPT]
18061        [FOLDED]
18062        [EXCERPT]
18063        a1
18064        ˇb1
18065        [EXCERPT]
18066        [FOLDED]
18067        [EXCERPT]
18068        [FOLDED]
18069        "
18070    });
18071    cx.simulate_keystroke("down");
18072    cx.assert_excerpts_with_selections(indoc! {"
18073        [EXCERPT]
18074        [FOLDED]
18075        [EXCERPT]
18076        a1
18077        b1
18078        ˇ[EXCERPT]
18079        [FOLDED]
18080        [EXCERPT]
18081        [FOLDED]
18082        "
18083    });
18084    cx.simulate_keystroke("down");
18085    cx.assert_excerpts_with_selections(indoc! {"
18086        [EXCERPT]
18087        [FOLDED]
18088        [EXCERPT]
18089        a1
18090        b1
18091        [EXCERPT]
18092        ˇ[FOLDED]
18093        [EXCERPT]
18094        [FOLDED]
18095        "
18096    });
18097    for _ in 0..5 {
18098        cx.simulate_keystroke("down");
18099        cx.assert_excerpts_with_selections(indoc! {"
18100            [EXCERPT]
18101            [FOLDED]
18102            [EXCERPT]
18103            a1
18104            b1
18105            [EXCERPT]
18106            [FOLDED]
18107            [EXCERPT]
18108            ˇ[FOLDED]
18109            "
18110        });
18111    }
18112
18113    cx.simulate_keystroke("up");
18114    cx.assert_excerpts_with_selections(indoc! {"
18115        [EXCERPT]
18116        [FOLDED]
18117        [EXCERPT]
18118        a1
18119        b1
18120        [EXCERPT]
18121        ˇ[FOLDED]
18122        [EXCERPT]
18123        [FOLDED]
18124        "
18125    });
18126    cx.simulate_keystroke("up");
18127    cx.assert_excerpts_with_selections(indoc! {"
18128        [EXCERPT]
18129        [FOLDED]
18130        [EXCERPT]
18131        a1
18132        b1
18133        ˇ[EXCERPT]
18134        [FOLDED]
18135        [EXCERPT]
18136        [FOLDED]
18137        "
18138    });
18139    cx.simulate_keystroke("up");
18140    cx.assert_excerpts_with_selections(indoc! {"
18141        [EXCERPT]
18142        [FOLDED]
18143        [EXCERPT]
18144        a1
18145        ˇb1
18146        [EXCERPT]
18147        [FOLDED]
18148        [EXCERPT]
18149        [FOLDED]
18150        "
18151    });
18152    cx.simulate_keystroke("up");
18153    cx.assert_excerpts_with_selections(indoc! {"
18154        [EXCERPT]
18155        [FOLDED]
18156        [EXCERPT]
18157        ˇa1
18158        b1
18159        [EXCERPT]
18160        [FOLDED]
18161        [EXCERPT]
18162        [FOLDED]
18163        "
18164    });
18165    for _ in 0..5 {
18166        cx.simulate_keystroke("up");
18167        cx.assert_excerpts_with_selections(indoc! {"
18168            [EXCERPT]
18169            ˇ[FOLDED]
18170            [EXCERPT]
18171            a1
18172            b1
18173            [EXCERPT]
18174            [FOLDED]
18175            [EXCERPT]
18176            [FOLDED]
18177            "
18178        });
18179    }
18180}
18181
18182#[gpui::test]
18183async fn test_inline_completion_text(cx: &mut TestAppContext) {
18184    init_test(cx, |_| {});
18185
18186    // Simple insertion
18187    assert_highlighted_edits(
18188        "Hello, world!",
18189        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18190        true,
18191        cx,
18192        |highlighted_edits, cx| {
18193            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18194            assert_eq!(highlighted_edits.highlights.len(), 1);
18195            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18196            assert_eq!(
18197                highlighted_edits.highlights[0].1.background_color,
18198                Some(cx.theme().status().created_background)
18199            );
18200        },
18201    )
18202    .await;
18203
18204    // Replacement
18205    assert_highlighted_edits(
18206        "This is a test.",
18207        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18208        false,
18209        cx,
18210        |highlighted_edits, cx| {
18211            assert_eq!(highlighted_edits.text, "That is a test.");
18212            assert_eq!(highlighted_edits.highlights.len(), 1);
18213            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18214            assert_eq!(
18215                highlighted_edits.highlights[0].1.background_color,
18216                Some(cx.theme().status().created_background)
18217            );
18218        },
18219    )
18220    .await;
18221
18222    // Multiple edits
18223    assert_highlighted_edits(
18224        "Hello, world!",
18225        vec![
18226            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18227            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18228        ],
18229        false,
18230        cx,
18231        |highlighted_edits, cx| {
18232            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18233            assert_eq!(highlighted_edits.highlights.len(), 2);
18234            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18235            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18236            assert_eq!(
18237                highlighted_edits.highlights[0].1.background_color,
18238                Some(cx.theme().status().created_background)
18239            );
18240            assert_eq!(
18241                highlighted_edits.highlights[1].1.background_color,
18242                Some(cx.theme().status().created_background)
18243            );
18244        },
18245    )
18246    .await;
18247
18248    // Multiple lines with edits
18249    assert_highlighted_edits(
18250        "First line\nSecond line\nThird line\nFourth line",
18251        vec![
18252            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18253            (
18254                Point::new(2, 0)..Point::new(2, 10),
18255                "New third line".to_string(),
18256            ),
18257            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18258        ],
18259        false,
18260        cx,
18261        |highlighted_edits, cx| {
18262            assert_eq!(
18263                highlighted_edits.text,
18264                "Second modified\nNew third line\nFourth updated line"
18265            );
18266            assert_eq!(highlighted_edits.highlights.len(), 3);
18267            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18268            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18269            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18270            for highlight in &highlighted_edits.highlights {
18271                assert_eq!(
18272                    highlight.1.background_color,
18273                    Some(cx.theme().status().created_background)
18274                );
18275            }
18276        },
18277    )
18278    .await;
18279}
18280
18281#[gpui::test]
18282async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18283    init_test(cx, |_| {});
18284
18285    // Deletion
18286    assert_highlighted_edits(
18287        "Hello, world!",
18288        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18289        true,
18290        cx,
18291        |highlighted_edits, cx| {
18292            assert_eq!(highlighted_edits.text, "Hello, world!");
18293            assert_eq!(highlighted_edits.highlights.len(), 1);
18294            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18295            assert_eq!(
18296                highlighted_edits.highlights[0].1.background_color,
18297                Some(cx.theme().status().deleted_background)
18298            );
18299        },
18300    )
18301    .await;
18302
18303    // Insertion
18304    assert_highlighted_edits(
18305        "Hello, world!",
18306        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18307        true,
18308        cx,
18309        |highlighted_edits, cx| {
18310            assert_eq!(highlighted_edits.highlights.len(), 1);
18311            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18312            assert_eq!(
18313                highlighted_edits.highlights[0].1.background_color,
18314                Some(cx.theme().status().created_background)
18315            );
18316        },
18317    )
18318    .await;
18319}
18320
18321async fn assert_highlighted_edits(
18322    text: &str,
18323    edits: Vec<(Range<Point>, String)>,
18324    include_deletions: bool,
18325    cx: &mut TestAppContext,
18326    assertion_fn: impl Fn(HighlightedText, &App),
18327) {
18328    let window = cx.add_window(|window, cx| {
18329        let buffer = MultiBuffer::build_simple(text, cx);
18330        Editor::new(EditorMode::full(), buffer, None, window, cx)
18331    });
18332    let cx = &mut VisualTestContext::from_window(*window, cx);
18333
18334    let (buffer, snapshot) = window
18335        .update(cx, |editor, _window, cx| {
18336            (
18337                editor.buffer().clone(),
18338                editor.buffer().read(cx).snapshot(cx),
18339            )
18340        })
18341        .unwrap();
18342
18343    let edits = edits
18344        .into_iter()
18345        .map(|(range, edit)| {
18346            (
18347                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18348                edit,
18349            )
18350        })
18351        .collect::<Vec<_>>();
18352
18353    let text_anchor_edits = edits
18354        .clone()
18355        .into_iter()
18356        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18357        .collect::<Vec<_>>();
18358
18359    let edit_preview = window
18360        .update(cx, |_, _window, cx| {
18361            buffer
18362                .read(cx)
18363                .as_singleton()
18364                .unwrap()
18365                .read(cx)
18366                .preview_edits(text_anchor_edits.into(), cx)
18367        })
18368        .unwrap()
18369        .await;
18370
18371    cx.update(|_window, cx| {
18372        let highlighted_edits = inline_completion_edit_text(
18373            &snapshot.as_singleton().unwrap().2,
18374            &edits,
18375            &edit_preview,
18376            include_deletions,
18377            cx,
18378        );
18379        assertion_fn(highlighted_edits, cx)
18380    });
18381}
18382
18383#[track_caller]
18384fn assert_breakpoint(
18385    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18386    path: &Arc<Path>,
18387    expected: Vec<(u32, Breakpoint)>,
18388) {
18389    if expected.len() == 0usize {
18390        assert!(!breakpoints.contains_key(path), "{}", path.display());
18391    } else {
18392        let mut breakpoint = breakpoints
18393            .get(path)
18394            .unwrap()
18395            .into_iter()
18396            .map(|breakpoint| {
18397                (
18398                    breakpoint.row,
18399                    Breakpoint {
18400                        message: breakpoint.message.clone(),
18401                        state: breakpoint.state,
18402                        condition: breakpoint.condition.clone(),
18403                        hit_condition: breakpoint.hit_condition.clone(),
18404                    },
18405                )
18406            })
18407            .collect::<Vec<_>>();
18408
18409        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18410
18411        assert_eq!(expected, breakpoint);
18412    }
18413}
18414
18415fn add_log_breakpoint_at_cursor(
18416    editor: &mut Editor,
18417    log_message: &str,
18418    window: &mut Window,
18419    cx: &mut Context<Editor>,
18420) {
18421    let (anchor, bp) = editor
18422        .breakpoints_at_cursors(window, cx)
18423        .first()
18424        .and_then(|(anchor, bp)| {
18425            if let Some(bp) = bp {
18426                Some((*anchor, bp.clone()))
18427            } else {
18428                None
18429            }
18430        })
18431        .unwrap_or_else(|| {
18432            let cursor_position: Point = editor.selections.newest(cx).head();
18433
18434            let breakpoint_position = editor
18435                .snapshot(window, cx)
18436                .display_snapshot
18437                .buffer_snapshot
18438                .anchor_before(Point::new(cursor_position.row, 0));
18439
18440            (breakpoint_position, Breakpoint::new_log(&log_message))
18441        });
18442
18443    editor.edit_breakpoint_at_anchor(
18444        anchor,
18445        bp,
18446        BreakpointEditAction::EditLogMessage(log_message.into()),
18447        cx,
18448    );
18449}
18450
18451#[gpui::test]
18452async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18453    init_test(cx, |_| {});
18454
18455    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18456    let fs = FakeFs::new(cx.executor());
18457    fs.insert_tree(
18458        path!("/a"),
18459        json!({
18460            "main.rs": sample_text,
18461        }),
18462    )
18463    .await;
18464    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18465    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18466    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18467
18468    let fs = FakeFs::new(cx.executor());
18469    fs.insert_tree(
18470        path!("/a"),
18471        json!({
18472            "main.rs": sample_text,
18473        }),
18474    )
18475    .await;
18476    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18477    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18478    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18479    let worktree_id = workspace
18480        .update(cx, |workspace, _window, cx| {
18481            workspace.project().update(cx, |project, cx| {
18482                project.worktrees(cx).next().unwrap().read(cx).id()
18483            })
18484        })
18485        .unwrap();
18486
18487    let buffer = project
18488        .update(cx, |project, cx| {
18489            project.open_buffer((worktree_id, "main.rs"), cx)
18490        })
18491        .await
18492        .unwrap();
18493
18494    let (editor, cx) = cx.add_window_view(|window, cx| {
18495        Editor::new(
18496            EditorMode::full(),
18497            MultiBuffer::build_from_buffer(buffer, cx),
18498            Some(project.clone()),
18499            window,
18500            cx,
18501        )
18502    });
18503
18504    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18505    let abs_path = project.read_with(cx, |project, cx| {
18506        project
18507            .absolute_path(&project_path, cx)
18508            .map(|path_buf| Arc::from(path_buf.to_owned()))
18509            .unwrap()
18510    });
18511
18512    // assert we can add breakpoint on the first line
18513    editor.update_in(cx, |editor, window, cx| {
18514        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18515        editor.move_to_end(&MoveToEnd, window, cx);
18516        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18517    });
18518
18519    let breakpoints = editor.update(cx, |editor, cx| {
18520        editor
18521            .breakpoint_store()
18522            .as_ref()
18523            .unwrap()
18524            .read(cx)
18525            .all_breakpoints(cx)
18526            .clone()
18527    });
18528
18529    assert_eq!(1, breakpoints.len());
18530    assert_breakpoint(
18531        &breakpoints,
18532        &abs_path,
18533        vec![
18534            (0, Breakpoint::new_standard()),
18535            (3, Breakpoint::new_standard()),
18536        ],
18537    );
18538
18539    editor.update_in(cx, |editor, window, cx| {
18540        editor.move_to_beginning(&MoveToBeginning, window, cx);
18541        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18542    });
18543
18544    let breakpoints = editor.update(cx, |editor, cx| {
18545        editor
18546            .breakpoint_store()
18547            .as_ref()
18548            .unwrap()
18549            .read(cx)
18550            .all_breakpoints(cx)
18551            .clone()
18552    });
18553
18554    assert_eq!(1, breakpoints.len());
18555    assert_breakpoint(
18556        &breakpoints,
18557        &abs_path,
18558        vec![(3, Breakpoint::new_standard())],
18559    );
18560
18561    editor.update_in(cx, |editor, window, cx| {
18562        editor.move_to_end(&MoveToEnd, window, cx);
18563        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18564    });
18565
18566    let breakpoints = editor.update(cx, |editor, cx| {
18567        editor
18568            .breakpoint_store()
18569            .as_ref()
18570            .unwrap()
18571            .read(cx)
18572            .all_breakpoints(cx)
18573            .clone()
18574    });
18575
18576    assert_eq!(0, breakpoints.len());
18577    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18578}
18579
18580#[gpui::test]
18581async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18582    init_test(cx, |_| {});
18583
18584    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18585
18586    let fs = FakeFs::new(cx.executor());
18587    fs.insert_tree(
18588        path!("/a"),
18589        json!({
18590            "main.rs": sample_text,
18591        }),
18592    )
18593    .await;
18594    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18595    let (workspace, cx) =
18596        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18597
18598    let worktree_id = workspace.update(cx, |workspace, cx| {
18599        workspace.project().update(cx, |project, cx| {
18600            project.worktrees(cx).next().unwrap().read(cx).id()
18601        })
18602    });
18603
18604    let buffer = project
18605        .update(cx, |project, cx| {
18606            project.open_buffer((worktree_id, "main.rs"), cx)
18607        })
18608        .await
18609        .unwrap();
18610
18611    let (editor, cx) = cx.add_window_view(|window, cx| {
18612        Editor::new(
18613            EditorMode::full(),
18614            MultiBuffer::build_from_buffer(buffer, cx),
18615            Some(project.clone()),
18616            window,
18617            cx,
18618        )
18619    });
18620
18621    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18622    let abs_path = project.read_with(cx, |project, cx| {
18623        project
18624            .absolute_path(&project_path, cx)
18625            .map(|path_buf| Arc::from(path_buf.to_owned()))
18626            .unwrap()
18627    });
18628
18629    editor.update_in(cx, |editor, window, cx| {
18630        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18631    });
18632
18633    let breakpoints = editor.update(cx, |editor, cx| {
18634        editor
18635            .breakpoint_store()
18636            .as_ref()
18637            .unwrap()
18638            .read(cx)
18639            .all_breakpoints(cx)
18640            .clone()
18641    });
18642
18643    assert_breakpoint(
18644        &breakpoints,
18645        &abs_path,
18646        vec![(0, Breakpoint::new_log("hello world"))],
18647    );
18648
18649    // Removing a log message from a log breakpoint should remove it
18650    editor.update_in(cx, |editor, window, cx| {
18651        add_log_breakpoint_at_cursor(editor, "", window, cx);
18652    });
18653
18654    let breakpoints = editor.update(cx, |editor, cx| {
18655        editor
18656            .breakpoint_store()
18657            .as_ref()
18658            .unwrap()
18659            .read(cx)
18660            .all_breakpoints(cx)
18661            .clone()
18662    });
18663
18664    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18665
18666    editor.update_in(cx, |editor, window, cx| {
18667        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18668        editor.move_to_end(&MoveToEnd, window, cx);
18669        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18670        // Not adding a log message to a standard breakpoint shouldn't remove it
18671        add_log_breakpoint_at_cursor(editor, "", window, cx);
18672    });
18673
18674    let breakpoints = editor.update(cx, |editor, cx| {
18675        editor
18676            .breakpoint_store()
18677            .as_ref()
18678            .unwrap()
18679            .read(cx)
18680            .all_breakpoints(cx)
18681            .clone()
18682    });
18683
18684    assert_breakpoint(
18685        &breakpoints,
18686        &abs_path,
18687        vec![
18688            (0, Breakpoint::new_standard()),
18689            (3, Breakpoint::new_standard()),
18690        ],
18691    );
18692
18693    editor.update_in(cx, |editor, window, cx| {
18694        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18695    });
18696
18697    let breakpoints = editor.update(cx, |editor, cx| {
18698        editor
18699            .breakpoint_store()
18700            .as_ref()
18701            .unwrap()
18702            .read(cx)
18703            .all_breakpoints(cx)
18704            .clone()
18705    });
18706
18707    assert_breakpoint(
18708        &breakpoints,
18709        &abs_path,
18710        vec![
18711            (0, Breakpoint::new_standard()),
18712            (3, Breakpoint::new_log("hello world")),
18713        ],
18714    );
18715
18716    editor.update_in(cx, |editor, window, cx| {
18717        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18718    });
18719
18720    let breakpoints = editor.update(cx, |editor, cx| {
18721        editor
18722            .breakpoint_store()
18723            .as_ref()
18724            .unwrap()
18725            .read(cx)
18726            .all_breakpoints(cx)
18727            .clone()
18728    });
18729
18730    assert_breakpoint(
18731        &breakpoints,
18732        &abs_path,
18733        vec![
18734            (0, Breakpoint::new_standard()),
18735            (3, Breakpoint::new_log("hello Earth!!")),
18736        ],
18737    );
18738}
18739
18740/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18741/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18742/// or when breakpoints were placed out of order. This tests for a regression too
18743#[gpui::test]
18744async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18745    init_test(cx, |_| {});
18746
18747    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18748    let fs = FakeFs::new(cx.executor());
18749    fs.insert_tree(
18750        path!("/a"),
18751        json!({
18752            "main.rs": sample_text,
18753        }),
18754    )
18755    .await;
18756    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18757    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18758    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18759
18760    let fs = FakeFs::new(cx.executor());
18761    fs.insert_tree(
18762        path!("/a"),
18763        json!({
18764            "main.rs": sample_text,
18765        }),
18766    )
18767    .await;
18768    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18769    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18770    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18771    let worktree_id = workspace
18772        .update(cx, |workspace, _window, cx| {
18773            workspace.project().update(cx, |project, cx| {
18774                project.worktrees(cx).next().unwrap().read(cx).id()
18775            })
18776        })
18777        .unwrap();
18778
18779    let buffer = project
18780        .update(cx, |project, cx| {
18781            project.open_buffer((worktree_id, "main.rs"), cx)
18782        })
18783        .await
18784        .unwrap();
18785
18786    let (editor, cx) = cx.add_window_view(|window, cx| {
18787        Editor::new(
18788            EditorMode::full(),
18789            MultiBuffer::build_from_buffer(buffer, cx),
18790            Some(project.clone()),
18791            window,
18792            cx,
18793        )
18794    });
18795
18796    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18797    let abs_path = project.read_with(cx, |project, cx| {
18798        project
18799            .absolute_path(&project_path, cx)
18800            .map(|path_buf| Arc::from(path_buf.to_owned()))
18801            .unwrap()
18802    });
18803
18804    // assert we can add breakpoint on the first line
18805    editor.update_in(cx, |editor, window, cx| {
18806        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18807        editor.move_to_end(&MoveToEnd, window, cx);
18808        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18809        editor.move_up(&MoveUp, window, cx);
18810        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18811    });
18812
18813    let breakpoints = editor.update(cx, |editor, cx| {
18814        editor
18815            .breakpoint_store()
18816            .as_ref()
18817            .unwrap()
18818            .read(cx)
18819            .all_breakpoints(cx)
18820            .clone()
18821    });
18822
18823    assert_eq!(1, breakpoints.len());
18824    assert_breakpoint(
18825        &breakpoints,
18826        &abs_path,
18827        vec![
18828            (0, Breakpoint::new_standard()),
18829            (2, Breakpoint::new_standard()),
18830            (3, Breakpoint::new_standard()),
18831        ],
18832    );
18833
18834    editor.update_in(cx, |editor, window, cx| {
18835        editor.move_to_beginning(&MoveToBeginning, window, cx);
18836        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18837        editor.move_to_end(&MoveToEnd, window, cx);
18838        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18839        // Disabling a breakpoint that doesn't exist should do nothing
18840        editor.move_up(&MoveUp, window, cx);
18841        editor.move_up(&MoveUp, window, cx);
18842        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18843    });
18844
18845    let breakpoints = editor.update(cx, |editor, cx| {
18846        editor
18847            .breakpoint_store()
18848            .as_ref()
18849            .unwrap()
18850            .read(cx)
18851            .all_breakpoints(cx)
18852            .clone()
18853    });
18854
18855    let disable_breakpoint = {
18856        let mut bp = Breakpoint::new_standard();
18857        bp.state = BreakpointState::Disabled;
18858        bp
18859    };
18860
18861    assert_eq!(1, breakpoints.len());
18862    assert_breakpoint(
18863        &breakpoints,
18864        &abs_path,
18865        vec![
18866            (0, disable_breakpoint.clone()),
18867            (2, Breakpoint::new_standard()),
18868            (3, disable_breakpoint.clone()),
18869        ],
18870    );
18871
18872    editor.update_in(cx, |editor, window, cx| {
18873        editor.move_to_beginning(&MoveToBeginning, window, cx);
18874        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18875        editor.move_to_end(&MoveToEnd, window, cx);
18876        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18877        editor.move_up(&MoveUp, window, cx);
18878        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18879    });
18880
18881    let breakpoints = editor.update(cx, |editor, cx| {
18882        editor
18883            .breakpoint_store()
18884            .as_ref()
18885            .unwrap()
18886            .read(cx)
18887            .all_breakpoints(cx)
18888            .clone()
18889    });
18890
18891    assert_eq!(1, breakpoints.len());
18892    assert_breakpoint(
18893        &breakpoints,
18894        &abs_path,
18895        vec![
18896            (0, Breakpoint::new_standard()),
18897            (2, disable_breakpoint),
18898            (3, Breakpoint::new_standard()),
18899        ],
18900    );
18901}
18902
18903#[gpui::test]
18904async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18905    init_test(cx, |_| {});
18906    let capabilities = lsp::ServerCapabilities {
18907        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18908            prepare_provider: Some(true),
18909            work_done_progress_options: Default::default(),
18910        })),
18911        ..Default::default()
18912    };
18913    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18914
18915    cx.set_state(indoc! {"
18916        struct Fˇoo {}
18917    "});
18918
18919    cx.update_editor(|editor, _, cx| {
18920        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18921        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18922        editor.highlight_background::<DocumentHighlightRead>(
18923            &[highlight_range],
18924            |c| c.editor_document_highlight_read_background,
18925            cx,
18926        );
18927    });
18928
18929    let mut prepare_rename_handler = cx
18930        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18931            move |_, _, _| async move {
18932                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18933                    start: lsp::Position {
18934                        line: 0,
18935                        character: 7,
18936                    },
18937                    end: lsp::Position {
18938                        line: 0,
18939                        character: 10,
18940                    },
18941                })))
18942            },
18943        );
18944    let prepare_rename_task = cx
18945        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18946        .expect("Prepare rename was not started");
18947    prepare_rename_handler.next().await.unwrap();
18948    prepare_rename_task.await.expect("Prepare rename failed");
18949
18950    let mut rename_handler =
18951        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18952            let edit = lsp::TextEdit {
18953                range: lsp::Range {
18954                    start: lsp::Position {
18955                        line: 0,
18956                        character: 7,
18957                    },
18958                    end: lsp::Position {
18959                        line: 0,
18960                        character: 10,
18961                    },
18962                },
18963                new_text: "FooRenamed".to_string(),
18964            };
18965            Ok(Some(lsp::WorkspaceEdit::new(
18966                // Specify the same edit twice
18967                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18968            )))
18969        });
18970    let rename_task = cx
18971        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18972        .expect("Confirm rename was not started");
18973    rename_handler.next().await.unwrap();
18974    rename_task.await.expect("Confirm rename failed");
18975    cx.run_until_parked();
18976
18977    // Despite two edits, only one is actually applied as those are identical
18978    cx.assert_editor_state(indoc! {"
18979        struct FooRenamedˇ {}
18980    "});
18981}
18982
18983#[gpui::test]
18984async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18985    init_test(cx, |_| {});
18986    // These capabilities indicate that the server does not support prepare rename.
18987    let capabilities = lsp::ServerCapabilities {
18988        rename_provider: Some(lsp::OneOf::Left(true)),
18989        ..Default::default()
18990    };
18991    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18992
18993    cx.set_state(indoc! {"
18994        struct Fˇoo {}
18995    "});
18996
18997    cx.update_editor(|editor, _window, cx| {
18998        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18999        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19000        editor.highlight_background::<DocumentHighlightRead>(
19001            &[highlight_range],
19002            |c| c.editor_document_highlight_read_background,
19003            cx,
19004        );
19005    });
19006
19007    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19008        .expect("Prepare rename was not started")
19009        .await
19010        .expect("Prepare rename failed");
19011
19012    let mut rename_handler =
19013        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19014            let edit = lsp::TextEdit {
19015                range: lsp::Range {
19016                    start: lsp::Position {
19017                        line: 0,
19018                        character: 7,
19019                    },
19020                    end: lsp::Position {
19021                        line: 0,
19022                        character: 10,
19023                    },
19024                },
19025                new_text: "FooRenamed".to_string(),
19026            };
19027            Ok(Some(lsp::WorkspaceEdit::new(
19028                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19029            )))
19030        });
19031    let rename_task = cx
19032        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19033        .expect("Confirm rename was not started");
19034    rename_handler.next().await.unwrap();
19035    rename_task.await.expect("Confirm rename failed");
19036    cx.run_until_parked();
19037
19038    // Correct range is renamed, as `surrounding_word` is used to find it.
19039    cx.assert_editor_state(indoc! {"
19040        struct FooRenamedˇ {}
19041    "});
19042}
19043
19044#[gpui::test]
19045async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19046    init_test(cx, |_| {});
19047    let mut cx = EditorTestContext::new(cx).await;
19048
19049    let language = Arc::new(
19050        Language::new(
19051            LanguageConfig::default(),
19052            Some(tree_sitter_html::LANGUAGE.into()),
19053        )
19054        .with_brackets_query(
19055            r#"
19056            ("<" @open "/>" @close)
19057            ("</" @open ">" @close)
19058            ("<" @open ">" @close)
19059            ("\"" @open "\"" @close)
19060            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19061        "#,
19062        )
19063        .unwrap(),
19064    );
19065    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19066
19067    cx.set_state(indoc! {"
19068        <span>ˇ</span>
19069    "});
19070    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19071    cx.assert_editor_state(indoc! {"
19072        <span>
19073        ˇ
19074        </span>
19075    "});
19076
19077    cx.set_state(indoc! {"
19078        <span><span></span>ˇ</span>
19079    "});
19080    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19081    cx.assert_editor_state(indoc! {"
19082        <span><span></span>
19083        ˇ</span>
19084    "});
19085
19086    cx.set_state(indoc! {"
19087        <span>ˇ
19088        </span>
19089    "});
19090    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19091    cx.assert_editor_state(indoc! {"
19092        <span>
19093        ˇ
19094        </span>
19095    "});
19096}
19097
19098#[gpui::test(iterations = 10)]
19099async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19100    init_test(cx, |_| {});
19101
19102    let fs = FakeFs::new(cx.executor());
19103    fs.insert_tree(
19104        path!("/dir"),
19105        json!({
19106            "a.ts": "a",
19107        }),
19108    )
19109    .await;
19110
19111    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19112    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19113    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19114
19115    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19116    language_registry.add(Arc::new(Language::new(
19117        LanguageConfig {
19118            name: "TypeScript".into(),
19119            matcher: LanguageMatcher {
19120                path_suffixes: vec!["ts".to_string()],
19121                ..Default::default()
19122            },
19123            ..Default::default()
19124        },
19125        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19126    )));
19127    let mut fake_language_servers = language_registry.register_fake_lsp(
19128        "TypeScript",
19129        FakeLspAdapter {
19130            capabilities: lsp::ServerCapabilities {
19131                code_lens_provider: Some(lsp::CodeLensOptions {
19132                    resolve_provider: Some(true),
19133                }),
19134                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19135                    commands: vec!["_the/command".to_string()],
19136                    ..lsp::ExecuteCommandOptions::default()
19137                }),
19138                ..lsp::ServerCapabilities::default()
19139            },
19140            ..FakeLspAdapter::default()
19141        },
19142    );
19143
19144    let (buffer, _handle) = project
19145        .update(cx, |p, cx| {
19146            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19147        })
19148        .await
19149        .unwrap();
19150    cx.executor().run_until_parked();
19151
19152    let fake_server = fake_language_servers.next().await.unwrap();
19153
19154    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19155    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19156    drop(buffer_snapshot);
19157    let actions = cx
19158        .update_window(*workspace, |_, window, cx| {
19159            project.code_actions(&buffer, anchor..anchor, window, cx)
19160        })
19161        .unwrap();
19162
19163    fake_server
19164        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19165            Ok(Some(vec![
19166                lsp::CodeLens {
19167                    range: lsp::Range::default(),
19168                    command: Some(lsp::Command {
19169                        title: "Code lens command".to_owned(),
19170                        command: "_the/command".to_owned(),
19171                        arguments: None,
19172                    }),
19173                    data: None,
19174                },
19175                lsp::CodeLens {
19176                    range: lsp::Range::default(),
19177                    command: Some(lsp::Command {
19178                        title: "Command not in capabilities".to_owned(),
19179                        command: "not in capabilities".to_owned(),
19180                        arguments: None,
19181                    }),
19182                    data: None,
19183                },
19184                lsp::CodeLens {
19185                    range: lsp::Range {
19186                        start: lsp::Position {
19187                            line: 1,
19188                            character: 1,
19189                        },
19190                        end: lsp::Position {
19191                            line: 1,
19192                            character: 1,
19193                        },
19194                    },
19195                    command: Some(lsp::Command {
19196                        title: "Command not in range".to_owned(),
19197                        command: "_the/command".to_owned(),
19198                        arguments: None,
19199                    }),
19200                    data: None,
19201                },
19202            ]))
19203        })
19204        .next()
19205        .await;
19206
19207    let actions = actions.await.unwrap();
19208    assert_eq!(
19209        actions.len(),
19210        1,
19211        "Should have only one valid action for the 0..0 range"
19212    );
19213    let action = actions[0].clone();
19214    let apply = project.update(cx, |project, cx| {
19215        project.apply_code_action(buffer.clone(), action, true, cx)
19216    });
19217
19218    // Resolving the code action does not populate its edits. In absence of
19219    // edits, we must execute the given command.
19220    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19221        |mut lens, _| async move {
19222            let lens_command = lens.command.as_mut().expect("should have a command");
19223            assert_eq!(lens_command.title, "Code lens command");
19224            lens_command.arguments = Some(vec![json!("the-argument")]);
19225            Ok(lens)
19226        },
19227    );
19228
19229    // While executing the command, the language server sends the editor
19230    // a `workspaceEdit` request.
19231    fake_server
19232        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19233            let fake = fake_server.clone();
19234            move |params, _| {
19235                assert_eq!(params.command, "_the/command");
19236                let fake = fake.clone();
19237                async move {
19238                    fake.server
19239                        .request::<lsp::request::ApplyWorkspaceEdit>(
19240                            lsp::ApplyWorkspaceEditParams {
19241                                label: None,
19242                                edit: lsp::WorkspaceEdit {
19243                                    changes: Some(
19244                                        [(
19245                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19246                                            vec![lsp::TextEdit {
19247                                                range: lsp::Range::new(
19248                                                    lsp::Position::new(0, 0),
19249                                                    lsp::Position::new(0, 0),
19250                                                ),
19251                                                new_text: "X".into(),
19252                                            }],
19253                                        )]
19254                                        .into_iter()
19255                                        .collect(),
19256                                    ),
19257                                    ..Default::default()
19258                                },
19259                            },
19260                        )
19261                        .await
19262                        .into_response()
19263                        .unwrap();
19264                    Ok(Some(json!(null)))
19265                }
19266            }
19267        })
19268        .next()
19269        .await;
19270
19271    // Applying the code lens command returns a project transaction containing the edits
19272    // sent by the language server in its `workspaceEdit` request.
19273    let transaction = apply.await.unwrap();
19274    assert!(transaction.0.contains_key(&buffer));
19275    buffer.update(cx, |buffer, cx| {
19276        assert_eq!(buffer.text(), "Xa");
19277        buffer.undo(cx);
19278        assert_eq!(buffer.text(), "a");
19279    });
19280}
19281
19282#[gpui::test]
19283async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19284    init_test(cx, |_| {});
19285
19286    let fs = FakeFs::new(cx.executor());
19287    let main_text = r#"fn main() {
19288println!("1");
19289println!("2");
19290println!("3");
19291println!("4");
19292println!("5");
19293}"#;
19294    let lib_text = "mod foo {}";
19295    fs.insert_tree(
19296        path!("/a"),
19297        json!({
19298            "lib.rs": lib_text,
19299            "main.rs": main_text,
19300        }),
19301    )
19302    .await;
19303
19304    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19305    let (workspace, cx) =
19306        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19307    let worktree_id = workspace.update(cx, |workspace, cx| {
19308        workspace.project().update(cx, |project, cx| {
19309            project.worktrees(cx).next().unwrap().read(cx).id()
19310        })
19311    });
19312
19313    let expected_ranges = vec![
19314        Point::new(0, 0)..Point::new(0, 0),
19315        Point::new(1, 0)..Point::new(1, 1),
19316        Point::new(2, 0)..Point::new(2, 2),
19317        Point::new(3, 0)..Point::new(3, 3),
19318    ];
19319
19320    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19321    let editor_1 = workspace
19322        .update_in(cx, |workspace, window, cx| {
19323            workspace.open_path(
19324                (worktree_id, "main.rs"),
19325                Some(pane_1.downgrade()),
19326                true,
19327                window,
19328                cx,
19329            )
19330        })
19331        .unwrap()
19332        .await
19333        .downcast::<Editor>()
19334        .unwrap();
19335    pane_1.update(cx, |pane, cx| {
19336        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19337        open_editor.update(cx, |editor, cx| {
19338            assert_eq!(
19339                editor.display_text(cx),
19340                main_text,
19341                "Original main.rs text on initial open",
19342            );
19343            assert_eq!(
19344                editor
19345                    .selections
19346                    .all::<Point>(cx)
19347                    .into_iter()
19348                    .map(|s| s.range())
19349                    .collect::<Vec<_>>(),
19350                vec![Point::zero()..Point::zero()],
19351                "Default selections on initial open",
19352            );
19353        })
19354    });
19355    editor_1.update_in(cx, |editor, window, cx| {
19356        editor.change_selections(None, window, cx, |s| {
19357            s.select_ranges(expected_ranges.clone());
19358        });
19359    });
19360
19361    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19362        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19363    });
19364    let editor_2 = workspace
19365        .update_in(cx, |workspace, window, cx| {
19366            workspace.open_path(
19367                (worktree_id, "main.rs"),
19368                Some(pane_2.downgrade()),
19369                true,
19370                window,
19371                cx,
19372            )
19373        })
19374        .unwrap()
19375        .await
19376        .downcast::<Editor>()
19377        .unwrap();
19378    pane_2.update(cx, |pane, cx| {
19379        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19380        open_editor.update(cx, |editor, cx| {
19381            assert_eq!(
19382                editor.display_text(cx),
19383                main_text,
19384                "Original main.rs text on initial open in another panel",
19385            );
19386            assert_eq!(
19387                editor
19388                    .selections
19389                    .all::<Point>(cx)
19390                    .into_iter()
19391                    .map(|s| s.range())
19392                    .collect::<Vec<_>>(),
19393                vec![Point::zero()..Point::zero()],
19394                "Default selections on initial open in another panel",
19395            );
19396        })
19397    });
19398
19399    editor_2.update_in(cx, |editor, window, cx| {
19400        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19401    });
19402
19403    let _other_editor_1 = workspace
19404        .update_in(cx, |workspace, window, cx| {
19405            workspace.open_path(
19406                (worktree_id, "lib.rs"),
19407                Some(pane_1.downgrade()),
19408                true,
19409                window,
19410                cx,
19411            )
19412        })
19413        .unwrap()
19414        .await
19415        .downcast::<Editor>()
19416        .unwrap();
19417    pane_1
19418        .update_in(cx, |pane, window, cx| {
19419            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19420                .unwrap()
19421        })
19422        .await
19423        .unwrap();
19424    drop(editor_1);
19425    pane_1.update(cx, |pane, cx| {
19426        pane.active_item()
19427            .unwrap()
19428            .downcast::<Editor>()
19429            .unwrap()
19430            .update(cx, |editor, cx| {
19431                assert_eq!(
19432                    editor.display_text(cx),
19433                    lib_text,
19434                    "Other file should be open and active",
19435                );
19436            });
19437        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19438    });
19439
19440    let _other_editor_2 = workspace
19441        .update_in(cx, |workspace, window, cx| {
19442            workspace.open_path(
19443                (worktree_id, "lib.rs"),
19444                Some(pane_2.downgrade()),
19445                true,
19446                window,
19447                cx,
19448            )
19449        })
19450        .unwrap()
19451        .await
19452        .downcast::<Editor>()
19453        .unwrap();
19454    pane_2
19455        .update_in(cx, |pane, window, cx| {
19456            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19457                .unwrap()
19458        })
19459        .await
19460        .unwrap();
19461    drop(editor_2);
19462    pane_2.update(cx, |pane, cx| {
19463        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19464        open_editor.update(cx, |editor, cx| {
19465            assert_eq!(
19466                editor.display_text(cx),
19467                lib_text,
19468                "Other file should be open and active in another panel too",
19469            );
19470        });
19471        assert_eq!(
19472            pane.items().count(),
19473            1,
19474            "No other editors should be open in another pane",
19475        );
19476    });
19477
19478    let _editor_1_reopened = workspace
19479        .update_in(cx, |workspace, window, cx| {
19480            workspace.open_path(
19481                (worktree_id, "main.rs"),
19482                Some(pane_1.downgrade()),
19483                true,
19484                window,
19485                cx,
19486            )
19487        })
19488        .unwrap()
19489        .await
19490        .downcast::<Editor>()
19491        .unwrap();
19492    let _editor_2_reopened = workspace
19493        .update_in(cx, |workspace, window, cx| {
19494            workspace.open_path(
19495                (worktree_id, "main.rs"),
19496                Some(pane_2.downgrade()),
19497                true,
19498                window,
19499                cx,
19500            )
19501        })
19502        .unwrap()
19503        .await
19504        .downcast::<Editor>()
19505        .unwrap();
19506    pane_1.update(cx, |pane, cx| {
19507        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19508        open_editor.update(cx, |editor, cx| {
19509            assert_eq!(
19510                editor.display_text(cx),
19511                main_text,
19512                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19513            );
19514            assert_eq!(
19515                editor
19516                    .selections
19517                    .all::<Point>(cx)
19518                    .into_iter()
19519                    .map(|s| s.range())
19520                    .collect::<Vec<_>>(),
19521                expected_ranges,
19522                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19523            );
19524        })
19525    });
19526    pane_2.update(cx, |pane, cx| {
19527        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19528        open_editor.update(cx, |editor, cx| {
19529            assert_eq!(
19530                editor.display_text(cx),
19531                r#"fn main() {
19532⋯rintln!("1");
19533⋯intln!("2");
19534⋯ntln!("3");
19535println!("4");
19536println!("5");
19537}"#,
19538                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19539            );
19540            assert_eq!(
19541                editor
19542                    .selections
19543                    .all::<Point>(cx)
19544                    .into_iter()
19545                    .map(|s| s.range())
19546                    .collect::<Vec<_>>(),
19547                vec![Point::zero()..Point::zero()],
19548                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19549            );
19550        })
19551    });
19552}
19553
19554#[gpui::test]
19555async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19556    init_test(cx, |_| {});
19557
19558    let fs = FakeFs::new(cx.executor());
19559    let main_text = r#"fn main() {
19560println!("1");
19561println!("2");
19562println!("3");
19563println!("4");
19564println!("5");
19565}"#;
19566    let lib_text = "mod foo {}";
19567    fs.insert_tree(
19568        path!("/a"),
19569        json!({
19570            "lib.rs": lib_text,
19571            "main.rs": main_text,
19572        }),
19573    )
19574    .await;
19575
19576    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19577    let (workspace, cx) =
19578        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19579    let worktree_id = workspace.update(cx, |workspace, cx| {
19580        workspace.project().update(cx, |project, cx| {
19581            project.worktrees(cx).next().unwrap().read(cx).id()
19582        })
19583    });
19584
19585    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19586    let editor = workspace
19587        .update_in(cx, |workspace, window, cx| {
19588            workspace.open_path(
19589                (worktree_id, "main.rs"),
19590                Some(pane.downgrade()),
19591                true,
19592                window,
19593                cx,
19594            )
19595        })
19596        .unwrap()
19597        .await
19598        .downcast::<Editor>()
19599        .unwrap();
19600    pane.update(cx, |pane, cx| {
19601        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19602        open_editor.update(cx, |editor, cx| {
19603            assert_eq!(
19604                editor.display_text(cx),
19605                main_text,
19606                "Original main.rs text on initial open",
19607            );
19608        })
19609    });
19610    editor.update_in(cx, |editor, window, cx| {
19611        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19612    });
19613
19614    cx.update_global(|store: &mut SettingsStore, cx| {
19615        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19616            s.restore_on_file_reopen = Some(false);
19617        });
19618    });
19619    editor.update_in(cx, |editor, window, cx| {
19620        editor.fold_ranges(
19621            vec![
19622                Point::new(1, 0)..Point::new(1, 1),
19623                Point::new(2, 0)..Point::new(2, 2),
19624                Point::new(3, 0)..Point::new(3, 3),
19625            ],
19626            false,
19627            window,
19628            cx,
19629        );
19630    });
19631    pane.update_in(cx, |pane, window, cx| {
19632        pane.close_all_items(&CloseAllItems::default(), window, cx)
19633            .unwrap()
19634    })
19635    .await
19636    .unwrap();
19637    pane.update(cx, |pane, _| {
19638        assert!(pane.active_item().is_none());
19639    });
19640    cx.update_global(|store: &mut SettingsStore, cx| {
19641        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19642            s.restore_on_file_reopen = Some(true);
19643        });
19644    });
19645
19646    let _editor_reopened = workspace
19647        .update_in(cx, |workspace, window, cx| {
19648            workspace.open_path(
19649                (worktree_id, "main.rs"),
19650                Some(pane.downgrade()),
19651                true,
19652                window,
19653                cx,
19654            )
19655        })
19656        .unwrap()
19657        .await
19658        .downcast::<Editor>()
19659        .unwrap();
19660    pane.update(cx, |pane, cx| {
19661        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19662        open_editor.update(cx, |editor, cx| {
19663            assert_eq!(
19664                editor.display_text(cx),
19665                main_text,
19666                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19667            );
19668        })
19669    });
19670}
19671
19672#[gpui::test]
19673async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19674    struct EmptyModalView {
19675        focus_handle: gpui::FocusHandle,
19676    }
19677    impl EventEmitter<DismissEvent> for EmptyModalView {}
19678    impl Render for EmptyModalView {
19679        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19680            div()
19681        }
19682    }
19683    impl Focusable for EmptyModalView {
19684        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19685            self.focus_handle.clone()
19686        }
19687    }
19688    impl workspace::ModalView for EmptyModalView {}
19689    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19690        EmptyModalView {
19691            focus_handle: cx.focus_handle(),
19692        }
19693    }
19694
19695    init_test(cx, |_| {});
19696
19697    let fs = FakeFs::new(cx.executor());
19698    let project = Project::test(fs, [], cx).await;
19699    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19700    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19701    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19702    let editor = cx.new_window_entity(|window, cx| {
19703        Editor::new(
19704            EditorMode::full(),
19705            buffer,
19706            Some(project.clone()),
19707            window,
19708            cx,
19709        )
19710    });
19711    workspace
19712        .update(cx, |workspace, window, cx| {
19713            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19714        })
19715        .unwrap();
19716    editor.update_in(cx, |editor, window, cx| {
19717        editor.open_context_menu(&OpenContextMenu, window, cx);
19718        assert!(editor.mouse_context_menu.is_some());
19719    });
19720    workspace
19721        .update(cx, |workspace, window, cx| {
19722            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19723        })
19724        .unwrap();
19725    cx.read(|cx| {
19726        assert!(editor.read(cx).mouse_context_menu.is_none());
19727    });
19728}
19729
19730#[gpui::test]
19731async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19732    init_test(cx, |_| {});
19733
19734    let fs = FakeFs::new(cx.executor());
19735    fs.insert_file(path!("/file.html"), Default::default())
19736        .await;
19737
19738    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19739
19740    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19741    let html_language = Arc::new(Language::new(
19742        LanguageConfig {
19743            name: "HTML".into(),
19744            matcher: LanguageMatcher {
19745                path_suffixes: vec!["html".to_string()],
19746                ..LanguageMatcher::default()
19747            },
19748            brackets: BracketPairConfig {
19749                pairs: vec![BracketPair {
19750                    start: "<".into(),
19751                    end: ">".into(),
19752                    close: true,
19753                    ..Default::default()
19754                }],
19755                ..Default::default()
19756            },
19757            ..Default::default()
19758        },
19759        Some(tree_sitter_html::LANGUAGE.into()),
19760    ));
19761    language_registry.add(html_language);
19762    let mut fake_servers = language_registry.register_fake_lsp(
19763        "HTML",
19764        FakeLspAdapter {
19765            capabilities: lsp::ServerCapabilities {
19766                completion_provider: Some(lsp::CompletionOptions {
19767                    resolve_provider: Some(true),
19768                    ..Default::default()
19769                }),
19770                ..Default::default()
19771            },
19772            ..Default::default()
19773        },
19774    );
19775
19776    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19777    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19778
19779    let worktree_id = workspace
19780        .update(cx, |workspace, _window, cx| {
19781            workspace.project().update(cx, |project, cx| {
19782                project.worktrees(cx).next().unwrap().read(cx).id()
19783            })
19784        })
19785        .unwrap();
19786    project
19787        .update(cx, |project, cx| {
19788            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19789        })
19790        .await
19791        .unwrap();
19792    let editor = workspace
19793        .update(cx, |workspace, window, cx| {
19794            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19795        })
19796        .unwrap()
19797        .await
19798        .unwrap()
19799        .downcast::<Editor>()
19800        .unwrap();
19801
19802    let fake_server = fake_servers.next().await.unwrap();
19803    editor.update_in(cx, |editor, window, cx| {
19804        editor.set_text("<ad></ad>", window, cx);
19805        editor.change_selections(None, window, cx, |selections| {
19806            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19807        });
19808        let Some((buffer, _)) = editor
19809            .buffer
19810            .read(cx)
19811            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19812        else {
19813            panic!("Failed to get buffer for selection position");
19814        };
19815        let buffer = buffer.read(cx);
19816        let buffer_id = buffer.remote_id();
19817        let opening_range =
19818            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19819        let closing_range =
19820            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19821        let mut linked_ranges = HashMap::default();
19822        linked_ranges.insert(
19823            buffer_id,
19824            vec![(opening_range.clone(), vec![closing_range.clone()])],
19825        );
19826        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19827    });
19828    let mut completion_handle =
19829        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19830            Ok(Some(lsp::CompletionResponse::Array(vec![
19831                lsp::CompletionItem {
19832                    label: "head".to_string(),
19833                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19834                        lsp::InsertReplaceEdit {
19835                            new_text: "head".to_string(),
19836                            insert: lsp::Range::new(
19837                                lsp::Position::new(0, 1),
19838                                lsp::Position::new(0, 3),
19839                            ),
19840                            replace: lsp::Range::new(
19841                                lsp::Position::new(0, 1),
19842                                lsp::Position::new(0, 3),
19843                            ),
19844                        },
19845                    )),
19846                    ..Default::default()
19847                },
19848            ])))
19849        });
19850    editor.update_in(cx, |editor, window, cx| {
19851        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19852    });
19853    cx.run_until_parked();
19854    completion_handle.next().await.unwrap();
19855    editor.update(cx, |editor, _| {
19856        assert!(
19857            editor.context_menu_visible(),
19858            "Completion menu should be visible"
19859        );
19860    });
19861    editor.update_in(cx, |editor, window, cx| {
19862        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19863    });
19864    cx.executor().run_until_parked();
19865    editor.update(cx, |editor, cx| {
19866        assert_eq!(editor.text(cx), "<head></head>");
19867    });
19868}
19869
19870fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19871    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19872    point..point
19873}
19874
19875fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19876    let (text, ranges) = marked_text_ranges(marked_text, true);
19877    assert_eq!(editor.text(cx), text);
19878    assert_eq!(
19879        editor.selections.ranges(cx),
19880        ranges,
19881        "Assert selections are {}",
19882        marked_text
19883    );
19884}
19885
19886pub fn handle_signature_help_request(
19887    cx: &mut EditorLspTestContext,
19888    mocked_response: lsp::SignatureHelp,
19889) -> impl Future<Output = ()> + use<> {
19890    let mut request =
19891        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19892            let mocked_response = mocked_response.clone();
19893            async move { Ok(Some(mocked_response)) }
19894        });
19895
19896    async move {
19897        request.next().await;
19898    }
19899}
19900
19901/// Handle completion request passing a marked string specifying where the completion
19902/// should be triggered from using '|' character, what range should be replaced, and what completions
19903/// should be returned using '<' and '>' to delimit the range.
19904///
19905/// Also see `handle_completion_request_with_insert_and_replace`.
19906#[track_caller]
19907pub fn handle_completion_request(
19908    cx: &mut EditorLspTestContext,
19909    marked_string: &str,
19910    completions: Vec<&'static str>,
19911    counter: Arc<AtomicUsize>,
19912) -> impl Future<Output = ()> {
19913    let complete_from_marker: TextRangeMarker = '|'.into();
19914    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19915    let (_, mut marked_ranges) = marked_text_ranges_by(
19916        marked_string,
19917        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19918    );
19919
19920    let complete_from_position =
19921        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19922    let replace_range =
19923        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19924
19925    let mut request =
19926        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19927            let completions = completions.clone();
19928            counter.fetch_add(1, atomic::Ordering::Release);
19929            async move {
19930                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19931                assert_eq!(
19932                    params.text_document_position.position,
19933                    complete_from_position
19934                );
19935                Ok(Some(lsp::CompletionResponse::Array(
19936                    completions
19937                        .iter()
19938                        .map(|completion_text| lsp::CompletionItem {
19939                            label: completion_text.to_string(),
19940                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19941                                range: replace_range,
19942                                new_text: completion_text.to_string(),
19943                            })),
19944                            ..Default::default()
19945                        })
19946                        .collect(),
19947                )))
19948            }
19949        });
19950
19951    async move {
19952        request.next().await;
19953    }
19954}
19955
19956/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19957/// given instead, which also contains an `insert` range.
19958///
19959/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19960/// that is, `replace_range.start..cursor_pos`.
19961pub fn handle_completion_request_with_insert_and_replace(
19962    cx: &mut EditorLspTestContext,
19963    marked_string: &str,
19964    completions: Vec<&'static str>,
19965    counter: Arc<AtomicUsize>,
19966) -> impl Future<Output = ()> {
19967    let complete_from_marker: TextRangeMarker = '|'.into();
19968    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19969    let (_, mut marked_ranges) = marked_text_ranges_by(
19970        marked_string,
19971        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19972    );
19973
19974    let complete_from_position =
19975        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19976    let replace_range =
19977        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19978
19979    let mut request =
19980        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19981            let completions = completions.clone();
19982            counter.fetch_add(1, atomic::Ordering::Release);
19983            async move {
19984                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19985                assert_eq!(
19986                    params.text_document_position.position, complete_from_position,
19987                    "marker `|` position doesn't match",
19988                );
19989                Ok(Some(lsp::CompletionResponse::Array(
19990                    completions
19991                        .iter()
19992                        .map(|completion_text| lsp::CompletionItem {
19993                            label: completion_text.to_string(),
19994                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19995                                lsp::InsertReplaceEdit {
19996                                    insert: lsp::Range {
19997                                        start: replace_range.start,
19998                                        end: complete_from_position,
19999                                    },
20000                                    replace: replace_range,
20001                                    new_text: completion_text.to_string(),
20002                                },
20003                            )),
20004                            ..Default::default()
20005                        })
20006                        .collect(),
20007                )))
20008            }
20009        });
20010
20011    async move {
20012        request.next().await;
20013    }
20014}
20015
20016fn handle_resolve_completion_request(
20017    cx: &mut EditorLspTestContext,
20018    edits: Option<Vec<(&'static str, &'static str)>>,
20019) -> impl Future<Output = ()> {
20020    let edits = edits.map(|edits| {
20021        edits
20022            .iter()
20023            .map(|(marked_string, new_text)| {
20024                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20025                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20026                lsp::TextEdit::new(replace_range, new_text.to_string())
20027            })
20028            .collect::<Vec<_>>()
20029    });
20030
20031    let mut request =
20032        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20033            let edits = edits.clone();
20034            async move {
20035                Ok(lsp::CompletionItem {
20036                    additional_text_edits: edits,
20037                    ..Default::default()
20038                })
20039            }
20040        });
20041
20042    async move {
20043        request.next().await;
20044    }
20045}
20046
20047pub(crate) fn update_test_language_settings(
20048    cx: &mut TestAppContext,
20049    f: impl Fn(&mut AllLanguageSettingsContent),
20050) {
20051    cx.update(|cx| {
20052        SettingsStore::update_global(cx, |store, cx| {
20053            store.update_user_settings::<AllLanguageSettings>(cx, f);
20054        });
20055    });
20056}
20057
20058pub(crate) fn update_test_project_settings(
20059    cx: &mut TestAppContext,
20060    f: impl Fn(&mut ProjectSettings),
20061) {
20062    cx.update(|cx| {
20063        SettingsStore::update_global(cx, |store, cx| {
20064            store.update_user_settings::<ProjectSettings>(cx, f);
20065        });
20066    });
20067}
20068
20069pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
20070    cx.update(|cx| {
20071        assets::Assets.load_test_fonts(cx);
20072        let store = SettingsStore::test(cx);
20073        cx.set_global(store);
20074        theme::init(theme::LoadThemes::JustBase, cx);
20075        release_channel::init(SemanticVersion::default(), cx);
20076        client::init_settings(cx);
20077        language::init(cx);
20078        Project::init_settings(cx);
20079        workspace::init_settings(cx);
20080        crate::init(cx);
20081    });
20082
20083    update_test_language_settings(cx, f);
20084}
20085
20086#[track_caller]
20087fn assert_hunk_revert(
20088    not_reverted_text_with_selections: &str,
20089    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
20090    expected_reverted_text_with_selections: &str,
20091    base_text: &str,
20092    cx: &mut EditorLspTestContext,
20093) {
20094    cx.set_state(not_reverted_text_with_selections);
20095    cx.set_head_text(base_text);
20096    cx.executor().run_until_parked();
20097
20098    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
20099        let snapshot = editor.snapshot(window, cx);
20100        let reverted_hunk_statuses = snapshot
20101            .buffer_snapshot
20102            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
20103            .map(|hunk| hunk.status().kind)
20104            .collect::<Vec<_>>();
20105
20106        editor.git_restore(&Default::default(), window, cx);
20107        reverted_hunk_statuses
20108    });
20109    cx.executor().run_until_parked();
20110    cx.assert_editor_state(expected_reverted_text_with_selections);
20111    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20112}