editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    linked_editing_ranges::LinkedEditingRanges,
    5    scroll::scroll_amount::ScrollAmount,
    6    test::{
    7        assert_text_with_selections, build_editor,
    8        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    9        editor_test_context::EditorTestContext,
   10        select_ranges,
   11    },
   12};
   13use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   14use futures::StreamExt;
   15use gpui::{
   16    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   17    VisualTestContext, WindowBounds, WindowOptions, div,
   18};
   19use indoc::indoc;
   20use language::{
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25    language_settings::{
   26        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   27        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   28    },
   29};
   30use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   31use lsp::CompletionParams;
   32use multi_buffer::{IndentGuide, PathKey};
   33use parking_lot::Mutex;
   34use pretty_assertions::{assert_eq, assert_ne};
   35use project::{
   36    FakeFs,
   37    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   38    project_settings::{LspSettings, ProjectSettings},
   39};
   40use serde_json::{self, json};
   41use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   42use std::{
   43    iter,
   44    sync::atomic::{self, AtomicUsize},
   45};
   46use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   47use text::ToPoint as _;
   48use unindent::Unindent;
   49use util::{
   50    assert_set_eq, path,
   51    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   52    uri,
   53};
   54use workspace::{
   55    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   56    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   57};
   58
   59#[gpui::test]
   60fn test_edit_events(cx: &mut TestAppContext) {
   61    init_test(cx, |_| {});
   62
   63    let buffer = cx.new(|cx| {
   64        let mut buffer = language::Buffer::local("123456", cx);
   65        buffer.set_group_interval(Duration::from_secs(1));
   66        buffer
   67    });
   68
   69    let events = Rc::new(RefCell::new(Vec::new()));
   70    let editor1 = cx.add_window({
   71        let events = events.clone();
   72        |window, cx| {
   73            let entity = cx.entity().clone();
   74            cx.subscribe_in(
   75                &entity,
   76                window,
   77                move |_, _, event: &EditorEvent, _, _| match event {
   78                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   79                    EditorEvent::BufferEdited => {
   80                        events.borrow_mut().push(("editor1", "buffer edited"))
   81                    }
   82                    _ => {}
   83                },
   84            )
   85            .detach();
   86            Editor::for_buffer(buffer.clone(), None, window, cx)
   87        }
   88    });
   89
   90    let editor2 = cx.add_window({
   91        let events = events.clone();
   92        |window, cx| {
   93            cx.subscribe_in(
   94                &cx.entity().clone(),
   95                window,
   96                move |_, _, event: &EditorEvent, _, _| match event {
   97                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   98                    EditorEvent::BufferEdited => {
   99                        events.borrow_mut().push(("editor2", "buffer edited"))
  100                    }
  101                    _ => {}
  102                },
  103            )
  104            .detach();
  105            Editor::for_buffer(buffer.clone(), None, window, cx)
  106        }
  107    });
  108
  109    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  110
  111    // Mutating editor 1 will emit an `Edited` event only for that editor.
  112    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  113    assert_eq!(
  114        mem::take(&mut *events.borrow_mut()),
  115        [
  116            ("editor1", "edited"),
  117            ("editor1", "buffer edited"),
  118            ("editor2", "buffer edited"),
  119        ]
  120    );
  121
  122    // Mutating editor 2 will emit an `Edited` event only for that editor.
  123    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  124    assert_eq!(
  125        mem::take(&mut *events.borrow_mut()),
  126        [
  127            ("editor2", "edited"),
  128            ("editor1", "buffer edited"),
  129            ("editor2", "buffer edited"),
  130        ]
  131    );
  132
  133    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  134    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  135    assert_eq!(
  136        mem::take(&mut *events.borrow_mut()),
  137        [
  138            ("editor1", "edited"),
  139            ("editor1", "buffer edited"),
  140            ("editor2", "buffer edited"),
  141        ]
  142    );
  143
  144    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  145    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  146    assert_eq!(
  147        mem::take(&mut *events.borrow_mut()),
  148        [
  149            ("editor1", "edited"),
  150            ("editor1", "buffer edited"),
  151            ("editor2", "buffer edited"),
  152        ]
  153    );
  154
  155    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  156    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  157    assert_eq!(
  158        mem::take(&mut *events.borrow_mut()),
  159        [
  160            ("editor2", "edited"),
  161            ("editor1", "buffer edited"),
  162            ("editor2", "buffer edited"),
  163        ]
  164    );
  165
  166    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  167    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  168    assert_eq!(
  169        mem::take(&mut *events.borrow_mut()),
  170        [
  171            ("editor2", "edited"),
  172            ("editor1", "buffer edited"),
  173            ("editor2", "buffer edited"),
  174        ]
  175    );
  176
  177    // No event is emitted when the mutation is a no-op.
  178    _ = editor2.update(cx, |editor, window, cx| {
  179        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  180
  181        editor.backspace(&Backspace, window, cx);
  182    });
  183    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  184}
  185
  186#[gpui::test]
  187fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  188    init_test(cx, |_| {});
  189
  190    let mut now = Instant::now();
  191    let group_interval = Duration::from_millis(1);
  192    let buffer = cx.new(|cx| {
  193        let mut buf = language::Buffer::local("123456", cx);
  194        buf.set_group_interval(group_interval);
  195        buf
  196    });
  197    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  198    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  199
  200    _ = editor.update(cx, |editor, window, cx| {
  201        editor.start_transaction_at(now, window, cx);
  202        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  203
  204        editor.insert("cd", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cd56");
  207        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  208
  209        editor.start_transaction_at(now, window, cx);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  211        editor.insert("e", window, cx);
  212        editor.end_transaction_at(now, cx);
  213        assert_eq!(editor.text(cx), "12cde6");
  214        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  215
  216        now += group_interval + Duration::from_millis(1);
  217        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  218
  219        // Simulate an edit in another editor
  220        buffer.update(cx, |buffer, cx| {
  221            buffer.start_transaction_at(now, cx);
  222            buffer.edit([(0..1, "a")], None, cx);
  223            buffer.edit([(1..1, "b")], None, cx);
  224            buffer.end_transaction_at(now, cx);
  225        });
  226
  227        assert_eq!(editor.text(cx), "ab2cde6");
  228        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  229
  230        // Last transaction happened past the group interval in a different editor.
  231        // Undo it individually and don't restore selections.
  232        editor.undo(&Undo, window, cx);
  233        assert_eq!(editor.text(cx), "12cde6");
  234        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  235
  236        // First two transactions happened within the group interval in this editor.
  237        // Undo them together and restore selections.
  238        editor.undo(&Undo, window, cx);
  239        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  240        assert_eq!(editor.text(cx), "123456");
  241        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  242
  243        // Redo the first two transactions together.
  244        editor.redo(&Redo, window, cx);
  245        assert_eq!(editor.text(cx), "12cde6");
  246        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  247
  248        // Redo the last transaction on its own.
  249        editor.redo(&Redo, window, cx);
  250        assert_eq!(editor.text(cx), "ab2cde6");
  251        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  252
  253        // Test empty transactions.
  254        editor.start_transaction_at(now, window, cx);
  255        editor.end_transaction_at(now, cx);
  256        editor.undo(&Undo, window, cx);
  257        assert_eq!(editor.text(cx), "12cde6");
  258    });
  259}
  260
  261#[gpui::test]
  262fn test_ime_composition(cx: &mut TestAppContext) {
  263    init_test(cx, |_| {});
  264
  265    let buffer = cx.new(|cx| {
  266        let mut buffer = language::Buffer::local("abcde", cx);
  267        // Ensure automatic grouping doesn't occur.
  268        buffer.set_group_interval(Duration::ZERO);
  269        buffer
  270    });
  271
  272    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  273    cx.add_window(|window, cx| {
  274        let mut editor = build_editor(buffer.clone(), window, cx);
  275
  276        // Start a new IME composition.
  277        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  279        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  280        assert_eq!(editor.text(cx), "äbcde");
  281        assert_eq!(
  282            editor.marked_text_ranges(cx),
  283            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  284        );
  285
  286        // Finalize IME composition.
  287        editor.replace_text_in_range(None, "ā", window, cx);
  288        assert_eq!(editor.text(cx), "ābcde");
  289        assert_eq!(editor.marked_text_ranges(cx), None);
  290
  291        // IME composition edits are grouped and are undone/redone at once.
  292        editor.undo(&Default::default(), window, cx);
  293        assert_eq!(editor.text(cx), "abcde");
  294        assert_eq!(editor.marked_text_ranges(cx), None);
  295        editor.redo(&Default::default(), window, cx);
  296        assert_eq!(editor.text(cx), "ābcde");
  297        assert_eq!(editor.marked_text_ranges(cx), None);
  298
  299        // Start a new IME composition.
  300        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  301        assert_eq!(
  302            editor.marked_text_ranges(cx),
  303            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  304        );
  305
  306        // Undoing during an IME composition cancels it.
  307        editor.undo(&Default::default(), window, cx);
  308        assert_eq!(editor.text(cx), "ābcde");
  309        assert_eq!(editor.marked_text_ranges(cx), None);
  310
  311        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  312        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  313        assert_eq!(editor.text(cx), "ābcdè");
  314        assert_eq!(
  315            editor.marked_text_ranges(cx),
  316            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  317        );
  318
  319        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  320        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  321        assert_eq!(editor.text(cx), "ābcdę");
  322        assert_eq!(editor.marked_text_ranges(cx), None);
  323
  324        // Start a new IME composition with multiple cursors.
  325        editor.change_selections(None, window, cx, |s| {
  326            s.select_ranges([
  327                OffsetUtf16(1)..OffsetUtf16(1),
  328                OffsetUtf16(3)..OffsetUtf16(3),
  329                OffsetUtf16(5)..OffsetUtf16(5),
  330            ])
  331        });
  332        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  333        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  334        assert_eq!(
  335            editor.marked_text_ranges(cx),
  336            Some(vec![
  337                OffsetUtf16(0)..OffsetUtf16(3),
  338                OffsetUtf16(4)..OffsetUtf16(7),
  339                OffsetUtf16(8)..OffsetUtf16(11)
  340            ])
  341        );
  342
  343        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  344        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  345        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  346        assert_eq!(
  347            editor.marked_text_ranges(cx),
  348            Some(vec![
  349                OffsetUtf16(1)..OffsetUtf16(2),
  350                OffsetUtf16(5)..OffsetUtf16(6),
  351                OffsetUtf16(9)..OffsetUtf16(10)
  352            ])
  353        );
  354
  355        // Finalize IME composition with multiple cursors.
  356        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  357        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  358        assert_eq!(editor.marked_text_ranges(cx), None);
  359
  360        editor
  361    });
  362}
  363
  364#[gpui::test]
  365fn test_selection_with_mouse(cx: &mut TestAppContext) {
  366    init_test(cx, |_| {});
  367
  368    let editor = cx.add_window(|window, cx| {
  369        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  370        build_editor(buffer, window, cx)
  371    });
  372
  373    _ = editor.update(cx, |editor, window, cx| {
  374        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  375    });
  376    assert_eq!(
  377        editor
  378            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  379            .unwrap(),
  380        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  381    );
  382
  383    _ = editor.update(cx, |editor, window, cx| {
  384        editor.update_selection(
  385            DisplayPoint::new(DisplayRow(3), 3),
  386            0,
  387            gpui::Point::<f32>::default(),
  388            window,
  389            cx,
  390        );
  391    });
  392
  393    assert_eq!(
  394        editor
  395            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  396            .unwrap(),
  397        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  398    );
  399
  400    _ = editor.update(cx, |editor, window, cx| {
  401        editor.update_selection(
  402            DisplayPoint::new(DisplayRow(1), 1),
  403            0,
  404            gpui::Point::<f32>::default(),
  405            window,
  406            cx,
  407        );
  408    });
  409
  410    assert_eq!(
  411        editor
  412            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  413            .unwrap(),
  414        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  415    );
  416
  417    _ = editor.update(cx, |editor, window, cx| {
  418        editor.end_selection(window, cx);
  419        editor.update_selection(
  420            DisplayPoint::new(DisplayRow(3), 3),
  421            0,
  422            gpui::Point::<f32>::default(),
  423            window,
  424            cx,
  425        );
  426    });
  427
  428    assert_eq!(
  429        editor
  430            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  431            .unwrap(),
  432        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  433    );
  434
  435    _ = editor.update(cx, |editor, window, cx| {
  436        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  437        editor.update_selection(
  438            DisplayPoint::new(DisplayRow(0), 0),
  439            0,
  440            gpui::Point::<f32>::default(),
  441            window,
  442            cx,
  443        );
  444    });
  445
  446    assert_eq!(
  447        editor
  448            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  449            .unwrap(),
  450        [
  451            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  452            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  453        ]
  454    );
  455
  456    _ = editor.update(cx, |editor, window, cx| {
  457        editor.end_selection(window, cx);
  458    });
  459
  460    assert_eq!(
  461        editor
  462            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  463            .unwrap(),
  464        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  465    );
  466}
  467
  468#[gpui::test]
  469fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  470    init_test(cx, |_| {});
  471
  472    let editor = cx.add_window(|window, cx| {
  473        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  474        build_editor(buffer, window, cx)
  475    });
  476
  477    _ = editor.update(cx, |editor, window, cx| {
  478        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  479    });
  480
  481    _ = editor.update(cx, |editor, window, cx| {
  482        editor.end_selection(window, cx);
  483    });
  484
  485    _ = editor.update(cx, |editor, window, cx| {
  486        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  487    });
  488
  489    _ = editor.update(cx, |editor, window, cx| {
  490        editor.end_selection(window, cx);
  491    });
  492
  493    assert_eq!(
  494        editor
  495            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  496            .unwrap(),
  497        [
  498            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  499            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  500        ]
  501    );
  502
  503    _ = editor.update(cx, |editor, window, cx| {
  504        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  505    });
  506
  507    _ = editor.update(cx, |editor, window, cx| {
  508        editor.end_selection(window, cx);
  509    });
  510
  511    assert_eq!(
  512        editor
  513            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  514            .unwrap(),
  515        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  516    );
  517}
  518
  519#[gpui::test]
  520fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  521    init_test(cx, |_| {});
  522
  523    let editor = cx.add_window(|window, cx| {
  524        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  525        build_editor(buffer, window, cx)
  526    });
  527
  528    _ = editor.update(cx, |editor, window, cx| {
  529        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  530        assert_eq!(
  531            editor.selections.display_ranges(cx),
  532            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  533        );
  534    });
  535
  536    _ = editor.update(cx, |editor, window, cx| {
  537        editor.update_selection(
  538            DisplayPoint::new(DisplayRow(3), 3),
  539            0,
  540            gpui::Point::<f32>::default(),
  541            window,
  542            cx,
  543        );
  544        assert_eq!(
  545            editor.selections.display_ranges(cx),
  546            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  547        );
  548    });
  549
  550    _ = editor.update(cx, |editor, window, cx| {
  551        editor.cancel(&Cancel, window, cx);
  552        editor.update_selection(
  553            DisplayPoint::new(DisplayRow(1), 1),
  554            0,
  555            gpui::Point::<f32>::default(),
  556            window,
  557            cx,
  558        );
  559        assert_eq!(
  560            editor.selections.display_ranges(cx),
  561            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  562        );
  563    });
  564}
  565
  566#[gpui::test]
  567fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  568    init_test(cx, |_| {});
  569
  570    let editor = cx.add_window(|window, cx| {
  571        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  572        build_editor(buffer, window, cx)
  573    });
  574
  575    _ = editor.update(cx, |editor, window, cx| {
  576        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  577        assert_eq!(
  578            editor.selections.display_ranges(cx),
  579            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  580        );
  581
  582        editor.move_down(&Default::default(), window, cx);
  583        assert_eq!(
  584            editor.selections.display_ranges(cx),
  585            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  586        );
  587
  588        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  589        assert_eq!(
  590            editor.selections.display_ranges(cx),
  591            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  592        );
  593
  594        editor.move_up(&Default::default(), window, cx);
  595        assert_eq!(
  596            editor.selections.display_ranges(cx),
  597            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  598        );
  599    });
  600}
  601
  602#[gpui::test]
  603fn test_clone(cx: &mut TestAppContext) {
  604    init_test(cx, |_| {});
  605
  606    let (text, selection_ranges) = marked_text_ranges(
  607        indoc! {"
  608            one
  609            two
  610            threeˇ
  611            four
  612            fiveˇ
  613        "},
  614        true,
  615    );
  616
  617    let editor = cx.add_window(|window, cx| {
  618        let buffer = MultiBuffer::build_simple(&text, cx);
  619        build_editor(buffer, window, cx)
  620    });
  621
  622    _ = editor.update(cx, |editor, window, cx| {
  623        editor.change_selections(None, window, cx, |s| {
  624            s.select_ranges(selection_ranges.clone())
  625        });
  626        editor.fold_creases(
  627            vec![
  628                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  629                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  630            ],
  631            true,
  632            window,
  633            cx,
  634        );
  635    });
  636
  637    let cloned_editor = editor
  638        .update(cx, |editor, _, cx| {
  639            cx.open_window(Default::default(), |window, cx| {
  640                cx.new(|cx| editor.clone(window, cx))
  641            })
  642        })
  643        .unwrap()
  644        .unwrap();
  645
  646    let snapshot = editor
  647        .update(cx, |e, window, cx| e.snapshot(window, cx))
  648        .unwrap();
  649    let cloned_snapshot = cloned_editor
  650        .update(cx, |e, window, cx| e.snapshot(window, cx))
  651        .unwrap();
  652
  653    assert_eq!(
  654        cloned_editor
  655            .update(cx, |e, _, cx| e.display_text(cx))
  656            .unwrap(),
  657        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  658    );
  659    assert_eq!(
  660        cloned_snapshot
  661            .folds_in_range(0..text.len())
  662            .collect::<Vec<_>>(),
  663        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  664    );
  665    assert_set_eq!(
  666        cloned_editor
  667            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  668            .unwrap(),
  669        editor
  670            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  671            .unwrap()
  672    );
  673    assert_set_eq!(
  674        cloned_editor
  675            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  676            .unwrap(),
  677        editor
  678            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  679            .unwrap()
  680    );
  681}
  682
  683#[gpui::test]
  684async fn test_navigation_history(cx: &mut TestAppContext) {
  685    init_test(cx, |_| {});
  686
  687    use workspace::item::Item;
  688
  689    let fs = FakeFs::new(cx.executor());
  690    let project = Project::test(fs, [], cx).await;
  691    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  692    let pane = workspace
  693        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  694        .unwrap();
  695
  696    _ = workspace.update(cx, |_v, window, cx| {
  697        cx.new(|cx| {
  698            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  699            let mut editor = build_editor(buffer.clone(), window, cx);
  700            let handle = cx.entity();
  701            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  702
  703            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  704                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  705            }
  706
  707            // Move the cursor a small distance.
  708            // Nothing is added to the navigation history.
  709            editor.change_selections(None, window, cx, |s| {
  710                s.select_display_ranges([
  711                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  712                ])
  713            });
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  717                ])
  718            });
  719            assert!(pop_history(&mut editor, cx).is_none());
  720
  721            // Move the cursor a large distance.
  722            // The history can jump back to the previous position.
  723            editor.change_selections(None, window, cx, |s| {
  724                s.select_display_ranges([
  725                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  726                ])
  727            });
  728            let nav_entry = pop_history(&mut editor, cx).unwrap();
  729            editor.navigate(nav_entry.data.unwrap(), window, cx);
  730            assert_eq!(nav_entry.item.id(), cx.entity_id());
  731            assert_eq!(
  732                editor.selections.display_ranges(cx),
  733                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  734            );
  735            assert!(pop_history(&mut editor, cx).is_none());
  736
  737            // Move the cursor a small distance via the mouse.
  738            // Nothing is added to the navigation history.
  739            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  740            editor.end_selection(window, cx);
  741            assert_eq!(
  742                editor.selections.display_ranges(cx),
  743                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  744            );
  745            assert!(pop_history(&mut editor, cx).is_none());
  746
  747            // Move the cursor a large distance via the mouse.
  748            // The history can jump back to the previous position.
  749            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  750            editor.end_selection(window, cx);
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  754            );
  755            let nav_entry = pop_history(&mut editor, cx).unwrap();
  756            editor.navigate(nav_entry.data.unwrap(), window, cx);
  757            assert_eq!(nav_entry.item.id(), cx.entity_id());
  758            assert_eq!(
  759                editor.selections.display_ranges(cx),
  760                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  761            );
  762            assert!(pop_history(&mut editor, cx).is_none());
  763
  764            // Set scroll position to check later
  765            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  766            let original_scroll_position = editor.scroll_manager.anchor();
  767
  768            // Jump to the end of the document and adjust scroll
  769            editor.move_to_end(&MoveToEnd, window, cx);
  770            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  771            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  772
  773            let nav_entry = pop_history(&mut editor, cx).unwrap();
  774            editor.navigate(nav_entry.data.unwrap(), window, cx);
  775            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  776
  777            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  778            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  779            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  780            let invalid_point = Point::new(9999, 0);
  781            editor.navigate(
  782                Box::new(NavigationData {
  783                    cursor_anchor: invalid_anchor,
  784                    cursor_position: invalid_point,
  785                    scroll_anchor: ScrollAnchor {
  786                        anchor: invalid_anchor,
  787                        offset: Default::default(),
  788                    },
  789                    scroll_top_row: invalid_point.row,
  790                }),
  791                window,
  792                cx,
  793            );
  794            assert_eq!(
  795                editor.selections.display_ranges(cx),
  796                &[editor.max_point(cx)..editor.max_point(cx)]
  797            );
  798            assert_eq!(
  799                editor.scroll_position(cx),
  800                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  801            );
  802
  803            editor
  804        })
  805    });
  806}
  807
  808#[gpui::test]
  809fn test_cancel(cx: &mut TestAppContext) {
  810    init_test(cx, |_| {});
  811
  812    let editor = cx.add_window(|window, cx| {
  813        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  814        build_editor(buffer, window, cx)
  815    });
  816
  817    _ = editor.update(cx, |editor, window, cx| {
  818        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  819        editor.update_selection(
  820            DisplayPoint::new(DisplayRow(1), 1),
  821            0,
  822            gpui::Point::<f32>::default(),
  823            window,
  824            cx,
  825        );
  826        editor.end_selection(window, cx);
  827
  828        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  829        editor.update_selection(
  830            DisplayPoint::new(DisplayRow(0), 3),
  831            0,
  832            gpui::Point::<f32>::default(),
  833            window,
  834            cx,
  835        );
  836        editor.end_selection(window, cx);
  837        assert_eq!(
  838            editor.selections.display_ranges(cx),
  839            [
  840                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  841                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  842            ]
  843        );
  844    });
  845
  846    _ = editor.update(cx, |editor, window, cx| {
  847        editor.cancel(&Cancel, window, cx);
  848        assert_eq!(
  849            editor.selections.display_ranges(cx),
  850            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  851        );
  852    });
  853
  854    _ = editor.update(cx, |editor, window, cx| {
  855        editor.cancel(&Cancel, window, cx);
  856        assert_eq!(
  857            editor.selections.display_ranges(cx),
  858            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  859        );
  860    });
  861}
  862
  863#[gpui::test]
  864fn test_fold_action(cx: &mut TestAppContext) {
  865    init_test(cx, |_| {});
  866
  867    let editor = cx.add_window(|window, cx| {
  868        let buffer = MultiBuffer::build_simple(
  869            &"
  870                impl Foo {
  871                    // Hello!
  872
  873                    fn a() {
  874                        1
  875                    }
  876
  877                    fn b() {
  878                        2
  879                    }
  880
  881                    fn c() {
  882                        3
  883                    }
  884                }
  885            "
  886            .unindent(),
  887            cx,
  888        );
  889        build_editor(buffer.clone(), window, cx)
  890    });
  891
  892    _ = editor.update(cx, |editor, window, cx| {
  893        editor.change_selections(None, window, cx, |s| {
  894            s.select_display_ranges([
  895                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  896            ]);
  897        });
  898        editor.fold(&Fold, window, cx);
  899        assert_eq!(
  900            editor.display_text(cx),
  901            "
  902                impl Foo {
  903                    // Hello!
  904
  905                    fn a() {
  906                        1
  907                    }
  908
  909                    fn b() {⋯
  910                    }
  911
  912                    fn c() {⋯
  913                    }
  914                }
  915            "
  916            .unindent(),
  917        );
  918
  919        editor.fold(&Fold, window, cx);
  920        assert_eq!(
  921            editor.display_text(cx),
  922            "
  923                impl Foo {⋯
  924                }
  925            "
  926            .unindent(),
  927        );
  928
  929        editor.unfold_lines(&UnfoldLines, window, cx);
  930        assert_eq!(
  931            editor.display_text(cx),
  932            "
  933                impl Foo {
  934                    // Hello!
  935
  936                    fn a() {
  937                        1
  938                    }
  939
  940                    fn b() {⋯
  941                    }
  942
  943                    fn c() {⋯
  944                    }
  945                }
  946            "
  947            .unindent(),
  948        );
  949
  950        editor.unfold_lines(&UnfoldLines, window, cx);
  951        assert_eq!(
  952            editor.display_text(cx),
  953            editor.buffer.read(cx).read(cx).text()
  954        );
  955    });
  956}
  957
  958#[gpui::test]
  959fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  960    init_test(cx, |_| {});
  961
  962    let editor = cx.add_window(|window, cx| {
  963        let buffer = MultiBuffer::build_simple(
  964            &"
  965                class Foo:
  966                    # Hello!
  967
  968                    def a():
  969                        print(1)
  970
  971                    def b():
  972                        print(2)
  973
  974                    def c():
  975                        print(3)
  976            "
  977            .unindent(),
  978            cx,
  979        );
  980        build_editor(buffer.clone(), window, cx)
  981    });
  982
  983    _ = editor.update(cx, |editor, window, cx| {
  984        editor.change_selections(None, window, cx, |s| {
  985            s.select_display_ranges([
  986                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  987            ]);
  988        });
  989        editor.fold(&Fold, window, cx);
  990        assert_eq!(
  991            editor.display_text(cx),
  992            "
  993                class Foo:
  994                    # Hello!
  995
  996                    def a():
  997                        print(1)
  998
  999                    def b():⋯
 1000
 1001                    def c():⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.fold(&Fold, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:⋯
 1011            "
 1012            .unindent(),
 1013        );
 1014
 1015        editor.unfold_lines(&UnfoldLines, window, cx);
 1016        assert_eq!(
 1017            editor.display_text(cx),
 1018            "
 1019                class Foo:
 1020                    # Hello!
 1021
 1022                    def a():
 1023                        print(1)
 1024
 1025                    def b():⋯
 1026
 1027                    def c():⋯
 1028            "
 1029            .unindent(),
 1030        );
 1031
 1032        editor.unfold_lines(&UnfoldLines, window, cx);
 1033        assert_eq!(
 1034            editor.display_text(cx),
 1035            editor.buffer.read(cx).read(cx).text()
 1036        );
 1037    });
 1038}
 1039
 1040#[gpui::test]
 1041fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1042    init_test(cx, |_| {});
 1043
 1044    let editor = cx.add_window(|window, cx| {
 1045        let buffer = MultiBuffer::build_simple(
 1046            &"
 1047                class Foo:
 1048                    # Hello!
 1049
 1050                    def a():
 1051                        print(1)
 1052
 1053                    def b():
 1054                        print(2)
 1055
 1056
 1057                    def c():
 1058                        print(3)
 1059
 1060
 1061            "
 1062            .unindent(),
 1063            cx,
 1064        );
 1065        build_editor(buffer.clone(), window, cx)
 1066    });
 1067
 1068    _ = editor.update(cx, |editor, window, cx| {
 1069        editor.change_selections(None, window, cx, |s| {
 1070            s.select_display_ranges([
 1071                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1072            ]);
 1073        });
 1074        editor.fold(&Fold, window, cx);
 1075        assert_eq!(
 1076            editor.display_text(cx),
 1077            "
 1078                class Foo:
 1079                    # Hello!
 1080
 1081                    def a():
 1082                        print(1)
 1083
 1084                    def b():⋯
 1085
 1086
 1087                    def c():⋯
 1088
 1089
 1090            "
 1091            .unindent(),
 1092        );
 1093
 1094        editor.fold(&Fold, window, cx);
 1095        assert_eq!(
 1096            editor.display_text(cx),
 1097            "
 1098                class Foo:⋯
 1099
 1100
 1101            "
 1102            .unindent(),
 1103        );
 1104
 1105        editor.unfold_lines(&UnfoldLines, window, cx);
 1106        assert_eq!(
 1107            editor.display_text(cx),
 1108            "
 1109                class Foo:
 1110                    # Hello!
 1111
 1112                    def a():
 1113                        print(1)
 1114
 1115                    def b():⋯
 1116
 1117
 1118                    def c():⋯
 1119
 1120
 1121            "
 1122            .unindent(),
 1123        );
 1124
 1125        editor.unfold_lines(&UnfoldLines, window, cx);
 1126        assert_eq!(
 1127            editor.display_text(cx),
 1128            editor.buffer.read(cx).read(cx).text()
 1129        );
 1130    });
 1131}
 1132
 1133#[gpui::test]
 1134fn test_fold_at_level(cx: &mut TestAppContext) {
 1135    init_test(cx, |_| {});
 1136
 1137    let editor = cx.add_window(|window, cx| {
 1138        let buffer = MultiBuffer::build_simple(
 1139            &"
 1140                class Foo:
 1141                    # Hello!
 1142
 1143                    def a():
 1144                        print(1)
 1145
 1146                    def b():
 1147                        print(2)
 1148
 1149
 1150                class Bar:
 1151                    # World!
 1152
 1153                    def a():
 1154                        print(1)
 1155
 1156                    def b():
 1157                        print(2)
 1158
 1159
 1160            "
 1161            .unindent(),
 1162            cx,
 1163        );
 1164        build_editor(buffer.clone(), window, cx)
 1165    });
 1166
 1167    _ = editor.update(cx, |editor, window, cx| {
 1168        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1169        assert_eq!(
 1170            editor.display_text(cx),
 1171            "
 1172                class Foo:
 1173                    # Hello!
 1174
 1175                    def a():⋯
 1176
 1177                    def b():⋯
 1178
 1179
 1180                class Bar:
 1181                    # World!
 1182
 1183                    def a():⋯
 1184
 1185                    def b():⋯
 1186
 1187
 1188            "
 1189            .unindent(),
 1190        );
 1191
 1192        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1193        assert_eq!(
 1194            editor.display_text(cx),
 1195            "
 1196                class Foo:⋯
 1197
 1198
 1199                class Bar:⋯
 1200
 1201
 1202            "
 1203            .unindent(),
 1204        );
 1205
 1206        editor.unfold_all(&UnfoldAll, window, cx);
 1207        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1208        assert_eq!(
 1209            editor.display_text(cx),
 1210            "
 1211                class Foo:
 1212                    # Hello!
 1213
 1214                    def a():
 1215                        print(1)
 1216
 1217                    def b():
 1218                        print(2)
 1219
 1220
 1221                class Bar:
 1222                    # World!
 1223
 1224                    def a():
 1225                        print(1)
 1226
 1227                    def b():
 1228                        print(2)
 1229
 1230
 1231            "
 1232            .unindent(),
 1233        );
 1234
 1235        assert_eq!(
 1236            editor.display_text(cx),
 1237            editor.buffer.read(cx).read(cx).text()
 1238        );
 1239    });
 1240}
 1241
 1242#[gpui::test]
 1243fn test_move_cursor(cx: &mut TestAppContext) {
 1244    init_test(cx, |_| {});
 1245
 1246    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1247    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1248
 1249    buffer.update(cx, |buffer, cx| {
 1250        buffer.edit(
 1251            vec![
 1252                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1253                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1254            ],
 1255            None,
 1256            cx,
 1257        );
 1258    });
 1259    _ = editor.update(cx, |editor, window, cx| {
 1260        assert_eq!(
 1261            editor.selections.display_ranges(cx),
 1262            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1263        );
 1264
 1265        editor.move_down(&MoveDown, window, cx);
 1266        assert_eq!(
 1267            editor.selections.display_ranges(cx),
 1268            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1269        );
 1270
 1271        editor.move_right(&MoveRight, window, cx);
 1272        assert_eq!(
 1273            editor.selections.display_ranges(cx),
 1274            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1275        );
 1276
 1277        editor.move_left(&MoveLeft, window, cx);
 1278        assert_eq!(
 1279            editor.selections.display_ranges(cx),
 1280            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1281        );
 1282
 1283        editor.move_up(&MoveUp, window, cx);
 1284        assert_eq!(
 1285            editor.selections.display_ranges(cx),
 1286            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1287        );
 1288
 1289        editor.move_to_end(&MoveToEnd, window, cx);
 1290        assert_eq!(
 1291            editor.selections.display_ranges(cx),
 1292            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1293        );
 1294
 1295        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1296        assert_eq!(
 1297            editor.selections.display_ranges(cx),
 1298            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1299        );
 1300
 1301        editor.change_selections(None, window, cx, |s| {
 1302            s.select_display_ranges([
 1303                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1304            ]);
 1305        });
 1306        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1307        assert_eq!(
 1308            editor.selections.display_ranges(cx),
 1309            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1310        );
 1311
 1312        editor.select_to_end(&SelectToEnd, window, cx);
 1313        assert_eq!(
 1314            editor.selections.display_ranges(cx),
 1315            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1316        );
 1317    });
 1318}
 1319
 1320#[gpui::test]
 1321fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1322    init_test(cx, |_| {});
 1323
 1324    let editor = cx.add_window(|window, cx| {
 1325        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1326        build_editor(buffer.clone(), window, cx)
 1327    });
 1328
 1329    assert_eq!('🟥'.len_utf8(), 4);
 1330    assert_eq!('α'.len_utf8(), 2);
 1331
 1332    _ = editor.update(cx, |editor, window, cx| {
 1333        editor.fold_creases(
 1334            vec![
 1335                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1338            ],
 1339            true,
 1340            window,
 1341            cx,
 1342        );
 1343        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1344
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧".len())]
 1354        );
 1355        editor.move_right(&MoveRight, window, cx);
 1356        assert_eq!(
 1357            editor.selections.display_ranges(cx),
 1358            &[empty_range(0, "🟥🟧⋯".len())]
 1359        );
 1360
 1361        editor.move_down(&MoveDown, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯e".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab⋯".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "ab".len())]
 1375        );
 1376        editor.move_left(&MoveLeft, window, cx);
 1377        assert_eq!(
 1378            editor.selections.display_ranges(cx),
 1379            &[empty_range(1, "a".len())]
 1380        );
 1381
 1382        editor.move_down(&MoveDown, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "α".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯".len())]
 1396        );
 1397        editor.move_right(&MoveRight, window, cx);
 1398        assert_eq!(
 1399            editor.selections.display_ranges(cx),
 1400            &[empty_range(2, "αβ⋯ε".len())]
 1401        );
 1402
 1403        editor.move_up(&MoveUp, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(1, "ab⋯e".len())]
 1407        );
 1408        editor.move_down(&MoveDown, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(2, "αβ⋯ε".len())]
 1412        );
 1413        editor.move_up(&MoveUp, window, cx);
 1414        assert_eq!(
 1415            editor.selections.display_ranges(cx),
 1416            &[empty_range(1, "ab⋯e".len())]
 1417        );
 1418
 1419        editor.move_up(&MoveUp, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥🟧".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "🟥".len())]
 1428        );
 1429        editor.move_left(&MoveLeft, window, cx);
 1430        assert_eq!(
 1431            editor.selections.display_ranges(cx),
 1432            &[empty_range(0, "".len())]
 1433        );
 1434    });
 1435}
 1436
 1437#[gpui::test]
 1438fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1439    init_test(cx, |_| {});
 1440
 1441    let editor = cx.add_window(|window, cx| {
 1442        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1443        build_editor(buffer.clone(), window, cx)
 1444    });
 1445    _ = editor.update(cx, |editor, window, cx| {
 1446        editor.change_selections(None, window, cx, |s| {
 1447            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1448        });
 1449
 1450        // moving above start of document should move selection to start of document,
 1451        // but the next move down should still be at the original goal_x
 1452        editor.move_up(&MoveUp, window, cx);
 1453        assert_eq!(
 1454            editor.selections.display_ranges(cx),
 1455            &[empty_range(0, "".len())]
 1456        );
 1457
 1458        editor.move_down(&MoveDown, window, cx);
 1459        assert_eq!(
 1460            editor.selections.display_ranges(cx),
 1461            &[empty_range(1, "abcd".len())]
 1462        );
 1463
 1464        editor.move_down(&MoveDown, window, cx);
 1465        assert_eq!(
 1466            editor.selections.display_ranges(cx),
 1467            &[empty_range(2, "αβγ".len())]
 1468        );
 1469
 1470        editor.move_down(&MoveDown, window, cx);
 1471        assert_eq!(
 1472            editor.selections.display_ranges(cx),
 1473            &[empty_range(3, "abcd".len())]
 1474        );
 1475
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1480        );
 1481
 1482        // moving past end of document should not change goal_x
 1483        editor.move_down(&MoveDown, window, cx);
 1484        assert_eq!(
 1485            editor.selections.display_ranges(cx),
 1486            &[empty_range(5, "".len())]
 1487        );
 1488
 1489        editor.move_down(&MoveDown, window, cx);
 1490        assert_eq!(
 1491            editor.selections.display_ranges(cx),
 1492            &[empty_range(5, "".len())]
 1493        );
 1494
 1495        editor.move_up(&MoveUp, window, cx);
 1496        assert_eq!(
 1497            editor.selections.display_ranges(cx),
 1498            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1499        );
 1500
 1501        editor.move_up(&MoveUp, window, cx);
 1502        assert_eq!(
 1503            editor.selections.display_ranges(cx),
 1504            &[empty_range(3, "abcd".len())]
 1505        );
 1506
 1507        editor.move_up(&MoveUp, window, cx);
 1508        assert_eq!(
 1509            editor.selections.display_ranges(cx),
 1510            &[empty_range(2, "αβγ".len())]
 1511        );
 1512    });
 1513}
 1514
 1515#[gpui::test]
 1516fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1517    init_test(cx, |_| {});
 1518    let move_to_beg = MoveToBeginningOfLine {
 1519        stop_at_soft_wraps: true,
 1520        stop_at_indent: true,
 1521    };
 1522
 1523    let delete_to_beg = DeleteToBeginningOfLine {
 1524        stop_at_indent: false,
 1525    };
 1526
 1527    let move_to_end = MoveToEndOfLine {
 1528        stop_at_soft_wraps: true,
 1529    };
 1530
 1531    let editor = cx.add_window(|window, cx| {
 1532        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1533        build_editor(buffer, window, cx)
 1534    });
 1535    _ = editor.update(cx, |editor, window, cx| {
 1536        editor.change_selections(None, window, cx, |s| {
 1537            s.select_display_ranges([
 1538                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1539                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1540            ]);
 1541        });
 1542    });
 1543
 1544    _ = editor.update(cx, |editor, window, cx| {
 1545        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1546        assert_eq!(
 1547            editor.selections.display_ranges(cx),
 1548            &[
 1549                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1550                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1551            ]
 1552        );
 1553    });
 1554
 1555    _ = editor.update(cx, |editor, window, cx| {
 1556        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1557        assert_eq!(
 1558            editor.selections.display_ranges(cx),
 1559            &[
 1560                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1561                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1562            ]
 1563        );
 1564    });
 1565
 1566    _ = editor.update(cx, |editor, window, cx| {
 1567        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1568        assert_eq!(
 1569            editor.selections.display_ranges(cx),
 1570            &[
 1571                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1572                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1573            ]
 1574        );
 1575    });
 1576
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    // Moving to the end of line again is a no-op.
 1589    _ = editor.update(cx, |editor, window, cx| {
 1590        editor.move_to_end_of_line(&move_to_end, window, cx);
 1591        assert_eq!(
 1592            editor.selections.display_ranges(cx),
 1593            &[
 1594                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1595                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1596            ]
 1597        );
 1598    });
 1599
 1600    _ = editor.update(cx, |editor, window, cx| {
 1601        editor.move_left(&MoveLeft, window, cx);
 1602        editor.select_to_beginning_of_line(
 1603            &SelectToBeginningOfLine {
 1604                stop_at_soft_wraps: true,
 1605                stop_at_indent: true,
 1606            },
 1607            window,
 1608            cx,
 1609        );
 1610        assert_eq!(
 1611            editor.selections.display_ranges(cx),
 1612            &[
 1613                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1614                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1615            ]
 1616        );
 1617    });
 1618
 1619    _ = editor.update(cx, |editor, window, cx| {
 1620        editor.select_to_beginning_of_line(
 1621            &SelectToBeginningOfLine {
 1622                stop_at_soft_wraps: true,
 1623                stop_at_indent: true,
 1624            },
 1625            window,
 1626            cx,
 1627        );
 1628        assert_eq!(
 1629            editor.selections.display_ranges(cx),
 1630            &[
 1631                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1632                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1633            ]
 1634        );
 1635    });
 1636
 1637    _ = editor.update(cx, |editor, window, cx| {
 1638        editor.select_to_beginning_of_line(
 1639            &SelectToBeginningOfLine {
 1640                stop_at_soft_wraps: true,
 1641                stop_at_indent: true,
 1642            },
 1643            window,
 1644            cx,
 1645        );
 1646        assert_eq!(
 1647            editor.selections.display_ranges(cx),
 1648            &[
 1649                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1650                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1651            ]
 1652        );
 1653    });
 1654
 1655    _ = editor.update(cx, |editor, window, cx| {
 1656        editor.select_to_end_of_line(
 1657            &SelectToEndOfLine {
 1658                stop_at_soft_wraps: true,
 1659            },
 1660            window,
 1661            cx,
 1662        );
 1663        assert_eq!(
 1664            editor.selections.display_ranges(cx),
 1665            &[
 1666                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1667                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1668            ]
 1669        );
 1670    });
 1671
 1672    _ = editor.update(cx, |editor, window, cx| {
 1673        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1674        assert_eq!(editor.display_text(cx), "ab\n  de");
 1675        assert_eq!(
 1676            editor.selections.display_ranges(cx),
 1677            &[
 1678                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1679                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1680            ]
 1681        );
 1682    });
 1683
 1684    _ = editor.update(cx, |editor, window, cx| {
 1685        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1686        assert_eq!(editor.display_text(cx), "\n");
 1687        assert_eq!(
 1688            editor.selections.display_ranges(cx),
 1689            &[
 1690                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1691                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1692            ]
 1693        );
 1694    });
 1695}
 1696
 1697#[gpui::test]
 1698fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1699    init_test(cx, |_| {});
 1700    let move_to_beg = MoveToBeginningOfLine {
 1701        stop_at_soft_wraps: false,
 1702        stop_at_indent: false,
 1703    };
 1704
 1705    let move_to_end = MoveToEndOfLine {
 1706        stop_at_soft_wraps: false,
 1707    };
 1708
 1709    let editor = cx.add_window(|window, cx| {
 1710        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1711        build_editor(buffer, window, cx)
 1712    });
 1713
 1714    _ = editor.update(cx, |editor, window, cx| {
 1715        editor.set_wrap_width(Some(140.0.into()), cx);
 1716
 1717        // We expect the following lines after wrapping
 1718        // ```
 1719        // thequickbrownfox
 1720        // jumpedoverthelazydo
 1721        // gs
 1722        // ```
 1723        // The final `gs` was soft-wrapped onto a new line.
 1724        assert_eq!(
 1725            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1726            editor.display_text(cx),
 1727        );
 1728
 1729        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1730        // Start the cursor at the `k` on the first line
 1731        editor.change_selections(None, window, cx, |s| {
 1732            s.select_display_ranges([
 1733                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1734            ]);
 1735        });
 1736
 1737        // Moving to the beginning of the line should put us at the beginning of the line.
 1738        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1739        assert_eq!(
 1740            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1741            editor.selections.display_ranges(cx)
 1742        );
 1743
 1744        // Moving to the end of the line should put us at the end of the line.
 1745        editor.move_to_end_of_line(&move_to_end, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1752        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1753        editor.change_selections(None, window, cx, |s| {
 1754            s.select_display_ranges([
 1755                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1756            ]);
 1757        });
 1758
 1759        // Moving to the beginning of the line should put us at the start of the second line of
 1760        // display text, i.e., the `j`.
 1761        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1762        assert_eq!(
 1763            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1764            editor.selections.display_ranges(cx)
 1765        );
 1766
 1767        // Moving to the beginning of the line again should be a no-op.
 1768        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1769        assert_eq!(
 1770            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1771            editor.selections.display_ranges(cx)
 1772        );
 1773
 1774        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1775        // next display line.
 1776        editor.move_to_end_of_line(&move_to_end, window, cx);
 1777        assert_eq!(
 1778            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1779            editor.selections.display_ranges(cx)
 1780        );
 1781
 1782        // Moving to the end of the line again should be a no-op.
 1783        editor.move_to_end_of_line(&move_to_end, window, cx);
 1784        assert_eq!(
 1785            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1786            editor.selections.display_ranges(cx)
 1787        );
 1788    });
 1789}
 1790
 1791#[gpui::test]
 1792fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1793    init_test(cx, |_| {});
 1794
 1795    let move_to_beg = MoveToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let select_to_beg = SelectToBeginningOfLine {
 1801        stop_at_soft_wraps: true,
 1802        stop_at_indent: true,
 1803    };
 1804
 1805    let delete_to_beg = DeleteToBeginningOfLine {
 1806        stop_at_indent: true,
 1807    };
 1808
 1809    let move_to_end = MoveToEndOfLine {
 1810        stop_at_soft_wraps: false,
 1811    };
 1812
 1813    let editor = cx.add_window(|window, cx| {
 1814        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1815        build_editor(buffer, window, cx)
 1816    });
 1817
 1818    _ = editor.update(cx, |editor, window, cx| {
 1819        editor.change_selections(None, window, cx, |s| {
 1820            s.select_display_ranges([
 1821                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1822                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1823            ]);
 1824        });
 1825
 1826        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1827        // and the second cursor at the first non-whitespace character in the line.
 1828        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1829        assert_eq!(
 1830            editor.selections.display_ranges(cx),
 1831            &[
 1832                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1833                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1834            ]
 1835        );
 1836
 1837        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1838        // and should move the second cursor to the beginning of the line.
 1839        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1840        assert_eq!(
 1841            editor.selections.display_ranges(cx),
 1842            &[
 1843                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1844                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1845            ]
 1846        );
 1847
 1848        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1849        // and should move the second cursor back to the first non-whitespace character in the line.
 1850        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1851        assert_eq!(
 1852            editor.selections.display_ranges(cx),
 1853            &[
 1854                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1855                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1856            ]
 1857        );
 1858
 1859        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1860        // and to the first non-whitespace character in the line for the second cursor.
 1861        editor.move_to_end_of_line(&move_to_end, window, cx);
 1862        editor.move_left(&MoveLeft, window, cx);
 1863        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1864        assert_eq!(
 1865            editor.selections.display_ranges(cx),
 1866            &[
 1867                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1868                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1869            ]
 1870        );
 1871
 1872        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1873        // and should select to the beginning of the line for the second cursor.
 1874        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1875        assert_eq!(
 1876            editor.selections.display_ranges(cx),
 1877            &[
 1878                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1879                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1880            ]
 1881        );
 1882
 1883        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1884        // and should delete to the first non-whitespace character in the line for the second cursor.
 1885        editor.move_to_end_of_line(&move_to_end, window, cx);
 1886        editor.move_left(&MoveLeft, window, cx);
 1887        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1888        assert_eq!(editor.text(cx), "c\n  f");
 1889    });
 1890}
 1891
 1892#[gpui::test]
 1893fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1894    init_test(cx, |_| {});
 1895
 1896    let editor = cx.add_window(|window, cx| {
 1897        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1898        build_editor(buffer, window, cx)
 1899    });
 1900    _ = editor.update(cx, |editor, window, cx| {
 1901        editor.change_selections(None, window, cx, |s| {
 1902            s.select_display_ranges([
 1903                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1904                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1905            ])
 1906        });
 1907
 1908        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1909        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1910
 1911        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1912        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1913
 1914        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1915        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1916
 1917        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1918        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1919
 1920        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1921        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1922
 1923        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1924        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1925
 1926        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1927        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1928
 1929        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1930        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1931
 1932        editor.move_right(&MoveRight, window, cx);
 1933        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1934        assert_selection_ranges(
 1935            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1936            editor,
 1937            cx,
 1938        );
 1939
 1940        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1941        assert_selection_ranges(
 1942            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1943            editor,
 1944            cx,
 1945        );
 1946
 1947        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1948        assert_selection_ranges(
 1949            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1950            editor,
 1951            cx,
 1952        );
 1953    });
 1954}
 1955
 1956#[gpui::test]
 1957fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1958    init_test(cx, |_| {});
 1959
 1960    let editor = cx.add_window(|window, cx| {
 1961        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1962        build_editor(buffer, window, cx)
 1963    });
 1964
 1965    _ = editor.update(cx, |editor, window, cx| {
 1966        editor.set_wrap_width(Some(140.0.into()), cx);
 1967        assert_eq!(
 1968            editor.display_text(cx),
 1969            "use one::{\n    two::three::\n    four::five\n};"
 1970        );
 1971
 1972        editor.change_selections(None, window, cx, |s| {
 1973            s.select_display_ranges([
 1974                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1975            ]);
 1976        });
 1977
 1978        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1979        assert_eq!(
 1980            editor.selections.display_ranges(cx),
 1981            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1982        );
 1983
 1984        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1985        assert_eq!(
 1986            editor.selections.display_ranges(cx),
 1987            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1988        );
 1989
 1990        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1991        assert_eq!(
 1992            editor.selections.display_ranges(cx),
 1993            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1994        );
 1995
 1996        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1997        assert_eq!(
 1998            editor.selections.display_ranges(cx),
 1999            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2000        );
 2001
 2002        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2003        assert_eq!(
 2004            editor.selections.display_ranges(cx),
 2005            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2006        );
 2007
 2008        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2009        assert_eq!(
 2010            editor.selections.display_ranges(cx),
 2011            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2012        );
 2013    });
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020
 2021    let line_height = cx.editor(|editor, window, _| {
 2022        editor
 2023            .style()
 2024            .unwrap()
 2025            .text
 2026            .line_height_in_pixels(window.rem_size())
 2027    });
 2028    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2029
 2030    cx.set_state(
 2031        &r#"ˇone
 2032        two
 2033
 2034        three
 2035        fourˇ
 2036        five
 2037
 2038        six"#
 2039            .unindent(),
 2040    );
 2041
 2042    cx.update_editor(|editor, window, cx| {
 2043        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2044    });
 2045    cx.assert_editor_state(
 2046        &r#"one
 2047        two
 2048        ˇ
 2049        three
 2050        four
 2051        five
 2052        ˇ
 2053        six"#
 2054            .unindent(),
 2055    );
 2056
 2057    cx.update_editor(|editor, window, cx| {
 2058        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2059    });
 2060    cx.assert_editor_state(
 2061        &r#"one
 2062        two
 2063
 2064        three
 2065        four
 2066        five
 2067        ˇ
 2068        sixˇ"#
 2069            .unindent(),
 2070    );
 2071
 2072    cx.update_editor(|editor, window, cx| {
 2073        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2074    });
 2075    cx.assert_editor_state(
 2076        &r#"one
 2077        two
 2078
 2079        three
 2080        four
 2081        five
 2082
 2083        sixˇ"#
 2084            .unindent(),
 2085    );
 2086
 2087    cx.update_editor(|editor, window, cx| {
 2088        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2089    });
 2090    cx.assert_editor_state(
 2091        &r#"one
 2092        two
 2093
 2094        three
 2095        four
 2096        five
 2097        ˇ
 2098        six"#
 2099            .unindent(),
 2100    );
 2101
 2102    cx.update_editor(|editor, window, cx| {
 2103        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2104    });
 2105    cx.assert_editor_state(
 2106        &r#"one
 2107        two
 2108        ˇ
 2109        three
 2110        four
 2111        five
 2112
 2113        six"#
 2114            .unindent(),
 2115    );
 2116
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2119    });
 2120    cx.assert_editor_state(
 2121        &r#"ˇone
 2122        two
 2123
 2124        three
 2125        four
 2126        five
 2127
 2128        six"#
 2129            .unindent(),
 2130    );
 2131}
 2132
 2133#[gpui::test]
 2134async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2135    init_test(cx, |_| {});
 2136    let mut cx = EditorTestContext::new(cx).await;
 2137    let line_height = cx.editor(|editor, window, _| {
 2138        editor
 2139            .style()
 2140            .unwrap()
 2141            .text
 2142            .line_height_in_pixels(window.rem_size())
 2143    });
 2144    let window = cx.window;
 2145    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2146
 2147    cx.set_state(
 2148        r#"ˇone
 2149        two
 2150        three
 2151        four
 2152        five
 2153        six
 2154        seven
 2155        eight
 2156        nine
 2157        ten
 2158        "#,
 2159    );
 2160
 2161    cx.update_editor(|editor, window, cx| {
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 0.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 3.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 6.)
 2175        );
 2176        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2177        assert_eq!(
 2178            editor.snapshot(window, cx).scroll_position(),
 2179            gpui::Point::new(0., 3.)
 2180        );
 2181
 2182        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 1.)
 2186        );
 2187        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2188        assert_eq!(
 2189            editor.snapshot(window, cx).scroll_position(),
 2190            gpui::Point::new(0., 3.)
 2191        );
 2192    });
 2193}
 2194
 2195#[gpui::test]
 2196async fn test_autoscroll(cx: &mut TestAppContext) {
 2197    init_test(cx, |_| {});
 2198    let mut cx = EditorTestContext::new(cx).await;
 2199
 2200    let line_height = cx.update_editor(|editor, window, cx| {
 2201        editor.set_vertical_scroll_margin(2, cx);
 2202        editor
 2203            .style()
 2204            .unwrap()
 2205            .text
 2206            .line_height_in_pixels(window.rem_size())
 2207    });
 2208    let window = cx.window;
 2209    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2210
 2211    cx.set_state(
 2212        r#"ˇone
 2213            two
 2214            three
 2215            four
 2216            five
 2217            six
 2218            seven
 2219            eight
 2220            nine
 2221            ten
 2222        "#,
 2223    );
 2224    cx.update_editor(|editor, window, cx| {
 2225        assert_eq!(
 2226            editor.snapshot(window, cx).scroll_position(),
 2227            gpui::Point::new(0., 0.0)
 2228        );
 2229    });
 2230
 2231    // Add a cursor below the visible area. Since both cursors cannot fit
 2232    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2233    // allows the vertical scroll margin below that cursor.
 2234    cx.update_editor(|editor, window, cx| {
 2235        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2236            selections.select_ranges([
 2237                Point::new(0, 0)..Point::new(0, 0),
 2238                Point::new(6, 0)..Point::new(6, 0),
 2239            ]);
 2240        })
 2241    });
 2242    cx.update_editor(|editor, window, cx| {
 2243        assert_eq!(
 2244            editor.snapshot(window, cx).scroll_position(),
 2245            gpui::Point::new(0., 3.0)
 2246        );
 2247    });
 2248
 2249    // Move down. The editor cursor scrolls down to track the newest cursor.
 2250    cx.update_editor(|editor, window, cx| {
 2251        editor.move_down(&Default::default(), window, cx);
 2252    });
 2253    cx.update_editor(|editor, window, cx| {
 2254        assert_eq!(
 2255            editor.snapshot(window, cx).scroll_position(),
 2256            gpui::Point::new(0., 4.0)
 2257        );
 2258    });
 2259
 2260    // Add a cursor above the visible area. Since both cursors fit on screen,
 2261    // the editor scrolls to show both.
 2262    cx.update_editor(|editor, window, cx| {
 2263        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2264            selections.select_ranges([
 2265                Point::new(1, 0)..Point::new(1, 0),
 2266                Point::new(6, 0)..Point::new(6, 0),
 2267            ]);
 2268        })
 2269    });
 2270    cx.update_editor(|editor, window, cx| {
 2271        assert_eq!(
 2272            editor.snapshot(window, cx).scroll_position(),
 2273            gpui::Point::new(0., 1.0)
 2274        );
 2275    });
 2276}
 2277
 2278#[gpui::test]
 2279async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2280    init_test(cx, |_| {});
 2281    let mut cx = EditorTestContext::new(cx).await;
 2282
 2283    let line_height = cx.editor(|editor, window, _cx| {
 2284        editor
 2285            .style()
 2286            .unwrap()
 2287            .text
 2288            .line_height_in_pixels(window.rem_size())
 2289    });
 2290    let window = cx.window;
 2291    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2292    cx.set_state(
 2293        &r#"
 2294        ˇone
 2295        two
 2296        threeˇ
 2297        four
 2298        five
 2299        six
 2300        seven
 2301        eight
 2302        nine
 2303        ten
 2304        "#
 2305        .unindent(),
 2306    );
 2307
 2308    cx.update_editor(|editor, window, cx| {
 2309        editor.move_page_down(&MovePageDown::default(), window, cx)
 2310    });
 2311    cx.assert_editor_state(
 2312        &r#"
 2313        one
 2314        two
 2315        three
 2316        ˇfour
 2317        five
 2318        sixˇ
 2319        seven
 2320        eight
 2321        nine
 2322        ten
 2323        "#
 2324        .unindent(),
 2325    );
 2326
 2327    cx.update_editor(|editor, window, cx| {
 2328        editor.move_page_down(&MovePageDown::default(), window, cx)
 2329    });
 2330    cx.assert_editor_state(
 2331        &r#"
 2332        one
 2333        two
 2334        three
 2335        four
 2336        five
 2337        six
 2338        ˇseven
 2339        eight
 2340        nineˇ
 2341        ten
 2342        "#
 2343        .unindent(),
 2344    );
 2345
 2346    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2347    cx.assert_editor_state(
 2348        &r#"
 2349        one
 2350        two
 2351        three
 2352        ˇfour
 2353        five
 2354        sixˇ
 2355        seven
 2356        eight
 2357        nine
 2358        ten
 2359        "#
 2360        .unindent(),
 2361    );
 2362
 2363    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2364    cx.assert_editor_state(
 2365        &r#"
 2366        ˇone
 2367        two
 2368        threeˇ
 2369        four
 2370        five
 2371        six
 2372        seven
 2373        eight
 2374        nine
 2375        ten
 2376        "#
 2377        .unindent(),
 2378    );
 2379
 2380    // Test select collapsing
 2381    cx.update_editor(|editor, window, cx| {
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385    });
 2386    cx.assert_editor_state(
 2387        &r#"
 2388        one
 2389        two
 2390        three
 2391        four
 2392        five
 2393        six
 2394        seven
 2395        eight
 2396        nine
 2397        ˇten
 2398        ˇ"#
 2399        .unindent(),
 2400    );
 2401}
 2402
 2403#[gpui::test]
 2404async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2405    init_test(cx, |_| {});
 2406    let mut cx = EditorTestContext::new(cx).await;
 2407    cx.set_state("one «two threeˇ» four");
 2408    cx.update_editor(|editor, window, cx| {
 2409        editor.delete_to_beginning_of_line(
 2410            &DeleteToBeginningOfLine {
 2411                stop_at_indent: false,
 2412            },
 2413            window,
 2414            cx,
 2415        );
 2416        assert_eq!(editor.text(cx), " four");
 2417    });
 2418}
 2419
 2420#[gpui::test]
 2421fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2422    init_test(cx, |_| {});
 2423
 2424    let editor = cx.add_window(|window, cx| {
 2425        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2426        build_editor(buffer.clone(), window, cx)
 2427    });
 2428
 2429    _ = editor.update(cx, |editor, window, cx| {
 2430        editor.change_selections(None, window, cx, |s| {
 2431            s.select_display_ranges([
 2432                // an empty selection - the preceding word fragment is deleted
 2433                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2434                // characters selected - they are deleted
 2435                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2436            ])
 2437        });
 2438        editor.delete_to_previous_word_start(
 2439            &DeleteToPreviousWordStart {
 2440                ignore_newlines: false,
 2441            },
 2442            window,
 2443            cx,
 2444        );
 2445        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2446    });
 2447
 2448    _ = editor.update(cx, |editor, window, cx| {
 2449        editor.change_selections(None, window, cx, |s| {
 2450            s.select_display_ranges([
 2451                // an empty selection - the following word fragment is deleted
 2452                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2453                // characters selected - they are deleted
 2454                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2455            ])
 2456        });
 2457        editor.delete_to_next_word_end(
 2458            &DeleteToNextWordEnd {
 2459                ignore_newlines: false,
 2460            },
 2461            window,
 2462            cx,
 2463        );
 2464        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2465    });
 2466}
 2467
 2468#[gpui::test]
 2469fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2470    init_test(cx, |_| {});
 2471
 2472    let editor = cx.add_window(|window, cx| {
 2473        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2474        build_editor(buffer.clone(), window, cx)
 2475    });
 2476    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2477        ignore_newlines: false,
 2478    };
 2479    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2480        ignore_newlines: true,
 2481    };
 2482
 2483    _ = editor.update(cx, |editor, window, cx| {
 2484        editor.change_selections(None, window, cx, |s| {
 2485            s.select_display_ranges([
 2486                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2487            ])
 2488        });
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2499        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2500        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2501    });
 2502}
 2503
 2504#[gpui::test]
 2505fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2506    init_test(cx, |_| {});
 2507
 2508    let editor = cx.add_window(|window, cx| {
 2509        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2510        build_editor(buffer.clone(), window, cx)
 2511    });
 2512    let del_to_next_word_end = DeleteToNextWordEnd {
 2513        ignore_newlines: false,
 2514    };
 2515    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2516        ignore_newlines: true,
 2517    };
 2518
 2519    _ = editor.update(cx, |editor, window, cx| {
 2520        editor.change_selections(None, window, cx, |s| {
 2521            s.select_display_ranges([
 2522                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2523            ])
 2524        });
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "one\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "\n   two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(
 2537            editor.buffer.read(cx).read(cx).text(),
 2538            "two\nthree\n   four"
 2539        );
 2540        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2541        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2542        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2544        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2545        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2546    });
 2547}
 2548
 2549#[gpui::test]
 2550fn test_newline(cx: &mut TestAppContext) {
 2551    init_test(cx, |_| {});
 2552
 2553    let editor = cx.add_window(|window, cx| {
 2554        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2555        build_editor(buffer.clone(), window, cx)
 2556    });
 2557
 2558    _ = editor.update(cx, |editor, window, cx| {
 2559        editor.change_selections(None, window, cx, |s| {
 2560            s.select_display_ranges([
 2561                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2562                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2563                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2564            ])
 2565        });
 2566
 2567        editor.newline(&Newline, window, cx);
 2568        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2569    });
 2570}
 2571
 2572#[gpui::test]
 2573fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2574    init_test(cx, |_| {});
 2575
 2576    let editor = cx.add_window(|window, cx| {
 2577        let buffer = MultiBuffer::build_simple(
 2578            "
 2579                a
 2580                b(
 2581                    X
 2582                )
 2583                c(
 2584                    X
 2585                )
 2586            "
 2587            .unindent()
 2588            .as_str(),
 2589            cx,
 2590        );
 2591        let mut editor = build_editor(buffer.clone(), window, cx);
 2592        editor.change_selections(None, window, cx, |s| {
 2593            s.select_ranges([
 2594                Point::new(2, 4)..Point::new(2, 5),
 2595                Point::new(5, 4)..Point::new(5, 5),
 2596            ])
 2597        });
 2598        editor
 2599    });
 2600
 2601    _ = editor.update(cx, |editor, window, cx| {
 2602        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2603        editor.buffer.update(cx, |buffer, cx| {
 2604            buffer.edit(
 2605                [
 2606                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2607                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2608                ],
 2609                None,
 2610                cx,
 2611            );
 2612            assert_eq!(
 2613                buffer.read(cx).text(),
 2614                "
 2615                    a
 2616                    b()
 2617                    c()
 2618                "
 2619                .unindent()
 2620            );
 2621        });
 2622        assert_eq!(
 2623            editor.selections.ranges(cx),
 2624            &[
 2625                Point::new(1, 2)..Point::new(1, 2),
 2626                Point::new(2, 2)..Point::new(2, 2),
 2627            ],
 2628        );
 2629
 2630        editor.newline(&Newline, window, cx);
 2631        assert_eq!(
 2632            editor.text(cx),
 2633            "
 2634                a
 2635                b(
 2636                )
 2637                c(
 2638                )
 2639            "
 2640            .unindent()
 2641        );
 2642
 2643        // The selections are moved after the inserted newlines
 2644        assert_eq!(
 2645            editor.selections.ranges(cx),
 2646            &[
 2647                Point::new(2, 0)..Point::new(2, 0),
 2648                Point::new(4, 0)..Point::new(4, 0),
 2649            ],
 2650        );
 2651    });
 2652}
 2653
 2654#[gpui::test]
 2655async fn test_newline_above(cx: &mut TestAppContext) {
 2656    init_test(cx, |settings| {
 2657        settings.defaults.tab_size = NonZeroU32::new(4)
 2658    });
 2659
 2660    let language = Arc::new(
 2661        Language::new(
 2662            LanguageConfig::default(),
 2663            Some(tree_sitter_rust::LANGUAGE.into()),
 2664        )
 2665        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2666        .unwrap(),
 2667    );
 2668
 2669    let mut cx = EditorTestContext::new(cx).await;
 2670    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2671    cx.set_state(indoc! {"
 2672        const a: ˇA = (
 2673 2674                «const_functionˇ»(ˇ),
 2675                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2676 2677        ˇ);ˇ
 2678    "});
 2679
 2680    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2681    cx.assert_editor_state(indoc! {"
 2682        ˇ
 2683        const a: A = (
 2684            ˇ
 2685            (
 2686                ˇ
 2687                ˇ
 2688                const_function(),
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                something_else,
 2694                ˇ
 2695            )
 2696            ˇ
 2697            ˇ
 2698        );
 2699    "});
 2700}
 2701
 2702#[gpui::test]
 2703async fn test_newline_below(cx: &mut TestAppContext) {
 2704    init_test(cx, |settings| {
 2705        settings.defaults.tab_size = NonZeroU32::new(4)
 2706    });
 2707
 2708    let language = Arc::new(
 2709        Language::new(
 2710            LanguageConfig::default(),
 2711            Some(tree_sitter_rust::LANGUAGE.into()),
 2712        )
 2713        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2714        .unwrap(),
 2715    );
 2716
 2717    let mut cx = EditorTestContext::new(cx).await;
 2718    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2719    cx.set_state(indoc! {"
 2720        const a: ˇA = (
 2721 2722                «const_functionˇ»(ˇ),
 2723                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2724 2725        ˇ);ˇ
 2726    "});
 2727
 2728    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2729    cx.assert_editor_state(indoc! {"
 2730        const a: A = (
 2731            ˇ
 2732            (
 2733                ˇ
 2734                const_function(),
 2735                ˇ
 2736                ˇ
 2737                something_else,
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742            )
 2743            ˇ
 2744        );
 2745        ˇ
 2746        ˇ
 2747    "});
 2748}
 2749
 2750#[gpui::test]
 2751async fn test_newline_comments(cx: &mut TestAppContext) {
 2752    init_test(cx, |settings| {
 2753        settings.defaults.tab_size = NonZeroU32::new(4)
 2754    });
 2755
 2756    let language = Arc::new(Language::new(
 2757        LanguageConfig {
 2758            line_comments: vec!["//".into()],
 2759            ..LanguageConfig::default()
 2760        },
 2761        None,
 2762    ));
 2763    {
 2764        let mut cx = EditorTestContext::new(cx).await;
 2765        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2766        cx.set_state(indoc! {"
 2767        // Fooˇ
 2768    "});
 2769
 2770        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2771        cx.assert_editor_state(indoc! {"
 2772        // Foo
 2773        //ˇ
 2774    "});
 2775        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2776        cx.set_state(indoc! {"
 2777        ˇ// Foo
 2778    "});
 2779        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2780        cx.assert_editor_state(indoc! {"
 2781
 2782        ˇ// Foo
 2783    "});
 2784    }
 2785    // Ensure that comment continuations can be disabled.
 2786    update_test_language_settings(cx, |settings| {
 2787        settings.defaults.extend_comment_on_newline = Some(false);
 2788    });
 2789    let mut cx = EditorTestContext::new(cx).await;
 2790    cx.set_state(indoc! {"
 2791        // Fooˇ
 2792    "});
 2793    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2794    cx.assert_editor_state(indoc! {"
 2795        // Foo
 2796        ˇ
 2797    "});
 2798}
 2799
 2800#[gpui::test]
 2801fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2802    init_test(cx, |_| {});
 2803
 2804    let editor = cx.add_window(|window, cx| {
 2805        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2806        let mut editor = build_editor(buffer.clone(), window, cx);
 2807        editor.change_selections(None, window, cx, |s| {
 2808            s.select_ranges([3..4, 11..12, 19..20])
 2809        });
 2810        editor
 2811    });
 2812
 2813    _ = editor.update(cx, |editor, window, cx| {
 2814        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2815        editor.buffer.update(cx, |buffer, cx| {
 2816            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2817            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2818        });
 2819        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2820
 2821        editor.insert("Z", window, cx);
 2822        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2823
 2824        // The selections are moved after the inserted characters
 2825        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2826    });
 2827}
 2828
 2829#[gpui::test]
 2830async fn test_tab(cx: &mut TestAppContext) {
 2831    init_test(cx, |settings| {
 2832        settings.defaults.tab_size = NonZeroU32::new(3)
 2833    });
 2834
 2835    let mut cx = EditorTestContext::new(cx).await;
 2836    cx.set_state(indoc! {"
 2837        ˇabˇc
 2838        ˇ🏀ˇ🏀ˇefg
 2839 2840    "});
 2841    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2842    cx.assert_editor_state(indoc! {"
 2843           ˇab ˇc
 2844           ˇ🏀  ˇ🏀  ˇefg
 2845        d  ˇ
 2846    "});
 2847
 2848    cx.set_state(indoc! {"
 2849        a
 2850        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2853    cx.assert_editor_state(indoc! {"
 2854        a
 2855           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2856    "});
 2857}
 2858
 2859#[gpui::test]
 2860async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2861    init_test(cx, |_| {});
 2862
 2863    let mut cx = EditorTestContext::new(cx).await;
 2864    let language = Arc::new(
 2865        Language::new(
 2866            LanguageConfig::default(),
 2867            Some(tree_sitter_rust::LANGUAGE.into()),
 2868        )
 2869        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2870        .unwrap(),
 2871    );
 2872    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2873
 2874    // when all cursors are to the left of the suggested indent, then auto-indent all.
 2875    cx.set_state(indoc! {"
 2876        const a: B = (
 2877            c(
 2878        ˇ
 2879        ˇ    )
 2880        );
 2881    "});
 2882    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2883    cx.assert_editor_state(indoc! {"
 2884        const a: B = (
 2885            c(
 2886                ˇ
 2887            ˇ)
 2888        );
 2889    "});
 2890
 2891    // cursors that are already at the suggested indent level do not move
 2892    // until other cursors that are to the left of the suggested indent
 2893    // auto-indent.
 2894    cx.set_state(indoc! {"
 2895        ˇ
 2896        const a: B = (
 2897            c(
 2898                d(
 2899        ˇ
 2900                )
 2901        ˇ
 2902        ˇ    )
 2903        );
 2904    "});
 2905    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2906    cx.assert_editor_state(indoc! {"
 2907        ˇ
 2908        const a: B = (
 2909            c(
 2910                d(
 2911                    ˇ
 2912                )
 2913                ˇ
 2914            ˇ)
 2915        );
 2916    "});
 2917    // once all multi-cursors are at the suggested
 2918    // indent level, they all insert a soft tab together.
 2919    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2920    cx.assert_editor_state(indoc! {"
 2921            ˇ
 2922        const a: B = (
 2923            c(
 2924                d(
 2925                        ˇ
 2926                )
 2927                    ˇ
 2928                ˇ)
 2929        );
 2930    "});
 2931
 2932    // handle auto-indent when there are multiple cursors on the same line
 2933    cx.set_state(indoc! {"
 2934        const a: B = (
 2935            c(
 2936        ˇ    ˇ
 2937        ˇ    )
 2938        );
 2939    "});
 2940    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2941    cx.assert_editor_state(indoc! {"
 2942        const a: B = (
 2943            c(
 2944                ˇ
 2945            ˇ)
 2946        );
 2947    "});
 2948}
 2949
 2950#[gpui::test]
 2951async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2952    init_test(cx, |settings| {
 2953        settings.defaults.tab_size = NonZeroU32::new(3)
 2954    });
 2955
 2956    let mut cx = EditorTestContext::new(cx).await;
 2957    cx.set_state(indoc! {"
 2958         ˇ
 2959        \t ˇ
 2960        \t  ˇ
 2961        \t   ˇ
 2962         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2963    "});
 2964
 2965    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2966    cx.assert_editor_state(indoc! {"
 2967           ˇ
 2968        \t   ˇ
 2969        \t   ˇ
 2970        \t      ˇ
 2971         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2972    "});
 2973}
 2974
 2975#[gpui::test]
 2976async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2977    init_test(cx, |settings| {
 2978        settings.defaults.tab_size = NonZeroU32::new(4)
 2979    });
 2980
 2981    let language = Arc::new(
 2982        Language::new(
 2983            LanguageConfig::default(),
 2984            Some(tree_sitter_rust::LANGUAGE.into()),
 2985        )
 2986        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2987        .unwrap(),
 2988    );
 2989
 2990    let mut cx = EditorTestContext::new(cx).await;
 2991    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2992    cx.set_state(indoc! {"
 2993        fn a() {
 2994            if b {
 2995        \t ˇc
 2996            }
 2997        }
 2998    "});
 2999
 3000    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3001    cx.assert_editor_state(indoc! {"
 3002        fn a() {
 3003            if b {
 3004                ˇc
 3005            }
 3006        }
 3007    "});
 3008}
 3009
 3010#[gpui::test]
 3011async fn test_indent_outdent(cx: &mut TestAppContext) {
 3012    init_test(cx, |settings| {
 3013        settings.defaults.tab_size = NonZeroU32::new(4);
 3014    });
 3015
 3016    let mut cx = EditorTestContext::new(cx).await;
 3017
 3018    cx.set_state(indoc! {"
 3019          «oneˇ» «twoˇ»
 3020        three
 3021         four
 3022    "});
 3023    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3024    cx.assert_editor_state(indoc! {"
 3025            «oneˇ» «twoˇ»
 3026        three
 3027         four
 3028    "});
 3029
 3030    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3031    cx.assert_editor_state(indoc! {"
 3032        «oneˇ» «twoˇ»
 3033        three
 3034         four
 3035    "});
 3036
 3037    // select across line ending
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        t«hree
 3041        ˇ» four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046            t«hree
 3047        ˇ» four
 3048    "});
 3049
 3050    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3051    cx.assert_editor_state(indoc! {"
 3052        one two
 3053        t«hree
 3054        ˇ» four
 3055    "});
 3056
 3057    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3058    cx.set_state(indoc! {"
 3059        one two
 3060        ˇthree
 3061            four
 3062    "});
 3063    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3064    cx.assert_editor_state(indoc! {"
 3065        one two
 3066            ˇthree
 3067            four
 3068    "});
 3069
 3070    cx.set_state(indoc! {"
 3071        one two
 3072        ˇ    three
 3073            four
 3074    "});
 3075    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3076    cx.assert_editor_state(indoc! {"
 3077        one two
 3078        ˇthree
 3079            four
 3080    "});
 3081}
 3082
 3083#[gpui::test]
 3084async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3085    init_test(cx, |settings| {
 3086        settings.defaults.hard_tabs = Some(true);
 3087    });
 3088
 3089    let mut cx = EditorTestContext::new(cx).await;
 3090
 3091    // select two ranges on one line
 3092    cx.set_state(indoc! {"
 3093        «oneˇ» «twoˇ»
 3094        three
 3095        four
 3096    "});
 3097    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3098    cx.assert_editor_state(indoc! {"
 3099        \t«oneˇ» «twoˇ»
 3100        three
 3101        four
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        \t\t«oneˇ» «twoˇ»
 3106        three
 3107        four
 3108    "});
 3109    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3110    cx.assert_editor_state(indoc! {"
 3111        \t«oneˇ» «twoˇ»
 3112        three
 3113        four
 3114    "});
 3115    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3116    cx.assert_editor_state(indoc! {"
 3117        «oneˇ» «twoˇ»
 3118        three
 3119        four
 3120    "});
 3121
 3122    // select across a line ending
 3123    cx.set_state(indoc! {"
 3124        one two
 3125        t«hree
 3126        ˇ»four
 3127    "});
 3128    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3129    cx.assert_editor_state(indoc! {"
 3130        one two
 3131        \tt«hree
 3132        ˇ»four
 3133    "});
 3134    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3135    cx.assert_editor_state(indoc! {"
 3136        one two
 3137        \t\tt«hree
 3138        ˇ»four
 3139    "});
 3140    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3141    cx.assert_editor_state(indoc! {"
 3142        one two
 3143        \tt«hree
 3144        ˇ»four
 3145    "});
 3146    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3147    cx.assert_editor_state(indoc! {"
 3148        one two
 3149        t«hree
 3150        ˇ»four
 3151    "});
 3152
 3153    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3154    cx.set_state(indoc! {"
 3155        one two
 3156        ˇthree
 3157        four
 3158    "});
 3159    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3160    cx.assert_editor_state(indoc! {"
 3161        one two
 3162        ˇthree
 3163        four
 3164    "});
 3165    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3166    cx.assert_editor_state(indoc! {"
 3167        one two
 3168        \tˇthree
 3169        four
 3170    "});
 3171    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3172    cx.assert_editor_state(indoc! {"
 3173        one two
 3174        ˇthree
 3175        four
 3176    "});
 3177}
 3178
 3179#[gpui::test]
 3180fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3181    init_test(cx, |settings| {
 3182        settings.languages.extend([
 3183            (
 3184                "TOML".into(),
 3185                LanguageSettingsContent {
 3186                    tab_size: NonZeroU32::new(2),
 3187                    ..Default::default()
 3188                },
 3189            ),
 3190            (
 3191                "Rust".into(),
 3192                LanguageSettingsContent {
 3193                    tab_size: NonZeroU32::new(4),
 3194                    ..Default::default()
 3195                },
 3196            ),
 3197        ]);
 3198    });
 3199
 3200    let toml_language = Arc::new(Language::new(
 3201        LanguageConfig {
 3202            name: "TOML".into(),
 3203            ..Default::default()
 3204        },
 3205        None,
 3206    ));
 3207    let rust_language = Arc::new(Language::new(
 3208        LanguageConfig {
 3209            name: "Rust".into(),
 3210            ..Default::default()
 3211        },
 3212        None,
 3213    ));
 3214
 3215    let toml_buffer =
 3216        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3217    let rust_buffer =
 3218        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3219    let multibuffer = cx.new(|cx| {
 3220        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3221        multibuffer.push_excerpts(
 3222            toml_buffer.clone(),
 3223            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3224            cx,
 3225        );
 3226        multibuffer.push_excerpts(
 3227            rust_buffer.clone(),
 3228            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3229            cx,
 3230        );
 3231        multibuffer
 3232    });
 3233
 3234    cx.add_window(|window, cx| {
 3235        let mut editor = build_editor(multibuffer, window, cx);
 3236
 3237        assert_eq!(
 3238            editor.text(cx),
 3239            indoc! {"
 3240                a = 1
 3241                b = 2
 3242
 3243                const c: usize = 3;
 3244            "}
 3245        );
 3246
 3247        select_ranges(
 3248            &mut editor,
 3249            indoc! {"
 3250                «aˇ» = 1
 3251                b = 2
 3252
 3253                «const c:ˇ» usize = 3;
 3254            "},
 3255            window,
 3256            cx,
 3257        );
 3258
 3259        editor.tab(&Tab, window, cx);
 3260        assert_text_with_selections(
 3261            &mut editor,
 3262            indoc! {"
 3263                  «aˇ» = 1
 3264                b = 2
 3265
 3266                    «const c:ˇ» usize = 3;
 3267            "},
 3268            cx,
 3269        );
 3270        editor.backtab(&Backtab, window, cx);
 3271        assert_text_with_selections(
 3272            &mut editor,
 3273            indoc! {"
 3274                «aˇ» = 1
 3275                b = 2
 3276
 3277                «const c:ˇ» usize = 3;
 3278            "},
 3279            cx,
 3280        );
 3281
 3282        editor
 3283    });
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_backspace(cx: &mut TestAppContext) {
 3288    init_test(cx, |_| {});
 3289
 3290    let mut cx = EditorTestContext::new(cx).await;
 3291
 3292    // Basic backspace
 3293    cx.set_state(indoc! {"
 3294        onˇe two three
 3295        fou«rˇ» five six
 3296        seven «ˇeight nine
 3297        »ten
 3298    "});
 3299    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3300    cx.assert_editor_state(indoc! {"
 3301        oˇe two three
 3302        fouˇ five six
 3303        seven ˇten
 3304    "});
 3305
 3306    // Test backspace inside and around indents
 3307    cx.set_state(indoc! {"
 3308        zero
 3309            ˇone
 3310                ˇtwo
 3311            ˇ ˇ ˇ  three
 3312        ˇ  ˇ  four
 3313    "});
 3314    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3315    cx.assert_editor_state(indoc! {"
 3316        zero
 3317        ˇone
 3318            ˇtwo
 3319        ˇ  threeˇ  four
 3320    "});
 3321}
 3322
 3323#[gpui::test]
 3324async fn test_delete(cx: &mut TestAppContext) {
 3325    init_test(cx, |_| {});
 3326
 3327    let mut cx = EditorTestContext::new(cx).await;
 3328    cx.set_state(indoc! {"
 3329        onˇe two three
 3330        fou«rˇ» five six
 3331        seven «ˇeight nine
 3332        »ten
 3333    "});
 3334    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3335    cx.assert_editor_state(indoc! {"
 3336        onˇ two three
 3337        fouˇ five six
 3338        seven ˇten
 3339    "});
 3340}
 3341
 3342#[gpui::test]
 3343fn test_delete_line(cx: &mut TestAppContext) {
 3344    init_test(cx, |_| {});
 3345
 3346    let editor = cx.add_window(|window, cx| {
 3347        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3348        build_editor(buffer, window, cx)
 3349    });
 3350    _ = editor.update(cx, |editor, window, cx| {
 3351        editor.change_selections(None, window, cx, |s| {
 3352            s.select_display_ranges([
 3353                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3354                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3355                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3356            ])
 3357        });
 3358        editor.delete_line(&DeleteLine, window, cx);
 3359        assert_eq!(editor.display_text(cx), "ghi");
 3360        assert_eq!(
 3361            editor.selections.display_ranges(cx),
 3362            vec![
 3363                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3364                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3365            ]
 3366        );
 3367    });
 3368
 3369    let editor = cx.add_window(|window, cx| {
 3370        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3371        build_editor(buffer, window, cx)
 3372    });
 3373    _ = editor.update(cx, |editor, window, cx| {
 3374        editor.change_selections(None, window, cx, |s| {
 3375            s.select_display_ranges([
 3376                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3377            ])
 3378        });
 3379        editor.delete_line(&DeleteLine, window, cx);
 3380        assert_eq!(editor.display_text(cx), "ghi\n");
 3381        assert_eq!(
 3382            editor.selections.display_ranges(cx),
 3383            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3384        );
 3385    });
 3386}
 3387
 3388#[gpui::test]
 3389fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3390    init_test(cx, |_| {});
 3391
 3392    cx.add_window(|window, cx| {
 3393        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3394        let mut editor = build_editor(buffer.clone(), window, cx);
 3395        let buffer = buffer.read(cx).as_singleton().unwrap();
 3396
 3397        assert_eq!(
 3398            editor.selections.ranges::<Point>(cx),
 3399            &[Point::new(0, 0)..Point::new(0, 0)]
 3400        );
 3401
 3402        // When on single line, replace newline at end by space
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            &[Point::new(0, 3)..Point::new(0, 3)]
 3408        );
 3409
 3410        // When multiple lines are selected, remove newlines that are spanned by the selection
 3411        editor.change_selections(None, window, cx, |s| {
 3412            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3413        });
 3414        editor.join_lines(&JoinLines, window, cx);
 3415        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3416        assert_eq!(
 3417            editor.selections.ranges::<Point>(cx),
 3418            &[Point::new(0, 11)..Point::new(0, 11)]
 3419        );
 3420
 3421        // Undo should be transactional
 3422        editor.undo(&Undo, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3424        assert_eq!(
 3425            editor.selections.ranges::<Point>(cx),
 3426            &[Point::new(0, 5)..Point::new(2, 2)]
 3427        );
 3428
 3429        // When joining an empty line don't insert a space
 3430        editor.change_selections(None, window, cx, |s| {
 3431            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3432        });
 3433        editor.join_lines(&JoinLines, window, cx);
 3434        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3435        assert_eq!(
 3436            editor.selections.ranges::<Point>(cx),
 3437            [Point::new(2, 3)..Point::new(2, 3)]
 3438        );
 3439
 3440        // We can remove trailing newlines
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3443        assert_eq!(
 3444            editor.selections.ranges::<Point>(cx),
 3445            [Point::new(2, 3)..Point::new(2, 3)]
 3446        );
 3447
 3448        // We don't blow up on the last line
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3451        assert_eq!(
 3452            editor.selections.ranges::<Point>(cx),
 3453            [Point::new(2, 3)..Point::new(2, 3)]
 3454        );
 3455
 3456        // reset to test indentation
 3457        editor.buffer.update(cx, |buffer, cx| {
 3458            buffer.edit(
 3459                [
 3460                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3461                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3462                ],
 3463                None,
 3464                cx,
 3465            )
 3466        });
 3467
 3468        // We remove any leading spaces
 3469        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3470        editor.change_selections(None, window, cx, |s| {
 3471            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3472        });
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3475
 3476        // We don't insert a space for a line containing only spaces
 3477        editor.join_lines(&JoinLines, window, cx);
 3478        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3479
 3480        // We ignore any leading tabs
 3481        editor.join_lines(&JoinLines, window, cx);
 3482        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3483
 3484        editor
 3485    });
 3486}
 3487
 3488#[gpui::test]
 3489fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3490    init_test(cx, |_| {});
 3491
 3492    cx.add_window(|window, cx| {
 3493        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3494        let mut editor = build_editor(buffer.clone(), window, cx);
 3495        let buffer = buffer.read(cx).as_singleton().unwrap();
 3496
 3497        editor.change_selections(None, window, cx, |s| {
 3498            s.select_ranges([
 3499                Point::new(0, 2)..Point::new(1, 1),
 3500                Point::new(1, 2)..Point::new(1, 2),
 3501                Point::new(3, 1)..Point::new(3, 2),
 3502            ])
 3503        });
 3504
 3505        editor.join_lines(&JoinLines, window, cx);
 3506        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3507
 3508        assert_eq!(
 3509            editor.selections.ranges::<Point>(cx),
 3510            [
 3511                Point::new(0, 7)..Point::new(0, 7),
 3512                Point::new(1, 3)..Point::new(1, 3)
 3513            ]
 3514        );
 3515        editor
 3516    });
 3517}
 3518
 3519#[gpui::test]
 3520async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3521    init_test(cx, |_| {});
 3522
 3523    let mut cx = EditorTestContext::new(cx).await;
 3524
 3525    let diff_base = r#"
 3526        Line 0
 3527        Line 1
 3528        Line 2
 3529        Line 3
 3530        "#
 3531    .unindent();
 3532
 3533    cx.set_state(
 3534        &r#"
 3535        ˇLine 0
 3536        Line 1
 3537        Line 2
 3538        Line 3
 3539        "#
 3540        .unindent(),
 3541    );
 3542
 3543    cx.set_head_text(&diff_base);
 3544    executor.run_until_parked();
 3545
 3546    // Join lines
 3547    cx.update_editor(|editor, window, cx| {
 3548        editor.join_lines(&JoinLines, window, cx);
 3549    });
 3550    executor.run_until_parked();
 3551
 3552    cx.assert_editor_state(
 3553        &r#"
 3554        Line 0ˇ Line 1
 3555        Line 2
 3556        Line 3
 3557        "#
 3558        .unindent(),
 3559    );
 3560    // Join again
 3561    cx.update_editor(|editor, window, cx| {
 3562        editor.join_lines(&JoinLines, window, cx);
 3563    });
 3564    executor.run_until_parked();
 3565
 3566    cx.assert_editor_state(
 3567        &r#"
 3568        Line 0 Line 1ˇ Line 2
 3569        Line 3
 3570        "#
 3571        .unindent(),
 3572    );
 3573}
 3574
 3575#[gpui::test]
 3576async fn test_custom_newlines_cause_no_false_positive_diffs(
 3577    executor: BackgroundExecutor,
 3578    cx: &mut TestAppContext,
 3579) {
 3580    init_test(cx, |_| {});
 3581    let mut cx = EditorTestContext::new(cx).await;
 3582    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3583    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3584    executor.run_until_parked();
 3585
 3586    cx.update_editor(|editor, window, cx| {
 3587        let snapshot = editor.snapshot(window, cx);
 3588        assert_eq!(
 3589            snapshot
 3590                .buffer_snapshot
 3591                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3592                .collect::<Vec<_>>(),
 3593            Vec::new(),
 3594            "Should not have any diffs for files with custom newlines"
 3595        );
 3596    });
 3597}
 3598
 3599#[gpui::test]
 3600async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3601    init_test(cx, |_| {});
 3602
 3603    let mut cx = EditorTestContext::new(cx).await;
 3604
 3605    // Test sort_lines_case_insensitive()
 3606    cx.set_state(indoc! {"
 3607        «z
 3608        y
 3609        x
 3610        Z
 3611        Y
 3612        Xˇ»
 3613    "});
 3614    cx.update_editor(|e, window, cx| {
 3615        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3616    });
 3617    cx.assert_editor_state(indoc! {"
 3618        «x
 3619        X
 3620        y
 3621        Y
 3622        z
 3623        Zˇ»
 3624    "});
 3625
 3626    // Test reverse_lines()
 3627    cx.set_state(indoc! {"
 3628        «5
 3629        4
 3630        3
 3631        2
 3632        1ˇ»
 3633    "});
 3634    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3635    cx.assert_editor_state(indoc! {"
 3636        «1
 3637        2
 3638        3
 3639        4
 3640        5ˇ»
 3641    "});
 3642
 3643    // Skip testing shuffle_line()
 3644
 3645    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3646    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3647
 3648    // Don't manipulate when cursor is on single line, but expand the selection
 3649    cx.set_state(indoc! {"
 3650        ddˇdd
 3651        ccc
 3652        bb
 3653        a
 3654    "});
 3655    cx.update_editor(|e, window, cx| {
 3656        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3657    });
 3658    cx.assert_editor_state(indoc! {"
 3659        «ddddˇ»
 3660        ccc
 3661        bb
 3662        a
 3663    "});
 3664
 3665    // Basic manipulate case
 3666    // Start selection moves to column 0
 3667    // End of selection shrinks to fit shorter line
 3668    cx.set_state(indoc! {"
 3669        dd«d
 3670        ccc
 3671        bb
 3672        aaaaaˇ»
 3673    "});
 3674    cx.update_editor(|e, window, cx| {
 3675        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3676    });
 3677    cx.assert_editor_state(indoc! {"
 3678        «aaaaa
 3679        bb
 3680        ccc
 3681        dddˇ»
 3682    "});
 3683
 3684    // Manipulate case with newlines
 3685    cx.set_state(indoc! {"
 3686        dd«d
 3687        ccc
 3688
 3689        bb
 3690        aaaaa
 3691
 3692        ˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3696    });
 3697    cx.assert_editor_state(indoc! {"
 3698        «
 3699
 3700        aaaaa
 3701        bb
 3702        ccc
 3703        dddˇ»
 3704
 3705    "});
 3706
 3707    // Adding new line
 3708    cx.set_state(indoc! {"
 3709        aa«a
 3710        bbˇ»b
 3711    "});
 3712    cx.update_editor(|e, window, cx| {
 3713        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        «aaa
 3717        bbb
 3718        added_lineˇ»
 3719    "});
 3720
 3721    // Removing line
 3722    cx.set_state(indoc! {"
 3723        aa«a
 3724        bbbˇ»
 3725    "});
 3726    cx.update_editor(|e, window, cx| {
 3727        e.manipulate_lines(window, cx, |lines| {
 3728            lines.pop();
 3729        })
 3730    });
 3731    cx.assert_editor_state(indoc! {"
 3732        «aaaˇ»
 3733    "});
 3734
 3735    // Removing all lines
 3736    cx.set_state(indoc! {"
 3737        aa«a
 3738        bbbˇ»
 3739    "});
 3740    cx.update_editor(|e, window, cx| {
 3741        e.manipulate_lines(window, cx, |lines| {
 3742            lines.drain(..);
 3743        })
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        ˇ
 3747    "});
 3748}
 3749
 3750#[gpui::test]
 3751async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3752    init_test(cx, |_| {});
 3753
 3754    let mut cx = EditorTestContext::new(cx).await;
 3755
 3756    // Consider continuous selection as single selection
 3757    cx.set_state(indoc! {"
 3758        Aaa«aa
 3759        cˇ»c«c
 3760        bb
 3761        aaaˇ»aa
 3762    "});
 3763    cx.update_editor(|e, window, cx| {
 3764        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3765    });
 3766    cx.assert_editor_state(indoc! {"
 3767        «Aaaaa
 3768        ccc
 3769        bb
 3770        aaaaaˇ»
 3771    "});
 3772
 3773    cx.set_state(indoc! {"
 3774        Aaa«aa
 3775        cˇ»c«c
 3776        bb
 3777        aaaˇ»aa
 3778    "});
 3779    cx.update_editor(|e, window, cx| {
 3780        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3781    });
 3782    cx.assert_editor_state(indoc! {"
 3783        «Aaaaa
 3784        ccc
 3785        bbˇ»
 3786    "});
 3787
 3788    // Consider non continuous selection as distinct dedup operations
 3789    cx.set_state(indoc! {"
 3790        «aaaaa
 3791        bb
 3792        aaaaa
 3793        aaaaaˇ»
 3794
 3795        aaa«aaˇ»
 3796    "});
 3797    cx.update_editor(|e, window, cx| {
 3798        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3799    });
 3800    cx.assert_editor_state(indoc! {"
 3801        «aaaaa
 3802        bbˇ»
 3803
 3804        «aaaaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    cx.set_state(indoc! {"
 3815        «Aaa
 3816        aAa
 3817        Aaaˇ»
 3818    "});
 3819    cx.update_editor(|e, window, cx| {
 3820        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3821    });
 3822    cx.assert_editor_state(indoc! {"
 3823        «Aaa
 3824        aAaˇ»
 3825    "});
 3826
 3827    cx.set_state(indoc! {"
 3828        «Aaa
 3829        aAa
 3830        aaAˇ»
 3831    "});
 3832    cx.update_editor(|e, window, cx| {
 3833        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3834    });
 3835    cx.assert_editor_state(indoc! {"
 3836        «Aaaˇ»
 3837    "});
 3838}
 3839
 3840#[gpui::test]
 3841async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3842    init_test(cx, |_| {});
 3843
 3844    let mut cx = EditorTestContext::new(cx).await;
 3845
 3846    // Manipulate with multiple selections on a single line
 3847    cx.set_state(indoc! {"
 3848        dd«dd
 3849        cˇ»c«c
 3850        bb
 3851        aaaˇ»aa
 3852    "});
 3853    cx.update_editor(|e, window, cx| {
 3854        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3855    });
 3856    cx.assert_editor_state(indoc! {"
 3857        «aaaaa
 3858        bb
 3859        ccc
 3860        ddddˇ»
 3861    "});
 3862
 3863    // Manipulate with multiple disjoin selections
 3864    cx.set_state(indoc! {"
 3865 3866        4
 3867        3
 3868        2
 3869        1ˇ»
 3870
 3871        dd«dd
 3872        ccc
 3873        bb
 3874        aaaˇ»aa
 3875    "});
 3876    cx.update_editor(|e, window, cx| {
 3877        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3878    });
 3879    cx.assert_editor_state(indoc! {"
 3880        «1
 3881        2
 3882        3
 3883        4
 3884        5ˇ»
 3885
 3886        «aaaaa
 3887        bb
 3888        ccc
 3889        ddddˇ»
 3890    "});
 3891
 3892    // Adding lines on each selection
 3893    cx.set_state(indoc! {"
 3894 3895        1ˇ»
 3896
 3897        bb«bb
 3898        aaaˇ»aa
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «2
 3905        1
 3906        added lineˇ»
 3907
 3908        «bbbb
 3909        aaaaa
 3910        added lineˇ»
 3911    "});
 3912
 3913    // Removing lines on each selection
 3914    cx.set_state(indoc! {"
 3915 3916        1ˇ»
 3917
 3918        bb«bb
 3919        aaaˇ»aa
 3920    "});
 3921    cx.update_editor(|e, window, cx| {
 3922        e.manipulate_lines(window, cx, |lines| {
 3923            lines.pop();
 3924        })
 3925    });
 3926    cx.assert_editor_state(indoc! {"
 3927        «2ˇ»
 3928
 3929        «bbbbˇ»
 3930    "});
 3931}
 3932
 3933#[gpui::test]
 3934async fn test_toggle_case(cx: &mut TestAppContext) {
 3935    init_test(cx, |_| {});
 3936
 3937    let mut cx = EditorTestContext::new(cx).await;
 3938
 3939    // If all lower case -> upper case
 3940    cx.set_state(indoc! {"
 3941        «hello worldˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3944    cx.assert_editor_state(indoc! {"
 3945        «HELLO WORLDˇ»
 3946    "});
 3947
 3948    // If all upper case -> lower case
 3949    cx.set_state(indoc! {"
 3950        «HELLO WORLDˇ»
 3951    "});
 3952    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3953    cx.assert_editor_state(indoc! {"
 3954        «hello worldˇ»
 3955    "});
 3956
 3957    // If any upper case characters are identified -> lower case
 3958    // This matches JetBrains IDEs
 3959    cx.set_state(indoc! {"
 3960        «hEllo worldˇ»
 3961    "});
 3962    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3963    cx.assert_editor_state(indoc! {"
 3964        «hello worldˇ»
 3965    "});
 3966}
 3967
 3968#[gpui::test]
 3969async fn test_manipulate_text(cx: &mut TestAppContext) {
 3970    init_test(cx, |_| {});
 3971
 3972    let mut cx = EditorTestContext::new(cx).await;
 3973
 3974    // Test convert_to_upper_case()
 3975    cx.set_state(indoc! {"
 3976        «hello worldˇ»
 3977    "});
 3978    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3979    cx.assert_editor_state(indoc! {"
 3980        «HELLO WORLDˇ»
 3981    "});
 3982
 3983    // Test convert_to_lower_case()
 3984    cx.set_state(indoc! {"
 3985        «HELLO WORLDˇ»
 3986    "});
 3987    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3988    cx.assert_editor_state(indoc! {"
 3989        «hello worldˇ»
 3990    "});
 3991
 3992    // Test multiple line, single selection case
 3993    cx.set_state(indoc! {"
 3994        «The quick brown
 3995        fox jumps over
 3996        the lazy dogˇ»
 3997    "});
 3998    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3999    cx.assert_editor_state(indoc! {"
 4000        «The Quick Brown
 4001        Fox Jumps Over
 4002        The Lazy Dogˇ»
 4003    "});
 4004
 4005    // Test multiple line, single selection case
 4006    cx.set_state(indoc! {"
 4007        «The quick brown
 4008        fox jumps over
 4009        the lazy dogˇ»
 4010    "});
 4011    cx.update_editor(|e, window, cx| {
 4012        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4013    });
 4014    cx.assert_editor_state(indoc! {"
 4015        «TheQuickBrown
 4016        FoxJumpsOver
 4017        TheLazyDogˇ»
 4018    "});
 4019
 4020    // From here on out, test more complex cases of manipulate_text()
 4021
 4022    // Test no selection case - should affect words cursors are in
 4023    // Cursor at beginning, middle, and end of word
 4024    cx.set_state(indoc! {"
 4025        ˇhello big beauˇtiful worldˇ
 4026    "});
 4027    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4028    cx.assert_editor_state(indoc! {"
 4029        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4030    "});
 4031
 4032    // Test multiple selections on a single line and across multiple lines
 4033    cx.set_state(indoc! {"
 4034        «Theˇ» quick «brown
 4035        foxˇ» jumps «overˇ»
 4036        the «lazyˇ» dog
 4037    "});
 4038    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4039    cx.assert_editor_state(indoc! {"
 4040        «THEˇ» quick «BROWN
 4041        FOXˇ» jumps «OVERˇ»
 4042        the «LAZYˇ» dog
 4043    "});
 4044
 4045    // Test case where text length grows
 4046    cx.set_state(indoc! {"
 4047        «tschüߡ»
 4048    "});
 4049    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4050    cx.assert_editor_state(indoc! {"
 4051        «TSCHÜSSˇ»
 4052    "});
 4053
 4054    // Test to make sure we don't crash when text shrinks
 4055    cx.set_state(indoc! {"
 4056        aaa_bbbˇ
 4057    "});
 4058    cx.update_editor(|e, window, cx| {
 4059        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4060    });
 4061    cx.assert_editor_state(indoc! {"
 4062        «aaaBbbˇ»
 4063    "});
 4064
 4065    // Test to make sure we all aware of the fact that each word can grow and shrink
 4066    // Final selections should be aware of this fact
 4067    cx.set_state(indoc! {"
 4068        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4069    "});
 4070    cx.update_editor(|e, window, cx| {
 4071        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4072    });
 4073    cx.assert_editor_state(indoc! {"
 4074        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4075    "});
 4076
 4077    cx.set_state(indoc! {"
 4078        «hElLo, WoRld!ˇ»
 4079    "});
 4080    cx.update_editor(|e, window, cx| {
 4081        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4082    });
 4083    cx.assert_editor_state(indoc! {"
 4084        «HeLlO, wOrLD!ˇ»
 4085    "});
 4086}
 4087
 4088#[gpui::test]
 4089fn test_duplicate_line(cx: &mut TestAppContext) {
 4090    init_test(cx, |_| {});
 4091
 4092    let editor = cx.add_window(|window, cx| {
 4093        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4094        build_editor(buffer, window, cx)
 4095    });
 4096    _ = editor.update(cx, |editor, window, cx| {
 4097        editor.change_selections(None, window, cx, |s| {
 4098            s.select_display_ranges([
 4099                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4100                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4101                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4102                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4103            ])
 4104        });
 4105        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4106        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4107        assert_eq!(
 4108            editor.selections.display_ranges(cx),
 4109            vec![
 4110                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4111                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4112                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4113                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4114            ]
 4115        );
 4116    });
 4117
 4118    let editor = cx.add_window(|window, cx| {
 4119        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4120        build_editor(buffer, window, cx)
 4121    });
 4122    _ = editor.update(cx, |editor, window, cx| {
 4123        editor.change_selections(None, window, cx, |s| {
 4124            s.select_display_ranges([
 4125                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4126                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4127            ])
 4128        });
 4129        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4130        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4131        assert_eq!(
 4132            editor.selections.display_ranges(cx),
 4133            vec![
 4134                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4135                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4136            ]
 4137        );
 4138    });
 4139
 4140    // With `move_upwards` the selections stay in place, except for
 4141    // the lines inserted above them
 4142    let editor = cx.add_window(|window, cx| {
 4143        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4144        build_editor(buffer, window, cx)
 4145    });
 4146    _ = editor.update(cx, |editor, window, cx| {
 4147        editor.change_selections(None, window, cx, |s| {
 4148            s.select_display_ranges([
 4149                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4150                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4151                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4152                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4153            ])
 4154        });
 4155        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4156        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4157        assert_eq!(
 4158            editor.selections.display_ranges(cx),
 4159            vec![
 4160                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4161                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4162                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4163                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4164            ]
 4165        );
 4166    });
 4167
 4168    let editor = cx.add_window(|window, cx| {
 4169        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4170        build_editor(buffer, window, cx)
 4171    });
 4172    _ = editor.update(cx, |editor, window, cx| {
 4173        editor.change_selections(None, window, cx, |s| {
 4174            s.select_display_ranges([
 4175                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4176                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4177            ])
 4178        });
 4179        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4180        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4181        assert_eq!(
 4182            editor.selections.display_ranges(cx),
 4183            vec![
 4184                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4185                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4186            ]
 4187        );
 4188    });
 4189
 4190    let editor = cx.add_window(|window, cx| {
 4191        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4192        build_editor(buffer, window, cx)
 4193    });
 4194    _ = editor.update(cx, |editor, window, cx| {
 4195        editor.change_selections(None, window, cx, |s| {
 4196            s.select_display_ranges([
 4197                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4198                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4199            ])
 4200        });
 4201        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4202        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4203        assert_eq!(
 4204            editor.selections.display_ranges(cx),
 4205            vec![
 4206                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4207                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4208            ]
 4209        );
 4210    });
 4211}
 4212
 4213#[gpui::test]
 4214fn test_move_line_up_down(cx: &mut TestAppContext) {
 4215    init_test(cx, |_| {});
 4216
 4217    let editor = cx.add_window(|window, cx| {
 4218        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4219        build_editor(buffer, window, cx)
 4220    });
 4221    _ = editor.update(cx, |editor, window, cx| {
 4222        editor.fold_creases(
 4223            vec![
 4224                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4225                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4226                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4227            ],
 4228            true,
 4229            window,
 4230            cx,
 4231        );
 4232        editor.change_selections(None, window, cx, |s| {
 4233            s.select_display_ranges([
 4234                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4235                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4236                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4237                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4238            ])
 4239        });
 4240        assert_eq!(
 4241            editor.display_text(cx),
 4242            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4243        );
 4244
 4245        editor.move_line_up(&MoveLineUp, window, cx);
 4246        assert_eq!(
 4247            editor.display_text(cx),
 4248            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4249        );
 4250        assert_eq!(
 4251            editor.selections.display_ranges(cx),
 4252            vec![
 4253                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4254                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4255                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4256                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4257            ]
 4258        );
 4259    });
 4260
 4261    _ = editor.update(cx, |editor, window, cx| {
 4262        editor.move_line_down(&MoveLineDown, window, cx);
 4263        assert_eq!(
 4264            editor.display_text(cx),
 4265            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4266        );
 4267        assert_eq!(
 4268            editor.selections.display_ranges(cx),
 4269            vec![
 4270                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4271                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4272                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4273                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4274            ]
 4275        );
 4276    });
 4277
 4278    _ = editor.update(cx, |editor, window, cx| {
 4279        editor.move_line_down(&MoveLineDown, window, cx);
 4280        assert_eq!(
 4281            editor.display_text(cx),
 4282            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4283        );
 4284        assert_eq!(
 4285            editor.selections.display_ranges(cx),
 4286            vec![
 4287                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4288                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4289                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4290                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4291            ]
 4292        );
 4293    });
 4294
 4295    _ = editor.update(cx, |editor, window, cx| {
 4296        editor.move_line_up(&MoveLineUp, window, cx);
 4297        assert_eq!(
 4298            editor.display_text(cx),
 4299            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4300        );
 4301        assert_eq!(
 4302            editor.selections.display_ranges(cx),
 4303            vec![
 4304                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4305                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4306                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4307                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4308            ]
 4309        );
 4310    });
 4311}
 4312
 4313#[gpui::test]
 4314fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4315    init_test(cx, |_| {});
 4316
 4317    let editor = cx.add_window(|window, cx| {
 4318        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4319        build_editor(buffer, window, cx)
 4320    });
 4321    _ = editor.update(cx, |editor, window, cx| {
 4322        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4323        editor.insert_blocks(
 4324            [BlockProperties {
 4325                style: BlockStyle::Fixed,
 4326                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4327                height: Some(1),
 4328                render: Arc::new(|_| div().into_any()),
 4329                priority: 0,
 4330                render_in_minimap: true,
 4331            }],
 4332            Some(Autoscroll::fit()),
 4333            cx,
 4334        );
 4335        editor.change_selections(None, window, cx, |s| {
 4336            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4337        });
 4338        editor.move_line_down(&MoveLineDown, window, cx);
 4339    });
 4340}
 4341
 4342#[gpui::test]
 4343async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4344    init_test(cx, |_| {});
 4345
 4346    let mut cx = EditorTestContext::new(cx).await;
 4347    cx.set_state(
 4348        &"
 4349            ˇzero
 4350            one
 4351            two
 4352            three
 4353            four
 4354            five
 4355        "
 4356        .unindent(),
 4357    );
 4358
 4359    // Create a four-line block that replaces three lines of text.
 4360    cx.update_editor(|editor, window, cx| {
 4361        let snapshot = editor.snapshot(window, cx);
 4362        let snapshot = &snapshot.buffer_snapshot;
 4363        let placement = BlockPlacement::Replace(
 4364            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4365        );
 4366        editor.insert_blocks(
 4367            [BlockProperties {
 4368                placement,
 4369                height: Some(4),
 4370                style: BlockStyle::Sticky,
 4371                render: Arc::new(|_| gpui::div().into_any_element()),
 4372                priority: 0,
 4373                render_in_minimap: true,
 4374            }],
 4375            None,
 4376            cx,
 4377        );
 4378    });
 4379
 4380    // Move down so that the cursor touches the block.
 4381    cx.update_editor(|editor, window, cx| {
 4382        editor.move_down(&Default::default(), window, cx);
 4383    });
 4384    cx.assert_editor_state(
 4385        &"
 4386            zero
 4387            «one
 4388            two
 4389            threeˇ»
 4390            four
 4391            five
 4392        "
 4393        .unindent(),
 4394    );
 4395
 4396    // Move down past the block.
 4397    cx.update_editor(|editor, window, cx| {
 4398        editor.move_down(&Default::default(), window, cx);
 4399    });
 4400    cx.assert_editor_state(
 4401        &"
 4402            zero
 4403            one
 4404            two
 4405            three
 4406            ˇfour
 4407            five
 4408        "
 4409        .unindent(),
 4410    );
 4411}
 4412
 4413#[gpui::test]
 4414fn test_transpose(cx: &mut TestAppContext) {
 4415    init_test(cx, |_| {});
 4416
 4417    _ = cx.add_window(|window, cx| {
 4418        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4419        editor.set_style(EditorStyle::default(), window, cx);
 4420        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4421        editor.transpose(&Default::default(), window, cx);
 4422        assert_eq!(editor.text(cx), "bac");
 4423        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4424
 4425        editor.transpose(&Default::default(), window, cx);
 4426        assert_eq!(editor.text(cx), "bca");
 4427        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4428
 4429        editor.transpose(&Default::default(), window, cx);
 4430        assert_eq!(editor.text(cx), "bac");
 4431        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4432
 4433        editor
 4434    });
 4435
 4436    _ = cx.add_window(|window, cx| {
 4437        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4438        editor.set_style(EditorStyle::default(), window, cx);
 4439        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4440        editor.transpose(&Default::default(), window, cx);
 4441        assert_eq!(editor.text(cx), "acb\nde");
 4442        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4443
 4444        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4445        editor.transpose(&Default::default(), window, cx);
 4446        assert_eq!(editor.text(cx), "acbd\ne");
 4447        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4448
 4449        editor.transpose(&Default::default(), window, cx);
 4450        assert_eq!(editor.text(cx), "acbde\n");
 4451        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4452
 4453        editor.transpose(&Default::default(), window, cx);
 4454        assert_eq!(editor.text(cx), "acbd\ne");
 4455        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4456
 4457        editor
 4458    });
 4459
 4460    _ = cx.add_window(|window, cx| {
 4461        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4462        editor.set_style(EditorStyle::default(), window, cx);
 4463        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4464        editor.transpose(&Default::default(), window, cx);
 4465        assert_eq!(editor.text(cx), "bacd\ne");
 4466        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4467
 4468        editor.transpose(&Default::default(), window, cx);
 4469        assert_eq!(editor.text(cx), "bcade\n");
 4470        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4471
 4472        editor.transpose(&Default::default(), window, cx);
 4473        assert_eq!(editor.text(cx), "bcda\ne");
 4474        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4475
 4476        editor.transpose(&Default::default(), window, cx);
 4477        assert_eq!(editor.text(cx), "bcade\n");
 4478        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4479
 4480        editor.transpose(&Default::default(), window, cx);
 4481        assert_eq!(editor.text(cx), "bcaed\n");
 4482        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4483
 4484        editor
 4485    });
 4486
 4487    _ = cx.add_window(|window, cx| {
 4488        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4489        editor.set_style(EditorStyle::default(), window, cx);
 4490        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4491        editor.transpose(&Default::default(), window, cx);
 4492        assert_eq!(editor.text(cx), "🏀🍐✋");
 4493        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4494
 4495        editor.transpose(&Default::default(), window, cx);
 4496        assert_eq!(editor.text(cx), "🏀✋🍐");
 4497        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4498
 4499        editor.transpose(&Default::default(), window, cx);
 4500        assert_eq!(editor.text(cx), "🏀🍐✋");
 4501        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4502
 4503        editor
 4504    });
 4505}
 4506
 4507#[gpui::test]
 4508async fn test_rewrap(cx: &mut TestAppContext) {
 4509    init_test(cx, |settings| {
 4510        settings.languages.extend([
 4511            (
 4512                "Markdown".into(),
 4513                LanguageSettingsContent {
 4514                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4515                    ..Default::default()
 4516                },
 4517            ),
 4518            (
 4519                "Plain Text".into(),
 4520                LanguageSettingsContent {
 4521                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4522                    ..Default::default()
 4523                },
 4524            ),
 4525        ])
 4526    });
 4527
 4528    let mut cx = EditorTestContext::new(cx).await;
 4529
 4530    let language_with_c_comments = Arc::new(Language::new(
 4531        LanguageConfig {
 4532            line_comments: vec!["// ".into()],
 4533            ..LanguageConfig::default()
 4534        },
 4535        None,
 4536    ));
 4537    let language_with_pound_comments = Arc::new(Language::new(
 4538        LanguageConfig {
 4539            line_comments: vec!["# ".into()],
 4540            ..LanguageConfig::default()
 4541        },
 4542        None,
 4543    ));
 4544    let markdown_language = Arc::new(Language::new(
 4545        LanguageConfig {
 4546            name: "Markdown".into(),
 4547            ..LanguageConfig::default()
 4548        },
 4549        None,
 4550    ));
 4551    let language_with_doc_comments = Arc::new(Language::new(
 4552        LanguageConfig {
 4553            line_comments: vec!["// ".into(), "/// ".into()],
 4554            ..LanguageConfig::default()
 4555        },
 4556        Some(tree_sitter_rust::LANGUAGE.into()),
 4557    ));
 4558
 4559    let plaintext_language = Arc::new(Language::new(
 4560        LanguageConfig {
 4561            name: "Plain Text".into(),
 4562            ..LanguageConfig::default()
 4563        },
 4564        None,
 4565    ));
 4566
 4567    assert_rewrap(
 4568        indoc! {"
 4569            // ˇ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.
 4570        "},
 4571        indoc! {"
 4572            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4573            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4574            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4575            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4576            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4577            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4578            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4579            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4580            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4581            // porttitor id. Aliquam id accumsan eros.
 4582        "},
 4583        language_with_c_comments.clone(),
 4584        &mut cx,
 4585    );
 4586
 4587    // Test that rewrapping works inside of a selection
 4588    assert_rewrap(
 4589        indoc! {"
 4590            «// 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.ˇ»
 4591        "},
 4592        indoc! {"
 4593            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4594            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4595            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4596            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4597            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4598            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4599            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4600            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4601            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4602            // porttitor id. Aliquam id accumsan eros.ˇ»
 4603        "},
 4604        language_with_c_comments.clone(),
 4605        &mut cx,
 4606    );
 4607
 4608    // Test that cursors that expand to the same region are collapsed.
 4609    assert_rewrap(
 4610        indoc! {"
 4611            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4612            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4613            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4614            // ˇ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.
 4615        "},
 4616        indoc! {"
 4617            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4618            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4619            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4620            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4621            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4622            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4623            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4624            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4625            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4626            // porttitor id. Aliquam id accumsan eros.
 4627        "},
 4628        language_with_c_comments.clone(),
 4629        &mut cx,
 4630    );
 4631
 4632    // Test that non-contiguous selections are treated separately.
 4633    assert_rewrap(
 4634        indoc! {"
 4635            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4636            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4637            //
 4638            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4639            // ˇ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.
 4640        "},
 4641        indoc! {"
 4642            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4643            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4644            // auctor, eu lacinia sapien scelerisque.
 4645            //
 4646            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4647            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4648            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4649            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4650            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4651            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4652            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4653        "},
 4654        language_with_c_comments.clone(),
 4655        &mut cx,
 4656    );
 4657
 4658    // Test that different comment prefixes are supported.
 4659    assert_rewrap(
 4660        indoc! {"
 4661            # ˇ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.
 4662        "},
 4663        indoc! {"
 4664            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4665            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4666            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4667            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4668            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4669            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4670            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4671            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4672            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4673            # accumsan eros.
 4674        "},
 4675        language_with_pound_comments.clone(),
 4676        &mut cx,
 4677    );
 4678
 4679    // Test that rewrapping is ignored outside of comments in most languages.
 4680    assert_rewrap(
 4681        indoc! {"
 4682            /// Adds two numbers.
 4683            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4684            fn add(a: u32, b: u32) -> u32 {
 4685                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ˇ
 4686            }
 4687        "},
 4688        indoc! {"
 4689            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4690            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4691            fn add(a: u32, b: u32) -> u32 {
 4692                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ˇ
 4693            }
 4694        "},
 4695        language_with_doc_comments.clone(),
 4696        &mut cx,
 4697    );
 4698
 4699    // Test that rewrapping works in Markdown and Plain Text languages.
 4700    assert_rewrap(
 4701        indoc! {"
 4702            # Hello
 4703
 4704            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.
 4705        "},
 4706        indoc! {"
 4707            # Hello
 4708
 4709            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4710            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4711            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4712            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4713            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4714            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4715            Integer sit amet scelerisque nisi.
 4716        "},
 4717        markdown_language,
 4718        &mut cx,
 4719    );
 4720
 4721    assert_rewrap(
 4722        indoc! {"
 4723            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.
 4724        "},
 4725        indoc! {"
 4726            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4727            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4728            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4729            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4730            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4731            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4732            Integer sit amet scelerisque nisi.
 4733        "},
 4734        plaintext_language,
 4735        &mut cx,
 4736    );
 4737
 4738    // Test rewrapping unaligned comments in a selection.
 4739    assert_rewrap(
 4740        indoc! {"
 4741            fn foo() {
 4742                if true {
 4743            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4744            // Praesent semper egestas tellus id dignissim.ˇ»
 4745                    do_something();
 4746                } else {
 4747                    //
 4748                }
 4749            }
 4750        "},
 4751        indoc! {"
 4752            fn foo() {
 4753                if true {
 4754            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4755                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4756                    // egestas tellus id dignissim.ˇ»
 4757                    do_something();
 4758                } else {
 4759                    //
 4760                }
 4761            }
 4762        "},
 4763        language_with_doc_comments.clone(),
 4764        &mut cx,
 4765    );
 4766
 4767    assert_rewrap(
 4768        indoc! {"
 4769            fn foo() {
 4770                if true {
 4771            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4772            // Praesent semper egestas tellus id dignissim.»
 4773                    do_something();
 4774                } else {
 4775                    //
 4776                }
 4777
 4778            }
 4779        "},
 4780        indoc! {"
 4781            fn foo() {
 4782                if true {
 4783            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4784                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4785                    // egestas tellus id dignissim.»
 4786                    do_something();
 4787                } else {
 4788                    //
 4789                }
 4790
 4791            }
 4792        "},
 4793        language_with_doc_comments.clone(),
 4794        &mut cx,
 4795    );
 4796
 4797    #[track_caller]
 4798    fn assert_rewrap(
 4799        unwrapped_text: &str,
 4800        wrapped_text: &str,
 4801        language: Arc<Language>,
 4802        cx: &mut EditorTestContext,
 4803    ) {
 4804        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4805        cx.set_state(unwrapped_text);
 4806        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4807        cx.assert_editor_state(wrapped_text);
 4808    }
 4809}
 4810
 4811#[gpui::test]
 4812async fn test_hard_wrap(cx: &mut TestAppContext) {
 4813    init_test(cx, |_| {});
 4814    let mut cx = EditorTestContext::new(cx).await;
 4815
 4816    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4817    cx.update_editor(|editor, _, cx| {
 4818        editor.set_hard_wrap(Some(14), cx);
 4819    });
 4820
 4821    cx.set_state(indoc!(
 4822        "
 4823        one two three ˇ
 4824        "
 4825    ));
 4826    cx.simulate_input("four");
 4827    cx.run_until_parked();
 4828
 4829    cx.assert_editor_state(indoc!(
 4830        "
 4831        one two three
 4832        fourˇ
 4833        "
 4834    ));
 4835
 4836    cx.update_editor(|editor, window, cx| {
 4837        editor.newline(&Default::default(), window, cx);
 4838    });
 4839    cx.run_until_parked();
 4840    cx.assert_editor_state(indoc!(
 4841        "
 4842        one two three
 4843        four
 4844        ˇ
 4845        "
 4846    ));
 4847
 4848    cx.simulate_input("five");
 4849    cx.run_until_parked();
 4850    cx.assert_editor_state(indoc!(
 4851        "
 4852        one two three
 4853        four
 4854        fiveˇ
 4855        "
 4856    ));
 4857
 4858    cx.update_editor(|editor, window, cx| {
 4859        editor.newline(&Default::default(), window, cx);
 4860    });
 4861    cx.run_until_parked();
 4862    cx.simulate_input("# ");
 4863    cx.run_until_parked();
 4864    cx.assert_editor_state(indoc!(
 4865        "
 4866        one two three
 4867        four
 4868        five
 4869        # ˇ
 4870        "
 4871    ));
 4872
 4873    cx.update_editor(|editor, window, cx| {
 4874        editor.newline(&Default::default(), window, cx);
 4875    });
 4876    cx.run_until_parked();
 4877    cx.assert_editor_state(indoc!(
 4878        "
 4879        one two three
 4880        four
 4881        five
 4882        #\x20
 4883 4884        "
 4885    ));
 4886
 4887    cx.simulate_input(" 6");
 4888    cx.run_until_parked();
 4889    cx.assert_editor_state(indoc!(
 4890        "
 4891        one two three
 4892        four
 4893        five
 4894        #
 4895        # 6ˇ
 4896        "
 4897    ));
 4898}
 4899
 4900#[gpui::test]
 4901async fn test_clipboard(cx: &mut TestAppContext) {
 4902    init_test(cx, |_| {});
 4903
 4904    let mut cx = EditorTestContext::new(cx).await;
 4905
 4906    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4907    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4908    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4909
 4910    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4911    cx.set_state("two ˇfour ˇsix ˇ");
 4912    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4913    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4914
 4915    // Paste again but with only two cursors. Since the number of cursors doesn't
 4916    // match the number of slices in the clipboard, the entire clipboard text
 4917    // is pasted at each cursor.
 4918    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4919    cx.update_editor(|e, window, cx| {
 4920        e.handle_input("( ", window, cx);
 4921        e.paste(&Paste, window, cx);
 4922        e.handle_input(") ", window, cx);
 4923    });
 4924    cx.assert_editor_state(
 4925        &([
 4926            "( one✅ ",
 4927            "three ",
 4928            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4929            "three ",
 4930            "five ) ˇ",
 4931        ]
 4932        .join("\n")),
 4933    );
 4934
 4935    // Cut with three selections, one of which is full-line.
 4936    cx.set_state(indoc! {"
 4937        1«2ˇ»3
 4938        4ˇ567
 4939        «8ˇ»9"});
 4940    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4941    cx.assert_editor_state(indoc! {"
 4942        1ˇ3
 4943        ˇ9"});
 4944
 4945    // Paste with three selections, noticing how the copied selection that was full-line
 4946    // gets inserted before the second cursor.
 4947    cx.set_state(indoc! {"
 4948        1ˇ3
 4949 4950        «oˇ»ne"});
 4951    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4952    cx.assert_editor_state(indoc! {"
 4953        12ˇ3
 4954        4567
 4955 4956        8ˇne"});
 4957
 4958    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4959    cx.set_state(indoc! {"
 4960        The quick brown
 4961        fox juˇmps over
 4962        the lazy dog"});
 4963    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4964    assert_eq!(
 4965        cx.read_from_clipboard()
 4966            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4967        Some("fox jumps over\n".to_string())
 4968    );
 4969
 4970    // Paste with three selections, noticing how the copied full-line selection is inserted
 4971    // before the empty selections but replaces the selection that is non-empty.
 4972    cx.set_state(indoc! {"
 4973        Tˇhe quick brown
 4974        «foˇ»x jumps over
 4975        tˇhe lazy dog"});
 4976    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4977    cx.assert_editor_state(indoc! {"
 4978        fox jumps over
 4979        Tˇhe quick brown
 4980        fox jumps over
 4981        ˇx jumps over
 4982        fox jumps over
 4983        tˇhe lazy dog"});
 4984}
 4985
 4986#[gpui::test]
 4987async fn test_copy_trim(cx: &mut TestAppContext) {
 4988    init_test(cx, |_| {});
 4989
 4990    let mut cx = EditorTestContext::new(cx).await;
 4991    cx.set_state(
 4992        r#"            «for selection in selections.iter() {
 4993            let mut start = selection.start;
 4994            let mut end = selection.end;
 4995            let is_entire_line = selection.is_empty();
 4996            if is_entire_line {
 4997                start = Point::new(start.row, 0);ˇ»
 4998                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4999            }
 5000        "#,
 5001    );
 5002    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5003    assert_eq!(
 5004        cx.read_from_clipboard()
 5005            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5006        Some(
 5007            "for selection in selections.iter() {
 5008            let mut start = selection.start;
 5009            let mut end = selection.end;
 5010            let is_entire_line = selection.is_empty();
 5011            if is_entire_line {
 5012                start = Point::new(start.row, 0);"
 5013                .to_string()
 5014        ),
 5015        "Regular copying preserves all indentation selected",
 5016    );
 5017    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5018    assert_eq!(
 5019        cx.read_from_clipboard()
 5020            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5021        Some(
 5022            "for selection in selections.iter() {
 5023let mut start = selection.start;
 5024let mut end = selection.end;
 5025let is_entire_line = selection.is_empty();
 5026if is_entire_line {
 5027    start = Point::new(start.row, 0);"
 5028                .to_string()
 5029        ),
 5030        "Copying with stripping should strip all leading whitespaces"
 5031    );
 5032
 5033    cx.set_state(
 5034        r#"       «     for selection in selections.iter() {
 5035            let mut start = selection.start;
 5036            let mut end = selection.end;
 5037            let is_entire_line = selection.is_empty();
 5038            if is_entire_line {
 5039                start = Point::new(start.row, 0);ˇ»
 5040                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5041            }
 5042        "#,
 5043    );
 5044    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5045    assert_eq!(
 5046        cx.read_from_clipboard()
 5047            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5048        Some(
 5049            "     for selection in selections.iter() {
 5050            let mut start = selection.start;
 5051            let mut end = selection.end;
 5052            let is_entire_line = selection.is_empty();
 5053            if is_entire_line {
 5054                start = Point::new(start.row, 0);"
 5055                .to_string()
 5056        ),
 5057        "Regular copying preserves all indentation selected",
 5058    );
 5059    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5060    assert_eq!(
 5061        cx.read_from_clipboard()
 5062            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5063        Some(
 5064            "for selection in selections.iter() {
 5065let mut start = selection.start;
 5066let mut end = selection.end;
 5067let is_entire_line = selection.is_empty();
 5068if is_entire_line {
 5069    start = Point::new(start.row, 0);"
 5070                .to_string()
 5071        ),
 5072        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5073    );
 5074
 5075    cx.set_state(
 5076        r#"       «ˇ     for selection in selections.iter() {
 5077            let mut start = selection.start;
 5078            let mut end = selection.end;
 5079            let is_entire_line = selection.is_empty();
 5080            if is_entire_line {
 5081                start = Point::new(start.row, 0);»
 5082                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5083            }
 5084        "#,
 5085    );
 5086    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5087    assert_eq!(
 5088        cx.read_from_clipboard()
 5089            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5090        Some(
 5091            "     for selection in selections.iter() {
 5092            let mut start = selection.start;
 5093            let mut end = selection.end;
 5094            let is_entire_line = selection.is_empty();
 5095            if is_entire_line {
 5096                start = Point::new(start.row, 0);"
 5097                .to_string()
 5098        ),
 5099        "Regular copying for reverse selection works the same",
 5100    );
 5101    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5102    assert_eq!(
 5103        cx.read_from_clipboard()
 5104            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5105        Some(
 5106            "for selection in selections.iter() {
 5107let mut start = selection.start;
 5108let mut end = selection.end;
 5109let is_entire_line = selection.is_empty();
 5110if is_entire_line {
 5111    start = Point::new(start.row, 0);"
 5112                .to_string()
 5113        ),
 5114        "Copying with stripping for reverse selection works the same"
 5115    );
 5116
 5117    cx.set_state(
 5118        r#"            for selection «in selections.iter() {
 5119            let mut start = selection.start;
 5120            let mut end = selection.end;
 5121            let is_entire_line = selection.is_empty();
 5122            if is_entire_line {
 5123                start = Point::new(start.row, 0);ˇ»
 5124                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5125            }
 5126        "#,
 5127    );
 5128    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5129    assert_eq!(
 5130        cx.read_from_clipboard()
 5131            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5132        Some(
 5133            "in selections.iter() {
 5134            let mut start = selection.start;
 5135            let mut end = selection.end;
 5136            let is_entire_line = selection.is_empty();
 5137            if is_entire_line {
 5138                start = Point::new(start.row, 0);"
 5139                .to_string()
 5140        ),
 5141        "When selecting past the indent, the copying works as usual",
 5142    );
 5143    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5144    assert_eq!(
 5145        cx.read_from_clipboard()
 5146            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5147        Some(
 5148            "in selections.iter() {
 5149            let mut start = selection.start;
 5150            let mut end = selection.end;
 5151            let is_entire_line = selection.is_empty();
 5152            if is_entire_line {
 5153                start = Point::new(start.row, 0);"
 5154                .to_string()
 5155        ),
 5156        "When selecting past the indent, nothing is trimmed"
 5157    );
 5158
 5159    cx.set_state(
 5160        r#"            «for selection in selections.iter() {
 5161            let mut start = selection.start;
 5162
 5163            let mut end = selection.end;
 5164            let is_entire_line = selection.is_empty();
 5165            if is_entire_line {
 5166                start = Point::new(start.row, 0);
 5167ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5168            }
 5169        "#,
 5170    );
 5171    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5172    assert_eq!(
 5173        cx.read_from_clipboard()
 5174            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5175        Some(
 5176            "for selection in selections.iter() {
 5177let mut start = selection.start;
 5178
 5179let mut end = selection.end;
 5180let is_entire_line = selection.is_empty();
 5181if is_entire_line {
 5182    start = Point::new(start.row, 0);
 5183"
 5184            .to_string()
 5185        ),
 5186        "Copying with stripping should ignore empty lines"
 5187    );
 5188}
 5189
 5190#[gpui::test]
 5191async fn test_paste_multiline(cx: &mut TestAppContext) {
 5192    init_test(cx, |_| {});
 5193
 5194    let mut cx = EditorTestContext::new(cx).await;
 5195    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5196
 5197    // Cut an indented block, without the leading whitespace.
 5198    cx.set_state(indoc! {"
 5199        const a: B = (
 5200            c(),
 5201            «d(
 5202                e,
 5203                f
 5204            )ˇ»
 5205        );
 5206    "});
 5207    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5208    cx.assert_editor_state(indoc! {"
 5209        const a: B = (
 5210            c(),
 5211            ˇ
 5212        );
 5213    "});
 5214
 5215    // Paste it at the same position.
 5216    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5217    cx.assert_editor_state(indoc! {"
 5218        const a: B = (
 5219            c(),
 5220            d(
 5221                e,
 5222                f
 5223 5224        );
 5225    "});
 5226
 5227    // Paste it at a line with a lower indent level.
 5228    cx.set_state(indoc! {"
 5229        ˇ
 5230        const a: B = (
 5231            c(),
 5232        );
 5233    "});
 5234    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5235    cx.assert_editor_state(indoc! {"
 5236        d(
 5237            e,
 5238            f
 5239 5240        const a: B = (
 5241            c(),
 5242        );
 5243    "});
 5244
 5245    // Cut an indented block, with the leading whitespace.
 5246    cx.set_state(indoc! {"
 5247        const a: B = (
 5248            c(),
 5249        «    d(
 5250                e,
 5251                f
 5252            )
 5253        ˇ»);
 5254    "});
 5255    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5256    cx.assert_editor_state(indoc! {"
 5257        const a: B = (
 5258            c(),
 5259        ˇ);
 5260    "});
 5261
 5262    // Paste it at the same position.
 5263    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5264    cx.assert_editor_state(indoc! {"
 5265        const a: B = (
 5266            c(),
 5267            d(
 5268                e,
 5269                f
 5270            )
 5271        ˇ);
 5272    "});
 5273
 5274    // Paste it at a line with a higher indent level.
 5275    cx.set_state(indoc! {"
 5276        const a: B = (
 5277            c(),
 5278            d(
 5279                e,
 5280 5281            )
 5282        );
 5283    "});
 5284    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5285    cx.assert_editor_state(indoc! {"
 5286        const a: B = (
 5287            c(),
 5288            d(
 5289                e,
 5290                f    d(
 5291                    e,
 5292                    f
 5293                )
 5294        ˇ
 5295            )
 5296        );
 5297    "});
 5298
 5299    // Copy an indented block, starting mid-line
 5300    cx.set_state(indoc! {"
 5301        const a: B = (
 5302            c(),
 5303            somethin«g(
 5304                e,
 5305                f
 5306            )ˇ»
 5307        );
 5308    "});
 5309    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5310
 5311    // Paste it on a line with a lower indent level
 5312    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5313    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5314    cx.assert_editor_state(indoc! {"
 5315        const a: B = (
 5316            c(),
 5317            something(
 5318                e,
 5319                f
 5320            )
 5321        );
 5322        g(
 5323            e,
 5324            f
 5325"});
 5326}
 5327
 5328#[gpui::test]
 5329async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5330    init_test(cx, |_| {});
 5331
 5332    cx.write_to_clipboard(ClipboardItem::new_string(
 5333        "    d(\n        e\n    );\n".into(),
 5334    ));
 5335
 5336    let mut cx = EditorTestContext::new(cx).await;
 5337    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5338
 5339    cx.set_state(indoc! {"
 5340        fn a() {
 5341            b();
 5342            if c() {
 5343                ˇ
 5344            }
 5345        }
 5346    "});
 5347
 5348    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5349    cx.assert_editor_state(indoc! {"
 5350        fn a() {
 5351            b();
 5352            if c() {
 5353                d(
 5354                    e
 5355                );
 5356        ˇ
 5357            }
 5358        }
 5359    "});
 5360
 5361    cx.set_state(indoc! {"
 5362        fn a() {
 5363            b();
 5364            ˇ
 5365        }
 5366    "});
 5367
 5368    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5369    cx.assert_editor_state(indoc! {"
 5370        fn a() {
 5371            b();
 5372            d(
 5373                e
 5374            );
 5375        ˇ
 5376        }
 5377    "});
 5378}
 5379
 5380#[gpui::test]
 5381fn test_select_all(cx: &mut TestAppContext) {
 5382    init_test(cx, |_| {});
 5383
 5384    let editor = cx.add_window(|window, cx| {
 5385        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5386        build_editor(buffer, window, cx)
 5387    });
 5388    _ = editor.update(cx, |editor, window, cx| {
 5389        editor.select_all(&SelectAll, window, cx);
 5390        assert_eq!(
 5391            editor.selections.display_ranges(cx),
 5392            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5393        );
 5394    });
 5395}
 5396
 5397#[gpui::test]
 5398fn test_select_line(cx: &mut TestAppContext) {
 5399    init_test(cx, |_| {});
 5400
 5401    let editor = cx.add_window(|window, cx| {
 5402        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5403        build_editor(buffer, window, cx)
 5404    });
 5405    _ = editor.update(cx, |editor, window, cx| {
 5406        editor.change_selections(None, window, cx, |s| {
 5407            s.select_display_ranges([
 5408                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5409                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5410                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5411                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5412            ])
 5413        });
 5414        editor.select_line(&SelectLine, window, cx);
 5415        assert_eq!(
 5416            editor.selections.display_ranges(cx),
 5417            vec![
 5418                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5419                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5420            ]
 5421        );
 5422    });
 5423
 5424    _ = editor.update(cx, |editor, window, cx| {
 5425        editor.select_line(&SelectLine, window, cx);
 5426        assert_eq!(
 5427            editor.selections.display_ranges(cx),
 5428            vec![
 5429                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5430                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5431            ]
 5432        );
 5433    });
 5434
 5435    _ = editor.update(cx, |editor, window, cx| {
 5436        editor.select_line(&SelectLine, window, cx);
 5437        assert_eq!(
 5438            editor.selections.display_ranges(cx),
 5439            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5440        );
 5441    });
 5442}
 5443
 5444#[gpui::test]
 5445async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5446    init_test(cx, |_| {});
 5447    let mut cx = EditorTestContext::new(cx).await;
 5448
 5449    #[track_caller]
 5450    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5451        cx.set_state(initial_state);
 5452        cx.update_editor(|e, window, cx| {
 5453            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5454        });
 5455        cx.assert_editor_state(expected_state);
 5456    }
 5457
 5458    // Selection starts and ends at the middle of lines, left-to-right
 5459    test(
 5460        &mut cx,
 5461        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5462        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5463    );
 5464    // Same thing, right-to-left
 5465    test(
 5466        &mut cx,
 5467        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5468        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5469    );
 5470
 5471    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5472    test(
 5473        &mut cx,
 5474        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5475        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5476    );
 5477    // Same thing, right-to-left
 5478    test(
 5479        &mut cx,
 5480        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5481        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5482    );
 5483
 5484    // Whole buffer, left-to-right, last line ends with newline
 5485    test(
 5486        &mut cx,
 5487        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5488        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5489    );
 5490    // Same thing, right-to-left
 5491    test(
 5492        &mut cx,
 5493        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5494        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5495    );
 5496
 5497    // Starts at the end of a line, ends at the start of another
 5498    test(
 5499        &mut cx,
 5500        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5501        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5502    );
 5503}
 5504
 5505#[gpui::test]
 5506async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5507    init_test(cx, |_| {});
 5508
 5509    let editor = cx.add_window(|window, cx| {
 5510        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5511        build_editor(buffer, window, cx)
 5512    });
 5513
 5514    // setup
 5515    _ = editor.update(cx, |editor, window, cx| {
 5516        editor.fold_creases(
 5517            vec![
 5518                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5519                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5520                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5521            ],
 5522            true,
 5523            window,
 5524            cx,
 5525        );
 5526        assert_eq!(
 5527            editor.display_text(cx),
 5528            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5529        );
 5530    });
 5531
 5532    _ = editor.update(cx, |editor, window, cx| {
 5533        editor.change_selections(None, window, cx, |s| {
 5534            s.select_display_ranges([
 5535                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5536                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5537                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5538                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5539            ])
 5540        });
 5541        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5542        assert_eq!(
 5543            editor.display_text(cx),
 5544            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5545        );
 5546    });
 5547    EditorTestContext::for_editor(editor, cx)
 5548        .await
 5549        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5550
 5551    _ = editor.update(cx, |editor, window, cx| {
 5552        editor.change_selections(None, window, cx, |s| {
 5553            s.select_display_ranges([
 5554                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5555            ])
 5556        });
 5557        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5558        assert_eq!(
 5559            editor.display_text(cx),
 5560            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5561        );
 5562        assert_eq!(
 5563            editor.selections.display_ranges(cx),
 5564            [
 5565                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5566                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5567                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5568                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5569                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5570                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5571                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5572            ]
 5573        );
 5574    });
 5575    EditorTestContext::for_editor(editor, cx)
 5576        .await
 5577        .assert_editor_state(
 5578            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5579        );
 5580}
 5581
 5582#[gpui::test]
 5583async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5584    init_test(cx, |_| {});
 5585
 5586    let mut cx = EditorTestContext::new(cx).await;
 5587
 5588    cx.set_state(indoc!(
 5589        r#"abc
 5590           defˇghi
 5591
 5592           jk
 5593           nlmo
 5594           "#
 5595    ));
 5596
 5597    cx.update_editor(|editor, window, cx| {
 5598        editor.add_selection_above(&Default::default(), window, cx);
 5599    });
 5600
 5601    cx.assert_editor_state(indoc!(
 5602        r#"abcˇ
 5603           defˇghi
 5604
 5605           jk
 5606           nlmo
 5607           "#
 5608    ));
 5609
 5610    cx.update_editor(|editor, window, cx| {
 5611        editor.add_selection_above(&Default::default(), window, cx);
 5612    });
 5613
 5614    cx.assert_editor_state(indoc!(
 5615        r#"abcˇ
 5616            defˇghi
 5617
 5618            jk
 5619            nlmo
 5620            "#
 5621    ));
 5622
 5623    cx.update_editor(|editor, window, cx| {
 5624        editor.add_selection_below(&Default::default(), window, cx);
 5625    });
 5626
 5627    cx.assert_editor_state(indoc!(
 5628        r#"abc
 5629           defˇghi
 5630
 5631           jk
 5632           nlmo
 5633           "#
 5634    ));
 5635
 5636    cx.update_editor(|editor, window, cx| {
 5637        editor.undo_selection(&Default::default(), window, cx);
 5638    });
 5639
 5640    cx.assert_editor_state(indoc!(
 5641        r#"abcˇ
 5642           defˇghi
 5643
 5644           jk
 5645           nlmo
 5646           "#
 5647    ));
 5648
 5649    cx.update_editor(|editor, window, cx| {
 5650        editor.redo_selection(&Default::default(), window, cx);
 5651    });
 5652
 5653    cx.assert_editor_state(indoc!(
 5654        r#"abc
 5655           defˇghi
 5656
 5657           jk
 5658           nlmo
 5659           "#
 5660    ));
 5661
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_below(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"abc
 5668           defˇghi
 5669
 5670           jk
 5671           nlmˇo
 5672           "#
 5673    ));
 5674
 5675    cx.update_editor(|editor, window, cx| {
 5676        editor.add_selection_below(&Default::default(), window, cx);
 5677    });
 5678
 5679    cx.assert_editor_state(indoc!(
 5680        r#"abc
 5681           defˇghi
 5682
 5683           jk
 5684           nlmˇo
 5685           "#
 5686    ));
 5687
 5688    // change selections
 5689    cx.set_state(indoc!(
 5690        r#"abc
 5691           def«ˇg»hi
 5692
 5693           jk
 5694           nlmo
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"abc
 5704           def«ˇg»hi
 5705
 5706           jk
 5707           nlm«ˇo»
 5708           "#
 5709    ));
 5710
 5711    cx.update_editor(|editor, window, cx| {
 5712        editor.add_selection_below(&Default::default(), window, cx);
 5713    });
 5714
 5715    cx.assert_editor_state(indoc!(
 5716        r#"abc
 5717           def«ˇg»hi
 5718
 5719           jk
 5720           nlm«ˇo»
 5721           "#
 5722    ));
 5723
 5724    cx.update_editor(|editor, window, cx| {
 5725        editor.add_selection_above(&Default::default(), window, cx);
 5726    });
 5727
 5728    cx.assert_editor_state(indoc!(
 5729        r#"abc
 5730           def«ˇg»hi
 5731
 5732           jk
 5733           nlmo
 5734           "#
 5735    ));
 5736
 5737    cx.update_editor(|editor, window, cx| {
 5738        editor.add_selection_above(&Default::default(), window, cx);
 5739    });
 5740
 5741    cx.assert_editor_state(indoc!(
 5742        r#"abc
 5743           def«ˇg»hi
 5744
 5745           jk
 5746           nlmo
 5747           "#
 5748    ));
 5749
 5750    // Change selections again
 5751    cx.set_state(indoc!(
 5752        r#"a«bc
 5753           defgˇ»hi
 5754
 5755           jk
 5756           nlmo
 5757           "#
 5758    ));
 5759
 5760    cx.update_editor(|editor, window, cx| {
 5761        editor.add_selection_below(&Default::default(), window, cx);
 5762    });
 5763
 5764    cx.assert_editor_state(indoc!(
 5765        r#"a«bcˇ»
 5766           d«efgˇ»hi
 5767
 5768           j«kˇ»
 5769           nlmo
 5770           "#
 5771    ));
 5772
 5773    cx.update_editor(|editor, window, cx| {
 5774        editor.add_selection_below(&Default::default(), window, cx);
 5775    });
 5776    cx.assert_editor_state(indoc!(
 5777        r#"a«bcˇ»
 5778           d«efgˇ»hi
 5779
 5780           j«kˇ»
 5781           n«lmoˇ»
 5782           "#
 5783    ));
 5784    cx.update_editor(|editor, window, cx| {
 5785        editor.add_selection_above(&Default::default(), window, cx);
 5786    });
 5787
 5788    cx.assert_editor_state(indoc!(
 5789        r#"a«bcˇ»
 5790           d«efgˇ»hi
 5791
 5792           j«kˇ»
 5793           nlmo
 5794           "#
 5795    ));
 5796
 5797    // Change selections again
 5798    cx.set_state(indoc!(
 5799        r#"abc
 5800           d«ˇefghi
 5801
 5802           jk
 5803           nlm»o
 5804           "#
 5805    ));
 5806
 5807    cx.update_editor(|editor, window, cx| {
 5808        editor.add_selection_above(&Default::default(), window, cx);
 5809    });
 5810
 5811    cx.assert_editor_state(indoc!(
 5812        r#"a«ˇbc»
 5813           d«ˇef»ghi
 5814
 5815           j«ˇk»
 5816           n«ˇlm»o
 5817           "#
 5818    ));
 5819
 5820    cx.update_editor(|editor, window, cx| {
 5821        editor.add_selection_below(&Default::default(), window, cx);
 5822    });
 5823
 5824    cx.assert_editor_state(indoc!(
 5825        r#"abc
 5826           d«ˇef»ghi
 5827
 5828           j«ˇk»
 5829           n«ˇlm»o
 5830           "#
 5831    ));
 5832}
 5833
 5834#[gpui::test]
 5835async fn test_select_next(cx: &mut TestAppContext) {
 5836    init_test(cx, |_| {});
 5837
 5838    let mut cx = EditorTestContext::new(cx).await;
 5839    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5840
 5841    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5842        .unwrap();
 5843    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5844
 5845    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5846        .unwrap();
 5847    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5848
 5849    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5850    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5851
 5852    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5853    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5854
 5855    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5856        .unwrap();
 5857    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5858
 5859    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5860        .unwrap();
 5861    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5862
 5863    // Test selection direction should be preserved
 5864    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5865
 5866    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5867        .unwrap();
 5868    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5869}
 5870
 5871#[gpui::test]
 5872async fn test_select_all_matches(cx: &mut TestAppContext) {
 5873    init_test(cx, |_| {});
 5874
 5875    let mut cx = EditorTestContext::new(cx).await;
 5876
 5877    // Test caret-only selections
 5878    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5879    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5880        .unwrap();
 5881    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5882
 5883    // Test left-to-right selections
 5884    cx.set_state("abc\n«abcˇ»\nabc");
 5885    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5886        .unwrap();
 5887    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5888
 5889    // Test right-to-left selections
 5890    cx.set_state("abc\n«ˇabc»\nabc");
 5891    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5892        .unwrap();
 5893    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5894
 5895    // Test selecting whitespace with caret selection
 5896    cx.set_state("abc\nˇ   abc\nabc");
 5897    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5898        .unwrap();
 5899    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5900
 5901    // Test selecting whitespace with left-to-right selection
 5902    cx.set_state("abc\n«ˇ  »abc\nabc");
 5903    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5904        .unwrap();
 5905    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5906
 5907    // Test no matches with right-to-left selection
 5908    cx.set_state("abc\n«  ˇ»abc\nabc");
 5909    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5910        .unwrap();
 5911    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5912}
 5913
 5914#[gpui::test]
 5915async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5916    init_test(cx, |_| {});
 5917
 5918    let mut cx = EditorTestContext::new(cx).await;
 5919
 5920    let large_body_1 = "\nd".repeat(200);
 5921    let large_body_2 = "\ne".repeat(200);
 5922
 5923    cx.set_state(&format!(
 5924        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5925    ));
 5926    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5927        let scroll_position = editor.scroll_position(cx);
 5928        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5929        scroll_position
 5930    });
 5931
 5932    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5933        .unwrap();
 5934    cx.assert_editor_state(&format!(
 5935        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5936    ));
 5937    let scroll_position_after_selection =
 5938        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5939    assert_eq!(
 5940        initial_scroll_position, scroll_position_after_selection,
 5941        "Scroll position should not change after selecting all matches"
 5942    );
 5943}
 5944
 5945#[gpui::test]
 5946async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5947    init_test(cx, |_| {});
 5948
 5949    let mut cx = EditorLspTestContext::new_rust(
 5950        lsp::ServerCapabilities {
 5951            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5952            ..Default::default()
 5953        },
 5954        cx,
 5955    )
 5956    .await;
 5957
 5958    cx.set_state(indoc! {"
 5959        line 1
 5960        line 2
 5961        linˇe 3
 5962        line 4
 5963        line 5
 5964    "});
 5965
 5966    // Make an edit
 5967    cx.update_editor(|editor, window, cx| {
 5968        editor.handle_input("X", window, cx);
 5969    });
 5970
 5971    // Move cursor to a different position
 5972    cx.update_editor(|editor, window, cx| {
 5973        editor.change_selections(None, window, cx, |s| {
 5974            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5975        });
 5976    });
 5977
 5978    cx.assert_editor_state(indoc! {"
 5979        line 1
 5980        line 2
 5981        linXe 3
 5982        line 4
 5983        liˇne 5
 5984    "});
 5985
 5986    cx.lsp
 5987        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5988            Ok(Some(vec![lsp::TextEdit::new(
 5989                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5990                "PREFIX ".to_string(),
 5991            )]))
 5992        });
 5993
 5994    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5995        .unwrap()
 5996        .await
 5997        .unwrap();
 5998
 5999    cx.assert_editor_state(indoc! {"
 6000        PREFIX line 1
 6001        line 2
 6002        linXe 3
 6003        line 4
 6004        liˇne 5
 6005    "});
 6006
 6007    // Undo formatting
 6008    cx.update_editor(|editor, window, cx| {
 6009        editor.undo(&Default::default(), window, cx);
 6010    });
 6011
 6012    // Verify cursor moved back to position after edit
 6013    cx.assert_editor_state(indoc! {"
 6014        line 1
 6015        line 2
 6016        linXˇe 3
 6017        line 4
 6018        line 5
 6019    "});
 6020}
 6021
 6022#[gpui::test]
 6023async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6024    init_test(cx, |_| {});
 6025
 6026    let mut cx = EditorTestContext::new(cx).await;
 6027    cx.set_state(
 6028        r#"let foo = 2;
 6029lˇet foo = 2;
 6030let fooˇ = 2;
 6031let foo = 2;
 6032let foo = ˇ2;"#,
 6033    );
 6034
 6035    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6036        .unwrap();
 6037    cx.assert_editor_state(
 6038        r#"let foo = 2;
 6039«letˇ» foo = 2;
 6040let «fooˇ» = 2;
 6041let foo = 2;
 6042let foo = «2ˇ»;"#,
 6043    );
 6044
 6045    // noop for multiple selections with different contents
 6046    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6047        .unwrap();
 6048    cx.assert_editor_state(
 6049        r#"let foo = 2;
 6050«letˇ» foo = 2;
 6051let «fooˇ» = 2;
 6052let foo = 2;
 6053let foo = «2ˇ»;"#,
 6054    );
 6055
 6056    // Test last selection direction should be preserved
 6057    cx.set_state(
 6058        r#"let foo = 2;
 6059let foo = 2;
 6060let «fooˇ» = 2;
 6061let «ˇfoo» = 2;
 6062let foo = 2;"#,
 6063    );
 6064
 6065    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6066        .unwrap();
 6067    cx.assert_editor_state(
 6068        r#"let foo = 2;
 6069let foo = 2;
 6070let «fooˇ» = 2;
 6071let «ˇfoo» = 2;
 6072let «ˇfoo» = 2;"#,
 6073    );
 6074}
 6075
 6076#[gpui::test]
 6077async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6078    init_test(cx, |_| {});
 6079
 6080    let mut cx =
 6081        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6082
 6083    cx.assert_editor_state(indoc! {"
 6084        ˇbbb
 6085        ccc
 6086
 6087        bbb
 6088        ccc
 6089        "});
 6090    cx.dispatch_action(SelectPrevious::default());
 6091    cx.assert_editor_state(indoc! {"
 6092                «bbbˇ»
 6093                ccc
 6094
 6095                bbb
 6096                ccc
 6097                "});
 6098    cx.dispatch_action(SelectPrevious::default());
 6099    cx.assert_editor_state(indoc! {"
 6100                «bbbˇ»
 6101                ccc
 6102
 6103                «bbbˇ»
 6104                ccc
 6105                "});
 6106}
 6107
 6108#[gpui::test]
 6109async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6110    init_test(cx, |_| {});
 6111
 6112    let mut cx = EditorTestContext::new(cx).await;
 6113    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6114
 6115    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6116        .unwrap();
 6117    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6118
 6119    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6120        .unwrap();
 6121    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6122
 6123    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6124    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6125
 6126    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6127    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6128
 6129    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6130        .unwrap();
 6131    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6132
 6133    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6134        .unwrap();
 6135    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6136}
 6137
 6138#[gpui::test]
 6139async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6140    init_test(cx, |_| {});
 6141
 6142    let mut cx = EditorTestContext::new(cx).await;
 6143    cx.set_state("");
 6144
 6145    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6146        .unwrap();
 6147    cx.assert_editor_state("«aˇ»");
 6148    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6149        .unwrap();
 6150    cx.assert_editor_state("«aˇ»");
 6151}
 6152
 6153#[gpui::test]
 6154async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6155    init_test(cx, |_| {});
 6156
 6157    let mut cx = EditorTestContext::new(cx).await;
 6158    cx.set_state(
 6159        r#"let foo = 2;
 6160lˇet foo = 2;
 6161let fooˇ = 2;
 6162let foo = 2;
 6163let foo = ˇ2;"#,
 6164    );
 6165
 6166    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6167        .unwrap();
 6168    cx.assert_editor_state(
 6169        r#"let foo = 2;
 6170«letˇ» foo = 2;
 6171let «fooˇ» = 2;
 6172let foo = 2;
 6173let foo = «2ˇ»;"#,
 6174    );
 6175
 6176    // noop for multiple selections with different contents
 6177    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6178        .unwrap();
 6179    cx.assert_editor_state(
 6180        r#"let foo = 2;
 6181«letˇ» foo = 2;
 6182let «fooˇ» = 2;
 6183let foo = 2;
 6184let foo = «2ˇ»;"#,
 6185    );
 6186}
 6187
 6188#[gpui::test]
 6189async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6190    init_test(cx, |_| {});
 6191
 6192    let mut cx = EditorTestContext::new(cx).await;
 6193    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6194
 6195    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6196        .unwrap();
 6197    // selection direction is preserved
 6198    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6199
 6200    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6201        .unwrap();
 6202    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6203
 6204    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6205    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6206
 6207    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6208    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6209
 6210    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6211        .unwrap();
 6212    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6213
 6214    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6215        .unwrap();
 6216    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6217}
 6218
 6219#[gpui::test]
 6220async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6221    init_test(cx, |_| {});
 6222
 6223    let language = Arc::new(Language::new(
 6224        LanguageConfig::default(),
 6225        Some(tree_sitter_rust::LANGUAGE.into()),
 6226    ));
 6227
 6228    let text = r#"
 6229        use mod1::mod2::{mod3, mod4};
 6230
 6231        fn fn_1(param1: bool, param2: &str) {
 6232            let var1 = "text";
 6233        }
 6234    "#
 6235    .unindent();
 6236
 6237    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6238    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6239    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6240
 6241    editor
 6242        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6243        .await;
 6244
 6245    editor.update_in(cx, |editor, window, cx| {
 6246        editor.change_selections(None, window, cx, |s| {
 6247            s.select_display_ranges([
 6248                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6249                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6250                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6251            ]);
 6252        });
 6253        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6254    });
 6255    editor.update(cx, |editor, cx| {
 6256        assert_text_with_selections(
 6257            editor,
 6258            indoc! {r#"
 6259                use mod1::mod2::{mod3, «mod4ˇ»};
 6260
 6261                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6262                    let var1 = "«ˇtext»";
 6263                }
 6264            "#},
 6265            cx,
 6266        );
 6267    });
 6268
 6269    editor.update_in(cx, |editor, window, cx| {
 6270        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6271    });
 6272    editor.update(cx, |editor, cx| {
 6273        assert_text_with_selections(
 6274            editor,
 6275            indoc! {r#"
 6276                use mod1::mod2::«{mod3, mod4}ˇ»;
 6277
 6278                «ˇfn fn_1(param1: bool, param2: &str) {
 6279                    let var1 = "text";
 6280 6281            "#},
 6282            cx,
 6283        );
 6284    });
 6285
 6286    editor.update_in(cx, |editor, window, cx| {
 6287        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6288    });
 6289    assert_eq!(
 6290        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6291        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6292    );
 6293
 6294    // Trying to expand the selected syntax node one more time has no effect.
 6295    editor.update_in(cx, |editor, window, cx| {
 6296        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6297    });
 6298    assert_eq!(
 6299        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6300        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6301    );
 6302
 6303    editor.update_in(cx, |editor, window, cx| {
 6304        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6305    });
 6306    editor.update(cx, |editor, cx| {
 6307        assert_text_with_selections(
 6308            editor,
 6309            indoc! {r#"
 6310                use mod1::mod2::«{mod3, mod4}ˇ»;
 6311
 6312                «ˇfn fn_1(param1: bool, param2: &str) {
 6313                    let var1 = "text";
 6314 6315            "#},
 6316            cx,
 6317        );
 6318    });
 6319
 6320    editor.update_in(cx, |editor, window, cx| {
 6321        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6322    });
 6323    editor.update(cx, |editor, cx| {
 6324        assert_text_with_selections(
 6325            editor,
 6326            indoc! {r#"
 6327                use mod1::mod2::{mod3, «mod4ˇ»};
 6328
 6329                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6330                    let var1 = "«ˇtext»";
 6331                }
 6332            "#},
 6333            cx,
 6334        );
 6335    });
 6336
 6337    editor.update_in(cx, |editor, window, cx| {
 6338        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6339    });
 6340    editor.update(cx, |editor, cx| {
 6341        assert_text_with_selections(
 6342            editor,
 6343            indoc! {r#"
 6344                use mod1::mod2::{mod3, mo«ˇ»d4};
 6345
 6346                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6347                    let var1 = "te«ˇ»xt";
 6348                }
 6349            "#},
 6350            cx,
 6351        );
 6352    });
 6353
 6354    // Trying to shrink the selected syntax node one more time has no effect.
 6355    editor.update_in(cx, |editor, window, cx| {
 6356        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6357    });
 6358    editor.update_in(cx, |editor, _, cx| {
 6359        assert_text_with_selections(
 6360            editor,
 6361            indoc! {r#"
 6362                use mod1::mod2::{mod3, mo«ˇ»d4};
 6363
 6364                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6365                    let var1 = "te«ˇ»xt";
 6366                }
 6367            "#},
 6368            cx,
 6369        );
 6370    });
 6371
 6372    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6373    // a fold.
 6374    editor.update_in(cx, |editor, window, cx| {
 6375        editor.fold_creases(
 6376            vec![
 6377                Crease::simple(
 6378                    Point::new(0, 21)..Point::new(0, 24),
 6379                    FoldPlaceholder::test(),
 6380                ),
 6381                Crease::simple(
 6382                    Point::new(3, 20)..Point::new(3, 22),
 6383                    FoldPlaceholder::test(),
 6384                ),
 6385            ],
 6386            true,
 6387            window,
 6388            cx,
 6389        );
 6390        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6391    });
 6392    editor.update(cx, |editor, cx| {
 6393        assert_text_with_selections(
 6394            editor,
 6395            indoc! {r#"
 6396                use mod1::mod2::«{mod3, mod4}ˇ»;
 6397
 6398                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6399                    let var1 = "«ˇtext»";
 6400                }
 6401            "#},
 6402            cx,
 6403        );
 6404    });
 6405}
 6406
 6407#[gpui::test]
 6408async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6409    init_test(cx, |_| {});
 6410
 6411    let language = Arc::new(Language::new(
 6412        LanguageConfig::default(),
 6413        Some(tree_sitter_rust::LANGUAGE.into()),
 6414    ));
 6415
 6416    let text = "let a = 2;";
 6417
 6418    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6419    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6420    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6421
 6422    editor
 6423        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6424        .await;
 6425
 6426    // Test case 1: Cursor at end of word
 6427    editor.update_in(cx, |editor, window, cx| {
 6428        editor.change_selections(None, window, cx, |s| {
 6429            s.select_display_ranges([
 6430                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6431            ]);
 6432        });
 6433    });
 6434    editor.update(cx, |editor, cx| {
 6435        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6436    });
 6437    editor.update_in(cx, |editor, window, cx| {
 6438        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6439    });
 6440    editor.update(cx, |editor, cx| {
 6441        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6442    });
 6443    editor.update_in(cx, |editor, window, cx| {
 6444        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6445    });
 6446    editor.update(cx, |editor, cx| {
 6447        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6448    });
 6449
 6450    // Test case 2: Cursor at end of statement
 6451    editor.update_in(cx, |editor, window, cx| {
 6452        editor.change_selections(None, window, cx, |s| {
 6453            s.select_display_ranges([
 6454                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6455            ]);
 6456        });
 6457    });
 6458    editor.update(cx, |editor, cx| {
 6459        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6460    });
 6461    editor.update_in(cx, |editor, window, cx| {
 6462        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6463    });
 6464    editor.update(cx, |editor, cx| {
 6465        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6466    });
 6467}
 6468
 6469#[gpui::test]
 6470async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6471    init_test(cx, |_| {});
 6472
 6473    let language = Arc::new(Language::new(
 6474        LanguageConfig::default(),
 6475        Some(tree_sitter_rust::LANGUAGE.into()),
 6476    ));
 6477
 6478    let text = r#"
 6479        use mod1::mod2::{mod3, mod4};
 6480
 6481        fn fn_1(param1: bool, param2: &str) {
 6482            let var1 = "hello world";
 6483        }
 6484    "#
 6485    .unindent();
 6486
 6487    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6488    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6489    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6490
 6491    editor
 6492        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6493        .await;
 6494
 6495    // Test 1: Cursor on a letter of a string word
 6496    editor.update_in(cx, |editor, window, cx| {
 6497        editor.change_selections(None, window, cx, |s| {
 6498            s.select_display_ranges([
 6499                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6500            ]);
 6501        });
 6502    });
 6503    editor.update_in(cx, |editor, window, cx| {
 6504        assert_text_with_selections(
 6505            editor,
 6506            indoc! {r#"
 6507                use mod1::mod2::{mod3, mod4};
 6508
 6509                fn fn_1(param1: bool, param2: &str) {
 6510                    let var1 = "hˇello world";
 6511                }
 6512            "#},
 6513            cx,
 6514        );
 6515        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6516        assert_text_with_selections(
 6517            editor,
 6518            indoc! {r#"
 6519                use mod1::mod2::{mod3, mod4};
 6520
 6521                fn fn_1(param1: bool, param2: &str) {
 6522                    let var1 = "«ˇhello» world";
 6523                }
 6524            "#},
 6525            cx,
 6526        );
 6527    });
 6528
 6529    // Test 2: Partial selection within a word
 6530    editor.update_in(cx, |editor, window, cx| {
 6531        editor.change_selections(None, window, cx, |s| {
 6532            s.select_display_ranges([
 6533                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6534            ]);
 6535        });
 6536    });
 6537    editor.update_in(cx, |editor, window, cx| {
 6538        assert_text_with_selections(
 6539            editor,
 6540            indoc! {r#"
 6541                use mod1::mod2::{mod3, mod4};
 6542
 6543                fn fn_1(param1: bool, param2: &str) {
 6544                    let var1 = "h«elˇ»lo world";
 6545                }
 6546            "#},
 6547            cx,
 6548        );
 6549        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6550        assert_text_with_selections(
 6551            editor,
 6552            indoc! {r#"
 6553                use mod1::mod2::{mod3, mod4};
 6554
 6555                fn fn_1(param1: bool, param2: &str) {
 6556                    let var1 = "«ˇhello» world";
 6557                }
 6558            "#},
 6559            cx,
 6560        );
 6561    });
 6562
 6563    // Test 3: Complete word already selected
 6564    editor.update_in(cx, |editor, window, cx| {
 6565        editor.change_selections(None, window, cx, |s| {
 6566            s.select_display_ranges([
 6567                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6568            ]);
 6569        });
 6570    });
 6571    editor.update_in(cx, |editor, window, cx| {
 6572        assert_text_with_selections(
 6573            editor,
 6574            indoc! {r#"
 6575                use mod1::mod2::{mod3, mod4};
 6576
 6577                fn fn_1(param1: bool, param2: &str) {
 6578                    let var1 = "«helloˇ» world";
 6579                }
 6580            "#},
 6581            cx,
 6582        );
 6583        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6584        assert_text_with_selections(
 6585            editor,
 6586            indoc! {r#"
 6587                use mod1::mod2::{mod3, mod4};
 6588
 6589                fn fn_1(param1: bool, param2: &str) {
 6590                    let var1 = "«hello worldˇ»";
 6591                }
 6592            "#},
 6593            cx,
 6594        );
 6595    });
 6596
 6597    // Test 4: Selection spanning across words
 6598    editor.update_in(cx, |editor, window, cx| {
 6599        editor.change_selections(None, window, cx, |s| {
 6600            s.select_display_ranges([
 6601                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6602            ]);
 6603        });
 6604    });
 6605    editor.update_in(cx, |editor, window, cx| {
 6606        assert_text_with_selections(
 6607            editor,
 6608            indoc! {r#"
 6609                use mod1::mod2::{mod3, mod4};
 6610
 6611                fn fn_1(param1: bool, param2: &str) {
 6612                    let var1 = "hel«lo woˇ»rld";
 6613                }
 6614            "#},
 6615            cx,
 6616        );
 6617        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6618        assert_text_with_selections(
 6619            editor,
 6620            indoc! {r#"
 6621                use mod1::mod2::{mod3, mod4};
 6622
 6623                fn fn_1(param1: bool, param2: &str) {
 6624                    let var1 = "«ˇhello world»";
 6625                }
 6626            "#},
 6627            cx,
 6628        );
 6629    });
 6630
 6631    // Test 5: Expansion beyond string
 6632    editor.update_in(cx, |editor, window, cx| {
 6633        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6634        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6635        assert_text_with_selections(
 6636            editor,
 6637            indoc! {r#"
 6638                use mod1::mod2::{mod3, mod4};
 6639
 6640                fn fn_1(param1: bool, param2: &str) {
 6641                    «ˇlet var1 = "hello world";»
 6642                }
 6643            "#},
 6644            cx,
 6645        );
 6646    });
 6647}
 6648
 6649#[gpui::test]
 6650async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6651    init_test(cx, |_| {});
 6652
 6653    let base_text = r#"
 6654        impl A {
 6655            // this is an uncommitted comment
 6656
 6657            fn b() {
 6658                c();
 6659            }
 6660
 6661            // this is another uncommitted comment
 6662
 6663            fn d() {
 6664                // e
 6665                // f
 6666            }
 6667        }
 6668
 6669        fn g() {
 6670            // h
 6671        }
 6672    "#
 6673    .unindent();
 6674
 6675    let text = r#"
 6676        ˇimpl A {
 6677
 6678            fn b() {
 6679                c();
 6680            }
 6681
 6682            fn d() {
 6683                // e
 6684                // f
 6685            }
 6686        }
 6687
 6688        fn g() {
 6689            // h
 6690        }
 6691    "#
 6692    .unindent();
 6693
 6694    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6695    cx.set_state(&text);
 6696    cx.set_head_text(&base_text);
 6697    cx.update_editor(|editor, window, cx| {
 6698        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6699    });
 6700
 6701    cx.assert_state_with_diff(
 6702        "
 6703        ˇimpl A {
 6704      -     // this is an uncommitted comment
 6705
 6706            fn b() {
 6707                c();
 6708            }
 6709
 6710      -     // this is another uncommitted comment
 6711      -
 6712            fn d() {
 6713                // e
 6714                // f
 6715            }
 6716        }
 6717
 6718        fn g() {
 6719            // h
 6720        }
 6721    "
 6722        .unindent(),
 6723    );
 6724
 6725    let expected_display_text = "
 6726        impl A {
 6727            // this is an uncommitted comment
 6728
 6729            fn b() {
 6730 6731            }
 6732
 6733            // this is another uncommitted comment
 6734
 6735            fn d() {
 6736 6737            }
 6738        }
 6739
 6740        fn g() {
 6741 6742        }
 6743        "
 6744    .unindent();
 6745
 6746    cx.update_editor(|editor, window, cx| {
 6747        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6748        assert_eq!(editor.display_text(cx), expected_display_text);
 6749    });
 6750}
 6751
 6752#[gpui::test]
 6753async fn test_autoindent(cx: &mut TestAppContext) {
 6754    init_test(cx, |_| {});
 6755
 6756    let language = Arc::new(
 6757        Language::new(
 6758            LanguageConfig {
 6759                brackets: BracketPairConfig {
 6760                    pairs: vec![
 6761                        BracketPair {
 6762                            start: "{".to_string(),
 6763                            end: "}".to_string(),
 6764                            close: false,
 6765                            surround: false,
 6766                            newline: true,
 6767                        },
 6768                        BracketPair {
 6769                            start: "(".to_string(),
 6770                            end: ")".to_string(),
 6771                            close: false,
 6772                            surround: false,
 6773                            newline: true,
 6774                        },
 6775                    ],
 6776                    ..Default::default()
 6777                },
 6778                ..Default::default()
 6779            },
 6780            Some(tree_sitter_rust::LANGUAGE.into()),
 6781        )
 6782        .with_indents_query(
 6783            r#"
 6784                (_ "(" ")" @end) @indent
 6785                (_ "{" "}" @end) @indent
 6786            "#,
 6787        )
 6788        .unwrap(),
 6789    );
 6790
 6791    let text = "fn a() {}";
 6792
 6793    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6794    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6795    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6796    editor
 6797        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6798        .await;
 6799
 6800    editor.update_in(cx, |editor, window, cx| {
 6801        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6802        editor.newline(&Newline, window, cx);
 6803        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6804        assert_eq!(
 6805            editor.selections.ranges(cx),
 6806            &[
 6807                Point::new(1, 4)..Point::new(1, 4),
 6808                Point::new(3, 4)..Point::new(3, 4),
 6809                Point::new(5, 0)..Point::new(5, 0)
 6810            ]
 6811        );
 6812    });
 6813}
 6814
 6815#[gpui::test]
 6816async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6817    init_test(cx, |_| {});
 6818
 6819    {
 6820        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6821        cx.set_state(indoc! {"
 6822            impl A {
 6823
 6824                fn b() {}
 6825
 6826            «fn c() {
 6827
 6828            }ˇ»
 6829            }
 6830        "});
 6831
 6832        cx.update_editor(|editor, window, cx| {
 6833            editor.autoindent(&Default::default(), window, cx);
 6834        });
 6835
 6836        cx.assert_editor_state(indoc! {"
 6837            impl A {
 6838
 6839                fn b() {}
 6840
 6841                «fn c() {
 6842
 6843                }ˇ»
 6844            }
 6845        "});
 6846    }
 6847
 6848    {
 6849        let mut cx = EditorTestContext::new_multibuffer(
 6850            cx,
 6851            [indoc! { "
 6852                impl A {
 6853                «
 6854                // a
 6855                fn b(){}
 6856                »
 6857                «
 6858                    }
 6859                    fn c(){}
 6860                »
 6861            "}],
 6862        );
 6863
 6864        let buffer = cx.update_editor(|editor, _, cx| {
 6865            let buffer = editor.buffer().update(cx, |buffer, _| {
 6866                buffer.all_buffers().iter().next().unwrap().clone()
 6867            });
 6868            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6869            buffer
 6870        });
 6871
 6872        cx.run_until_parked();
 6873        cx.update_editor(|editor, window, cx| {
 6874            editor.select_all(&Default::default(), window, cx);
 6875            editor.autoindent(&Default::default(), window, cx)
 6876        });
 6877        cx.run_until_parked();
 6878
 6879        cx.update(|_, cx| {
 6880            assert_eq!(
 6881                buffer.read(cx).text(),
 6882                indoc! { "
 6883                    impl A {
 6884
 6885                        // a
 6886                        fn b(){}
 6887
 6888
 6889                    }
 6890                    fn c(){}
 6891
 6892                " }
 6893            )
 6894        });
 6895    }
 6896}
 6897
 6898#[gpui::test]
 6899async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6900    init_test(cx, |_| {});
 6901
 6902    let mut cx = EditorTestContext::new(cx).await;
 6903
 6904    let language = Arc::new(Language::new(
 6905        LanguageConfig {
 6906            brackets: BracketPairConfig {
 6907                pairs: vec![
 6908                    BracketPair {
 6909                        start: "{".to_string(),
 6910                        end: "}".to_string(),
 6911                        close: true,
 6912                        surround: true,
 6913                        newline: true,
 6914                    },
 6915                    BracketPair {
 6916                        start: "(".to_string(),
 6917                        end: ")".to_string(),
 6918                        close: true,
 6919                        surround: true,
 6920                        newline: true,
 6921                    },
 6922                    BracketPair {
 6923                        start: "/*".to_string(),
 6924                        end: " */".to_string(),
 6925                        close: true,
 6926                        surround: true,
 6927                        newline: true,
 6928                    },
 6929                    BracketPair {
 6930                        start: "[".to_string(),
 6931                        end: "]".to_string(),
 6932                        close: false,
 6933                        surround: false,
 6934                        newline: true,
 6935                    },
 6936                    BracketPair {
 6937                        start: "\"".to_string(),
 6938                        end: "\"".to_string(),
 6939                        close: true,
 6940                        surround: true,
 6941                        newline: false,
 6942                    },
 6943                    BracketPair {
 6944                        start: "<".to_string(),
 6945                        end: ">".to_string(),
 6946                        close: false,
 6947                        surround: true,
 6948                        newline: true,
 6949                    },
 6950                ],
 6951                ..Default::default()
 6952            },
 6953            autoclose_before: "})]".to_string(),
 6954            ..Default::default()
 6955        },
 6956        Some(tree_sitter_rust::LANGUAGE.into()),
 6957    ));
 6958
 6959    cx.language_registry().add(language.clone());
 6960    cx.update_buffer(|buffer, cx| {
 6961        buffer.set_language(Some(language), cx);
 6962    });
 6963
 6964    cx.set_state(
 6965        &r#"
 6966            🏀ˇ
 6967            εˇ
 6968            ❤️ˇ
 6969        "#
 6970        .unindent(),
 6971    );
 6972
 6973    // autoclose multiple nested brackets at multiple cursors
 6974    cx.update_editor(|editor, window, cx| {
 6975        editor.handle_input("{", window, cx);
 6976        editor.handle_input("{", window, cx);
 6977        editor.handle_input("{", window, cx);
 6978    });
 6979    cx.assert_editor_state(
 6980        &"
 6981            🏀{{{ˇ}}}
 6982            ε{{{ˇ}}}
 6983            ❤️{{{ˇ}}}
 6984        "
 6985        .unindent(),
 6986    );
 6987
 6988    // insert a different closing bracket
 6989    cx.update_editor(|editor, window, cx| {
 6990        editor.handle_input(")", window, cx);
 6991    });
 6992    cx.assert_editor_state(
 6993        &"
 6994            🏀{{{)ˇ}}}
 6995            ε{{{)ˇ}}}
 6996            ❤️{{{)ˇ}}}
 6997        "
 6998        .unindent(),
 6999    );
 7000
 7001    // skip over the auto-closed brackets when typing a closing bracket
 7002    cx.update_editor(|editor, window, cx| {
 7003        editor.move_right(&MoveRight, window, cx);
 7004        editor.handle_input("}", window, cx);
 7005        editor.handle_input("}", window, cx);
 7006        editor.handle_input("}", window, cx);
 7007    });
 7008    cx.assert_editor_state(
 7009        &"
 7010            🏀{{{)}}}}ˇ
 7011            ε{{{)}}}}ˇ
 7012            ❤️{{{)}}}}ˇ
 7013        "
 7014        .unindent(),
 7015    );
 7016
 7017    // autoclose multi-character pairs
 7018    cx.set_state(
 7019        &"
 7020            ˇ
 7021            ˇ
 7022        "
 7023        .unindent(),
 7024    );
 7025    cx.update_editor(|editor, window, cx| {
 7026        editor.handle_input("/", window, cx);
 7027        editor.handle_input("*", window, cx);
 7028    });
 7029    cx.assert_editor_state(
 7030        &"
 7031            /*ˇ */
 7032            /*ˇ */
 7033        "
 7034        .unindent(),
 7035    );
 7036
 7037    // one cursor autocloses a multi-character pair, one cursor
 7038    // does not autoclose.
 7039    cx.set_state(
 7040        &"
 7041 7042            ˇ
 7043        "
 7044        .unindent(),
 7045    );
 7046    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7047    cx.assert_editor_state(
 7048        &"
 7049            /*ˇ */
 7050 7051        "
 7052        .unindent(),
 7053    );
 7054
 7055    // Don't autoclose if the next character isn't whitespace and isn't
 7056    // listed in the language's "autoclose_before" section.
 7057    cx.set_state("ˇa b");
 7058    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7059    cx.assert_editor_state("{ˇa b");
 7060
 7061    // Don't autoclose if `close` is false for the bracket pair
 7062    cx.set_state("ˇ");
 7063    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7064    cx.assert_editor_state("");
 7065
 7066    // Surround with brackets if text is selected
 7067    cx.set_state("«aˇ» b");
 7068    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7069    cx.assert_editor_state("{«aˇ»} b");
 7070
 7071    // Autoclose when not immediately after a word character
 7072    cx.set_state("a ˇ");
 7073    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7074    cx.assert_editor_state("a \"ˇ\"");
 7075
 7076    // Autoclose pair where the start and end characters are the same
 7077    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7078    cx.assert_editor_state("a \"\"ˇ");
 7079
 7080    // Don't autoclose when immediately after a word character
 7081    cx.set_state("");
 7082    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7083    cx.assert_editor_state("a\"ˇ");
 7084
 7085    // Do autoclose when after a non-word character
 7086    cx.set_state("");
 7087    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7088    cx.assert_editor_state("{\"ˇ\"");
 7089
 7090    // Non identical pairs autoclose regardless of preceding character
 7091    cx.set_state("");
 7092    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7093    cx.assert_editor_state("a{ˇ}");
 7094
 7095    // Don't autoclose pair if autoclose is disabled
 7096    cx.set_state("ˇ");
 7097    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7098    cx.assert_editor_state("");
 7099
 7100    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7101    cx.set_state("«aˇ» b");
 7102    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7103    cx.assert_editor_state("<«aˇ»> b");
 7104}
 7105
 7106#[gpui::test]
 7107async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7108    init_test(cx, |settings| {
 7109        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7110    });
 7111
 7112    let mut cx = EditorTestContext::new(cx).await;
 7113
 7114    let language = Arc::new(Language::new(
 7115        LanguageConfig {
 7116            brackets: BracketPairConfig {
 7117                pairs: vec![
 7118                    BracketPair {
 7119                        start: "{".to_string(),
 7120                        end: "}".to_string(),
 7121                        close: true,
 7122                        surround: true,
 7123                        newline: true,
 7124                    },
 7125                    BracketPair {
 7126                        start: "(".to_string(),
 7127                        end: ")".to_string(),
 7128                        close: true,
 7129                        surround: true,
 7130                        newline: true,
 7131                    },
 7132                    BracketPair {
 7133                        start: "[".to_string(),
 7134                        end: "]".to_string(),
 7135                        close: false,
 7136                        surround: false,
 7137                        newline: true,
 7138                    },
 7139                ],
 7140                ..Default::default()
 7141            },
 7142            autoclose_before: "})]".to_string(),
 7143            ..Default::default()
 7144        },
 7145        Some(tree_sitter_rust::LANGUAGE.into()),
 7146    ));
 7147
 7148    cx.language_registry().add(language.clone());
 7149    cx.update_buffer(|buffer, cx| {
 7150        buffer.set_language(Some(language), cx);
 7151    });
 7152
 7153    cx.set_state(
 7154        &"
 7155            ˇ
 7156            ˇ
 7157            ˇ
 7158        "
 7159        .unindent(),
 7160    );
 7161
 7162    // ensure only matching closing brackets are skipped over
 7163    cx.update_editor(|editor, window, cx| {
 7164        editor.handle_input("}", window, cx);
 7165        editor.move_left(&MoveLeft, window, cx);
 7166        editor.handle_input(")", window, cx);
 7167        editor.move_left(&MoveLeft, window, cx);
 7168    });
 7169    cx.assert_editor_state(
 7170        &"
 7171            ˇ)}
 7172            ˇ)}
 7173            ˇ)}
 7174        "
 7175        .unindent(),
 7176    );
 7177
 7178    // skip-over closing brackets at multiple cursors
 7179    cx.update_editor(|editor, window, cx| {
 7180        editor.handle_input(")", window, cx);
 7181        editor.handle_input("}", window, cx);
 7182    });
 7183    cx.assert_editor_state(
 7184        &"
 7185            )}ˇ
 7186            )}ˇ
 7187            )}ˇ
 7188        "
 7189        .unindent(),
 7190    );
 7191
 7192    // ignore non-close brackets
 7193    cx.update_editor(|editor, window, cx| {
 7194        editor.handle_input("]", window, cx);
 7195        editor.move_left(&MoveLeft, window, cx);
 7196        editor.handle_input("]", window, cx);
 7197    });
 7198    cx.assert_editor_state(
 7199        &"
 7200            )}]ˇ]
 7201            )}]ˇ]
 7202            )}]ˇ]
 7203        "
 7204        .unindent(),
 7205    );
 7206}
 7207
 7208#[gpui::test]
 7209async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7210    init_test(cx, |_| {});
 7211
 7212    let mut cx = EditorTestContext::new(cx).await;
 7213
 7214    let html_language = Arc::new(
 7215        Language::new(
 7216            LanguageConfig {
 7217                name: "HTML".into(),
 7218                brackets: BracketPairConfig {
 7219                    pairs: vec![
 7220                        BracketPair {
 7221                            start: "<".into(),
 7222                            end: ">".into(),
 7223                            close: true,
 7224                            ..Default::default()
 7225                        },
 7226                        BracketPair {
 7227                            start: "{".into(),
 7228                            end: "}".into(),
 7229                            close: true,
 7230                            ..Default::default()
 7231                        },
 7232                        BracketPair {
 7233                            start: "(".into(),
 7234                            end: ")".into(),
 7235                            close: true,
 7236                            ..Default::default()
 7237                        },
 7238                    ],
 7239                    ..Default::default()
 7240                },
 7241                autoclose_before: "})]>".into(),
 7242                ..Default::default()
 7243            },
 7244            Some(tree_sitter_html::LANGUAGE.into()),
 7245        )
 7246        .with_injection_query(
 7247            r#"
 7248            (script_element
 7249                (raw_text) @injection.content
 7250                (#set! injection.language "javascript"))
 7251            "#,
 7252        )
 7253        .unwrap(),
 7254    );
 7255
 7256    let javascript_language = Arc::new(Language::new(
 7257        LanguageConfig {
 7258            name: "JavaScript".into(),
 7259            brackets: BracketPairConfig {
 7260                pairs: vec![
 7261                    BracketPair {
 7262                        start: "/*".into(),
 7263                        end: " */".into(),
 7264                        close: true,
 7265                        ..Default::default()
 7266                    },
 7267                    BracketPair {
 7268                        start: "{".into(),
 7269                        end: "}".into(),
 7270                        close: true,
 7271                        ..Default::default()
 7272                    },
 7273                    BracketPair {
 7274                        start: "(".into(),
 7275                        end: ")".into(),
 7276                        close: true,
 7277                        ..Default::default()
 7278                    },
 7279                ],
 7280                ..Default::default()
 7281            },
 7282            autoclose_before: "})]>".into(),
 7283            ..Default::default()
 7284        },
 7285        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7286    ));
 7287
 7288    cx.language_registry().add(html_language.clone());
 7289    cx.language_registry().add(javascript_language.clone());
 7290
 7291    cx.update_buffer(|buffer, cx| {
 7292        buffer.set_language(Some(html_language), cx);
 7293    });
 7294
 7295    cx.set_state(
 7296        &r#"
 7297            <body>ˇ
 7298                <script>
 7299                    var x = 1;ˇ
 7300                </script>
 7301            </body>ˇ
 7302        "#
 7303        .unindent(),
 7304    );
 7305
 7306    // Precondition: different languages are active at different locations.
 7307    cx.update_editor(|editor, window, cx| {
 7308        let snapshot = editor.snapshot(window, cx);
 7309        let cursors = editor.selections.ranges::<usize>(cx);
 7310        let languages = cursors
 7311            .iter()
 7312            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7313            .collect::<Vec<_>>();
 7314        assert_eq!(
 7315            languages,
 7316            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7317        );
 7318    });
 7319
 7320    // Angle brackets autoclose in HTML, but not JavaScript.
 7321    cx.update_editor(|editor, window, cx| {
 7322        editor.handle_input("<", window, cx);
 7323        editor.handle_input("a", window, cx);
 7324    });
 7325    cx.assert_editor_state(
 7326        &r#"
 7327            <body><aˇ>
 7328                <script>
 7329                    var x = 1;<aˇ
 7330                </script>
 7331            </body><aˇ>
 7332        "#
 7333        .unindent(),
 7334    );
 7335
 7336    // Curly braces and parens autoclose in both HTML and JavaScript.
 7337    cx.update_editor(|editor, window, cx| {
 7338        editor.handle_input(" b=", window, cx);
 7339        editor.handle_input("{", window, cx);
 7340        editor.handle_input("c", window, cx);
 7341        editor.handle_input("(", window, cx);
 7342    });
 7343    cx.assert_editor_state(
 7344        &r#"
 7345            <body><a b={c(ˇ)}>
 7346                <script>
 7347                    var x = 1;<a b={c(ˇ)}
 7348                </script>
 7349            </body><a b={c(ˇ)}>
 7350        "#
 7351        .unindent(),
 7352    );
 7353
 7354    // Brackets that were already autoclosed are skipped.
 7355    cx.update_editor(|editor, window, cx| {
 7356        editor.handle_input(")", window, cx);
 7357        editor.handle_input("d", window, cx);
 7358        editor.handle_input("}", window, cx);
 7359    });
 7360    cx.assert_editor_state(
 7361        &r#"
 7362            <body><a b={c()d}ˇ>
 7363                <script>
 7364                    var x = 1;<a b={c()d}ˇ
 7365                </script>
 7366            </body><a b={c()d}ˇ>
 7367        "#
 7368        .unindent(),
 7369    );
 7370    cx.update_editor(|editor, window, cx| {
 7371        editor.handle_input(">", window, cx);
 7372    });
 7373    cx.assert_editor_state(
 7374        &r#"
 7375            <body><a b={c()d}>ˇ
 7376                <script>
 7377                    var x = 1;<a b={c()d}>ˇ
 7378                </script>
 7379            </body><a b={c()d}>ˇ
 7380        "#
 7381        .unindent(),
 7382    );
 7383
 7384    // Reset
 7385    cx.set_state(
 7386        &r#"
 7387            <body>ˇ
 7388                <script>
 7389                    var x = 1;ˇ
 7390                </script>
 7391            </body>ˇ
 7392        "#
 7393        .unindent(),
 7394    );
 7395
 7396    cx.update_editor(|editor, window, cx| {
 7397        editor.handle_input("<", window, cx);
 7398    });
 7399    cx.assert_editor_state(
 7400        &r#"
 7401            <body><ˇ>
 7402                <script>
 7403                    var x = 1;<ˇ
 7404                </script>
 7405            </body><ˇ>
 7406        "#
 7407        .unindent(),
 7408    );
 7409
 7410    // When backspacing, the closing angle brackets are removed.
 7411    cx.update_editor(|editor, window, cx| {
 7412        editor.backspace(&Backspace, window, cx);
 7413    });
 7414    cx.assert_editor_state(
 7415        &r#"
 7416            <body>ˇ
 7417                <script>
 7418                    var x = 1;ˇ
 7419                </script>
 7420            </body>ˇ
 7421        "#
 7422        .unindent(),
 7423    );
 7424
 7425    // Block comments autoclose in JavaScript, but not HTML.
 7426    cx.update_editor(|editor, window, cx| {
 7427        editor.handle_input("/", window, cx);
 7428        editor.handle_input("*", window, cx);
 7429    });
 7430    cx.assert_editor_state(
 7431        &r#"
 7432            <body>/*ˇ
 7433                <script>
 7434                    var x = 1;/*ˇ */
 7435                </script>
 7436            </body>/*ˇ
 7437        "#
 7438        .unindent(),
 7439    );
 7440}
 7441
 7442#[gpui::test]
 7443async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7444    init_test(cx, |_| {});
 7445
 7446    let mut cx = EditorTestContext::new(cx).await;
 7447
 7448    let rust_language = Arc::new(
 7449        Language::new(
 7450            LanguageConfig {
 7451                name: "Rust".into(),
 7452                brackets: serde_json::from_value(json!([
 7453                    { "start": "{", "end": "}", "close": true, "newline": true },
 7454                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7455                ]))
 7456                .unwrap(),
 7457                autoclose_before: "})]>".into(),
 7458                ..Default::default()
 7459            },
 7460            Some(tree_sitter_rust::LANGUAGE.into()),
 7461        )
 7462        .with_override_query("(string_literal) @string")
 7463        .unwrap(),
 7464    );
 7465
 7466    cx.language_registry().add(rust_language.clone());
 7467    cx.update_buffer(|buffer, cx| {
 7468        buffer.set_language(Some(rust_language), cx);
 7469    });
 7470
 7471    cx.set_state(
 7472        &r#"
 7473            let x = ˇ
 7474        "#
 7475        .unindent(),
 7476    );
 7477
 7478    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7479    cx.update_editor(|editor, window, cx| {
 7480        editor.handle_input("\"", window, cx);
 7481    });
 7482    cx.assert_editor_state(
 7483        &r#"
 7484            let x = "ˇ"
 7485        "#
 7486        .unindent(),
 7487    );
 7488
 7489    // Inserting another quotation mark. The cursor moves across the existing
 7490    // automatically-inserted quotation mark.
 7491    cx.update_editor(|editor, window, cx| {
 7492        editor.handle_input("\"", window, cx);
 7493    });
 7494    cx.assert_editor_state(
 7495        &r#"
 7496            let x = ""ˇ
 7497        "#
 7498        .unindent(),
 7499    );
 7500
 7501    // Reset
 7502    cx.set_state(
 7503        &r#"
 7504            let x = ˇ
 7505        "#
 7506        .unindent(),
 7507    );
 7508
 7509    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7510    cx.update_editor(|editor, window, cx| {
 7511        editor.handle_input("\"", window, cx);
 7512        editor.handle_input(" ", window, cx);
 7513        editor.move_left(&Default::default(), window, cx);
 7514        editor.handle_input("\\", window, cx);
 7515        editor.handle_input("\"", window, cx);
 7516    });
 7517    cx.assert_editor_state(
 7518        &r#"
 7519            let x = "\"ˇ "
 7520        "#
 7521        .unindent(),
 7522    );
 7523
 7524    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7525    // mark. Nothing is inserted.
 7526    cx.update_editor(|editor, window, cx| {
 7527        editor.move_right(&Default::default(), window, cx);
 7528        editor.handle_input("\"", window, cx);
 7529    });
 7530    cx.assert_editor_state(
 7531        &r#"
 7532            let x = "\" "ˇ
 7533        "#
 7534        .unindent(),
 7535    );
 7536}
 7537
 7538#[gpui::test]
 7539async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7540    init_test(cx, |_| {});
 7541
 7542    let language = Arc::new(Language::new(
 7543        LanguageConfig {
 7544            brackets: BracketPairConfig {
 7545                pairs: vec![
 7546                    BracketPair {
 7547                        start: "{".to_string(),
 7548                        end: "}".to_string(),
 7549                        close: true,
 7550                        surround: true,
 7551                        newline: true,
 7552                    },
 7553                    BracketPair {
 7554                        start: "/* ".to_string(),
 7555                        end: "*/".to_string(),
 7556                        close: true,
 7557                        surround: true,
 7558                        ..Default::default()
 7559                    },
 7560                ],
 7561                ..Default::default()
 7562            },
 7563            ..Default::default()
 7564        },
 7565        Some(tree_sitter_rust::LANGUAGE.into()),
 7566    ));
 7567
 7568    let text = r#"
 7569        a
 7570        b
 7571        c
 7572    "#
 7573    .unindent();
 7574
 7575    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7576    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7577    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7578    editor
 7579        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7580        .await;
 7581
 7582    editor.update_in(cx, |editor, window, cx| {
 7583        editor.change_selections(None, window, cx, |s| {
 7584            s.select_display_ranges([
 7585                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7586                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7587                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7588            ])
 7589        });
 7590
 7591        editor.handle_input("{", window, cx);
 7592        editor.handle_input("{", window, cx);
 7593        editor.handle_input("{", window, cx);
 7594        assert_eq!(
 7595            editor.text(cx),
 7596            "
 7597                {{{a}}}
 7598                {{{b}}}
 7599                {{{c}}}
 7600            "
 7601            .unindent()
 7602        );
 7603        assert_eq!(
 7604            editor.selections.display_ranges(cx),
 7605            [
 7606                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7607                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7608                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7609            ]
 7610        );
 7611
 7612        editor.undo(&Undo, window, cx);
 7613        editor.undo(&Undo, window, cx);
 7614        editor.undo(&Undo, window, cx);
 7615        assert_eq!(
 7616            editor.text(cx),
 7617            "
 7618                a
 7619                b
 7620                c
 7621            "
 7622            .unindent()
 7623        );
 7624        assert_eq!(
 7625            editor.selections.display_ranges(cx),
 7626            [
 7627                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7628                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7629                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7630            ]
 7631        );
 7632
 7633        // Ensure inserting the first character of a multi-byte bracket pair
 7634        // doesn't surround the selections with the bracket.
 7635        editor.handle_input("/", window, cx);
 7636        assert_eq!(
 7637            editor.text(cx),
 7638            "
 7639                /
 7640                /
 7641                /
 7642            "
 7643            .unindent()
 7644        );
 7645        assert_eq!(
 7646            editor.selections.display_ranges(cx),
 7647            [
 7648                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7649                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7650                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7651            ]
 7652        );
 7653
 7654        editor.undo(&Undo, window, cx);
 7655        assert_eq!(
 7656            editor.text(cx),
 7657            "
 7658                a
 7659                b
 7660                c
 7661            "
 7662            .unindent()
 7663        );
 7664        assert_eq!(
 7665            editor.selections.display_ranges(cx),
 7666            [
 7667                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7668                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7669                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7670            ]
 7671        );
 7672
 7673        // Ensure inserting the last character of a multi-byte bracket pair
 7674        // doesn't surround the selections with the bracket.
 7675        editor.handle_input("*", window, cx);
 7676        assert_eq!(
 7677            editor.text(cx),
 7678            "
 7679                *
 7680                *
 7681                *
 7682            "
 7683            .unindent()
 7684        );
 7685        assert_eq!(
 7686            editor.selections.display_ranges(cx),
 7687            [
 7688                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7689                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7690                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7691            ]
 7692        );
 7693    });
 7694}
 7695
 7696#[gpui::test]
 7697async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7698    init_test(cx, |_| {});
 7699
 7700    let language = Arc::new(Language::new(
 7701        LanguageConfig {
 7702            brackets: BracketPairConfig {
 7703                pairs: vec![BracketPair {
 7704                    start: "{".to_string(),
 7705                    end: "}".to_string(),
 7706                    close: true,
 7707                    surround: true,
 7708                    newline: true,
 7709                }],
 7710                ..Default::default()
 7711            },
 7712            autoclose_before: "}".to_string(),
 7713            ..Default::default()
 7714        },
 7715        Some(tree_sitter_rust::LANGUAGE.into()),
 7716    ));
 7717
 7718    let text = r#"
 7719        a
 7720        b
 7721        c
 7722    "#
 7723    .unindent();
 7724
 7725    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7726    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7727    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7728    editor
 7729        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7730        .await;
 7731
 7732    editor.update_in(cx, |editor, window, cx| {
 7733        editor.change_selections(None, window, cx, |s| {
 7734            s.select_ranges([
 7735                Point::new(0, 1)..Point::new(0, 1),
 7736                Point::new(1, 1)..Point::new(1, 1),
 7737                Point::new(2, 1)..Point::new(2, 1),
 7738            ])
 7739        });
 7740
 7741        editor.handle_input("{", window, cx);
 7742        editor.handle_input("{", window, cx);
 7743        editor.handle_input("_", window, cx);
 7744        assert_eq!(
 7745            editor.text(cx),
 7746            "
 7747                a{{_}}
 7748                b{{_}}
 7749                c{{_}}
 7750            "
 7751            .unindent()
 7752        );
 7753        assert_eq!(
 7754            editor.selections.ranges::<Point>(cx),
 7755            [
 7756                Point::new(0, 4)..Point::new(0, 4),
 7757                Point::new(1, 4)..Point::new(1, 4),
 7758                Point::new(2, 4)..Point::new(2, 4)
 7759            ]
 7760        );
 7761
 7762        editor.backspace(&Default::default(), window, cx);
 7763        editor.backspace(&Default::default(), window, cx);
 7764        assert_eq!(
 7765            editor.text(cx),
 7766            "
 7767                a{}
 7768                b{}
 7769                c{}
 7770            "
 7771            .unindent()
 7772        );
 7773        assert_eq!(
 7774            editor.selections.ranges::<Point>(cx),
 7775            [
 7776                Point::new(0, 2)..Point::new(0, 2),
 7777                Point::new(1, 2)..Point::new(1, 2),
 7778                Point::new(2, 2)..Point::new(2, 2)
 7779            ]
 7780        );
 7781
 7782        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7783        assert_eq!(
 7784            editor.text(cx),
 7785            "
 7786                a
 7787                b
 7788                c
 7789            "
 7790            .unindent()
 7791        );
 7792        assert_eq!(
 7793            editor.selections.ranges::<Point>(cx),
 7794            [
 7795                Point::new(0, 1)..Point::new(0, 1),
 7796                Point::new(1, 1)..Point::new(1, 1),
 7797                Point::new(2, 1)..Point::new(2, 1)
 7798            ]
 7799        );
 7800    });
 7801}
 7802
 7803#[gpui::test]
 7804async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7805    init_test(cx, |settings| {
 7806        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7807    });
 7808
 7809    let mut cx = EditorTestContext::new(cx).await;
 7810
 7811    let language = Arc::new(Language::new(
 7812        LanguageConfig {
 7813            brackets: BracketPairConfig {
 7814                pairs: vec![
 7815                    BracketPair {
 7816                        start: "{".to_string(),
 7817                        end: "}".to_string(),
 7818                        close: true,
 7819                        surround: true,
 7820                        newline: true,
 7821                    },
 7822                    BracketPair {
 7823                        start: "(".to_string(),
 7824                        end: ")".to_string(),
 7825                        close: true,
 7826                        surround: true,
 7827                        newline: true,
 7828                    },
 7829                    BracketPair {
 7830                        start: "[".to_string(),
 7831                        end: "]".to_string(),
 7832                        close: false,
 7833                        surround: true,
 7834                        newline: true,
 7835                    },
 7836                ],
 7837                ..Default::default()
 7838            },
 7839            autoclose_before: "})]".to_string(),
 7840            ..Default::default()
 7841        },
 7842        Some(tree_sitter_rust::LANGUAGE.into()),
 7843    ));
 7844
 7845    cx.language_registry().add(language.clone());
 7846    cx.update_buffer(|buffer, cx| {
 7847        buffer.set_language(Some(language), cx);
 7848    });
 7849
 7850    cx.set_state(
 7851        &"
 7852            {(ˇ)}
 7853            [[ˇ]]
 7854            {(ˇ)}
 7855        "
 7856        .unindent(),
 7857    );
 7858
 7859    cx.update_editor(|editor, window, cx| {
 7860        editor.backspace(&Default::default(), window, cx);
 7861        editor.backspace(&Default::default(), window, cx);
 7862    });
 7863
 7864    cx.assert_editor_state(
 7865        &"
 7866            ˇ
 7867            ˇ]]
 7868            ˇ
 7869        "
 7870        .unindent(),
 7871    );
 7872
 7873    cx.update_editor(|editor, window, cx| {
 7874        editor.handle_input("{", window, cx);
 7875        editor.handle_input("{", window, cx);
 7876        editor.move_right(&MoveRight, window, cx);
 7877        editor.move_right(&MoveRight, window, cx);
 7878        editor.move_left(&MoveLeft, window, cx);
 7879        editor.move_left(&MoveLeft, window, cx);
 7880        editor.backspace(&Default::default(), window, cx);
 7881    });
 7882
 7883    cx.assert_editor_state(
 7884        &"
 7885            {ˇ}
 7886            {ˇ}]]
 7887            {ˇ}
 7888        "
 7889        .unindent(),
 7890    );
 7891
 7892    cx.update_editor(|editor, window, cx| {
 7893        editor.backspace(&Default::default(), window, cx);
 7894    });
 7895
 7896    cx.assert_editor_state(
 7897        &"
 7898            ˇ
 7899            ˇ]]
 7900            ˇ
 7901        "
 7902        .unindent(),
 7903    );
 7904}
 7905
 7906#[gpui::test]
 7907async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7908    init_test(cx, |_| {});
 7909
 7910    let language = Arc::new(Language::new(
 7911        LanguageConfig::default(),
 7912        Some(tree_sitter_rust::LANGUAGE.into()),
 7913    ));
 7914
 7915    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7916    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7917    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7918    editor
 7919        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7920        .await;
 7921
 7922    editor.update_in(cx, |editor, window, cx| {
 7923        editor.set_auto_replace_emoji_shortcode(true);
 7924
 7925        editor.handle_input("Hello ", window, cx);
 7926        editor.handle_input(":wave", window, cx);
 7927        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7928
 7929        editor.handle_input(":", window, cx);
 7930        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7931
 7932        editor.handle_input(" :smile", window, cx);
 7933        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7934
 7935        editor.handle_input(":", window, cx);
 7936        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7937
 7938        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7939        editor.handle_input(":wave", window, cx);
 7940        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7941
 7942        editor.handle_input(":", window, cx);
 7943        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7944
 7945        editor.handle_input(":1", window, cx);
 7946        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7947
 7948        editor.handle_input(":", window, cx);
 7949        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7950
 7951        // Ensure shortcode does not get replaced when it is part of a word
 7952        editor.handle_input(" Test:wave", window, cx);
 7953        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7954
 7955        editor.handle_input(":", window, cx);
 7956        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7957
 7958        editor.set_auto_replace_emoji_shortcode(false);
 7959
 7960        // Ensure shortcode does not get replaced when auto replace is off
 7961        editor.handle_input(" :wave", window, cx);
 7962        assert_eq!(
 7963            editor.text(cx),
 7964            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7965        );
 7966
 7967        editor.handle_input(":", window, cx);
 7968        assert_eq!(
 7969            editor.text(cx),
 7970            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7971        );
 7972    });
 7973}
 7974
 7975#[gpui::test]
 7976async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7977    init_test(cx, |_| {});
 7978
 7979    let (text, insertion_ranges) = marked_text_ranges(
 7980        indoc! {"
 7981            ˇ
 7982        "},
 7983        false,
 7984    );
 7985
 7986    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7987    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7988
 7989    _ = editor.update_in(cx, |editor, window, cx| {
 7990        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7991
 7992        editor
 7993            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7994            .unwrap();
 7995
 7996        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7997            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7998            assert_eq!(editor.text(cx), expected_text);
 7999            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8000        }
 8001
 8002        assert(
 8003            editor,
 8004            cx,
 8005            indoc! {"
 8006            type «» =•
 8007            "},
 8008        );
 8009
 8010        assert!(editor.context_menu_visible(), "There should be a matches");
 8011    });
 8012}
 8013
 8014#[gpui::test]
 8015async fn test_snippets(cx: &mut TestAppContext) {
 8016    init_test(cx, |_| {});
 8017
 8018    let (text, insertion_ranges) = marked_text_ranges(
 8019        indoc! {"
 8020            a.ˇ b
 8021            a.ˇ b
 8022            a.ˇ b
 8023        "},
 8024        false,
 8025    );
 8026
 8027    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8028    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8029
 8030    editor.update_in(cx, |editor, window, cx| {
 8031        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8032
 8033        editor
 8034            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8035            .unwrap();
 8036
 8037        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8038            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8039            assert_eq!(editor.text(cx), expected_text);
 8040            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8041        }
 8042
 8043        assert(
 8044            editor,
 8045            cx,
 8046            indoc! {"
 8047                a.f(«one», two, «three») b
 8048                a.f(«one», two, «three») b
 8049                a.f(«one», two, «three») b
 8050            "},
 8051        );
 8052
 8053        // Can't move earlier than the first tab stop
 8054        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8055        assert(
 8056            editor,
 8057            cx,
 8058            indoc! {"
 8059                a.f(«one», two, «three») b
 8060                a.f(«one», two, «three») b
 8061                a.f(«one», two, «three») b
 8062            "},
 8063        );
 8064
 8065        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8066        assert(
 8067            editor,
 8068            cx,
 8069            indoc! {"
 8070                a.f(one, «two», three) b
 8071                a.f(one, «two», three) b
 8072                a.f(one, «two», three) b
 8073            "},
 8074        );
 8075
 8076        editor.move_to_prev_snippet_tabstop(window, cx);
 8077        assert(
 8078            editor,
 8079            cx,
 8080            indoc! {"
 8081                a.f(«one», two, «three») b
 8082                a.f(«one», two, «three») b
 8083                a.f(«one», two, «three») b
 8084            "},
 8085        );
 8086
 8087        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8088        assert(
 8089            editor,
 8090            cx,
 8091            indoc! {"
 8092                a.f(one, «two», three) b
 8093                a.f(one, «two», three) b
 8094                a.f(one, «two», three) b
 8095            "},
 8096        );
 8097        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8098        assert(
 8099            editor,
 8100            cx,
 8101            indoc! {"
 8102                a.f(one, two, three)ˇ b
 8103                a.f(one, two, three)ˇ b
 8104                a.f(one, two, three)ˇ b
 8105            "},
 8106        );
 8107
 8108        // As soon as the last tab stop is reached, snippet state is gone
 8109        editor.move_to_prev_snippet_tabstop(window, cx);
 8110        assert(
 8111            editor,
 8112            cx,
 8113            indoc! {"
 8114                a.f(one, two, three)ˇ b
 8115                a.f(one, two, three)ˇ b
 8116                a.f(one, two, three)ˇ b
 8117            "},
 8118        );
 8119    });
 8120}
 8121
 8122#[gpui::test]
 8123async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8124    init_test(cx, |_| {});
 8125
 8126    let fs = FakeFs::new(cx.executor());
 8127    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8128
 8129    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8130
 8131    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8132    language_registry.add(rust_lang());
 8133    let mut fake_servers = language_registry.register_fake_lsp(
 8134        "Rust",
 8135        FakeLspAdapter {
 8136            capabilities: lsp::ServerCapabilities {
 8137                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8138                ..Default::default()
 8139            },
 8140            ..Default::default()
 8141        },
 8142    );
 8143
 8144    let buffer = project
 8145        .update(cx, |project, cx| {
 8146            project.open_local_buffer(path!("/file.rs"), cx)
 8147        })
 8148        .await
 8149        .unwrap();
 8150
 8151    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8152    let (editor, cx) = cx.add_window_view(|window, cx| {
 8153        build_editor_with_project(project.clone(), buffer, window, cx)
 8154    });
 8155    editor.update_in(cx, |editor, window, cx| {
 8156        editor.set_text("one\ntwo\nthree\n", window, cx)
 8157    });
 8158    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8159
 8160    cx.executor().start_waiting();
 8161    let fake_server = fake_servers.next().await.unwrap();
 8162
 8163    {
 8164        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8165            move |params, _| async move {
 8166                assert_eq!(
 8167                    params.text_document.uri,
 8168                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8169                );
 8170                assert_eq!(params.options.tab_size, 4);
 8171                Ok(Some(vec![lsp::TextEdit::new(
 8172                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8173                    ", ".to_string(),
 8174                )]))
 8175            },
 8176        );
 8177        let save = editor
 8178            .update_in(cx, |editor, window, cx| {
 8179                editor.save(true, project.clone(), window, cx)
 8180            })
 8181            .unwrap();
 8182        cx.executor().start_waiting();
 8183        save.await;
 8184
 8185        assert_eq!(
 8186            editor.update(cx, |editor, cx| editor.text(cx)),
 8187            "one, two\nthree\n"
 8188        );
 8189        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8190    }
 8191
 8192    {
 8193        editor.update_in(cx, |editor, window, cx| {
 8194            editor.set_text("one\ntwo\nthree\n", window, cx)
 8195        });
 8196        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8197
 8198        // Ensure we can still save even if formatting hangs.
 8199        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8200            move |params, _| async move {
 8201                assert_eq!(
 8202                    params.text_document.uri,
 8203                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8204                );
 8205                futures::future::pending::<()>().await;
 8206                unreachable!()
 8207            },
 8208        );
 8209        let save = editor
 8210            .update_in(cx, |editor, window, cx| {
 8211                editor.save(true, project.clone(), window, cx)
 8212            })
 8213            .unwrap();
 8214        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8215        cx.executor().start_waiting();
 8216        save.await;
 8217        assert_eq!(
 8218            editor.update(cx, |editor, cx| editor.text(cx)),
 8219            "one\ntwo\nthree\n"
 8220        );
 8221    }
 8222
 8223    // For non-dirty buffer, no formatting request should be sent
 8224    {
 8225        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8226
 8227        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8228            panic!("Should not be invoked on non-dirty buffer");
 8229        });
 8230        let save = editor
 8231            .update_in(cx, |editor, window, cx| {
 8232                editor.save(true, project.clone(), window, cx)
 8233            })
 8234            .unwrap();
 8235        cx.executor().start_waiting();
 8236        save.await;
 8237    }
 8238
 8239    // Set rust language override and assert overridden tabsize is sent to language server
 8240    update_test_language_settings(cx, |settings| {
 8241        settings.languages.insert(
 8242            "Rust".into(),
 8243            LanguageSettingsContent {
 8244                tab_size: NonZeroU32::new(8),
 8245                ..Default::default()
 8246            },
 8247        );
 8248    });
 8249
 8250    {
 8251        editor.update_in(cx, |editor, window, cx| {
 8252            editor.set_text("somehting_new\n", window, cx)
 8253        });
 8254        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8255        let _formatting_request_signal = fake_server
 8256            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8257                assert_eq!(
 8258                    params.text_document.uri,
 8259                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8260                );
 8261                assert_eq!(params.options.tab_size, 8);
 8262                Ok(Some(vec![]))
 8263            });
 8264        let save = editor
 8265            .update_in(cx, |editor, window, cx| {
 8266                editor.save(true, project.clone(), window, cx)
 8267            })
 8268            .unwrap();
 8269        cx.executor().start_waiting();
 8270        save.await;
 8271    }
 8272}
 8273
 8274#[gpui::test]
 8275async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8276    init_test(cx, |_| {});
 8277
 8278    let cols = 4;
 8279    let rows = 10;
 8280    let sample_text_1 = sample_text(rows, cols, 'a');
 8281    assert_eq!(
 8282        sample_text_1,
 8283        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8284    );
 8285    let sample_text_2 = sample_text(rows, cols, 'l');
 8286    assert_eq!(
 8287        sample_text_2,
 8288        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8289    );
 8290    let sample_text_3 = sample_text(rows, cols, 'v');
 8291    assert_eq!(
 8292        sample_text_3,
 8293        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8294    );
 8295
 8296    let fs = FakeFs::new(cx.executor());
 8297    fs.insert_tree(
 8298        path!("/a"),
 8299        json!({
 8300            "main.rs": sample_text_1,
 8301            "other.rs": sample_text_2,
 8302            "lib.rs": sample_text_3,
 8303        }),
 8304    )
 8305    .await;
 8306
 8307    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8308    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8309    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8310
 8311    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8312    language_registry.add(rust_lang());
 8313    let mut fake_servers = language_registry.register_fake_lsp(
 8314        "Rust",
 8315        FakeLspAdapter {
 8316            capabilities: lsp::ServerCapabilities {
 8317                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8318                ..Default::default()
 8319            },
 8320            ..Default::default()
 8321        },
 8322    );
 8323
 8324    let worktree = project.update(cx, |project, cx| {
 8325        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8326        assert_eq!(worktrees.len(), 1);
 8327        worktrees.pop().unwrap()
 8328    });
 8329    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8330
 8331    let buffer_1 = project
 8332        .update(cx, |project, cx| {
 8333            project.open_buffer((worktree_id, "main.rs"), cx)
 8334        })
 8335        .await
 8336        .unwrap();
 8337    let buffer_2 = project
 8338        .update(cx, |project, cx| {
 8339            project.open_buffer((worktree_id, "other.rs"), cx)
 8340        })
 8341        .await
 8342        .unwrap();
 8343    let buffer_3 = project
 8344        .update(cx, |project, cx| {
 8345            project.open_buffer((worktree_id, "lib.rs"), cx)
 8346        })
 8347        .await
 8348        .unwrap();
 8349
 8350    let multi_buffer = cx.new(|cx| {
 8351        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8352        multi_buffer.push_excerpts(
 8353            buffer_1.clone(),
 8354            [
 8355                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8356                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8357                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8358            ],
 8359            cx,
 8360        );
 8361        multi_buffer.push_excerpts(
 8362            buffer_2.clone(),
 8363            [
 8364                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8365                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8366                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8367            ],
 8368            cx,
 8369        );
 8370        multi_buffer.push_excerpts(
 8371            buffer_3.clone(),
 8372            [
 8373                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8374                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8375                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8376            ],
 8377            cx,
 8378        );
 8379        multi_buffer
 8380    });
 8381    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8382        Editor::new(
 8383            EditorMode::full(),
 8384            multi_buffer,
 8385            Some(project.clone()),
 8386            window,
 8387            cx,
 8388        )
 8389    });
 8390
 8391    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8392        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8393            s.select_ranges(Some(1..2))
 8394        });
 8395        editor.insert("|one|two|three|", window, cx);
 8396    });
 8397    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8398    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8399        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8400            s.select_ranges(Some(60..70))
 8401        });
 8402        editor.insert("|four|five|six|", window, cx);
 8403    });
 8404    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8405
 8406    // First two buffers should be edited, but not the third one.
 8407    assert_eq!(
 8408        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8409        "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}",
 8410    );
 8411    buffer_1.update(cx, |buffer, _| {
 8412        assert!(buffer.is_dirty());
 8413        assert_eq!(
 8414            buffer.text(),
 8415            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8416        )
 8417    });
 8418    buffer_2.update(cx, |buffer, _| {
 8419        assert!(buffer.is_dirty());
 8420        assert_eq!(
 8421            buffer.text(),
 8422            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8423        )
 8424    });
 8425    buffer_3.update(cx, |buffer, _| {
 8426        assert!(!buffer.is_dirty());
 8427        assert_eq!(buffer.text(), sample_text_3,)
 8428    });
 8429    cx.executor().run_until_parked();
 8430
 8431    cx.executor().start_waiting();
 8432    let save = multi_buffer_editor
 8433        .update_in(cx, |editor, window, cx| {
 8434            editor.save(true, project.clone(), window, cx)
 8435        })
 8436        .unwrap();
 8437
 8438    let fake_server = fake_servers.next().await.unwrap();
 8439    fake_server
 8440        .server
 8441        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8442            Ok(Some(vec![lsp::TextEdit::new(
 8443                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8444                format!("[{} formatted]", params.text_document.uri),
 8445            )]))
 8446        })
 8447        .detach();
 8448    save.await;
 8449
 8450    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8451    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8452    assert_eq!(
 8453        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8454        uri!(
 8455            "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}"
 8456        ),
 8457    );
 8458    buffer_1.update(cx, |buffer, _| {
 8459        assert!(!buffer.is_dirty());
 8460        assert_eq!(
 8461            buffer.text(),
 8462            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8463        )
 8464    });
 8465    buffer_2.update(cx, |buffer, _| {
 8466        assert!(!buffer.is_dirty());
 8467        assert_eq!(
 8468            buffer.text(),
 8469            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8470        )
 8471    });
 8472    buffer_3.update(cx, |buffer, _| {
 8473        assert!(!buffer.is_dirty());
 8474        assert_eq!(buffer.text(), sample_text_3,)
 8475    });
 8476}
 8477
 8478#[gpui::test]
 8479async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8480    init_test(cx, |_| {});
 8481
 8482    let fs = FakeFs::new(cx.executor());
 8483    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8484
 8485    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8486
 8487    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8488    language_registry.add(rust_lang());
 8489    let mut fake_servers = language_registry.register_fake_lsp(
 8490        "Rust",
 8491        FakeLspAdapter {
 8492            capabilities: lsp::ServerCapabilities {
 8493                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8494                ..Default::default()
 8495            },
 8496            ..Default::default()
 8497        },
 8498    );
 8499
 8500    let buffer = project
 8501        .update(cx, |project, cx| {
 8502            project.open_local_buffer(path!("/file.rs"), cx)
 8503        })
 8504        .await
 8505        .unwrap();
 8506
 8507    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8508    let (editor, cx) = cx.add_window_view(|window, cx| {
 8509        build_editor_with_project(project.clone(), buffer, window, cx)
 8510    });
 8511    editor.update_in(cx, |editor, window, cx| {
 8512        editor.set_text("one\ntwo\nthree\n", window, cx)
 8513    });
 8514    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8515
 8516    cx.executor().start_waiting();
 8517    let fake_server = fake_servers.next().await.unwrap();
 8518
 8519    let save = editor
 8520        .update_in(cx, |editor, window, cx| {
 8521            editor.save(true, project.clone(), window, cx)
 8522        })
 8523        .unwrap();
 8524    fake_server
 8525        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8526            assert_eq!(
 8527                params.text_document.uri,
 8528                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8529            );
 8530            assert_eq!(params.options.tab_size, 4);
 8531            Ok(Some(vec![lsp::TextEdit::new(
 8532                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8533                ", ".to_string(),
 8534            )]))
 8535        })
 8536        .next()
 8537        .await;
 8538    cx.executor().start_waiting();
 8539    save.await;
 8540    assert_eq!(
 8541        editor.update(cx, |editor, cx| editor.text(cx)),
 8542        "one, two\nthree\n"
 8543    );
 8544    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8545
 8546    editor.update_in(cx, |editor, window, cx| {
 8547        editor.set_text("one\ntwo\nthree\n", window, cx)
 8548    });
 8549    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8550
 8551    // Ensure we can still save even if formatting hangs.
 8552    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8553        move |params, _| async move {
 8554            assert_eq!(
 8555                params.text_document.uri,
 8556                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8557            );
 8558            futures::future::pending::<()>().await;
 8559            unreachable!()
 8560        },
 8561    );
 8562    let save = editor
 8563        .update_in(cx, |editor, window, cx| {
 8564            editor.save(true, project.clone(), window, cx)
 8565        })
 8566        .unwrap();
 8567    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8568    cx.executor().start_waiting();
 8569    save.await;
 8570    assert_eq!(
 8571        editor.update(cx, |editor, cx| editor.text(cx)),
 8572        "one\ntwo\nthree\n"
 8573    );
 8574    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8575
 8576    // For non-dirty buffer, no formatting request should be sent
 8577    let save = editor
 8578        .update_in(cx, |editor, window, cx| {
 8579            editor.save(true, project.clone(), window, cx)
 8580        })
 8581        .unwrap();
 8582    let _pending_format_request = fake_server
 8583        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8584            panic!("Should not be invoked on non-dirty buffer");
 8585        })
 8586        .next();
 8587    cx.executor().start_waiting();
 8588    save.await;
 8589
 8590    // Set Rust language override and assert overridden tabsize is sent to language server
 8591    update_test_language_settings(cx, |settings| {
 8592        settings.languages.insert(
 8593            "Rust".into(),
 8594            LanguageSettingsContent {
 8595                tab_size: NonZeroU32::new(8),
 8596                ..Default::default()
 8597            },
 8598        );
 8599    });
 8600
 8601    editor.update_in(cx, |editor, window, cx| {
 8602        editor.set_text("somehting_new\n", window, cx)
 8603    });
 8604    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8605    let save = editor
 8606        .update_in(cx, |editor, window, cx| {
 8607            editor.save(true, project.clone(), window, cx)
 8608        })
 8609        .unwrap();
 8610    fake_server
 8611        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8612            assert_eq!(
 8613                params.text_document.uri,
 8614                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8615            );
 8616            assert_eq!(params.options.tab_size, 8);
 8617            Ok(Some(vec![]))
 8618        })
 8619        .next()
 8620        .await;
 8621    cx.executor().start_waiting();
 8622    save.await;
 8623}
 8624
 8625#[gpui::test]
 8626async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8627    init_test(cx, |settings| {
 8628        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8629            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8630        ))
 8631    });
 8632
 8633    let fs = FakeFs::new(cx.executor());
 8634    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8635
 8636    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8637
 8638    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8639    language_registry.add(Arc::new(Language::new(
 8640        LanguageConfig {
 8641            name: "Rust".into(),
 8642            matcher: LanguageMatcher {
 8643                path_suffixes: vec!["rs".to_string()],
 8644                ..Default::default()
 8645            },
 8646            ..LanguageConfig::default()
 8647        },
 8648        Some(tree_sitter_rust::LANGUAGE.into()),
 8649    )));
 8650    update_test_language_settings(cx, |settings| {
 8651        // Enable Prettier formatting for the same buffer, and ensure
 8652        // LSP is called instead of Prettier.
 8653        settings.defaults.prettier = Some(PrettierSettings {
 8654            allowed: true,
 8655            ..PrettierSettings::default()
 8656        });
 8657    });
 8658    let mut fake_servers = language_registry.register_fake_lsp(
 8659        "Rust",
 8660        FakeLspAdapter {
 8661            capabilities: lsp::ServerCapabilities {
 8662                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8663                ..Default::default()
 8664            },
 8665            ..Default::default()
 8666        },
 8667    );
 8668
 8669    let buffer = project
 8670        .update(cx, |project, cx| {
 8671            project.open_local_buffer(path!("/file.rs"), cx)
 8672        })
 8673        .await
 8674        .unwrap();
 8675
 8676    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8677    let (editor, cx) = cx.add_window_view(|window, cx| {
 8678        build_editor_with_project(project.clone(), buffer, window, cx)
 8679    });
 8680    editor.update_in(cx, |editor, window, cx| {
 8681        editor.set_text("one\ntwo\nthree\n", window, cx)
 8682    });
 8683
 8684    cx.executor().start_waiting();
 8685    let fake_server = fake_servers.next().await.unwrap();
 8686
 8687    let format = editor
 8688        .update_in(cx, |editor, window, cx| {
 8689            editor.perform_format(
 8690                project.clone(),
 8691                FormatTrigger::Manual,
 8692                FormatTarget::Buffers,
 8693                window,
 8694                cx,
 8695            )
 8696        })
 8697        .unwrap();
 8698    fake_server
 8699        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8700            assert_eq!(
 8701                params.text_document.uri,
 8702                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8703            );
 8704            assert_eq!(params.options.tab_size, 4);
 8705            Ok(Some(vec![lsp::TextEdit::new(
 8706                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8707                ", ".to_string(),
 8708            )]))
 8709        })
 8710        .next()
 8711        .await;
 8712    cx.executor().start_waiting();
 8713    format.await;
 8714    assert_eq!(
 8715        editor.update(cx, |editor, cx| editor.text(cx)),
 8716        "one, two\nthree\n"
 8717    );
 8718
 8719    editor.update_in(cx, |editor, window, cx| {
 8720        editor.set_text("one\ntwo\nthree\n", window, cx)
 8721    });
 8722    // Ensure we don't lock if formatting hangs.
 8723    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8724        move |params, _| async move {
 8725            assert_eq!(
 8726                params.text_document.uri,
 8727                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8728            );
 8729            futures::future::pending::<()>().await;
 8730            unreachable!()
 8731        },
 8732    );
 8733    let format = editor
 8734        .update_in(cx, |editor, window, cx| {
 8735            editor.perform_format(
 8736                project,
 8737                FormatTrigger::Manual,
 8738                FormatTarget::Buffers,
 8739                window,
 8740                cx,
 8741            )
 8742        })
 8743        .unwrap();
 8744    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8745    cx.executor().start_waiting();
 8746    format.await;
 8747    assert_eq!(
 8748        editor.update(cx, |editor, cx| editor.text(cx)),
 8749        "one\ntwo\nthree\n"
 8750    );
 8751}
 8752
 8753#[gpui::test]
 8754async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8755    init_test(cx, |settings| {
 8756        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8757        settings.defaults.formatter =
 8758            Some(language_settings::SelectedFormatter::List(FormatterList(
 8759                vec![
 8760                    Formatter::LanguageServer { name: None },
 8761                    Formatter::CodeActions(
 8762                        [
 8763                            ("code-action-1".into(), true),
 8764                            ("code-action-2".into(), true),
 8765                        ]
 8766                        .into_iter()
 8767                        .collect(),
 8768                    ),
 8769                ]
 8770                .into(),
 8771            )))
 8772    });
 8773
 8774    let fs = FakeFs::new(cx.executor());
 8775    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8776        .await;
 8777
 8778    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8779    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8780    language_registry.add(rust_lang());
 8781
 8782    let mut fake_servers = language_registry.register_fake_lsp(
 8783        "Rust",
 8784        FakeLspAdapter {
 8785            capabilities: lsp::ServerCapabilities {
 8786                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8787                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8788                    commands: vec!["the-command-for-code-action-1".into()],
 8789                    ..Default::default()
 8790                }),
 8791                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8792                ..Default::default()
 8793            },
 8794            ..Default::default()
 8795        },
 8796    );
 8797
 8798    let buffer = project
 8799        .update(cx, |project, cx| {
 8800            project.open_local_buffer(path!("/file.rs"), cx)
 8801        })
 8802        .await
 8803        .unwrap();
 8804
 8805    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8806    let (editor, cx) = cx.add_window_view(|window, cx| {
 8807        build_editor_with_project(project.clone(), buffer, window, cx)
 8808    });
 8809
 8810    cx.executor().start_waiting();
 8811
 8812    let fake_server = fake_servers.next().await.unwrap();
 8813    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8814        move |_params, _| async move {
 8815            Ok(Some(vec![lsp::TextEdit::new(
 8816                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8817                "applied-formatting\n".to_string(),
 8818            )]))
 8819        },
 8820    );
 8821    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8822        move |params, _| async move {
 8823            assert_eq!(
 8824                params.context.only,
 8825                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8826            );
 8827            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8828            Ok(Some(vec![
 8829                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8830                    kind: Some("code-action-1".into()),
 8831                    edit: Some(lsp::WorkspaceEdit::new(
 8832                        [(
 8833                            uri.clone(),
 8834                            vec![lsp::TextEdit::new(
 8835                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8836                                "applied-code-action-1-edit\n".to_string(),
 8837                            )],
 8838                        )]
 8839                        .into_iter()
 8840                        .collect(),
 8841                    )),
 8842                    command: Some(lsp::Command {
 8843                        command: "the-command-for-code-action-1".into(),
 8844                        ..Default::default()
 8845                    }),
 8846                    ..Default::default()
 8847                }),
 8848                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8849                    kind: Some("code-action-2".into()),
 8850                    edit: Some(lsp::WorkspaceEdit::new(
 8851                        [(
 8852                            uri.clone(),
 8853                            vec![lsp::TextEdit::new(
 8854                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8855                                "applied-code-action-2-edit\n".to_string(),
 8856                            )],
 8857                        )]
 8858                        .into_iter()
 8859                        .collect(),
 8860                    )),
 8861                    ..Default::default()
 8862                }),
 8863            ]))
 8864        },
 8865    );
 8866
 8867    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8868        move |params, _| async move { Ok(params) }
 8869    });
 8870
 8871    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8872    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8873        let fake = fake_server.clone();
 8874        let lock = command_lock.clone();
 8875        move |params, _| {
 8876            assert_eq!(params.command, "the-command-for-code-action-1");
 8877            let fake = fake.clone();
 8878            let lock = lock.clone();
 8879            async move {
 8880                lock.lock().await;
 8881                fake.server
 8882                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8883                        label: None,
 8884                        edit: lsp::WorkspaceEdit {
 8885                            changes: Some(
 8886                                [(
 8887                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8888                                    vec![lsp::TextEdit {
 8889                                        range: lsp::Range::new(
 8890                                            lsp::Position::new(0, 0),
 8891                                            lsp::Position::new(0, 0),
 8892                                        ),
 8893                                        new_text: "applied-code-action-1-command\n".into(),
 8894                                    }],
 8895                                )]
 8896                                .into_iter()
 8897                                .collect(),
 8898                            ),
 8899                            ..Default::default()
 8900                        },
 8901                    })
 8902                    .await
 8903                    .into_response()
 8904                    .unwrap();
 8905                Ok(Some(json!(null)))
 8906            }
 8907        }
 8908    });
 8909
 8910    cx.executor().start_waiting();
 8911    editor
 8912        .update_in(cx, |editor, window, cx| {
 8913            editor.perform_format(
 8914                project.clone(),
 8915                FormatTrigger::Manual,
 8916                FormatTarget::Buffers,
 8917                window,
 8918                cx,
 8919            )
 8920        })
 8921        .unwrap()
 8922        .await;
 8923    editor.update(cx, |editor, cx| {
 8924        assert_eq!(
 8925            editor.text(cx),
 8926            r#"
 8927                applied-code-action-2-edit
 8928                applied-code-action-1-command
 8929                applied-code-action-1-edit
 8930                applied-formatting
 8931                one
 8932                two
 8933                three
 8934            "#
 8935            .unindent()
 8936        );
 8937    });
 8938
 8939    editor.update_in(cx, |editor, window, cx| {
 8940        editor.undo(&Default::default(), window, cx);
 8941        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8942    });
 8943
 8944    // Perform a manual edit while waiting for an LSP command
 8945    // that's being run as part of a formatting code action.
 8946    let lock_guard = command_lock.lock().await;
 8947    let format = editor
 8948        .update_in(cx, |editor, window, cx| {
 8949            editor.perform_format(
 8950                project.clone(),
 8951                FormatTrigger::Manual,
 8952                FormatTarget::Buffers,
 8953                window,
 8954                cx,
 8955            )
 8956        })
 8957        .unwrap();
 8958    cx.run_until_parked();
 8959    editor.update(cx, |editor, cx| {
 8960        assert_eq!(
 8961            editor.text(cx),
 8962            r#"
 8963                applied-code-action-1-edit
 8964                applied-formatting
 8965                one
 8966                two
 8967                three
 8968            "#
 8969            .unindent()
 8970        );
 8971
 8972        editor.buffer.update(cx, |buffer, cx| {
 8973            let ix = buffer.len(cx);
 8974            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8975        });
 8976    });
 8977
 8978    // Allow the LSP command to proceed. Because the buffer was edited,
 8979    // the second code action will not be run.
 8980    drop(lock_guard);
 8981    format.await;
 8982    editor.update_in(cx, |editor, window, cx| {
 8983        assert_eq!(
 8984            editor.text(cx),
 8985            r#"
 8986                applied-code-action-1-command
 8987                applied-code-action-1-edit
 8988                applied-formatting
 8989                one
 8990                two
 8991                three
 8992                edited
 8993            "#
 8994            .unindent()
 8995        );
 8996
 8997        // The manual edit is undone first, because it is the last thing the user did
 8998        // (even though the command completed afterwards).
 8999        editor.undo(&Default::default(), window, cx);
 9000        assert_eq!(
 9001            editor.text(cx),
 9002            r#"
 9003                applied-code-action-1-command
 9004                applied-code-action-1-edit
 9005                applied-formatting
 9006                one
 9007                two
 9008                three
 9009            "#
 9010            .unindent()
 9011        );
 9012
 9013        // All the formatting (including the command, which completed after the manual edit)
 9014        // is undone together.
 9015        editor.undo(&Default::default(), window, cx);
 9016        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9017    });
 9018}
 9019
 9020#[gpui::test]
 9021async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9022    init_test(cx, |settings| {
 9023        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9024            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9025        ))
 9026    });
 9027
 9028    let fs = FakeFs::new(cx.executor());
 9029    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9030
 9031    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9032
 9033    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9034    language_registry.add(Arc::new(Language::new(
 9035        LanguageConfig {
 9036            name: "TypeScript".into(),
 9037            matcher: LanguageMatcher {
 9038                path_suffixes: vec!["ts".to_string()],
 9039                ..Default::default()
 9040            },
 9041            ..LanguageConfig::default()
 9042        },
 9043        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9044    )));
 9045    update_test_language_settings(cx, |settings| {
 9046        settings.defaults.prettier = Some(PrettierSettings {
 9047            allowed: true,
 9048            ..PrettierSettings::default()
 9049        });
 9050    });
 9051    let mut fake_servers = language_registry.register_fake_lsp(
 9052        "TypeScript",
 9053        FakeLspAdapter {
 9054            capabilities: lsp::ServerCapabilities {
 9055                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9056                ..Default::default()
 9057            },
 9058            ..Default::default()
 9059        },
 9060    );
 9061
 9062    let buffer = project
 9063        .update(cx, |project, cx| {
 9064            project.open_local_buffer(path!("/file.ts"), cx)
 9065        })
 9066        .await
 9067        .unwrap();
 9068
 9069    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9070    let (editor, cx) = cx.add_window_view(|window, cx| {
 9071        build_editor_with_project(project.clone(), buffer, window, cx)
 9072    });
 9073    editor.update_in(cx, |editor, window, cx| {
 9074        editor.set_text(
 9075            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9076            window,
 9077            cx,
 9078        )
 9079    });
 9080
 9081    cx.executor().start_waiting();
 9082    let fake_server = fake_servers.next().await.unwrap();
 9083
 9084    let format = editor
 9085        .update_in(cx, |editor, window, cx| {
 9086            editor.perform_code_action_kind(
 9087                project.clone(),
 9088                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9089                window,
 9090                cx,
 9091            )
 9092        })
 9093        .unwrap();
 9094    fake_server
 9095        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9096            assert_eq!(
 9097                params.text_document.uri,
 9098                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9099            );
 9100            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9101                lsp::CodeAction {
 9102                    title: "Organize Imports".to_string(),
 9103                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9104                    edit: Some(lsp::WorkspaceEdit {
 9105                        changes: Some(
 9106                            [(
 9107                                params.text_document.uri.clone(),
 9108                                vec![lsp::TextEdit::new(
 9109                                    lsp::Range::new(
 9110                                        lsp::Position::new(1, 0),
 9111                                        lsp::Position::new(2, 0),
 9112                                    ),
 9113                                    "".to_string(),
 9114                                )],
 9115                            )]
 9116                            .into_iter()
 9117                            .collect(),
 9118                        ),
 9119                        ..Default::default()
 9120                    }),
 9121                    ..Default::default()
 9122                },
 9123            )]))
 9124        })
 9125        .next()
 9126        .await;
 9127    cx.executor().start_waiting();
 9128    format.await;
 9129    assert_eq!(
 9130        editor.update(cx, |editor, cx| editor.text(cx)),
 9131        "import { a } from 'module';\n\nconst x = a;\n"
 9132    );
 9133
 9134    editor.update_in(cx, |editor, window, cx| {
 9135        editor.set_text(
 9136            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9137            window,
 9138            cx,
 9139        )
 9140    });
 9141    // Ensure we don't lock if code action hangs.
 9142    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9143        move |params, _| async move {
 9144            assert_eq!(
 9145                params.text_document.uri,
 9146                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9147            );
 9148            futures::future::pending::<()>().await;
 9149            unreachable!()
 9150        },
 9151    );
 9152    let format = editor
 9153        .update_in(cx, |editor, window, cx| {
 9154            editor.perform_code_action_kind(
 9155                project,
 9156                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9157                window,
 9158                cx,
 9159            )
 9160        })
 9161        .unwrap();
 9162    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9163    cx.executor().start_waiting();
 9164    format.await;
 9165    assert_eq!(
 9166        editor.update(cx, |editor, cx| editor.text(cx)),
 9167        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9168    );
 9169}
 9170
 9171#[gpui::test]
 9172async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9173    init_test(cx, |_| {});
 9174
 9175    let mut cx = EditorLspTestContext::new_rust(
 9176        lsp::ServerCapabilities {
 9177            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9178            ..Default::default()
 9179        },
 9180        cx,
 9181    )
 9182    .await;
 9183
 9184    cx.set_state(indoc! {"
 9185        one.twoˇ
 9186    "});
 9187
 9188    // The format request takes a long time. When it completes, it inserts
 9189    // a newline and an indent before the `.`
 9190    cx.lsp
 9191        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9192            let executor = cx.background_executor().clone();
 9193            async move {
 9194                executor.timer(Duration::from_millis(100)).await;
 9195                Ok(Some(vec![lsp::TextEdit {
 9196                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9197                    new_text: "\n    ".into(),
 9198                }]))
 9199            }
 9200        });
 9201
 9202    // Submit a format request.
 9203    let format_1 = cx
 9204        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9205        .unwrap();
 9206    cx.executor().run_until_parked();
 9207
 9208    // Submit a second format request.
 9209    let format_2 = cx
 9210        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9211        .unwrap();
 9212    cx.executor().run_until_parked();
 9213
 9214    // Wait for both format requests to complete
 9215    cx.executor().advance_clock(Duration::from_millis(200));
 9216    cx.executor().start_waiting();
 9217    format_1.await.unwrap();
 9218    cx.executor().start_waiting();
 9219    format_2.await.unwrap();
 9220
 9221    // The formatting edits only happens once.
 9222    cx.assert_editor_state(indoc! {"
 9223        one
 9224            .twoˇ
 9225    "});
 9226}
 9227
 9228#[gpui::test]
 9229async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9230    init_test(cx, |settings| {
 9231        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9232    });
 9233
 9234    let mut cx = EditorLspTestContext::new_rust(
 9235        lsp::ServerCapabilities {
 9236            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9237            ..Default::default()
 9238        },
 9239        cx,
 9240    )
 9241    .await;
 9242
 9243    // Set up a buffer white some trailing whitespace and no trailing newline.
 9244    cx.set_state(
 9245        &[
 9246            "one ",   //
 9247            "twoˇ",   //
 9248            "three ", //
 9249            "four",   //
 9250        ]
 9251        .join("\n"),
 9252    );
 9253
 9254    // Submit a format request.
 9255    let format = cx
 9256        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9257        .unwrap();
 9258
 9259    // Record which buffer changes have been sent to the language server
 9260    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9261    cx.lsp
 9262        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9263            let buffer_changes = buffer_changes.clone();
 9264            move |params, _| {
 9265                buffer_changes.lock().extend(
 9266                    params
 9267                        .content_changes
 9268                        .into_iter()
 9269                        .map(|e| (e.range.unwrap(), e.text)),
 9270                );
 9271            }
 9272        });
 9273
 9274    // Handle formatting requests to the language server.
 9275    cx.lsp
 9276        .set_request_handler::<lsp::request::Formatting, _, _>({
 9277            let buffer_changes = buffer_changes.clone();
 9278            move |_, _| {
 9279                // When formatting is requested, trailing whitespace has already been stripped,
 9280                // and the trailing newline has already been added.
 9281                assert_eq!(
 9282                    &buffer_changes.lock()[1..],
 9283                    &[
 9284                        (
 9285                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9286                            "".into()
 9287                        ),
 9288                        (
 9289                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9290                            "".into()
 9291                        ),
 9292                        (
 9293                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9294                            "\n".into()
 9295                        ),
 9296                    ]
 9297                );
 9298
 9299                // Insert blank lines between each line of the buffer.
 9300                async move {
 9301                    Ok(Some(vec![
 9302                        lsp::TextEdit {
 9303                            range: lsp::Range::new(
 9304                                lsp::Position::new(1, 0),
 9305                                lsp::Position::new(1, 0),
 9306                            ),
 9307                            new_text: "\n".into(),
 9308                        },
 9309                        lsp::TextEdit {
 9310                            range: lsp::Range::new(
 9311                                lsp::Position::new(2, 0),
 9312                                lsp::Position::new(2, 0),
 9313                            ),
 9314                            new_text: "\n".into(),
 9315                        },
 9316                    ]))
 9317                }
 9318            }
 9319        });
 9320
 9321    // After formatting the buffer, the trailing whitespace is stripped,
 9322    // a newline is appended, and the edits provided by the language server
 9323    // have been applied.
 9324    format.await.unwrap();
 9325    cx.assert_editor_state(
 9326        &[
 9327            "one",   //
 9328            "",      //
 9329            "twoˇ",  //
 9330            "",      //
 9331            "three", //
 9332            "four",  //
 9333            "",      //
 9334        ]
 9335        .join("\n"),
 9336    );
 9337
 9338    // Undoing the formatting undoes the trailing whitespace removal, the
 9339    // trailing newline, and the LSP edits.
 9340    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9341    cx.assert_editor_state(
 9342        &[
 9343            "one ",   //
 9344            "twoˇ",   //
 9345            "three ", //
 9346            "four",   //
 9347        ]
 9348        .join("\n"),
 9349    );
 9350}
 9351
 9352#[gpui::test]
 9353async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9354    cx: &mut TestAppContext,
 9355) {
 9356    init_test(cx, |_| {});
 9357
 9358    cx.update(|cx| {
 9359        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9360            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9361                settings.auto_signature_help = Some(true);
 9362            });
 9363        });
 9364    });
 9365
 9366    let mut cx = EditorLspTestContext::new_rust(
 9367        lsp::ServerCapabilities {
 9368            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9369                ..Default::default()
 9370            }),
 9371            ..Default::default()
 9372        },
 9373        cx,
 9374    )
 9375    .await;
 9376
 9377    let language = Language::new(
 9378        LanguageConfig {
 9379            name: "Rust".into(),
 9380            brackets: BracketPairConfig {
 9381                pairs: vec![
 9382                    BracketPair {
 9383                        start: "{".to_string(),
 9384                        end: "}".to_string(),
 9385                        close: true,
 9386                        surround: true,
 9387                        newline: true,
 9388                    },
 9389                    BracketPair {
 9390                        start: "(".to_string(),
 9391                        end: ")".to_string(),
 9392                        close: true,
 9393                        surround: true,
 9394                        newline: true,
 9395                    },
 9396                    BracketPair {
 9397                        start: "/*".to_string(),
 9398                        end: " */".to_string(),
 9399                        close: true,
 9400                        surround: true,
 9401                        newline: true,
 9402                    },
 9403                    BracketPair {
 9404                        start: "[".to_string(),
 9405                        end: "]".to_string(),
 9406                        close: false,
 9407                        surround: false,
 9408                        newline: true,
 9409                    },
 9410                    BracketPair {
 9411                        start: "\"".to_string(),
 9412                        end: "\"".to_string(),
 9413                        close: true,
 9414                        surround: true,
 9415                        newline: false,
 9416                    },
 9417                    BracketPair {
 9418                        start: "<".to_string(),
 9419                        end: ">".to_string(),
 9420                        close: false,
 9421                        surround: true,
 9422                        newline: true,
 9423                    },
 9424                ],
 9425                ..Default::default()
 9426            },
 9427            autoclose_before: "})]".to_string(),
 9428            ..Default::default()
 9429        },
 9430        Some(tree_sitter_rust::LANGUAGE.into()),
 9431    );
 9432    let language = Arc::new(language);
 9433
 9434    cx.language_registry().add(language.clone());
 9435    cx.update_buffer(|buffer, cx| {
 9436        buffer.set_language(Some(language), cx);
 9437    });
 9438
 9439    cx.set_state(
 9440        &r#"
 9441            fn main() {
 9442                sampleˇ
 9443            }
 9444        "#
 9445        .unindent(),
 9446    );
 9447
 9448    cx.update_editor(|editor, window, cx| {
 9449        editor.handle_input("(", window, cx);
 9450    });
 9451    cx.assert_editor_state(
 9452        &"
 9453            fn main() {
 9454                sample(ˇ)
 9455            }
 9456        "
 9457        .unindent(),
 9458    );
 9459
 9460    let mocked_response = lsp::SignatureHelp {
 9461        signatures: vec![lsp::SignatureInformation {
 9462            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9463            documentation: None,
 9464            parameters: Some(vec![
 9465                lsp::ParameterInformation {
 9466                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9467                    documentation: None,
 9468                },
 9469                lsp::ParameterInformation {
 9470                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9471                    documentation: None,
 9472                },
 9473            ]),
 9474            active_parameter: None,
 9475        }],
 9476        active_signature: Some(0),
 9477        active_parameter: Some(0),
 9478    };
 9479    handle_signature_help_request(&mut cx, mocked_response).await;
 9480
 9481    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9482        .await;
 9483
 9484    cx.editor(|editor, _, _| {
 9485        let signature_help_state = editor.signature_help_state.popover().cloned();
 9486        assert_eq!(
 9487            signature_help_state.unwrap().label,
 9488            "param1: u8, param2: u8"
 9489        );
 9490    });
 9491}
 9492
 9493#[gpui::test]
 9494async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9495    init_test(cx, |_| {});
 9496
 9497    cx.update(|cx| {
 9498        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9499            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9500                settings.auto_signature_help = Some(false);
 9501                settings.show_signature_help_after_edits = Some(false);
 9502            });
 9503        });
 9504    });
 9505
 9506    let mut cx = EditorLspTestContext::new_rust(
 9507        lsp::ServerCapabilities {
 9508            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9509                ..Default::default()
 9510            }),
 9511            ..Default::default()
 9512        },
 9513        cx,
 9514    )
 9515    .await;
 9516
 9517    let language = Language::new(
 9518        LanguageConfig {
 9519            name: "Rust".into(),
 9520            brackets: BracketPairConfig {
 9521                pairs: vec![
 9522                    BracketPair {
 9523                        start: "{".to_string(),
 9524                        end: "}".to_string(),
 9525                        close: true,
 9526                        surround: true,
 9527                        newline: true,
 9528                    },
 9529                    BracketPair {
 9530                        start: "(".to_string(),
 9531                        end: ")".to_string(),
 9532                        close: true,
 9533                        surround: true,
 9534                        newline: true,
 9535                    },
 9536                    BracketPair {
 9537                        start: "/*".to_string(),
 9538                        end: " */".to_string(),
 9539                        close: true,
 9540                        surround: true,
 9541                        newline: true,
 9542                    },
 9543                    BracketPair {
 9544                        start: "[".to_string(),
 9545                        end: "]".to_string(),
 9546                        close: false,
 9547                        surround: false,
 9548                        newline: true,
 9549                    },
 9550                    BracketPair {
 9551                        start: "\"".to_string(),
 9552                        end: "\"".to_string(),
 9553                        close: true,
 9554                        surround: true,
 9555                        newline: false,
 9556                    },
 9557                    BracketPair {
 9558                        start: "<".to_string(),
 9559                        end: ">".to_string(),
 9560                        close: false,
 9561                        surround: true,
 9562                        newline: true,
 9563                    },
 9564                ],
 9565                ..Default::default()
 9566            },
 9567            autoclose_before: "})]".to_string(),
 9568            ..Default::default()
 9569        },
 9570        Some(tree_sitter_rust::LANGUAGE.into()),
 9571    );
 9572    let language = Arc::new(language);
 9573
 9574    cx.language_registry().add(language.clone());
 9575    cx.update_buffer(|buffer, cx| {
 9576        buffer.set_language(Some(language), cx);
 9577    });
 9578
 9579    // Ensure that signature_help is not called when no signature help is enabled.
 9580    cx.set_state(
 9581        &r#"
 9582            fn main() {
 9583                sampleˇ
 9584            }
 9585        "#
 9586        .unindent(),
 9587    );
 9588    cx.update_editor(|editor, window, cx| {
 9589        editor.handle_input("(", window, cx);
 9590    });
 9591    cx.assert_editor_state(
 9592        &"
 9593            fn main() {
 9594                sample(ˇ)
 9595            }
 9596        "
 9597        .unindent(),
 9598    );
 9599    cx.editor(|editor, _, _| {
 9600        assert!(editor.signature_help_state.task().is_none());
 9601    });
 9602
 9603    let mocked_response = lsp::SignatureHelp {
 9604        signatures: vec![lsp::SignatureInformation {
 9605            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9606            documentation: None,
 9607            parameters: Some(vec![
 9608                lsp::ParameterInformation {
 9609                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9610                    documentation: None,
 9611                },
 9612                lsp::ParameterInformation {
 9613                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9614                    documentation: None,
 9615                },
 9616            ]),
 9617            active_parameter: None,
 9618        }],
 9619        active_signature: Some(0),
 9620        active_parameter: Some(0),
 9621    };
 9622
 9623    // Ensure that signature_help is called when enabled afte edits
 9624    cx.update(|_, cx| {
 9625        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9626            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9627                settings.auto_signature_help = Some(false);
 9628                settings.show_signature_help_after_edits = Some(true);
 9629            });
 9630        });
 9631    });
 9632    cx.set_state(
 9633        &r#"
 9634            fn main() {
 9635                sampleˇ
 9636            }
 9637        "#
 9638        .unindent(),
 9639    );
 9640    cx.update_editor(|editor, window, cx| {
 9641        editor.handle_input("(", window, cx);
 9642    });
 9643    cx.assert_editor_state(
 9644        &"
 9645            fn main() {
 9646                sample(ˇ)
 9647            }
 9648        "
 9649        .unindent(),
 9650    );
 9651    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9652    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9653        .await;
 9654    cx.update_editor(|editor, _, _| {
 9655        let signature_help_state = editor.signature_help_state.popover().cloned();
 9656        assert!(signature_help_state.is_some());
 9657        assert_eq!(
 9658            signature_help_state.unwrap().label,
 9659            "param1: u8, param2: u8"
 9660        );
 9661        editor.signature_help_state = SignatureHelpState::default();
 9662    });
 9663
 9664    // Ensure that signature_help is called when auto signature help override is enabled
 9665    cx.update(|_, cx| {
 9666        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9667            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9668                settings.auto_signature_help = Some(true);
 9669                settings.show_signature_help_after_edits = Some(false);
 9670            });
 9671        });
 9672    });
 9673    cx.set_state(
 9674        &r#"
 9675            fn main() {
 9676                sampleˇ
 9677            }
 9678        "#
 9679        .unindent(),
 9680    );
 9681    cx.update_editor(|editor, window, cx| {
 9682        editor.handle_input("(", window, cx);
 9683    });
 9684    cx.assert_editor_state(
 9685        &"
 9686            fn main() {
 9687                sample(ˇ)
 9688            }
 9689        "
 9690        .unindent(),
 9691    );
 9692    handle_signature_help_request(&mut cx, mocked_response).await;
 9693    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9694        .await;
 9695    cx.editor(|editor, _, _| {
 9696        let signature_help_state = editor.signature_help_state.popover().cloned();
 9697        assert!(signature_help_state.is_some());
 9698        assert_eq!(
 9699            signature_help_state.unwrap().label,
 9700            "param1: u8, param2: u8"
 9701        );
 9702    });
 9703}
 9704
 9705#[gpui::test]
 9706async fn test_signature_help(cx: &mut TestAppContext) {
 9707    init_test(cx, |_| {});
 9708    cx.update(|cx| {
 9709        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9710            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9711                settings.auto_signature_help = Some(true);
 9712            });
 9713        });
 9714    });
 9715
 9716    let mut cx = EditorLspTestContext::new_rust(
 9717        lsp::ServerCapabilities {
 9718            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9719                ..Default::default()
 9720            }),
 9721            ..Default::default()
 9722        },
 9723        cx,
 9724    )
 9725    .await;
 9726
 9727    // A test that directly calls `show_signature_help`
 9728    cx.update_editor(|editor, window, cx| {
 9729        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9730    });
 9731
 9732    let mocked_response = lsp::SignatureHelp {
 9733        signatures: vec![lsp::SignatureInformation {
 9734            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9735            documentation: None,
 9736            parameters: Some(vec![
 9737                lsp::ParameterInformation {
 9738                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9739                    documentation: None,
 9740                },
 9741                lsp::ParameterInformation {
 9742                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9743                    documentation: None,
 9744                },
 9745            ]),
 9746            active_parameter: None,
 9747        }],
 9748        active_signature: Some(0),
 9749        active_parameter: Some(0),
 9750    };
 9751    handle_signature_help_request(&mut cx, mocked_response).await;
 9752
 9753    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9754        .await;
 9755
 9756    cx.editor(|editor, _, _| {
 9757        let signature_help_state = editor.signature_help_state.popover().cloned();
 9758        assert!(signature_help_state.is_some());
 9759        assert_eq!(
 9760            signature_help_state.unwrap().label,
 9761            "param1: u8, param2: u8"
 9762        );
 9763    });
 9764
 9765    // When exiting outside from inside the brackets, `signature_help` is closed.
 9766    cx.set_state(indoc! {"
 9767        fn main() {
 9768            sample(ˇ);
 9769        }
 9770
 9771        fn sample(param1: u8, param2: u8) {}
 9772    "});
 9773
 9774    cx.update_editor(|editor, window, cx| {
 9775        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9776    });
 9777
 9778    let mocked_response = lsp::SignatureHelp {
 9779        signatures: Vec::new(),
 9780        active_signature: None,
 9781        active_parameter: None,
 9782    };
 9783    handle_signature_help_request(&mut cx, mocked_response).await;
 9784
 9785    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9786        .await;
 9787
 9788    cx.editor(|editor, _, _| {
 9789        assert!(!editor.signature_help_state.is_shown());
 9790    });
 9791
 9792    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9793    cx.set_state(indoc! {"
 9794        fn main() {
 9795            sample(ˇ);
 9796        }
 9797
 9798        fn sample(param1: u8, param2: u8) {}
 9799    "});
 9800
 9801    let mocked_response = lsp::SignatureHelp {
 9802        signatures: vec![lsp::SignatureInformation {
 9803            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9804            documentation: None,
 9805            parameters: Some(vec![
 9806                lsp::ParameterInformation {
 9807                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9808                    documentation: None,
 9809                },
 9810                lsp::ParameterInformation {
 9811                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9812                    documentation: None,
 9813                },
 9814            ]),
 9815            active_parameter: None,
 9816        }],
 9817        active_signature: Some(0),
 9818        active_parameter: Some(0),
 9819    };
 9820    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9821    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9822        .await;
 9823    cx.editor(|editor, _, _| {
 9824        assert!(editor.signature_help_state.is_shown());
 9825    });
 9826
 9827    // Restore the popover with more parameter input
 9828    cx.set_state(indoc! {"
 9829        fn main() {
 9830            sample(param1, param2ˇ);
 9831        }
 9832
 9833        fn sample(param1: u8, param2: u8) {}
 9834    "});
 9835
 9836    let mocked_response = lsp::SignatureHelp {
 9837        signatures: vec![lsp::SignatureInformation {
 9838            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9839            documentation: None,
 9840            parameters: Some(vec![
 9841                lsp::ParameterInformation {
 9842                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9843                    documentation: None,
 9844                },
 9845                lsp::ParameterInformation {
 9846                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9847                    documentation: None,
 9848                },
 9849            ]),
 9850            active_parameter: None,
 9851        }],
 9852        active_signature: Some(0),
 9853        active_parameter: Some(1),
 9854    };
 9855    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9856    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9857        .await;
 9858
 9859    // When selecting a range, the popover is gone.
 9860    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9861    cx.update_editor(|editor, window, cx| {
 9862        editor.change_selections(None, window, cx, |s| {
 9863            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9864        })
 9865    });
 9866    cx.assert_editor_state(indoc! {"
 9867        fn main() {
 9868            sample(param1, «ˇparam2»);
 9869        }
 9870
 9871        fn sample(param1: u8, param2: u8) {}
 9872    "});
 9873    cx.editor(|editor, _, _| {
 9874        assert!(!editor.signature_help_state.is_shown());
 9875    });
 9876
 9877    // When unselecting again, the popover is back if within the brackets.
 9878    cx.update_editor(|editor, window, cx| {
 9879        editor.change_selections(None, window, cx, |s| {
 9880            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9881        })
 9882    });
 9883    cx.assert_editor_state(indoc! {"
 9884        fn main() {
 9885            sample(param1, ˇparam2);
 9886        }
 9887
 9888        fn sample(param1: u8, param2: u8) {}
 9889    "});
 9890    handle_signature_help_request(&mut cx, mocked_response).await;
 9891    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9892        .await;
 9893    cx.editor(|editor, _, _| {
 9894        assert!(editor.signature_help_state.is_shown());
 9895    });
 9896
 9897    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9898    cx.update_editor(|editor, window, cx| {
 9899        editor.change_selections(None, window, cx, |s| {
 9900            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9901            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9902        })
 9903    });
 9904    cx.assert_editor_state(indoc! {"
 9905        fn main() {
 9906            sample(param1, ˇparam2);
 9907        }
 9908
 9909        fn sample(param1: u8, param2: u8) {}
 9910    "});
 9911
 9912    let mocked_response = lsp::SignatureHelp {
 9913        signatures: vec![lsp::SignatureInformation {
 9914            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9915            documentation: None,
 9916            parameters: Some(vec![
 9917                lsp::ParameterInformation {
 9918                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9919                    documentation: None,
 9920                },
 9921                lsp::ParameterInformation {
 9922                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9923                    documentation: None,
 9924                },
 9925            ]),
 9926            active_parameter: None,
 9927        }],
 9928        active_signature: Some(0),
 9929        active_parameter: Some(1),
 9930    };
 9931    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9932    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9933        .await;
 9934    cx.update_editor(|editor, _, cx| {
 9935        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9936    });
 9937    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9938        .await;
 9939    cx.update_editor(|editor, window, cx| {
 9940        editor.change_selections(None, window, cx, |s| {
 9941            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9942        })
 9943    });
 9944    cx.assert_editor_state(indoc! {"
 9945        fn main() {
 9946            sample(param1, «ˇparam2»);
 9947        }
 9948
 9949        fn sample(param1: u8, param2: u8) {}
 9950    "});
 9951    cx.update_editor(|editor, window, cx| {
 9952        editor.change_selections(None, window, cx, |s| {
 9953            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9954        })
 9955    });
 9956    cx.assert_editor_state(indoc! {"
 9957        fn main() {
 9958            sample(param1, ˇparam2);
 9959        }
 9960
 9961        fn sample(param1: u8, param2: u8) {}
 9962    "});
 9963    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9964        .await;
 9965}
 9966
 9967#[gpui::test]
 9968async fn test_completion_mode(cx: &mut TestAppContext) {
 9969    init_test(cx, |_| {});
 9970    let mut cx = EditorLspTestContext::new_rust(
 9971        lsp::ServerCapabilities {
 9972            completion_provider: Some(lsp::CompletionOptions {
 9973                resolve_provider: Some(true),
 9974                ..Default::default()
 9975            }),
 9976            ..Default::default()
 9977        },
 9978        cx,
 9979    )
 9980    .await;
 9981
 9982    struct Run {
 9983        run_description: &'static str,
 9984        initial_state: String,
 9985        buffer_marked_text: String,
 9986        completion_text: &'static str,
 9987        expected_with_insert_mode: String,
 9988        expected_with_replace_mode: String,
 9989        expected_with_replace_subsequence_mode: String,
 9990        expected_with_replace_suffix_mode: String,
 9991    }
 9992
 9993    let runs = [
 9994        Run {
 9995            run_description: "Start of word matches completion text",
 9996            initial_state: "before ediˇ after".into(),
 9997            buffer_marked_text: "before <edi|> after".into(),
 9998            completion_text: "editor",
 9999            expected_with_insert_mode: "before editorˇ after".into(),
10000            expected_with_replace_mode: "before editorˇ after".into(),
10001            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10002            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10003        },
10004        Run {
10005            run_description: "Accept same text at the middle of the word",
10006            initial_state: "before ediˇtor after".into(),
10007            buffer_marked_text: "before <edi|tor> after".into(),
10008            completion_text: "editor",
10009            expected_with_insert_mode: "before editorˇtor after".into(),
10010            expected_with_replace_mode: "before editorˇ after".into(),
10011            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10012            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10013        },
10014        Run {
10015            run_description: "End of word matches completion text -- cursor at end",
10016            initial_state: "before torˇ after".into(),
10017            buffer_marked_text: "before <tor|> after".into(),
10018            completion_text: "editor",
10019            expected_with_insert_mode: "before editorˇ after".into(),
10020            expected_with_replace_mode: "before editorˇ after".into(),
10021            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10022            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10023        },
10024        Run {
10025            run_description: "End of word matches completion text -- cursor at start",
10026            initial_state: "before ˇtor after".into(),
10027            buffer_marked_text: "before <|tor> after".into(),
10028            completion_text: "editor",
10029            expected_with_insert_mode: "before editorˇtor after".into(),
10030            expected_with_replace_mode: "before editorˇ after".into(),
10031            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10032            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10033        },
10034        Run {
10035            run_description: "Prepend text containing whitespace",
10036            initial_state: "pˇfield: bool".into(),
10037            buffer_marked_text: "<p|field>: bool".into(),
10038            completion_text: "pub ",
10039            expected_with_insert_mode: "pub ˇfield: bool".into(),
10040            expected_with_replace_mode: "pub ˇ: bool".into(),
10041            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10042            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10043        },
10044        Run {
10045            run_description: "Add element to start of list",
10046            initial_state: "[element_ˇelement_2]".into(),
10047            buffer_marked_text: "[<element_|element_2>]".into(),
10048            completion_text: "element_1",
10049            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10050            expected_with_replace_mode: "[element_1ˇ]".into(),
10051            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10052            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10053        },
10054        Run {
10055            run_description: "Add element to start of list -- first and second elements are equal",
10056            initial_state: "[elˇelement]".into(),
10057            buffer_marked_text: "[<el|element>]".into(),
10058            completion_text: "element",
10059            expected_with_insert_mode: "[elementˇelement]".into(),
10060            expected_with_replace_mode: "[elementˇ]".into(),
10061            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10062            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10063        },
10064        Run {
10065            run_description: "Ends with matching suffix",
10066            initial_state: "SubˇError".into(),
10067            buffer_marked_text: "<Sub|Error>".into(),
10068            completion_text: "SubscriptionError",
10069            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10070            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10071            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10072            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10073        },
10074        Run {
10075            run_description: "Suffix is a subsequence -- contiguous",
10076            initial_state: "SubˇErr".into(),
10077            buffer_marked_text: "<Sub|Err>".into(),
10078            completion_text: "SubscriptionError",
10079            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10080            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10081            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10082            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10083        },
10084        Run {
10085            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10086            initial_state: "Suˇscrirr".into(),
10087            buffer_marked_text: "<Su|scrirr>".into(),
10088            completion_text: "SubscriptionError",
10089            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10090            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10091            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10092            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10093        },
10094        Run {
10095            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10096            initial_state: "foo(indˇix)".into(),
10097            buffer_marked_text: "foo(<ind|ix>)".into(),
10098            completion_text: "node_index",
10099            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10100            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10101            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10102            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10103        },
10104    ];
10105
10106    for run in runs {
10107        let run_variations = [
10108            (LspInsertMode::Insert, run.expected_with_insert_mode),
10109            (LspInsertMode::Replace, run.expected_with_replace_mode),
10110            (
10111                LspInsertMode::ReplaceSubsequence,
10112                run.expected_with_replace_subsequence_mode,
10113            ),
10114            (
10115                LspInsertMode::ReplaceSuffix,
10116                run.expected_with_replace_suffix_mode,
10117            ),
10118        ];
10119
10120        for (lsp_insert_mode, expected_text) in run_variations {
10121            eprintln!(
10122                "run = {:?}, mode = {lsp_insert_mode:.?}",
10123                run.run_description,
10124            );
10125
10126            update_test_language_settings(&mut cx, |settings| {
10127                settings.defaults.completions = Some(CompletionSettings {
10128                    lsp_insert_mode,
10129                    words: WordsCompletionMode::Disabled,
10130                    lsp: true,
10131                    lsp_fetch_timeout_ms: 0,
10132                });
10133            });
10134
10135            cx.set_state(&run.initial_state);
10136            cx.update_editor(|editor, window, cx| {
10137                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10138            });
10139
10140            let counter = Arc::new(AtomicUsize::new(0));
10141            handle_completion_request_with_insert_and_replace(
10142                &mut cx,
10143                &run.buffer_marked_text,
10144                vec![run.completion_text],
10145                counter.clone(),
10146            )
10147            .await;
10148            cx.condition(|editor, _| editor.context_menu_visible())
10149                .await;
10150            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10151
10152            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10153                editor
10154                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10155                    .unwrap()
10156            });
10157            cx.assert_editor_state(&expected_text);
10158            handle_resolve_completion_request(&mut cx, None).await;
10159            apply_additional_edits.await.unwrap();
10160        }
10161    }
10162}
10163
10164#[gpui::test]
10165async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10166    init_test(cx, |_| {});
10167    let mut cx = EditorLspTestContext::new_rust(
10168        lsp::ServerCapabilities {
10169            completion_provider: Some(lsp::CompletionOptions {
10170                resolve_provider: Some(true),
10171                ..Default::default()
10172            }),
10173            ..Default::default()
10174        },
10175        cx,
10176    )
10177    .await;
10178
10179    let initial_state = "SubˇError";
10180    let buffer_marked_text = "<Sub|Error>";
10181    let completion_text = "SubscriptionError";
10182    let expected_with_insert_mode = "SubscriptionErrorˇError";
10183    let expected_with_replace_mode = "SubscriptionErrorˇ";
10184
10185    update_test_language_settings(&mut cx, |settings| {
10186        settings.defaults.completions = Some(CompletionSettings {
10187            words: WordsCompletionMode::Disabled,
10188            // set the opposite here to ensure that the action is overriding the default behavior
10189            lsp_insert_mode: LspInsertMode::Insert,
10190            lsp: true,
10191            lsp_fetch_timeout_ms: 0,
10192        });
10193    });
10194
10195    cx.set_state(initial_state);
10196    cx.update_editor(|editor, window, cx| {
10197        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10198    });
10199
10200    let counter = Arc::new(AtomicUsize::new(0));
10201    handle_completion_request_with_insert_and_replace(
10202        &mut cx,
10203        &buffer_marked_text,
10204        vec![completion_text],
10205        counter.clone(),
10206    )
10207    .await;
10208    cx.condition(|editor, _| editor.context_menu_visible())
10209        .await;
10210    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10211
10212    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10213        editor
10214            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10215            .unwrap()
10216    });
10217    cx.assert_editor_state(&expected_with_replace_mode);
10218    handle_resolve_completion_request(&mut cx, None).await;
10219    apply_additional_edits.await.unwrap();
10220
10221    update_test_language_settings(&mut cx, |settings| {
10222        settings.defaults.completions = Some(CompletionSettings {
10223            words: WordsCompletionMode::Disabled,
10224            // set the opposite here to ensure that the action is overriding the default behavior
10225            lsp_insert_mode: LspInsertMode::Replace,
10226            lsp: true,
10227            lsp_fetch_timeout_ms: 0,
10228        });
10229    });
10230
10231    cx.set_state(initial_state);
10232    cx.update_editor(|editor, window, cx| {
10233        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10234    });
10235    handle_completion_request_with_insert_and_replace(
10236        &mut cx,
10237        &buffer_marked_text,
10238        vec![completion_text],
10239        counter.clone(),
10240    )
10241    .await;
10242    cx.condition(|editor, _| editor.context_menu_visible())
10243        .await;
10244    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10245
10246    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10247        editor
10248            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10249            .unwrap()
10250    });
10251    cx.assert_editor_state(&expected_with_insert_mode);
10252    handle_resolve_completion_request(&mut cx, None).await;
10253    apply_additional_edits.await.unwrap();
10254}
10255
10256#[gpui::test]
10257async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10258    init_test(cx, |_| {});
10259    let mut cx = EditorLspTestContext::new_rust(
10260        lsp::ServerCapabilities {
10261            completion_provider: Some(lsp::CompletionOptions {
10262                resolve_provider: Some(true),
10263                ..Default::default()
10264            }),
10265            ..Default::default()
10266        },
10267        cx,
10268    )
10269    .await;
10270
10271    // scenario: surrounding text matches completion text
10272    let completion_text = "to_offset";
10273    let initial_state = indoc! {"
10274        1. buf.to_offˇsuffix
10275        2. buf.to_offˇsuf
10276        3. buf.to_offˇfix
10277        4. buf.to_offˇ
10278        5. into_offˇensive
10279        6. ˇsuffix
10280        7. let ˇ //
10281        8. aaˇzz
10282        9. buf.to_off«zzzzzˇ»suffix
10283        10. buf.«ˇzzzzz»suffix
10284        11. to_off«ˇzzzzz»
10285
10286        buf.to_offˇsuffix  // newest cursor
10287    "};
10288    let completion_marked_buffer = indoc! {"
10289        1. buf.to_offsuffix
10290        2. buf.to_offsuf
10291        3. buf.to_offfix
10292        4. buf.to_off
10293        5. into_offensive
10294        6. suffix
10295        7. let  //
10296        8. aazz
10297        9. buf.to_offzzzzzsuffix
10298        10. buf.zzzzzsuffix
10299        11. to_offzzzzz
10300
10301        buf.<to_off|suffix>  // newest cursor
10302    "};
10303    let expected = indoc! {"
10304        1. buf.to_offsetˇ
10305        2. buf.to_offsetˇsuf
10306        3. buf.to_offsetˇfix
10307        4. buf.to_offsetˇ
10308        5. into_offsetˇensive
10309        6. to_offsetˇsuffix
10310        7. let to_offsetˇ //
10311        8. aato_offsetˇzz
10312        9. buf.to_offsetˇ
10313        10. buf.to_offsetˇsuffix
10314        11. to_offsetˇ
10315
10316        buf.to_offsetˇ  // newest cursor
10317    "};
10318    cx.set_state(initial_state);
10319    cx.update_editor(|editor, window, cx| {
10320        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10321    });
10322    handle_completion_request_with_insert_and_replace(
10323        &mut cx,
10324        completion_marked_buffer,
10325        vec![completion_text],
10326        Arc::new(AtomicUsize::new(0)),
10327    )
10328    .await;
10329    cx.condition(|editor, _| editor.context_menu_visible())
10330        .await;
10331    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10332        editor
10333            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10334            .unwrap()
10335    });
10336    cx.assert_editor_state(expected);
10337    handle_resolve_completion_request(&mut cx, None).await;
10338    apply_additional_edits.await.unwrap();
10339
10340    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10341    let completion_text = "foo_and_bar";
10342    let initial_state = indoc! {"
10343        1. ooanbˇ
10344        2. zooanbˇ
10345        3. ooanbˇz
10346        4. zooanbˇz
10347        5. ooanˇ
10348        6. oanbˇ
10349
10350        ooanbˇ
10351    "};
10352    let completion_marked_buffer = indoc! {"
10353        1. ooanb
10354        2. zooanb
10355        3. ooanbz
10356        4. zooanbz
10357        5. ooan
10358        6. oanb
10359
10360        <ooanb|>
10361    "};
10362    let expected = indoc! {"
10363        1. foo_and_barˇ
10364        2. zfoo_and_barˇ
10365        3. foo_and_barˇz
10366        4. zfoo_and_barˇz
10367        5. ooanfoo_and_barˇ
10368        6. oanbfoo_and_barˇ
10369
10370        foo_and_barˇ
10371    "};
10372    cx.set_state(initial_state);
10373    cx.update_editor(|editor, window, cx| {
10374        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10375    });
10376    handle_completion_request_with_insert_and_replace(
10377        &mut cx,
10378        completion_marked_buffer,
10379        vec![completion_text],
10380        Arc::new(AtomicUsize::new(0)),
10381    )
10382    .await;
10383    cx.condition(|editor, _| editor.context_menu_visible())
10384        .await;
10385    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10386        editor
10387            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10388            .unwrap()
10389    });
10390    cx.assert_editor_state(expected);
10391    handle_resolve_completion_request(&mut cx, None).await;
10392    apply_additional_edits.await.unwrap();
10393
10394    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10395    // (expects the same as if it was inserted at the end)
10396    let completion_text = "foo_and_bar";
10397    let initial_state = indoc! {"
10398        1. ooˇanb
10399        2. zooˇanb
10400        3. ooˇanbz
10401        4. zooˇanbz
10402
10403        ooˇanb
10404    "};
10405    let completion_marked_buffer = indoc! {"
10406        1. ooanb
10407        2. zooanb
10408        3. ooanbz
10409        4. zooanbz
10410
10411        <oo|anb>
10412    "};
10413    let expected = indoc! {"
10414        1. foo_and_barˇ
10415        2. zfoo_and_barˇ
10416        3. foo_and_barˇz
10417        4. zfoo_and_barˇz
10418
10419        foo_and_barˇ
10420    "};
10421    cx.set_state(initial_state);
10422    cx.update_editor(|editor, window, cx| {
10423        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10424    });
10425    handle_completion_request_with_insert_and_replace(
10426        &mut cx,
10427        completion_marked_buffer,
10428        vec![completion_text],
10429        Arc::new(AtomicUsize::new(0)),
10430    )
10431    .await;
10432    cx.condition(|editor, _| editor.context_menu_visible())
10433        .await;
10434    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10435        editor
10436            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10437            .unwrap()
10438    });
10439    cx.assert_editor_state(expected);
10440    handle_resolve_completion_request(&mut cx, None).await;
10441    apply_additional_edits.await.unwrap();
10442}
10443
10444// This used to crash
10445#[gpui::test]
10446async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10447    init_test(cx, |_| {});
10448
10449    let buffer_text = indoc! {"
10450        fn main() {
10451            10.satu;
10452
10453            //
10454            // separate cursors so they open in different excerpts (manually reproducible)
10455            //
10456
10457            10.satu20;
10458        }
10459    "};
10460    let multibuffer_text_with_selections = indoc! {"
10461        fn main() {
10462            10.satuˇ;
10463
10464            //
10465
10466            //
10467
10468            10.satuˇ20;
10469        }
10470    "};
10471    let expected_multibuffer = indoc! {"
10472        fn main() {
10473            10.saturating_sub()ˇ;
10474
10475            //
10476
10477            //
10478
10479            10.saturating_sub()ˇ;
10480        }
10481    "};
10482
10483    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10484    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10485
10486    let fs = FakeFs::new(cx.executor());
10487    fs.insert_tree(
10488        path!("/a"),
10489        json!({
10490            "main.rs": buffer_text,
10491        }),
10492    )
10493    .await;
10494
10495    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10496    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10497    language_registry.add(rust_lang());
10498    let mut fake_servers = language_registry.register_fake_lsp(
10499        "Rust",
10500        FakeLspAdapter {
10501            capabilities: lsp::ServerCapabilities {
10502                completion_provider: Some(lsp::CompletionOptions {
10503                    resolve_provider: None,
10504                    ..lsp::CompletionOptions::default()
10505                }),
10506                ..lsp::ServerCapabilities::default()
10507            },
10508            ..FakeLspAdapter::default()
10509        },
10510    );
10511    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10512    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10513    let buffer = project
10514        .update(cx, |project, cx| {
10515            project.open_local_buffer(path!("/a/main.rs"), cx)
10516        })
10517        .await
10518        .unwrap();
10519
10520    let multi_buffer = cx.new(|cx| {
10521        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10522        multi_buffer.push_excerpts(
10523            buffer.clone(),
10524            [ExcerptRange::new(0..first_excerpt_end)],
10525            cx,
10526        );
10527        multi_buffer.push_excerpts(
10528            buffer.clone(),
10529            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10530            cx,
10531        );
10532        multi_buffer
10533    });
10534
10535    let editor = workspace
10536        .update(cx, |_, window, cx| {
10537            cx.new(|cx| {
10538                Editor::new(
10539                    EditorMode::Full {
10540                        scale_ui_elements_with_buffer_font_size: false,
10541                        show_active_line_background: false,
10542                        sized_by_content: false,
10543                    },
10544                    multi_buffer.clone(),
10545                    Some(project.clone()),
10546                    window,
10547                    cx,
10548                )
10549            })
10550        })
10551        .unwrap();
10552
10553    let pane = workspace
10554        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10555        .unwrap();
10556    pane.update_in(cx, |pane, window, cx| {
10557        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10558    });
10559
10560    let fake_server = fake_servers.next().await.unwrap();
10561
10562    editor.update_in(cx, |editor, window, cx| {
10563        editor.change_selections(None, window, cx, |s| {
10564            s.select_ranges([
10565                Point::new(1, 11)..Point::new(1, 11),
10566                Point::new(7, 11)..Point::new(7, 11),
10567            ])
10568        });
10569
10570        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10571    });
10572
10573    editor.update_in(cx, |editor, window, cx| {
10574        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10575    });
10576
10577    fake_server
10578        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10579            let completion_item = lsp::CompletionItem {
10580                label: "saturating_sub()".into(),
10581                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10582                    lsp::InsertReplaceEdit {
10583                        new_text: "saturating_sub()".to_owned(),
10584                        insert: lsp::Range::new(
10585                            lsp::Position::new(7, 7),
10586                            lsp::Position::new(7, 11),
10587                        ),
10588                        replace: lsp::Range::new(
10589                            lsp::Position::new(7, 7),
10590                            lsp::Position::new(7, 13),
10591                        ),
10592                    },
10593                )),
10594                ..lsp::CompletionItem::default()
10595            };
10596
10597            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10598        })
10599        .next()
10600        .await
10601        .unwrap();
10602
10603    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10604        .await;
10605
10606    editor
10607        .update_in(cx, |editor, window, cx| {
10608            editor
10609                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10610                .unwrap()
10611        })
10612        .await
10613        .unwrap();
10614
10615    editor.update(cx, |editor, cx| {
10616        assert_text_with_selections(editor, expected_multibuffer, cx);
10617    })
10618}
10619
10620#[gpui::test]
10621async fn test_completion(cx: &mut TestAppContext) {
10622    init_test(cx, |_| {});
10623
10624    let mut cx = EditorLspTestContext::new_rust(
10625        lsp::ServerCapabilities {
10626            completion_provider: Some(lsp::CompletionOptions {
10627                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10628                resolve_provider: Some(true),
10629                ..Default::default()
10630            }),
10631            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10632            ..Default::default()
10633        },
10634        cx,
10635    )
10636    .await;
10637    let counter = Arc::new(AtomicUsize::new(0));
10638
10639    cx.set_state(indoc! {"
10640        oneˇ
10641        two
10642        three
10643    "});
10644    cx.simulate_keystroke(".");
10645    handle_completion_request(
10646        &mut cx,
10647        indoc! {"
10648            one.|<>
10649            two
10650            three
10651        "},
10652        vec!["first_completion", "second_completion"],
10653        counter.clone(),
10654    )
10655    .await;
10656    cx.condition(|editor, _| editor.context_menu_visible())
10657        .await;
10658    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10659
10660    let _handler = handle_signature_help_request(
10661        &mut cx,
10662        lsp::SignatureHelp {
10663            signatures: vec![lsp::SignatureInformation {
10664                label: "test signature".to_string(),
10665                documentation: None,
10666                parameters: Some(vec![lsp::ParameterInformation {
10667                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10668                    documentation: None,
10669                }]),
10670                active_parameter: None,
10671            }],
10672            active_signature: None,
10673            active_parameter: None,
10674        },
10675    );
10676    cx.update_editor(|editor, window, cx| {
10677        assert!(
10678            !editor.signature_help_state.is_shown(),
10679            "No signature help was called for"
10680        );
10681        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10682    });
10683    cx.run_until_parked();
10684    cx.update_editor(|editor, _, _| {
10685        assert!(
10686            !editor.signature_help_state.is_shown(),
10687            "No signature help should be shown when completions menu is open"
10688        );
10689    });
10690
10691    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10692        editor.context_menu_next(&Default::default(), window, cx);
10693        editor
10694            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10695            .unwrap()
10696    });
10697    cx.assert_editor_state(indoc! {"
10698        one.second_completionˇ
10699        two
10700        three
10701    "});
10702
10703    handle_resolve_completion_request(
10704        &mut cx,
10705        Some(vec![
10706            (
10707                //This overlaps with the primary completion edit which is
10708                //misbehavior from the LSP spec, test that we filter it out
10709                indoc! {"
10710                    one.second_ˇcompletion
10711                    two
10712                    threeˇ
10713                "},
10714                "overlapping additional edit",
10715            ),
10716            (
10717                indoc! {"
10718                    one.second_completion
10719                    two
10720                    threeˇ
10721                "},
10722                "\nadditional edit",
10723            ),
10724        ]),
10725    )
10726    .await;
10727    apply_additional_edits.await.unwrap();
10728    cx.assert_editor_state(indoc! {"
10729        one.second_completionˇ
10730        two
10731        three
10732        additional edit
10733    "});
10734
10735    cx.set_state(indoc! {"
10736        one.second_completion
10737        twoˇ
10738        threeˇ
10739        additional edit
10740    "});
10741    cx.simulate_keystroke(" ");
10742    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10743    cx.simulate_keystroke("s");
10744    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10745
10746    cx.assert_editor_state(indoc! {"
10747        one.second_completion
10748        two sˇ
10749        three sˇ
10750        additional edit
10751    "});
10752    handle_completion_request(
10753        &mut cx,
10754        indoc! {"
10755            one.second_completion
10756            two s
10757            three <s|>
10758            additional edit
10759        "},
10760        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10761        counter.clone(),
10762    )
10763    .await;
10764    cx.condition(|editor, _| editor.context_menu_visible())
10765        .await;
10766    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10767
10768    cx.simulate_keystroke("i");
10769
10770    handle_completion_request(
10771        &mut cx,
10772        indoc! {"
10773            one.second_completion
10774            two si
10775            three <si|>
10776            additional edit
10777        "},
10778        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10779        counter.clone(),
10780    )
10781    .await;
10782    cx.condition(|editor, _| editor.context_menu_visible())
10783        .await;
10784    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10785
10786    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10787        editor
10788            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10789            .unwrap()
10790    });
10791    cx.assert_editor_state(indoc! {"
10792        one.second_completion
10793        two sixth_completionˇ
10794        three sixth_completionˇ
10795        additional edit
10796    "});
10797
10798    apply_additional_edits.await.unwrap();
10799
10800    update_test_language_settings(&mut cx, |settings| {
10801        settings.defaults.show_completions_on_input = Some(false);
10802    });
10803    cx.set_state("editorˇ");
10804    cx.simulate_keystroke(".");
10805    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10806    cx.simulate_keystrokes("c l o");
10807    cx.assert_editor_state("editor.cloˇ");
10808    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10809    cx.update_editor(|editor, window, cx| {
10810        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10811    });
10812    handle_completion_request(
10813        &mut cx,
10814        "editor.<clo|>",
10815        vec!["close", "clobber"],
10816        counter.clone(),
10817    )
10818    .await;
10819    cx.condition(|editor, _| editor.context_menu_visible())
10820        .await;
10821    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10822
10823    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10824        editor
10825            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10826            .unwrap()
10827    });
10828    cx.assert_editor_state("editor.closeˇ");
10829    handle_resolve_completion_request(&mut cx, None).await;
10830    apply_additional_edits.await.unwrap();
10831}
10832
10833#[gpui::test]
10834async fn test_word_completion(cx: &mut TestAppContext) {
10835    let lsp_fetch_timeout_ms = 10;
10836    init_test(cx, |language_settings| {
10837        language_settings.defaults.completions = Some(CompletionSettings {
10838            words: WordsCompletionMode::Fallback,
10839            lsp: true,
10840            lsp_fetch_timeout_ms: 10,
10841            lsp_insert_mode: LspInsertMode::Insert,
10842        });
10843    });
10844
10845    let mut cx = EditorLspTestContext::new_rust(
10846        lsp::ServerCapabilities {
10847            completion_provider: Some(lsp::CompletionOptions {
10848                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10849                ..lsp::CompletionOptions::default()
10850            }),
10851            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10852            ..lsp::ServerCapabilities::default()
10853        },
10854        cx,
10855    )
10856    .await;
10857
10858    let throttle_completions = Arc::new(AtomicBool::new(false));
10859
10860    let lsp_throttle_completions = throttle_completions.clone();
10861    let _completion_requests_handler =
10862        cx.lsp
10863            .server
10864            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10865                let lsp_throttle_completions = lsp_throttle_completions.clone();
10866                let cx = cx.clone();
10867                async move {
10868                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10869                        cx.background_executor()
10870                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10871                            .await;
10872                    }
10873                    Ok(Some(lsp::CompletionResponse::Array(vec![
10874                        lsp::CompletionItem {
10875                            label: "first".into(),
10876                            ..lsp::CompletionItem::default()
10877                        },
10878                        lsp::CompletionItem {
10879                            label: "last".into(),
10880                            ..lsp::CompletionItem::default()
10881                        },
10882                    ])))
10883                }
10884            });
10885
10886    cx.set_state(indoc! {"
10887        oneˇ
10888        two
10889        three
10890    "});
10891    cx.simulate_keystroke(".");
10892    cx.executor().run_until_parked();
10893    cx.condition(|editor, _| editor.context_menu_visible())
10894        .await;
10895    cx.update_editor(|editor, window, cx| {
10896        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10897        {
10898            assert_eq!(
10899                completion_menu_entries(&menu),
10900                &["first", "last"],
10901                "When LSP server is fast to reply, no fallback word completions are used"
10902            );
10903        } else {
10904            panic!("expected completion menu to be open");
10905        }
10906        editor.cancel(&Cancel, window, cx);
10907    });
10908    cx.executor().run_until_parked();
10909    cx.condition(|editor, _| !editor.context_menu_visible())
10910        .await;
10911
10912    throttle_completions.store(true, atomic::Ordering::Release);
10913    cx.simulate_keystroke(".");
10914    cx.executor()
10915        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10916    cx.executor().run_until_parked();
10917    cx.condition(|editor, _| editor.context_menu_visible())
10918        .await;
10919    cx.update_editor(|editor, _, _| {
10920        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10921        {
10922            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10923                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10924        } else {
10925            panic!("expected completion menu to be open");
10926        }
10927    });
10928}
10929
10930#[gpui::test]
10931async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10932    init_test(cx, |language_settings| {
10933        language_settings.defaults.completions = Some(CompletionSettings {
10934            words: WordsCompletionMode::Enabled,
10935            lsp: true,
10936            lsp_fetch_timeout_ms: 0,
10937            lsp_insert_mode: LspInsertMode::Insert,
10938        });
10939    });
10940
10941    let mut cx = EditorLspTestContext::new_rust(
10942        lsp::ServerCapabilities {
10943            completion_provider: Some(lsp::CompletionOptions {
10944                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10945                ..lsp::CompletionOptions::default()
10946            }),
10947            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10948            ..lsp::ServerCapabilities::default()
10949        },
10950        cx,
10951    )
10952    .await;
10953
10954    let _completion_requests_handler =
10955        cx.lsp
10956            .server
10957            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10958                Ok(Some(lsp::CompletionResponse::Array(vec![
10959                    lsp::CompletionItem {
10960                        label: "first".into(),
10961                        ..lsp::CompletionItem::default()
10962                    },
10963                    lsp::CompletionItem {
10964                        label: "last".into(),
10965                        ..lsp::CompletionItem::default()
10966                    },
10967                ])))
10968            });
10969
10970    cx.set_state(indoc! {"ˇ
10971        first
10972        last
10973        second
10974    "});
10975    cx.simulate_keystroke(".");
10976    cx.executor().run_until_parked();
10977    cx.condition(|editor, _| editor.context_menu_visible())
10978        .await;
10979    cx.update_editor(|editor, _, _| {
10980        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10981        {
10982            assert_eq!(
10983                completion_menu_entries(&menu),
10984                &["first", "last", "second"],
10985                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10986            );
10987        } else {
10988            panic!("expected completion menu to be open");
10989        }
10990    });
10991}
10992
10993#[gpui::test]
10994async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10995    init_test(cx, |language_settings| {
10996        language_settings.defaults.completions = Some(CompletionSettings {
10997            words: WordsCompletionMode::Disabled,
10998            lsp: true,
10999            lsp_fetch_timeout_ms: 0,
11000            lsp_insert_mode: LspInsertMode::Insert,
11001        });
11002    });
11003
11004    let mut cx = EditorLspTestContext::new_rust(
11005        lsp::ServerCapabilities {
11006            completion_provider: Some(lsp::CompletionOptions {
11007                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11008                ..lsp::CompletionOptions::default()
11009            }),
11010            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11011            ..lsp::ServerCapabilities::default()
11012        },
11013        cx,
11014    )
11015    .await;
11016
11017    let _completion_requests_handler =
11018        cx.lsp
11019            .server
11020            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11021                panic!("LSP completions should not be queried when dealing with word completions")
11022            });
11023
11024    cx.set_state(indoc! {"ˇ
11025        first
11026        last
11027        second
11028    "});
11029    cx.update_editor(|editor, window, cx| {
11030        editor.show_word_completions(&ShowWordCompletions, window, cx);
11031    });
11032    cx.executor().run_until_parked();
11033    cx.condition(|editor, _| editor.context_menu_visible())
11034        .await;
11035    cx.update_editor(|editor, _, _| {
11036        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11037        {
11038            assert_eq!(
11039                completion_menu_entries(&menu),
11040                &["first", "last", "second"],
11041                "`ShowWordCompletions` action should show word completions"
11042            );
11043        } else {
11044            panic!("expected completion menu to be open");
11045        }
11046    });
11047
11048    cx.simulate_keystroke("l");
11049    cx.executor().run_until_parked();
11050    cx.condition(|editor, _| editor.context_menu_visible())
11051        .await;
11052    cx.update_editor(|editor, _, _| {
11053        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11054        {
11055            assert_eq!(
11056                completion_menu_entries(&menu),
11057                &["last"],
11058                "After showing word completions, further editing should filter them and not query the LSP"
11059            );
11060        } else {
11061            panic!("expected completion menu to be open");
11062        }
11063    });
11064}
11065
11066#[gpui::test]
11067async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11068    init_test(cx, |language_settings| {
11069        language_settings.defaults.completions = Some(CompletionSettings {
11070            words: WordsCompletionMode::Fallback,
11071            lsp: false,
11072            lsp_fetch_timeout_ms: 0,
11073            lsp_insert_mode: LspInsertMode::Insert,
11074        });
11075    });
11076
11077    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11078
11079    cx.set_state(indoc! {"ˇ
11080        0_usize
11081        let
11082        33
11083        4.5f32
11084    "});
11085    cx.update_editor(|editor, window, cx| {
11086        editor.show_completions(&ShowCompletions::default(), window, cx);
11087    });
11088    cx.executor().run_until_parked();
11089    cx.condition(|editor, _| editor.context_menu_visible())
11090        .await;
11091    cx.update_editor(|editor, window, cx| {
11092        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11093        {
11094            assert_eq!(
11095                completion_menu_entries(&menu),
11096                &["let"],
11097                "With no digits in the completion query, no digits should be in the word completions"
11098            );
11099        } else {
11100            panic!("expected completion menu to be open");
11101        }
11102        editor.cancel(&Cancel, window, cx);
11103    });
11104
11105    cx.set_state(indoc! {"11106        0_usize
11107        let
11108        3
11109        33.35f32
11110    "});
11111    cx.update_editor(|editor, window, cx| {
11112        editor.show_completions(&ShowCompletions::default(), window, cx);
11113    });
11114    cx.executor().run_until_parked();
11115    cx.condition(|editor, _| editor.context_menu_visible())
11116        .await;
11117    cx.update_editor(|editor, _, _| {
11118        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11119        {
11120            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11121                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11122        } else {
11123            panic!("expected completion menu to be open");
11124        }
11125    });
11126}
11127
11128fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11129    let position = || lsp::Position {
11130        line: params.text_document_position.position.line,
11131        character: params.text_document_position.position.character,
11132    };
11133    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11134        range: lsp::Range {
11135            start: position(),
11136            end: position(),
11137        },
11138        new_text: text.to_string(),
11139    }))
11140}
11141
11142#[gpui::test]
11143async fn test_multiline_completion(cx: &mut TestAppContext) {
11144    init_test(cx, |_| {});
11145
11146    let fs = FakeFs::new(cx.executor());
11147    fs.insert_tree(
11148        path!("/a"),
11149        json!({
11150            "main.ts": "a",
11151        }),
11152    )
11153    .await;
11154
11155    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11156    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11157    let typescript_language = Arc::new(Language::new(
11158        LanguageConfig {
11159            name: "TypeScript".into(),
11160            matcher: LanguageMatcher {
11161                path_suffixes: vec!["ts".to_string()],
11162                ..LanguageMatcher::default()
11163            },
11164            line_comments: vec!["// ".into()],
11165            ..LanguageConfig::default()
11166        },
11167        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11168    ));
11169    language_registry.add(typescript_language.clone());
11170    let mut fake_servers = language_registry.register_fake_lsp(
11171        "TypeScript",
11172        FakeLspAdapter {
11173            capabilities: lsp::ServerCapabilities {
11174                completion_provider: Some(lsp::CompletionOptions {
11175                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11176                    ..lsp::CompletionOptions::default()
11177                }),
11178                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11179                ..lsp::ServerCapabilities::default()
11180            },
11181            // Emulate vtsls label generation
11182            label_for_completion: Some(Box::new(|item, _| {
11183                let text = if let Some(description) = item
11184                    .label_details
11185                    .as_ref()
11186                    .and_then(|label_details| label_details.description.as_ref())
11187                {
11188                    format!("{} {}", item.label, description)
11189                } else if let Some(detail) = &item.detail {
11190                    format!("{} {}", item.label, detail)
11191                } else {
11192                    item.label.clone()
11193                };
11194                let len = text.len();
11195                Some(language::CodeLabel {
11196                    text,
11197                    runs: Vec::new(),
11198                    filter_range: 0..len,
11199                })
11200            })),
11201            ..FakeLspAdapter::default()
11202        },
11203    );
11204    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11205    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11206    let worktree_id = workspace
11207        .update(cx, |workspace, _window, cx| {
11208            workspace.project().update(cx, |project, cx| {
11209                project.worktrees(cx).next().unwrap().read(cx).id()
11210            })
11211        })
11212        .unwrap();
11213    let _buffer = project
11214        .update(cx, |project, cx| {
11215            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11216        })
11217        .await
11218        .unwrap();
11219    let editor = workspace
11220        .update(cx, |workspace, window, cx| {
11221            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11222        })
11223        .unwrap()
11224        .await
11225        .unwrap()
11226        .downcast::<Editor>()
11227        .unwrap();
11228    let fake_server = fake_servers.next().await.unwrap();
11229
11230    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11231    let multiline_label_2 = "a\nb\nc\n";
11232    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11233    let multiline_description = "d\ne\nf\n";
11234    let multiline_detail_2 = "g\nh\ni\n";
11235
11236    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11237        move |params, _| async move {
11238            Ok(Some(lsp::CompletionResponse::Array(vec![
11239                lsp::CompletionItem {
11240                    label: multiline_label.to_string(),
11241                    text_edit: gen_text_edit(&params, "new_text_1"),
11242                    ..lsp::CompletionItem::default()
11243                },
11244                lsp::CompletionItem {
11245                    label: "single line label 1".to_string(),
11246                    detail: Some(multiline_detail.to_string()),
11247                    text_edit: gen_text_edit(&params, "new_text_2"),
11248                    ..lsp::CompletionItem::default()
11249                },
11250                lsp::CompletionItem {
11251                    label: "single line label 2".to_string(),
11252                    label_details: Some(lsp::CompletionItemLabelDetails {
11253                        description: Some(multiline_description.to_string()),
11254                        detail: None,
11255                    }),
11256                    text_edit: gen_text_edit(&params, "new_text_2"),
11257                    ..lsp::CompletionItem::default()
11258                },
11259                lsp::CompletionItem {
11260                    label: multiline_label_2.to_string(),
11261                    detail: Some(multiline_detail_2.to_string()),
11262                    text_edit: gen_text_edit(&params, "new_text_3"),
11263                    ..lsp::CompletionItem::default()
11264                },
11265                lsp::CompletionItem {
11266                    label: "Label with many     spaces and \t but without newlines".to_string(),
11267                    detail: Some(
11268                        "Details with many     spaces and \t but without newlines".to_string(),
11269                    ),
11270                    text_edit: gen_text_edit(&params, "new_text_4"),
11271                    ..lsp::CompletionItem::default()
11272                },
11273            ])))
11274        },
11275    );
11276
11277    editor.update_in(cx, |editor, window, cx| {
11278        cx.focus_self(window);
11279        editor.move_to_end(&MoveToEnd, window, cx);
11280        editor.handle_input(".", window, cx);
11281    });
11282    cx.run_until_parked();
11283    completion_handle.next().await.unwrap();
11284
11285    editor.update(cx, |editor, _| {
11286        assert!(editor.context_menu_visible());
11287        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11288        {
11289            let completion_labels = menu
11290                .completions
11291                .borrow()
11292                .iter()
11293                .map(|c| c.label.text.clone())
11294                .collect::<Vec<_>>();
11295            assert_eq!(
11296                completion_labels,
11297                &[
11298                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11299                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11300                    "single line label 2 d e f ",
11301                    "a b c g h i ",
11302                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11303                ],
11304                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11305            );
11306
11307            for completion in menu
11308                .completions
11309                .borrow()
11310                .iter() {
11311                    assert_eq!(
11312                        completion.label.filter_range,
11313                        0..completion.label.text.len(),
11314                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11315                    );
11316                }
11317        } else {
11318            panic!("expected completion menu to be open");
11319        }
11320    });
11321}
11322
11323#[gpui::test]
11324async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11325    init_test(cx, |_| {});
11326    let mut cx = EditorLspTestContext::new_rust(
11327        lsp::ServerCapabilities {
11328            completion_provider: Some(lsp::CompletionOptions {
11329                trigger_characters: Some(vec![".".to_string()]),
11330                ..Default::default()
11331            }),
11332            ..Default::default()
11333        },
11334        cx,
11335    )
11336    .await;
11337    cx.lsp
11338        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11339            Ok(Some(lsp::CompletionResponse::Array(vec![
11340                lsp::CompletionItem {
11341                    label: "first".into(),
11342                    ..Default::default()
11343                },
11344                lsp::CompletionItem {
11345                    label: "last".into(),
11346                    ..Default::default()
11347                },
11348            ])))
11349        });
11350    cx.set_state("variableˇ");
11351    cx.simulate_keystroke(".");
11352    cx.executor().run_until_parked();
11353
11354    cx.update_editor(|editor, _, _| {
11355        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11356        {
11357            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11358        } else {
11359            panic!("expected completion menu to be open");
11360        }
11361    });
11362
11363    cx.update_editor(|editor, window, cx| {
11364        editor.move_page_down(&MovePageDown::default(), window, cx);
11365        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11366        {
11367            assert!(
11368                menu.selected_item == 1,
11369                "expected PageDown to select the last item from the context menu"
11370            );
11371        } else {
11372            panic!("expected completion menu to stay open after PageDown");
11373        }
11374    });
11375
11376    cx.update_editor(|editor, window, cx| {
11377        editor.move_page_up(&MovePageUp::default(), window, cx);
11378        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11379        {
11380            assert!(
11381                menu.selected_item == 0,
11382                "expected PageUp to select the first item from the context menu"
11383            );
11384        } else {
11385            panic!("expected completion menu to stay open after PageUp");
11386        }
11387    });
11388}
11389
11390#[gpui::test]
11391async fn test_as_is_completions(cx: &mut TestAppContext) {
11392    init_test(cx, |_| {});
11393    let mut cx = EditorLspTestContext::new_rust(
11394        lsp::ServerCapabilities {
11395            completion_provider: Some(lsp::CompletionOptions {
11396                ..Default::default()
11397            }),
11398            ..Default::default()
11399        },
11400        cx,
11401    )
11402    .await;
11403    cx.lsp
11404        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11405            Ok(Some(lsp::CompletionResponse::Array(vec![
11406                lsp::CompletionItem {
11407                    label: "unsafe".into(),
11408                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11409                        range: lsp::Range {
11410                            start: lsp::Position {
11411                                line: 1,
11412                                character: 2,
11413                            },
11414                            end: lsp::Position {
11415                                line: 1,
11416                                character: 3,
11417                            },
11418                        },
11419                        new_text: "unsafe".to_string(),
11420                    })),
11421                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11422                    ..Default::default()
11423                },
11424            ])))
11425        });
11426    cx.set_state("fn a() {}\n");
11427    cx.executor().run_until_parked();
11428    cx.update_editor(|editor, window, cx| {
11429        editor.show_completions(
11430            &ShowCompletions {
11431                trigger: Some("\n".into()),
11432            },
11433            window,
11434            cx,
11435        );
11436    });
11437    cx.executor().run_until_parked();
11438
11439    cx.update_editor(|editor, window, cx| {
11440        editor.confirm_completion(&Default::default(), window, cx)
11441    });
11442    cx.executor().run_until_parked();
11443    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11444}
11445
11446#[gpui::test]
11447async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11448    init_test(cx, |_| {});
11449
11450    let mut cx = EditorLspTestContext::new_rust(
11451        lsp::ServerCapabilities {
11452            completion_provider: Some(lsp::CompletionOptions {
11453                trigger_characters: Some(vec![".".to_string()]),
11454                resolve_provider: Some(true),
11455                ..Default::default()
11456            }),
11457            ..Default::default()
11458        },
11459        cx,
11460    )
11461    .await;
11462
11463    cx.set_state("fn main() { let a = 2ˇ; }");
11464    cx.simulate_keystroke(".");
11465    let completion_item = lsp::CompletionItem {
11466        label: "Some".into(),
11467        kind: Some(lsp::CompletionItemKind::SNIPPET),
11468        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11469        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11470            kind: lsp::MarkupKind::Markdown,
11471            value: "```rust\nSome(2)\n```".to_string(),
11472        })),
11473        deprecated: Some(false),
11474        sort_text: Some("Some".to_string()),
11475        filter_text: Some("Some".to_string()),
11476        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11477        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11478            range: lsp::Range {
11479                start: lsp::Position {
11480                    line: 0,
11481                    character: 22,
11482                },
11483                end: lsp::Position {
11484                    line: 0,
11485                    character: 22,
11486                },
11487            },
11488            new_text: "Some(2)".to_string(),
11489        })),
11490        additional_text_edits: Some(vec![lsp::TextEdit {
11491            range: lsp::Range {
11492                start: lsp::Position {
11493                    line: 0,
11494                    character: 20,
11495                },
11496                end: lsp::Position {
11497                    line: 0,
11498                    character: 22,
11499                },
11500            },
11501            new_text: "".to_string(),
11502        }]),
11503        ..Default::default()
11504    };
11505
11506    let closure_completion_item = completion_item.clone();
11507    let counter = Arc::new(AtomicUsize::new(0));
11508    let counter_clone = counter.clone();
11509    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11510        let task_completion_item = closure_completion_item.clone();
11511        counter_clone.fetch_add(1, atomic::Ordering::Release);
11512        async move {
11513            Ok(Some(lsp::CompletionResponse::Array(vec![
11514                task_completion_item,
11515            ])))
11516        }
11517    });
11518
11519    cx.condition(|editor, _| editor.context_menu_visible())
11520        .await;
11521    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11522    assert!(request.next().await.is_some());
11523    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11524
11525    cx.simulate_keystrokes("S o m");
11526    cx.condition(|editor, _| editor.context_menu_visible())
11527        .await;
11528    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11529    assert!(request.next().await.is_some());
11530    assert!(request.next().await.is_some());
11531    assert!(request.next().await.is_some());
11532    request.close();
11533    assert!(request.next().await.is_none());
11534    assert_eq!(
11535        counter.load(atomic::Ordering::Acquire),
11536        4,
11537        "With the completions menu open, only one LSP request should happen per input"
11538    );
11539}
11540
11541#[gpui::test]
11542async fn test_toggle_comment(cx: &mut TestAppContext) {
11543    init_test(cx, |_| {});
11544    let mut cx = EditorTestContext::new(cx).await;
11545    let language = Arc::new(Language::new(
11546        LanguageConfig {
11547            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11548            ..Default::default()
11549        },
11550        Some(tree_sitter_rust::LANGUAGE.into()),
11551    ));
11552    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11553
11554    // If multiple selections intersect a line, the line is only toggled once.
11555    cx.set_state(indoc! {"
11556        fn a() {
11557            «//b();
11558            ˇ»// «c();
11559            //ˇ»  d();
11560        }
11561    "});
11562
11563    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11564
11565    cx.assert_editor_state(indoc! {"
11566        fn a() {
11567            «b();
11568            c();
11569            ˇ» d();
11570        }
11571    "});
11572
11573    // The comment prefix is inserted at the same column for every line in a
11574    // selection.
11575    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11576
11577    cx.assert_editor_state(indoc! {"
11578        fn a() {
11579            // «b();
11580            // c();
11581            ˇ»//  d();
11582        }
11583    "});
11584
11585    // If a selection ends at the beginning of a line, that line is not toggled.
11586    cx.set_selections_state(indoc! {"
11587        fn a() {
11588            // b();
11589            «// c();
11590        ˇ»    //  d();
11591        }
11592    "});
11593
11594    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11595
11596    cx.assert_editor_state(indoc! {"
11597        fn a() {
11598            // b();
11599            «c();
11600        ˇ»    //  d();
11601        }
11602    "});
11603
11604    // If a selection span a single line and is empty, the line is toggled.
11605    cx.set_state(indoc! {"
11606        fn a() {
11607            a();
11608            b();
11609        ˇ
11610        }
11611    "});
11612
11613    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11614
11615    cx.assert_editor_state(indoc! {"
11616        fn a() {
11617            a();
11618            b();
11619        //•ˇ
11620        }
11621    "});
11622
11623    // If a selection span multiple lines, empty lines are not toggled.
11624    cx.set_state(indoc! {"
11625        fn a() {
11626            «a();
11627
11628            c();ˇ»
11629        }
11630    "});
11631
11632    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11633
11634    cx.assert_editor_state(indoc! {"
11635        fn a() {
11636            // «a();
11637
11638            // c();ˇ»
11639        }
11640    "});
11641
11642    // If a selection includes multiple comment prefixes, all lines are uncommented.
11643    cx.set_state(indoc! {"
11644        fn a() {
11645            «// a();
11646            /// b();
11647            //! c();ˇ»
11648        }
11649    "});
11650
11651    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11652
11653    cx.assert_editor_state(indoc! {"
11654        fn a() {
11655            «a();
11656            b();
11657            c();ˇ»
11658        }
11659    "});
11660}
11661
11662#[gpui::test]
11663async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11664    init_test(cx, |_| {});
11665    let mut cx = EditorTestContext::new(cx).await;
11666    let language = Arc::new(Language::new(
11667        LanguageConfig {
11668            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11669            ..Default::default()
11670        },
11671        Some(tree_sitter_rust::LANGUAGE.into()),
11672    ));
11673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11674
11675    let toggle_comments = &ToggleComments {
11676        advance_downwards: false,
11677        ignore_indent: true,
11678    };
11679
11680    // If multiple selections intersect a line, the line is only toggled once.
11681    cx.set_state(indoc! {"
11682        fn a() {
11683        //    «b();
11684        //    c();
11685        //    ˇ» d();
11686        }
11687    "});
11688
11689    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11690
11691    cx.assert_editor_state(indoc! {"
11692        fn a() {
11693            «b();
11694            c();
11695            ˇ» d();
11696        }
11697    "});
11698
11699    // The comment prefix is inserted at the beginning of each line
11700    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11701
11702    cx.assert_editor_state(indoc! {"
11703        fn a() {
11704        //    «b();
11705        //    c();
11706        //    ˇ» d();
11707        }
11708    "});
11709
11710    // If a selection ends at the beginning of a line, that line is not toggled.
11711    cx.set_selections_state(indoc! {"
11712        fn a() {
11713        //    b();
11714        //    «c();
11715        ˇ»//     d();
11716        }
11717    "});
11718
11719    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11720
11721    cx.assert_editor_state(indoc! {"
11722        fn a() {
11723        //    b();
11724            «c();
11725        ˇ»//     d();
11726        }
11727    "});
11728
11729    // If a selection span a single line and is empty, the line is toggled.
11730    cx.set_state(indoc! {"
11731        fn a() {
11732            a();
11733            b();
11734        ˇ
11735        }
11736    "});
11737
11738    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11739
11740    cx.assert_editor_state(indoc! {"
11741        fn a() {
11742            a();
11743            b();
11744        //ˇ
11745        }
11746    "});
11747
11748    // If a selection span multiple lines, empty lines are not toggled.
11749    cx.set_state(indoc! {"
11750        fn a() {
11751            «a();
11752
11753            c();ˇ»
11754        }
11755    "});
11756
11757    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11758
11759    cx.assert_editor_state(indoc! {"
11760        fn a() {
11761        //    «a();
11762
11763        //    c();ˇ»
11764        }
11765    "});
11766
11767    // If a selection includes multiple comment prefixes, all lines are uncommented.
11768    cx.set_state(indoc! {"
11769        fn a() {
11770        //    «a();
11771        ///    b();
11772        //!    c();ˇ»
11773        }
11774    "});
11775
11776    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11777
11778    cx.assert_editor_state(indoc! {"
11779        fn a() {
11780            «a();
11781            b();
11782            c();ˇ»
11783        }
11784    "});
11785}
11786
11787#[gpui::test]
11788async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11789    init_test(cx, |_| {});
11790
11791    let language = Arc::new(Language::new(
11792        LanguageConfig {
11793            line_comments: vec!["// ".into()],
11794            ..Default::default()
11795        },
11796        Some(tree_sitter_rust::LANGUAGE.into()),
11797    ));
11798
11799    let mut cx = EditorTestContext::new(cx).await;
11800
11801    cx.language_registry().add(language.clone());
11802    cx.update_buffer(|buffer, cx| {
11803        buffer.set_language(Some(language), cx);
11804    });
11805
11806    let toggle_comments = &ToggleComments {
11807        advance_downwards: true,
11808        ignore_indent: false,
11809    };
11810
11811    // Single cursor on one line -> advance
11812    // Cursor moves horizontally 3 characters as well on non-blank line
11813    cx.set_state(indoc!(
11814        "fn a() {
11815             ˇdog();
11816             cat();
11817        }"
11818    ));
11819    cx.update_editor(|editor, window, cx| {
11820        editor.toggle_comments(toggle_comments, window, cx);
11821    });
11822    cx.assert_editor_state(indoc!(
11823        "fn a() {
11824             // dog();
11825             catˇ();
11826        }"
11827    ));
11828
11829    // Single selection on one line -> don't advance
11830    cx.set_state(indoc!(
11831        "fn a() {
11832             «dog()ˇ»;
11833             cat();
11834        }"
11835    ));
11836    cx.update_editor(|editor, window, cx| {
11837        editor.toggle_comments(toggle_comments, window, cx);
11838    });
11839    cx.assert_editor_state(indoc!(
11840        "fn a() {
11841             // «dog()ˇ»;
11842             cat();
11843        }"
11844    ));
11845
11846    // Multiple cursors on one line -> advance
11847    cx.set_state(indoc!(
11848        "fn a() {
11849             ˇdˇog();
11850             cat();
11851        }"
11852    ));
11853    cx.update_editor(|editor, window, cx| {
11854        editor.toggle_comments(toggle_comments, window, cx);
11855    });
11856    cx.assert_editor_state(indoc!(
11857        "fn a() {
11858             // dog();
11859             catˇ(ˇ);
11860        }"
11861    ));
11862
11863    // Multiple cursors on one line, with selection -> don't advance
11864    cx.set_state(indoc!(
11865        "fn a() {
11866             ˇdˇog«()ˇ»;
11867             cat();
11868        }"
11869    ));
11870    cx.update_editor(|editor, window, cx| {
11871        editor.toggle_comments(toggle_comments, window, cx);
11872    });
11873    cx.assert_editor_state(indoc!(
11874        "fn a() {
11875             // ˇdˇog«()ˇ»;
11876             cat();
11877        }"
11878    ));
11879
11880    // Single cursor on one line -> advance
11881    // Cursor moves to column 0 on blank line
11882    cx.set_state(indoc!(
11883        "fn a() {
11884             ˇdog();
11885
11886             cat();
11887        }"
11888    ));
11889    cx.update_editor(|editor, window, cx| {
11890        editor.toggle_comments(toggle_comments, window, cx);
11891    });
11892    cx.assert_editor_state(indoc!(
11893        "fn a() {
11894             // dog();
11895        ˇ
11896             cat();
11897        }"
11898    ));
11899
11900    // Single cursor on one line -> advance
11901    // Cursor starts and ends at column 0
11902    cx.set_state(indoc!(
11903        "fn a() {
11904         ˇ    dog();
11905             cat();
11906        }"
11907    ));
11908    cx.update_editor(|editor, window, cx| {
11909        editor.toggle_comments(toggle_comments, window, cx);
11910    });
11911    cx.assert_editor_state(indoc!(
11912        "fn a() {
11913             // dog();
11914         ˇ    cat();
11915        }"
11916    ));
11917}
11918
11919#[gpui::test]
11920async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11921    init_test(cx, |_| {});
11922
11923    let mut cx = EditorTestContext::new(cx).await;
11924
11925    let html_language = Arc::new(
11926        Language::new(
11927            LanguageConfig {
11928                name: "HTML".into(),
11929                block_comment: Some(("<!-- ".into(), " -->".into())),
11930                ..Default::default()
11931            },
11932            Some(tree_sitter_html::LANGUAGE.into()),
11933        )
11934        .with_injection_query(
11935            r#"
11936            (script_element
11937                (raw_text) @injection.content
11938                (#set! injection.language "javascript"))
11939            "#,
11940        )
11941        .unwrap(),
11942    );
11943
11944    let javascript_language = Arc::new(Language::new(
11945        LanguageConfig {
11946            name: "JavaScript".into(),
11947            line_comments: vec!["// ".into()],
11948            ..Default::default()
11949        },
11950        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11951    ));
11952
11953    cx.language_registry().add(html_language.clone());
11954    cx.language_registry().add(javascript_language.clone());
11955    cx.update_buffer(|buffer, cx| {
11956        buffer.set_language(Some(html_language), cx);
11957    });
11958
11959    // Toggle comments for empty selections
11960    cx.set_state(
11961        &r#"
11962            <p>A</p>ˇ
11963            <p>B</p>ˇ
11964            <p>C</p>ˇ
11965        "#
11966        .unindent(),
11967    );
11968    cx.update_editor(|editor, window, cx| {
11969        editor.toggle_comments(&ToggleComments::default(), window, cx)
11970    });
11971    cx.assert_editor_state(
11972        &r#"
11973            <!-- <p>A</p>ˇ -->
11974            <!-- <p>B</p>ˇ -->
11975            <!-- <p>C</p>ˇ -->
11976        "#
11977        .unindent(),
11978    );
11979    cx.update_editor(|editor, window, cx| {
11980        editor.toggle_comments(&ToggleComments::default(), window, cx)
11981    });
11982    cx.assert_editor_state(
11983        &r#"
11984            <p>A</p>ˇ
11985            <p>B</p>ˇ
11986            <p>C</p>ˇ
11987        "#
11988        .unindent(),
11989    );
11990
11991    // Toggle comments for mixture of empty and non-empty selections, where
11992    // multiple selections occupy a given line.
11993    cx.set_state(
11994        &r#"
11995            <p>A«</p>
11996            <p>ˇ»B</p>ˇ
11997            <p>C«</p>
11998            <p>ˇ»D</p>ˇ
11999        "#
12000        .unindent(),
12001    );
12002
12003    cx.update_editor(|editor, window, cx| {
12004        editor.toggle_comments(&ToggleComments::default(), window, cx)
12005    });
12006    cx.assert_editor_state(
12007        &r#"
12008            <!-- <p>A«</p>
12009            <p>ˇ»B</p>ˇ -->
12010            <!-- <p>C«</p>
12011            <p>ˇ»D</p>ˇ -->
12012        "#
12013        .unindent(),
12014    );
12015    cx.update_editor(|editor, window, cx| {
12016        editor.toggle_comments(&ToggleComments::default(), window, cx)
12017    });
12018    cx.assert_editor_state(
12019        &r#"
12020            <p>A«</p>
12021            <p>ˇ»B</p>ˇ
12022            <p>C«</p>
12023            <p>ˇ»D</p>ˇ
12024        "#
12025        .unindent(),
12026    );
12027
12028    // Toggle comments when different languages are active for different
12029    // selections.
12030    cx.set_state(
12031        &r#"
12032            ˇ<script>
12033                ˇvar x = new Y();
12034            ˇ</script>
12035        "#
12036        .unindent(),
12037    );
12038    cx.executor().run_until_parked();
12039    cx.update_editor(|editor, window, cx| {
12040        editor.toggle_comments(&ToggleComments::default(), window, cx)
12041    });
12042    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12043    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12044    cx.assert_editor_state(
12045        &r#"
12046            <!-- ˇ<script> -->
12047                // ˇvar x = new Y();
12048            <!-- ˇ</script> -->
12049        "#
12050        .unindent(),
12051    );
12052}
12053
12054#[gpui::test]
12055fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12056    init_test(cx, |_| {});
12057
12058    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12059    let multibuffer = cx.new(|cx| {
12060        let mut multibuffer = MultiBuffer::new(ReadWrite);
12061        multibuffer.push_excerpts(
12062            buffer.clone(),
12063            [
12064                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12065                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12066            ],
12067            cx,
12068        );
12069        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12070        multibuffer
12071    });
12072
12073    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12074    editor.update_in(cx, |editor, window, cx| {
12075        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12076        editor.change_selections(None, window, cx, |s| {
12077            s.select_ranges([
12078                Point::new(0, 0)..Point::new(0, 0),
12079                Point::new(1, 0)..Point::new(1, 0),
12080            ])
12081        });
12082
12083        editor.handle_input("X", window, cx);
12084        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12085        assert_eq!(
12086            editor.selections.ranges(cx),
12087            [
12088                Point::new(0, 1)..Point::new(0, 1),
12089                Point::new(1, 1)..Point::new(1, 1),
12090            ]
12091        );
12092
12093        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12094        editor.change_selections(None, window, cx, |s| {
12095            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12096        });
12097        editor.backspace(&Default::default(), window, cx);
12098        assert_eq!(editor.text(cx), "Xa\nbbb");
12099        assert_eq!(
12100            editor.selections.ranges(cx),
12101            [Point::new(1, 0)..Point::new(1, 0)]
12102        );
12103
12104        editor.change_selections(None, window, cx, |s| {
12105            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12106        });
12107        editor.backspace(&Default::default(), window, cx);
12108        assert_eq!(editor.text(cx), "X\nbb");
12109        assert_eq!(
12110            editor.selections.ranges(cx),
12111            [Point::new(0, 1)..Point::new(0, 1)]
12112        );
12113    });
12114}
12115
12116#[gpui::test]
12117fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12118    init_test(cx, |_| {});
12119
12120    let markers = vec![('[', ']').into(), ('(', ')').into()];
12121    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12122        indoc! {"
12123            [aaaa
12124            (bbbb]
12125            cccc)",
12126        },
12127        markers.clone(),
12128    );
12129    let excerpt_ranges = markers.into_iter().map(|marker| {
12130        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12131        ExcerptRange::new(context.clone())
12132    });
12133    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12134    let multibuffer = cx.new(|cx| {
12135        let mut multibuffer = MultiBuffer::new(ReadWrite);
12136        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12137        multibuffer
12138    });
12139
12140    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12141    editor.update_in(cx, |editor, window, cx| {
12142        let (expected_text, selection_ranges) = marked_text_ranges(
12143            indoc! {"
12144                aaaa
12145                bˇbbb
12146                bˇbbˇb
12147                cccc"
12148            },
12149            true,
12150        );
12151        assert_eq!(editor.text(cx), expected_text);
12152        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12153
12154        editor.handle_input("X", window, cx);
12155
12156        let (expected_text, expected_selections) = marked_text_ranges(
12157            indoc! {"
12158                aaaa
12159                bXˇbbXb
12160                bXˇbbXˇb
12161                cccc"
12162            },
12163            false,
12164        );
12165        assert_eq!(editor.text(cx), expected_text);
12166        assert_eq!(editor.selections.ranges(cx), expected_selections);
12167
12168        editor.newline(&Newline, window, cx);
12169        let (expected_text, expected_selections) = marked_text_ranges(
12170            indoc! {"
12171                aaaa
12172                bX
12173                ˇbbX
12174                b
12175                bX
12176                ˇbbX
12177                ˇb
12178                cccc"
12179            },
12180            false,
12181        );
12182        assert_eq!(editor.text(cx), expected_text);
12183        assert_eq!(editor.selections.ranges(cx), expected_selections);
12184    });
12185}
12186
12187#[gpui::test]
12188fn test_refresh_selections(cx: &mut TestAppContext) {
12189    init_test(cx, |_| {});
12190
12191    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12192    let mut excerpt1_id = None;
12193    let multibuffer = cx.new(|cx| {
12194        let mut multibuffer = MultiBuffer::new(ReadWrite);
12195        excerpt1_id = multibuffer
12196            .push_excerpts(
12197                buffer.clone(),
12198                [
12199                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12200                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12201                ],
12202                cx,
12203            )
12204            .into_iter()
12205            .next();
12206        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12207        multibuffer
12208    });
12209
12210    let editor = cx.add_window(|window, cx| {
12211        let mut editor = build_editor(multibuffer.clone(), window, cx);
12212        let snapshot = editor.snapshot(window, cx);
12213        editor.change_selections(None, window, cx, |s| {
12214            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12215        });
12216        editor.begin_selection(
12217            Point::new(2, 1).to_display_point(&snapshot),
12218            true,
12219            1,
12220            window,
12221            cx,
12222        );
12223        assert_eq!(
12224            editor.selections.ranges(cx),
12225            [
12226                Point::new(1, 3)..Point::new(1, 3),
12227                Point::new(2, 1)..Point::new(2, 1),
12228            ]
12229        );
12230        editor
12231    });
12232
12233    // Refreshing selections is a no-op when excerpts haven't changed.
12234    _ = editor.update(cx, |editor, window, cx| {
12235        editor.change_selections(None, window, cx, |s| s.refresh());
12236        assert_eq!(
12237            editor.selections.ranges(cx),
12238            [
12239                Point::new(1, 3)..Point::new(1, 3),
12240                Point::new(2, 1)..Point::new(2, 1),
12241            ]
12242        );
12243    });
12244
12245    multibuffer.update(cx, |multibuffer, cx| {
12246        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12247    });
12248    _ = editor.update(cx, |editor, window, cx| {
12249        // Removing an excerpt causes the first selection to become degenerate.
12250        assert_eq!(
12251            editor.selections.ranges(cx),
12252            [
12253                Point::new(0, 0)..Point::new(0, 0),
12254                Point::new(0, 1)..Point::new(0, 1)
12255            ]
12256        );
12257
12258        // Refreshing selections will relocate the first selection to the original buffer
12259        // location.
12260        editor.change_selections(None, window, cx, |s| s.refresh());
12261        assert_eq!(
12262            editor.selections.ranges(cx),
12263            [
12264                Point::new(0, 1)..Point::new(0, 1),
12265                Point::new(0, 3)..Point::new(0, 3)
12266            ]
12267        );
12268        assert!(editor.selections.pending_anchor().is_some());
12269    });
12270}
12271
12272#[gpui::test]
12273fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12274    init_test(cx, |_| {});
12275
12276    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12277    let mut excerpt1_id = None;
12278    let multibuffer = cx.new(|cx| {
12279        let mut multibuffer = MultiBuffer::new(ReadWrite);
12280        excerpt1_id = multibuffer
12281            .push_excerpts(
12282                buffer.clone(),
12283                [
12284                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12285                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12286                ],
12287                cx,
12288            )
12289            .into_iter()
12290            .next();
12291        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12292        multibuffer
12293    });
12294
12295    let editor = cx.add_window(|window, cx| {
12296        let mut editor = build_editor(multibuffer.clone(), window, cx);
12297        let snapshot = editor.snapshot(window, cx);
12298        editor.begin_selection(
12299            Point::new(1, 3).to_display_point(&snapshot),
12300            false,
12301            1,
12302            window,
12303            cx,
12304        );
12305        assert_eq!(
12306            editor.selections.ranges(cx),
12307            [Point::new(1, 3)..Point::new(1, 3)]
12308        );
12309        editor
12310    });
12311
12312    multibuffer.update(cx, |multibuffer, cx| {
12313        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12314    });
12315    _ = editor.update(cx, |editor, window, cx| {
12316        assert_eq!(
12317            editor.selections.ranges(cx),
12318            [Point::new(0, 0)..Point::new(0, 0)]
12319        );
12320
12321        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12322        editor.change_selections(None, window, cx, |s| s.refresh());
12323        assert_eq!(
12324            editor.selections.ranges(cx),
12325            [Point::new(0, 3)..Point::new(0, 3)]
12326        );
12327        assert!(editor.selections.pending_anchor().is_some());
12328    });
12329}
12330
12331#[gpui::test]
12332async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12333    init_test(cx, |_| {});
12334
12335    let language = Arc::new(
12336        Language::new(
12337            LanguageConfig {
12338                brackets: BracketPairConfig {
12339                    pairs: vec![
12340                        BracketPair {
12341                            start: "{".to_string(),
12342                            end: "}".to_string(),
12343                            close: true,
12344                            surround: true,
12345                            newline: true,
12346                        },
12347                        BracketPair {
12348                            start: "/* ".to_string(),
12349                            end: " */".to_string(),
12350                            close: true,
12351                            surround: true,
12352                            newline: true,
12353                        },
12354                    ],
12355                    ..Default::default()
12356                },
12357                ..Default::default()
12358            },
12359            Some(tree_sitter_rust::LANGUAGE.into()),
12360        )
12361        .with_indents_query("")
12362        .unwrap(),
12363    );
12364
12365    let text = concat!(
12366        "{   }\n",     //
12367        "  x\n",       //
12368        "  /*   */\n", //
12369        "x\n",         //
12370        "{{} }\n",     //
12371    );
12372
12373    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12374    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12375    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12376    editor
12377        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12378        .await;
12379
12380    editor.update_in(cx, |editor, window, cx| {
12381        editor.change_selections(None, window, cx, |s| {
12382            s.select_display_ranges([
12383                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12384                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12385                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12386            ])
12387        });
12388        editor.newline(&Newline, window, cx);
12389
12390        assert_eq!(
12391            editor.buffer().read(cx).read(cx).text(),
12392            concat!(
12393                "{ \n",    // Suppress rustfmt
12394                "\n",      //
12395                "}\n",     //
12396                "  x\n",   //
12397                "  /* \n", //
12398                "  \n",    //
12399                "  */\n",  //
12400                "x\n",     //
12401                "{{} \n",  //
12402                "}\n",     //
12403            )
12404        );
12405    });
12406}
12407
12408#[gpui::test]
12409fn test_highlighted_ranges(cx: &mut TestAppContext) {
12410    init_test(cx, |_| {});
12411
12412    let editor = cx.add_window(|window, cx| {
12413        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12414        build_editor(buffer.clone(), window, cx)
12415    });
12416
12417    _ = editor.update(cx, |editor, window, cx| {
12418        struct Type1;
12419        struct Type2;
12420
12421        let buffer = editor.buffer.read(cx).snapshot(cx);
12422
12423        let anchor_range =
12424            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12425
12426        editor.highlight_background::<Type1>(
12427            &[
12428                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12429                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12430                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12431                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12432            ],
12433            |_| Hsla::red(),
12434            cx,
12435        );
12436        editor.highlight_background::<Type2>(
12437            &[
12438                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12439                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12440                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12441                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12442            ],
12443            |_| Hsla::green(),
12444            cx,
12445        );
12446
12447        let snapshot = editor.snapshot(window, cx);
12448        let mut highlighted_ranges = editor.background_highlights_in_range(
12449            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12450            &snapshot,
12451            cx.theme().colors(),
12452        );
12453        // Enforce a consistent ordering based on color without relying on the ordering of the
12454        // highlight's `TypeId` which is non-executor.
12455        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12456        assert_eq!(
12457            highlighted_ranges,
12458            &[
12459                (
12460                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12461                    Hsla::red(),
12462                ),
12463                (
12464                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12465                    Hsla::red(),
12466                ),
12467                (
12468                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12469                    Hsla::green(),
12470                ),
12471                (
12472                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12473                    Hsla::green(),
12474                ),
12475            ]
12476        );
12477        assert_eq!(
12478            editor.background_highlights_in_range(
12479                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12480                &snapshot,
12481                cx.theme().colors(),
12482            ),
12483            &[(
12484                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12485                Hsla::red(),
12486            )]
12487        );
12488    });
12489}
12490
12491#[gpui::test]
12492async fn test_following(cx: &mut TestAppContext) {
12493    init_test(cx, |_| {});
12494
12495    let fs = FakeFs::new(cx.executor());
12496    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12497
12498    let buffer = project.update(cx, |project, cx| {
12499        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12500        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12501    });
12502    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12503    let follower = cx.update(|cx| {
12504        cx.open_window(
12505            WindowOptions {
12506                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12507                    gpui::Point::new(px(0.), px(0.)),
12508                    gpui::Point::new(px(10.), px(80.)),
12509                ))),
12510                ..Default::default()
12511            },
12512            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12513        )
12514        .unwrap()
12515    });
12516
12517    let is_still_following = Rc::new(RefCell::new(true));
12518    let follower_edit_event_count = Rc::new(RefCell::new(0));
12519    let pending_update = Rc::new(RefCell::new(None));
12520    let leader_entity = leader.root(cx).unwrap();
12521    let follower_entity = follower.root(cx).unwrap();
12522    _ = follower.update(cx, {
12523        let update = pending_update.clone();
12524        let is_still_following = is_still_following.clone();
12525        let follower_edit_event_count = follower_edit_event_count.clone();
12526        |_, window, cx| {
12527            cx.subscribe_in(
12528                &leader_entity,
12529                window,
12530                move |_, leader, event, window, cx| {
12531                    leader.read(cx).add_event_to_update_proto(
12532                        event,
12533                        &mut update.borrow_mut(),
12534                        window,
12535                        cx,
12536                    );
12537                },
12538            )
12539            .detach();
12540
12541            cx.subscribe_in(
12542                &follower_entity,
12543                window,
12544                move |_, _, event: &EditorEvent, _window, _cx| {
12545                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12546                        *is_still_following.borrow_mut() = false;
12547                    }
12548
12549                    if let EditorEvent::BufferEdited = event {
12550                        *follower_edit_event_count.borrow_mut() += 1;
12551                    }
12552                },
12553            )
12554            .detach();
12555        }
12556    });
12557
12558    // Update the selections only
12559    _ = leader.update(cx, |leader, window, cx| {
12560        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12561    });
12562    follower
12563        .update(cx, |follower, window, cx| {
12564            follower.apply_update_proto(
12565                &project,
12566                pending_update.borrow_mut().take().unwrap(),
12567                window,
12568                cx,
12569            )
12570        })
12571        .unwrap()
12572        .await
12573        .unwrap();
12574    _ = follower.update(cx, |follower, _, cx| {
12575        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12576    });
12577    assert!(*is_still_following.borrow());
12578    assert_eq!(*follower_edit_event_count.borrow(), 0);
12579
12580    // Update the scroll position only
12581    _ = leader.update(cx, |leader, window, cx| {
12582        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12583    });
12584    follower
12585        .update(cx, |follower, window, cx| {
12586            follower.apply_update_proto(
12587                &project,
12588                pending_update.borrow_mut().take().unwrap(),
12589                window,
12590                cx,
12591            )
12592        })
12593        .unwrap()
12594        .await
12595        .unwrap();
12596    assert_eq!(
12597        follower
12598            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12599            .unwrap(),
12600        gpui::Point::new(1.5, 3.5)
12601    );
12602    assert!(*is_still_following.borrow());
12603    assert_eq!(*follower_edit_event_count.borrow(), 0);
12604
12605    // Update the selections and scroll position. The follower's scroll position is updated
12606    // via autoscroll, not via the leader's exact scroll position.
12607    _ = leader.update(cx, |leader, window, cx| {
12608        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12609        leader.request_autoscroll(Autoscroll::newest(), cx);
12610        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12611    });
12612    follower
12613        .update(cx, |follower, window, cx| {
12614            follower.apply_update_proto(
12615                &project,
12616                pending_update.borrow_mut().take().unwrap(),
12617                window,
12618                cx,
12619            )
12620        })
12621        .unwrap()
12622        .await
12623        .unwrap();
12624    _ = follower.update(cx, |follower, _, cx| {
12625        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12626        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12627    });
12628    assert!(*is_still_following.borrow());
12629
12630    // Creating a pending selection that precedes another selection
12631    _ = leader.update(cx, |leader, window, cx| {
12632        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12633        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12634    });
12635    follower
12636        .update(cx, |follower, window, cx| {
12637            follower.apply_update_proto(
12638                &project,
12639                pending_update.borrow_mut().take().unwrap(),
12640                window,
12641                cx,
12642            )
12643        })
12644        .unwrap()
12645        .await
12646        .unwrap();
12647    _ = follower.update(cx, |follower, _, cx| {
12648        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12649    });
12650    assert!(*is_still_following.borrow());
12651
12652    // Extend the pending selection so that it surrounds another selection
12653    _ = leader.update(cx, |leader, window, cx| {
12654        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12655    });
12656    follower
12657        .update(cx, |follower, window, cx| {
12658            follower.apply_update_proto(
12659                &project,
12660                pending_update.borrow_mut().take().unwrap(),
12661                window,
12662                cx,
12663            )
12664        })
12665        .unwrap()
12666        .await
12667        .unwrap();
12668    _ = follower.update(cx, |follower, _, cx| {
12669        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12670    });
12671
12672    // Scrolling locally breaks the follow
12673    _ = follower.update(cx, |follower, window, cx| {
12674        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12675        follower.set_scroll_anchor(
12676            ScrollAnchor {
12677                anchor: top_anchor,
12678                offset: gpui::Point::new(0.0, 0.5),
12679            },
12680            window,
12681            cx,
12682        );
12683    });
12684    assert!(!(*is_still_following.borrow()));
12685}
12686
12687#[gpui::test]
12688async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12689    init_test(cx, |_| {});
12690
12691    let fs = FakeFs::new(cx.executor());
12692    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12693    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12694    let pane = workspace
12695        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12696        .unwrap();
12697
12698    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12699
12700    let leader = pane.update_in(cx, |_, window, cx| {
12701        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12702        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12703    });
12704
12705    // Start following the editor when it has no excerpts.
12706    let mut state_message =
12707        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12708    let workspace_entity = workspace.root(cx).unwrap();
12709    let follower_1 = cx
12710        .update_window(*workspace.deref(), |_, window, cx| {
12711            Editor::from_state_proto(
12712                workspace_entity,
12713                ViewId {
12714                    creator: CollaboratorId::PeerId(PeerId::default()),
12715                    id: 0,
12716                },
12717                &mut state_message,
12718                window,
12719                cx,
12720            )
12721        })
12722        .unwrap()
12723        .unwrap()
12724        .await
12725        .unwrap();
12726
12727    let update_message = Rc::new(RefCell::new(None));
12728    follower_1.update_in(cx, {
12729        let update = update_message.clone();
12730        |_, window, cx| {
12731            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12732                leader.read(cx).add_event_to_update_proto(
12733                    event,
12734                    &mut update.borrow_mut(),
12735                    window,
12736                    cx,
12737                );
12738            })
12739            .detach();
12740        }
12741    });
12742
12743    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12744        (
12745            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12746            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12747        )
12748    });
12749
12750    // Insert some excerpts.
12751    leader.update(cx, |leader, cx| {
12752        leader.buffer.update(cx, |multibuffer, cx| {
12753            multibuffer.set_excerpts_for_path(
12754                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12755                buffer_1.clone(),
12756                vec![
12757                    Point::row_range(0..3),
12758                    Point::row_range(1..6),
12759                    Point::row_range(12..15),
12760                ],
12761                0,
12762                cx,
12763            );
12764            multibuffer.set_excerpts_for_path(
12765                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12766                buffer_2.clone(),
12767                vec![Point::row_range(0..6), Point::row_range(8..12)],
12768                0,
12769                cx,
12770            );
12771        });
12772    });
12773
12774    // Apply the update of adding the excerpts.
12775    follower_1
12776        .update_in(cx, |follower, window, cx| {
12777            follower.apply_update_proto(
12778                &project,
12779                update_message.borrow().clone().unwrap(),
12780                window,
12781                cx,
12782            )
12783        })
12784        .await
12785        .unwrap();
12786    assert_eq!(
12787        follower_1.update(cx, |editor, cx| editor.text(cx)),
12788        leader.update(cx, |editor, cx| editor.text(cx))
12789    );
12790    update_message.borrow_mut().take();
12791
12792    // Start following separately after it already has excerpts.
12793    let mut state_message =
12794        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12795    let workspace_entity = workspace.root(cx).unwrap();
12796    let follower_2 = cx
12797        .update_window(*workspace.deref(), |_, window, cx| {
12798            Editor::from_state_proto(
12799                workspace_entity,
12800                ViewId {
12801                    creator: CollaboratorId::PeerId(PeerId::default()),
12802                    id: 0,
12803                },
12804                &mut state_message,
12805                window,
12806                cx,
12807            )
12808        })
12809        .unwrap()
12810        .unwrap()
12811        .await
12812        .unwrap();
12813    assert_eq!(
12814        follower_2.update(cx, |editor, cx| editor.text(cx)),
12815        leader.update(cx, |editor, cx| editor.text(cx))
12816    );
12817
12818    // Remove some excerpts.
12819    leader.update(cx, |leader, cx| {
12820        leader.buffer.update(cx, |multibuffer, cx| {
12821            let excerpt_ids = multibuffer.excerpt_ids();
12822            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12823            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12824        });
12825    });
12826
12827    // Apply the update of removing the excerpts.
12828    follower_1
12829        .update_in(cx, |follower, window, cx| {
12830            follower.apply_update_proto(
12831                &project,
12832                update_message.borrow().clone().unwrap(),
12833                window,
12834                cx,
12835            )
12836        })
12837        .await
12838        .unwrap();
12839    follower_2
12840        .update_in(cx, |follower, window, cx| {
12841            follower.apply_update_proto(
12842                &project,
12843                update_message.borrow().clone().unwrap(),
12844                window,
12845                cx,
12846            )
12847        })
12848        .await
12849        .unwrap();
12850    update_message.borrow_mut().take();
12851    assert_eq!(
12852        follower_1.update(cx, |editor, cx| editor.text(cx)),
12853        leader.update(cx, |editor, cx| editor.text(cx))
12854    );
12855}
12856
12857#[gpui::test]
12858async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12859    init_test(cx, |_| {});
12860
12861    let mut cx = EditorTestContext::new(cx).await;
12862    let lsp_store =
12863        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12864
12865    cx.set_state(indoc! {"
12866        ˇfn func(abc def: i32) -> u32 {
12867        }
12868    "});
12869
12870    cx.update(|_, cx| {
12871        lsp_store.update(cx, |lsp_store, cx| {
12872            lsp_store
12873                .update_diagnostics(
12874                    LanguageServerId(0),
12875                    lsp::PublishDiagnosticsParams {
12876                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12877                        version: None,
12878                        diagnostics: vec![
12879                            lsp::Diagnostic {
12880                                range: lsp::Range::new(
12881                                    lsp::Position::new(0, 11),
12882                                    lsp::Position::new(0, 12),
12883                                ),
12884                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12885                                ..Default::default()
12886                            },
12887                            lsp::Diagnostic {
12888                                range: lsp::Range::new(
12889                                    lsp::Position::new(0, 12),
12890                                    lsp::Position::new(0, 15),
12891                                ),
12892                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12893                                ..Default::default()
12894                            },
12895                            lsp::Diagnostic {
12896                                range: lsp::Range::new(
12897                                    lsp::Position::new(0, 25),
12898                                    lsp::Position::new(0, 28),
12899                                ),
12900                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12901                                ..Default::default()
12902                            },
12903                        ],
12904                    },
12905                    &[],
12906                    cx,
12907                )
12908                .unwrap()
12909        });
12910    });
12911
12912    executor.run_until_parked();
12913
12914    cx.update_editor(|editor, window, cx| {
12915        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12916    });
12917
12918    cx.assert_editor_state(indoc! {"
12919        fn func(abc def: i32) -> ˇu32 {
12920        }
12921    "});
12922
12923    cx.update_editor(|editor, window, cx| {
12924        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12925    });
12926
12927    cx.assert_editor_state(indoc! {"
12928        fn func(abc ˇdef: i32) -> u32 {
12929        }
12930    "});
12931
12932    cx.update_editor(|editor, window, cx| {
12933        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12934    });
12935
12936    cx.assert_editor_state(indoc! {"
12937        fn func(abcˇ def: i32) -> u32 {
12938        }
12939    "});
12940
12941    cx.update_editor(|editor, window, cx| {
12942        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12943    });
12944
12945    cx.assert_editor_state(indoc! {"
12946        fn func(abc def: i32) -> ˇu32 {
12947        }
12948    "});
12949}
12950
12951#[gpui::test]
12952async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12953    init_test(cx, |_| {});
12954
12955    let mut cx = EditorTestContext::new(cx).await;
12956
12957    let diff_base = r#"
12958        use some::mod;
12959
12960        const A: u32 = 42;
12961
12962        fn main() {
12963            println!("hello");
12964
12965            println!("world");
12966        }
12967        "#
12968    .unindent();
12969
12970    // Edits are modified, removed, modified, added
12971    cx.set_state(
12972        &r#"
12973        use some::modified;
12974
12975        ˇ
12976        fn main() {
12977            println!("hello there");
12978
12979            println!("around the");
12980            println!("world");
12981        }
12982        "#
12983        .unindent(),
12984    );
12985
12986    cx.set_head_text(&diff_base);
12987    executor.run_until_parked();
12988
12989    cx.update_editor(|editor, window, cx| {
12990        //Wrap around the bottom of the buffer
12991        for _ in 0..3 {
12992            editor.go_to_next_hunk(&GoToHunk, window, cx);
12993        }
12994    });
12995
12996    cx.assert_editor_state(
12997        &r#"
12998        ˇuse some::modified;
12999
13000
13001        fn main() {
13002            println!("hello there");
13003
13004            println!("around the");
13005            println!("world");
13006        }
13007        "#
13008        .unindent(),
13009    );
13010
13011    cx.update_editor(|editor, window, cx| {
13012        //Wrap around the top of the buffer
13013        for _ in 0..2 {
13014            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13015        }
13016    });
13017
13018    cx.assert_editor_state(
13019        &r#"
13020        use some::modified;
13021
13022
13023        fn main() {
13024        ˇ    println!("hello there");
13025
13026            println!("around the");
13027            println!("world");
13028        }
13029        "#
13030        .unindent(),
13031    );
13032
13033    cx.update_editor(|editor, window, cx| {
13034        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13035    });
13036
13037    cx.assert_editor_state(
13038        &r#"
13039        use some::modified;
13040
13041        ˇ
13042        fn main() {
13043            println!("hello there");
13044
13045            println!("around the");
13046            println!("world");
13047        }
13048        "#
13049        .unindent(),
13050    );
13051
13052    cx.update_editor(|editor, window, cx| {
13053        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13054    });
13055
13056    cx.assert_editor_state(
13057        &r#"
13058        ˇuse some::modified;
13059
13060
13061        fn main() {
13062            println!("hello there");
13063
13064            println!("around the");
13065            println!("world");
13066        }
13067        "#
13068        .unindent(),
13069    );
13070
13071    cx.update_editor(|editor, window, cx| {
13072        for _ in 0..2 {
13073            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13074        }
13075    });
13076
13077    cx.assert_editor_state(
13078        &r#"
13079        use some::modified;
13080
13081
13082        fn main() {
13083        ˇ    println!("hello there");
13084
13085            println!("around the");
13086            println!("world");
13087        }
13088        "#
13089        .unindent(),
13090    );
13091
13092    cx.update_editor(|editor, window, cx| {
13093        editor.fold(&Fold, window, cx);
13094    });
13095
13096    cx.update_editor(|editor, window, cx| {
13097        editor.go_to_next_hunk(&GoToHunk, window, cx);
13098    });
13099
13100    cx.assert_editor_state(
13101        &r#"
13102        ˇuse some::modified;
13103
13104
13105        fn main() {
13106            println!("hello there");
13107
13108            println!("around the");
13109            println!("world");
13110        }
13111        "#
13112        .unindent(),
13113    );
13114}
13115
13116#[test]
13117fn test_split_words() {
13118    fn split(text: &str) -> Vec<&str> {
13119        split_words(text).collect()
13120    }
13121
13122    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13123    assert_eq!(split("hello_world"), &["hello_", "world"]);
13124    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13125    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13126    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13127    assert_eq!(split("helloworld"), &["helloworld"]);
13128
13129    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13130}
13131
13132#[gpui::test]
13133async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13134    init_test(cx, |_| {});
13135
13136    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13137    let mut assert = |before, after| {
13138        let _state_context = cx.set_state(before);
13139        cx.run_until_parked();
13140        cx.update_editor(|editor, window, cx| {
13141            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13142        });
13143        cx.run_until_parked();
13144        cx.assert_editor_state(after);
13145    };
13146
13147    // Outside bracket jumps to outside of matching bracket
13148    assert("console.logˇ(var);", "console.log(var)ˇ;");
13149    assert("console.log(var)ˇ;", "console.logˇ(var);");
13150
13151    // Inside bracket jumps to inside of matching bracket
13152    assert("console.log(ˇvar);", "console.log(varˇ);");
13153    assert("console.log(varˇ);", "console.log(ˇvar);");
13154
13155    // When outside a bracket and inside, favor jumping to the inside bracket
13156    assert(
13157        "console.log('foo', [1, 2, 3]ˇ);",
13158        "console.log(ˇ'foo', [1, 2, 3]);",
13159    );
13160    assert(
13161        "console.log(ˇ'foo', [1, 2, 3]);",
13162        "console.log('foo', [1, 2, 3]ˇ);",
13163    );
13164
13165    // Bias forward if two options are equally likely
13166    assert(
13167        "let result = curried_fun()ˇ();",
13168        "let result = curried_fun()()ˇ;",
13169    );
13170
13171    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13172    assert(
13173        indoc! {"
13174            function test() {
13175                console.log('test')ˇ
13176            }"},
13177        indoc! {"
13178            function test() {
13179                console.logˇ('test')
13180            }"},
13181    );
13182}
13183
13184#[gpui::test]
13185async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13186    init_test(cx, |_| {});
13187
13188    let fs = FakeFs::new(cx.executor());
13189    fs.insert_tree(
13190        path!("/a"),
13191        json!({
13192            "main.rs": "fn main() { let a = 5; }",
13193            "other.rs": "// Test file",
13194        }),
13195    )
13196    .await;
13197    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13198
13199    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13200    language_registry.add(Arc::new(Language::new(
13201        LanguageConfig {
13202            name: "Rust".into(),
13203            matcher: LanguageMatcher {
13204                path_suffixes: vec!["rs".to_string()],
13205                ..Default::default()
13206            },
13207            brackets: BracketPairConfig {
13208                pairs: vec![BracketPair {
13209                    start: "{".to_string(),
13210                    end: "}".to_string(),
13211                    close: true,
13212                    surround: true,
13213                    newline: true,
13214                }],
13215                disabled_scopes_by_bracket_ix: Vec::new(),
13216            },
13217            ..Default::default()
13218        },
13219        Some(tree_sitter_rust::LANGUAGE.into()),
13220    )));
13221    let mut fake_servers = language_registry.register_fake_lsp(
13222        "Rust",
13223        FakeLspAdapter {
13224            capabilities: lsp::ServerCapabilities {
13225                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13226                    first_trigger_character: "{".to_string(),
13227                    more_trigger_character: None,
13228                }),
13229                ..Default::default()
13230            },
13231            ..Default::default()
13232        },
13233    );
13234
13235    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13236
13237    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13238
13239    let worktree_id = workspace
13240        .update(cx, |workspace, _, cx| {
13241            workspace.project().update(cx, |project, cx| {
13242                project.worktrees(cx).next().unwrap().read(cx).id()
13243            })
13244        })
13245        .unwrap();
13246
13247    let buffer = project
13248        .update(cx, |project, cx| {
13249            project.open_local_buffer(path!("/a/main.rs"), cx)
13250        })
13251        .await
13252        .unwrap();
13253    let editor_handle = workspace
13254        .update(cx, |workspace, window, cx| {
13255            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13256        })
13257        .unwrap()
13258        .await
13259        .unwrap()
13260        .downcast::<Editor>()
13261        .unwrap();
13262
13263    cx.executor().start_waiting();
13264    let fake_server = fake_servers.next().await.unwrap();
13265
13266    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13267        |params, _| async move {
13268            assert_eq!(
13269                params.text_document_position.text_document.uri,
13270                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13271            );
13272            assert_eq!(
13273                params.text_document_position.position,
13274                lsp::Position::new(0, 21),
13275            );
13276
13277            Ok(Some(vec![lsp::TextEdit {
13278                new_text: "]".to_string(),
13279                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13280            }]))
13281        },
13282    );
13283
13284    editor_handle.update_in(cx, |editor, window, cx| {
13285        window.focus(&editor.focus_handle(cx));
13286        editor.change_selections(None, window, cx, |s| {
13287            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13288        });
13289        editor.handle_input("{", window, cx);
13290    });
13291
13292    cx.executor().run_until_parked();
13293
13294    buffer.update(cx, |buffer, _| {
13295        assert_eq!(
13296            buffer.text(),
13297            "fn main() { let a = {5}; }",
13298            "No extra braces from on type formatting should appear in the buffer"
13299        )
13300    });
13301}
13302
13303#[gpui::test]
13304async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13305    init_test(cx, |_| {});
13306
13307    let fs = FakeFs::new(cx.executor());
13308    fs.insert_tree(
13309        path!("/a"),
13310        json!({
13311            "main.rs": "fn main() { let a = 5; }",
13312            "other.rs": "// Test file",
13313        }),
13314    )
13315    .await;
13316
13317    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13318
13319    let server_restarts = Arc::new(AtomicUsize::new(0));
13320    let closure_restarts = Arc::clone(&server_restarts);
13321    let language_server_name = "test language server";
13322    let language_name: LanguageName = "Rust".into();
13323
13324    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13325    language_registry.add(Arc::new(Language::new(
13326        LanguageConfig {
13327            name: language_name.clone(),
13328            matcher: LanguageMatcher {
13329                path_suffixes: vec!["rs".to_string()],
13330                ..Default::default()
13331            },
13332            ..Default::default()
13333        },
13334        Some(tree_sitter_rust::LANGUAGE.into()),
13335    )));
13336    let mut fake_servers = language_registry.register_fake_lsp(
13337        "Rust",
13338        FakeLspAdapter {
13339            name: language_server_name,
13340            initialization_options: Some(json!({
13341                "testOptionValue": true
13342            })),
13343            initializer: Some(Box::new(move |fake_server| {
13344                let task_restarts = Arc::clone(&closure_restarts);
13345                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13346                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13347                    futures::future::ready(Ok(()))
13348                });
13349            })),
13350            ..Default::default()
13351        },
13352    );
13353
13354    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13355    let _buffer = project
13356        .update(cx, |project, cx| {
13357            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13358        })
13359        .await
13360        .unwrap();
13361    let _fake_server = fake_servers.next().await.unwrap();
13362    update_test_language_settings(cx, |language_settings| {
13363        language_settings.languages.insert(
13364            language_name.clone(),
13365            LanguageSettingsContent {
13366                tab_size: NonZeroU32::new(8),
13367                ..Default::default()
13368            },
13369        );
13370    });
13371    cx.executor().run_until_parked();
13372    assert_eq!(
13373        server_restarts.load(atomic::Ordering::Acquire),
13374        0,
13375        "Should not restart LSP server on an unrelated change"
13376    );
13377
13378    update_test_project_settings(cx, |project_settings| {
13379        project_settings.lsp.insert(
13380            "Some other server name".into(),
13381            LspSettings {
13382                binary: None,
13383                settings: None,
13384                initialization_options: Some(json!({
13385                    "some other init value": false
13386                })),
13387                enable_lsp_tasks: false,
13388            },
13389        );
13390    });
13391    cx.executor().run_until_parked();
13392    assert_eq!(
13393        server_restarts.load(atomic::Ordering::Acquire),
13394        0,
13395        "Should not restart LSP server on an unrelated LSP settings change"
13396    );
13397
13398    update_test_project_settings(cx, |project_settings| {
13399        project_settings.lsp.insert(
13400            language_server_name.into(),
13401            LspSettings {
13402                binary: None,
13403                settings: None,
13404                initialization_options: Some(json!({
13405                    "anotherInitValue": false
13406                })),
13407                enable_lsp_tasks: false,
13408            },
13409        );
13410    });
13411    cx.executor().run_until_parked();
13412    assert_eq!(
13413        server_restarts.load(atomic::Ordering::Acquire),
13414        1,
13415        "Should restart LSP server on a related LSP settings change"
13416    );
13417
13418    update_test_project_settings(cx, |project_settings| {
13419        project_settings.lsp.insert(
13420            language_server_name.into(),
13421            LspSettings {
13422                binary: None,
13423                settings: None,
13424                initialization_options: Some(json!({
13425                    "anotherInitValue": false
13426                })),
13427                enable_lsp_tasks: false,
13428            },
13429        );
13430    });
13431    cx.executor().run_until_parked();
13432    assert_eq!(
13433        server_restarts.load(atomic::Ordering::Acquire),
13434        1,
13435        "Should not restart LSP server on a related LSP settings change that is the same"
13436    );
13437
13438    update_test_project_settings(cx, |project_settings| {
13439        project_settings.lsp.insert(
13440            language_server_name.into(),
13441            LspSettings {
13442                binary: None,
13443                settings: None,
13444                initialization_options: None,
13445                enable_lsp_tasks: false,
13446            },
13447        );
13448    });
13449    cx.executor().run_until_parked();
13450    assert_eq!(
13451        server_restarts.load(atomic::Ordering::Acquire),
13452        2,
13453        "Should restart LSP server on another related LSP settings change"
13454    );
13455}
13456
13457#[gpui::test]
13458async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13459    init_test(cx, |_| {});
13460
13461    let mut cx = EditorLspTestContext::new_rust(
13462        lsp::ServerCapabilities {
13463            completion_provider: Some(lsp::CompletionOptions {
13464                trigger_characters: Some(vec![".".to_string()]),
13465                resolve_provider: Some(true),
13466                ..Default::default()
13467            }),
13468            ..Default::default()
13469        },
13470        cx,
13471    )
13472    .await;
13473
13474    cx.set_state("fn main() { let a = 2ˇ; }");
13475    cx.simulate_keystroke(".");
13476    let completion_item = lsp::CompletionItem {
13477        label: "some".into(),
13478        kind: Some(lsp::CompletionItemKind::SNIPPET),
13479        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13480        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13481            kind: lsp::MarkupKind::Markdown,
13482            value: "```rust\nSome(2)\n```".to_string(),
13483        })),
13484        deprecated: Some(false),
13485        sort_text: Some("fffffff2".to_string()),
13486        filter_text: Some("some".to_string()),
13487        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13488        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13489            range: lsp::Range {
13490                start: lsp::Position {
13491                    line: 0,
13492                    character: 22,
13493                },
13494                end: lsp::Position {
13495                    line: 0,
13496                    character: 22,
13497                },
13498            },
13499            new_text: "Some(2)".to_string(),
13500        })),
13501        additional_text_edits: Some(vec![lsp::TextEdit {
13502            range: lsp::Range {
13503                start: lsp::Position {
13504                    line: 0,
13505                    character: 20,
13506                },
13507                end: lsp::Position {
13508                    line: 0,
13509                    character: 22,
13510                },
13511            },
13512            new_text: "".to_string(),
13513        }]),
13514        ..Default::default()
13515    };
13516
13517    let closure_completion_item = completion_item.clone();
13518    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13519        let task_completion_item = closure_completion_item.clone();
13520        async move {
13521            Ok(Some(lsp::CompletionResponse::Array(vec![
13522                task_completion_item,
13523            ])))
13524        }
13525    });
13526
13527    request.next().await;
13528
13529    cx.condition(|editor, _| editor.context_menu_visible())
13530        .await;
13531    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13532        editor
13533            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13534            .unwrap()
13535    });
13536    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13537
13538    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13539        let task_completion_item = completion_item.clone();
13540        async move { Ok(task_completion_item) }
13541    })
13542    .next()
13543    .await
13544    .unwrap();
13545    apply_additional_edits.await.unwrap();
13546    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13547}
13548
13549#[gpui::test]
13550async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13551    init_test(cx, |_| {});
13552
13553    let mut cx = EditorLspTestContext::new_rust(
13554        lsp::ServerCapabilities {
13555            completion_provider: Some(lsp::CompletionOptions {
13556                trigger_characters: Some(vec![".".to_string()]),
13557                resolve_provider: Some(true),
13558                ..Default::default()
13559            }),
13560            ..Default::default()
13561        },
13562        cx,
13563    )
13564    .await;
13565
13566    cx.set_state("fn main() { let a = 2ˇ; }");
13567    cx.simulate_keystroke(".");
13568
13569    let item1 = lsp::CompletionItem {
13570        label: "method id()".to_string(),
13571        filter_text: Some("id".to_string()),
13572        detail: None,
13573        documentation: None,
13574        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13575            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13576            new_text: ".id".to_string(),
13577        })),
13578        ..lsp::CompletionItem::default()
13579    };
13580
13581    let item2 = lsp::CompletionItem {
13582        label: "other".to_string(),
13583        filter_text: Some("other".to_string()),
13584        detail: None,
13585        documentation: None,
13586        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13587            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13588            new_text: ".other".to_string(),
13589        })),
13590        ..lsp::CompletionItem::default()
13591    };
13592
13593    let item1 = item1.clone();
13594    cx.set_request_handler::<lsp::request::Completion, _, _>({
13595        let item1 = item1.clone();
13596        move |_, _, _| {
13597            let item1 = item1.clone();
13598            let item2 = item2.clone();
13599            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13600        }
13601    })
13602    .next()
13603    .await;
13604
13605    cx.condition(|editor, _| editor.context_menu_visible())
13606        .await;
13607    cx.update_editor(|editor, _, _| {
13608        let context_menu = editor.context_menu.borrow_mut();
13609        let context_menu = context_menu
13610            .as_ref()
13611            .expect("Should have the context menu deployed");
13612        match context_menu {
13613            CodeContextMenu::Completions(completions_menu) => {
13614                let completions = completions_menu.completions.borrow_mut();
13615                assert_eq!(
13616                    completions
13617                        .iter()
13618                        .map(|completion| &completion.label.text)
13619                        .collect::<Vec<_>>(),
13620                    vec!["method id()", "other"]
13621                )
13622            }
13623            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13624        }
13625    });
13626
13627    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13628        let item1 = item1.clone();
13629        move |_, item_to_resolve, _| {
13630            let item1 = item1.clone();
13631            async move {
13632                if item1 == item_to_resolve {
13633                    Ok(lsp::CompletionItem {
13634                        label: "method id()".to_string(),
13635                        filter_text: Some("id".to_string()),
13636                        detail: Some("Now resolved!".to_string()),
13637                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13638                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13639                            range: lsp::Range::new(
13640                                lsp::Position::new(0, 22),
13641                                lsp::Position::new(0, 22),
13642                            ),
13643                            new_text: ".id".to_string(),
13644                        })),
13645                        ..lsp::CompletionItem::default()
13646                    })
13647                } else {
13648                    Ok(item_to_resolve)
13649                }
13650            }
13651        }
13652    })
13653    .next()
13654    .await
13655    .unwrap();
13656    cx.run_until_parked();
13657
13658    cx.update_editor(|editor, window, cx| {
13659        editor.context_menu_next(&Default::default(), window, cx);
13660    });
13661
13662    cx.update_editor(|editor, _, _| {
13663        let context_menu = editor.context_menu.borrow_mut();
13664        let context_menu = context_menu
13665            .as_ref()
13666            .expect("Should have the context menu deployed");
13667        match context_menu {
13668            CodeContextMenu::Completions(completions_menu) => {
13669                let completions = completions_menu.completions.borrow_mut();
13670                assert_eq!(
13671                    completions
13672                        .iter()
13673                        .map(|completion| &completion.label.text)
13674                        .collect::<Vec<_>>(),
13675                    vec!["method id() Now resolved!", "other"],
13676                    "Should update first completion label, but not second as the filter text did not match."
13677                );
13678            }
13679            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13680        }
13681    });
13682}
13683
13684#[gpui::test]
13685async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13686    init_test(cx, |_| {});
13687
13688    let mut cx = EditorLspTestContext::new_rust(
13689        lsp::ServerCapabilities {
13690            completion_provider: Some(lsp::CompletionOptions {
13691                trigger_characters: Some(vec![".".to_string()]),
13692                resolve_provider: Some(true),
13693                ..Default::default()
13694            }),
13695            ..Default::default()
13696        },
13697        cx,
13698    )
13699    .await;
13700
13701    cx.set_state("fn main() { let a = 2ˇ; }");
13702    cx.simulate_keystroke(".");
13703
13704    let unresolved_item_1 = lsp::CompletionItem {
13705        label: "id".to_string(),
13706        filter_text: Some("id".to_string()),
13707        detail: None,
13708        documentation: None,
13709        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13710            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13711            new_text: ".id".to_string(),
13712        })),
13713        ..lsp::CompletionItem::default()
13714    };
13715    let resolved_item_1 = lsp::CompletionItem {
13716        additional_text_edits: Some(vec![lsp::TextEdit {
13717            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13718            new_text: "!!".to_string(),
13719        }]),
13720        ..unresolved_item_1.clone()
13721    };
13722    let unresolved_item_2 = lsp::CompletionItem {
13723        label: "other".to_string(),
13724        filter_text: Some("other".to_string()),
13725        detail: None,
13726        documentation: None,
13727        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13728            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13729            new_text: ".other".to_string(),
13730        })),
13731        ..lsp::CompletionItem::default()
13732    };
13733    let resolved_item_2 = lsp::CompletionItem {
13734        additional_text_edits: Some(vec![lsp::TextEdit {
13735            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13736            new_text: "??".to_string(),
13737        }]),
13738        ..unresolved_item_2.clone()
13739    };
13740
13741    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13742    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13743    cx.lsp
13744        .server
13745        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13746            let unresolved_item_1 = unresolved_item_1.clone();
13747            let resolved_item_1 = resolved_item_1.clone();
13748            let unresolved_item_2 = unresolved_item_2.clone();
13749            let resolved_item_2 = resolved_item_2.clone();
13750            let resolve_requests_1 = resolve_requests_1.clone();
13751            let resolve_requests_2 = resolve_requests_2.clone();
13752            move |unresolved_request, _| {
13753                let unresolved_item_1 = unresolved_item_1.clone();
13754                let resolved_item_1 = resolved_item_1.clone();
13755                let unresolved_item_2 = unresolved_item_2.clone();
13756                let resolved_item_2 = resolved_item_2.clone();
13757                let resolve_requests_1 = resolve_requests_1.clone();
13758                let resolve_requests_2 = resolve_requests_2.clone();
13759                async move {
13760                    if unresolved_request == unresolved_item_1 {
13761                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13762                        Ok(resolved_item_1.clone())
13763                    } else if unresolved_request == unresolved_item_2 {
13764                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13765                        Ok(resolved_item_2.clone())
13766                    } else {
13767                        panic!("Unexpected completion item {unresolved_request:?}")
13768                    }
13769                }
13770            }
13771        })
13772        .detach();
13773
13774    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13775        let unresolved_item_1 = unresolved_item_1.clone();
13776        let unresolved_item_2 = unresolved_item_2.clone();
13777        async move {
13778            Ok(Some(lsp::CompletionResponse::Array(vec![
13779                unresolved_item_1,
13780                unresolved_item_2,
13781            ])))
13782        }
13783    })
13784    .next()
13785    .await;
13786
13787    cx.condition(|editor, _| editor.context_menu_visible())
13788        .await;
13789    cx.update_editor(|editor, _, _| {
13790        let context_menu = editor.context_menu.borrow_mut();
13791        let context_menu = context_menu
13792            .as_ref()
13793            .expect("Should have the context menu deployed");
13794        match context_menu {
13795            CodeContextMenu::Completions(completions_menu) => {
13796                let completions = completions_menu.completions.borrow_mut();
13797                assert_eq!(
13798                    completions
13799                        .iter()
13800                        .map(|completion| &completion.label.text)
13801                        .collect::<Vec<_>>(),
13802                    vec!["id", "other"]
13803                )
13804            }
13805            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13806        }
13807    });
13808    cx.run_until_parked();
13809
13810    cx.update_editor(|editor, window, cx| {
13811        editor.context_menu_next(&ContextMenuNext, window, cx);
13812    });
13813    cx.run_until_parked();
13814    cx.update_editor(|editor, window, cx| {
13815        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13816    });
13817    cx.run_until_parked();
13818    cx.update_editor(|editor, window, cx| {
13819        editor.context_menu_next(&ContextMenuNext, window, cx);
13820    });
13821    cx.run_until_parked();
13822    cx.update_editor(|editor, window, cx| {
13823        editor
13824            .compose_completion(&ComposeCompletion::default(), window, cx)
13825            .expect("No task returned")
13826    })
13827    .await
13828    .expect("Completion failed");
13829    cx.run_until_parked();
13830
13831    cx.update_editor(|editor, _, cx| {
13832        assert_eq!(
13833            resolve_requests_1.load(atomic::Ordering::Acquire),
13834            1,
13835            "Should always resolve once despite multiple selections"
13836        );
13837        assert_eq!(
13838            resolve_requests_2.load(atomic::Ordering::Acquire),
13839            1,
13840            "Should always resolve once after multiple selections and applying the completion"
13841        );
13842        assert_eq!(
13843            editor.text(cx),
13844            "fn main() { let a = ??.other; }",
13845            "Should use resolved data when applying the completion"
13846        );
13847    });
13848}
13849
13850#[gpui::test]
13851async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13852    init_test(cx, |_| {});
13853
13854    let item_0 = lsp::CompletionItem {
13855        label: "abs".into(),
13856        insert_text: Some("abs".into()),
13857        data: Some(json!({ "very": "special"})),
13858        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13859        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13860            lsp::InsertReplaceEdit {
13861                new_text: "abs".to_string(),
13862                insert: lsp::Range::default(),
13863                replace: lsp::Range::default(),
13864            },
13865        )),
13866        ..lsp::CompletionItem::default()
13867    };
13868    let items = iter::once(item_0.clone())
13869        .chain((11..51).map(|i| lsp::CompletionItem {
13870            label: format!("item_{}", i),
13871            insert_text: Some(format!("item_{}", i)),
13872            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13873            ..lsp::CompletionItem::default()
13874        }))
13875        .collect::<Vec<_>>();
13876
13877    let default_commit_characters = vec!["?".to_string()];
13878    let default_data = json!({ "default": "data"});
13879    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13880    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13881    let default_edit_range = lsp::Range {
13882        start: lsp::Position {
13883            line: 0,
13884            character: 5,
13885        },
13886        end: lsp::Position {
13887            line: 0,
13888            character: 5,
13889        },
13890    };
13891
13892    let mut cx = EditorLspTestContext::new_rust(
13893        lsp::ServerCapabilities {
13894            completion_provider: Some(lsp::CompletionOptions {
13895                trigger_characters: Some(vec![".".to_string()]),
13896                resolve_provider: Some(true),
13897                ..Default::default()
13898            }),
13899            ..Default::default()
13900        },
13901        cx,
13902    )
13903    .await;
13904
13905    cx.set_state("fn main() { let a = 2ˇ; }");
13906    cx.simulate_keystroke(".");
13907
13908    let completion_data = default_data.clone();
13909    let completion_characters = default_commit_characters.clone();
13910    let completion_items = items.clone();
13911    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13912        let default_data = completion_data.clone();
13913        let default_commit_characters = completion_characters.clone();
13914        let items = completion_items.clone();
13915        async move {
13916            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13917                items,
13918                item_defaults: Some(lsp::CompletionListItemDefaults {
13919                    data: Some(default_data.clone()),
13920                    commit_characters: Some(default_commit_characters.clone()),
13921                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13922                        default_edit_range,
13923                    )),
13924                    insert_text_format: Some(default_insert_text_format),
13925                    insert_text_mode: Some(default_insert_text_mode),
13926                }),
13927                ..lsp::CompletionList::default()
13928            })))
13929        }
13930    })
13931    .next()
13932    .await;
13933
13934    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13935    cx.lsp
13936        .server
13937        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13938            let closure_resolved_items = resolved_items.clone();
13939            move |item_to_resolve, _| {
13940                let closure_resolved_items = closure_resolved_items.clone();
13941                async move {
13942                    closure_resolved_items.lock().push(item_to_resolve.clone());
13943                    Ok(item_to_resolve)
13944                }
13945            }
13946        })
13947        .detach();
13948
13949    cx.condition(|editor, _| editor.context_menu_visible())
13950        .await;
13951    cx.run_until_parked();
13952    cx.update_editor(|editor, _, _| {
13953        let menu = editor.context_menu.borrow_mut();
13954        match menu.as_ref().expect("should have the completions menu") {
13955            CodeContextMenu::Completions(completions_menu) => {
13956                assert_eq!(
13957                    completions_menu
13958                        .entries
13959                        .borrow()
13960                        .iter()
13961                        .map(|mat| mat.string.clone())
13962                        .collect::<Vec<String>>(),
13963                    items
13964                        .iter()
13965                        .map(|completion| completion.label.clone())
13966                        .collect::<Vec<String>>()
13967                );
13968            }
13969            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13970        }
13971    });
13972    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13973    // with 4 from the end.
13974    assert_eq!(
13975        *resolved_items.lock(),
13976        [&items[0..16], &items[items.len() - 4..items.len()]]
13977            .concat()
13978            .iter()
13979            .cloned()
13980            .map(|mut item| {
13981                if item.data.is_none() {
13982                    item.data = Some(default_data.clone());
13983                }
13984                item
13985            })
13986            .collect::<Vec<lsp::CompletionItem>>(),
13987        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13988    );
13989    resolved_items.lock().clear();
13990
13991    cx.update_editor(|editor, window, cx| {
13992        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13993    });
13994    cx.run_until_parked();
13995    // Completions that have already been resolved are skipped.
13996    assert_eq!(
13997        *resolved_items.lock(),
13998        items[items.len() - 16..items.len() - 4]
13999            .iter()
14000            .cloned()
14001            .map(|mut item| {
14002                if item.data.is_none() {
14003                    item.data = Some(default_data.clone());
14004                }
14005                item
14006            })
14007            .collect::<Vec<lsp::CompletionItem>>()
14008    );
14009    resolved_items.lock().clear();
14010}
14011
14012#[gpui::test]
14013async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14014    init_test(cx, |_| {});
14015
14016    let mut cx = EditorLspTestContext::new(
14017        Language::new(
14018            LanguageConfig {
14019                matcher: LanguageMatcher {
14020                    path_suffixes: vec!["jsx".into()],
14021                    ..Default::default()
14022                },
14023                overrides: [(
14024                    "element".into(),
14025                    LanguageConfigOverride {
14026                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14027                        ..Default::default()
14028                    },
14029                )]
14030                .into_iter()
14031                .collect(),
14032                ..Default::default()
14033            },
14034            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14035        )
14036        .with_override_query("(jsx_self_closing_element) @element")
14037        .unwrap(),
14038        lsp::ServerCapabilities {
14039            completion_provider: Some(lsp::CompletionOptions {
14040                trigger_characters: Some(vec![":".to_string()]),
14041                ..Default::default()
14042            }),
14043            ..Default::default()
14044        },
14045        cx,
14046    )
14047    .await;
14048
14049    cx.lsp
14050        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14051            Ok(Some(lsp::CompletionResponse::Array(vec![
14052                lsp::CompletionItem {
14053                    label: "bg-blue".into(),
14054                    ..Default::default()
14055                },
14056                lsp::CompletionItem {
14057                    label: "bg-red".into(),
14058                    ..Default::default()
14059                },
14060                lsp::CompletionItem {
14061                    label: "bg-yellow".into(),
14062                    ..Default::default()
14063                },
14064            ])))
14065        });
14066
14067    cx.set_state(r#"<p class="bgˇ" />"#);
14068
14069    // Trigger completion when typing a dash, because the dash is an extra
14070    // word character in the 'element' scope, which contains the cursor.
14071    cx.simulate_keystroke("-");
14072    cx.executor().run_until_parked();
14073    cx.update_editor(|editor, _, _| {
14074        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14075        {
14076            assert_eq!(
14077                completion_menu_entries(&menu),
14078                &["bg-red", "bg-blue", "bg-yellow"]
14079            );
14080        } else {
14081            panic!("expected completion menu to be open");
14082        }
14083    });
14084
14085    cx.simulate_keystroke("l");
14086    cx.executor().run_until_parked();
14087    cx.update_editor(|editor, _, _| {
14088        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14089        {
14090            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14091        } else {
14092            panic!("expected completion menu to be open");
14093        }
14094    });
14095
14096    // When filtering completions, consider the character after the '-' to
14097    // be the start of a subword.
14098    cx.set_state(r#"<p class="yelˇ" />"#);
14099    cx.simulate_keystroke("l");
14100    cx.executor().run_until_parked();
14101    cx.update_editor(|editor, _, _| {
14102        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14103        {
14104            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14105        } else {
14106            panic!("expected completion menu to be open");
14107        }
14108    });
14109}
14110
14111fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14112    let entries = menu.entries.borrow();
14113    entries.iter().map(|mat| mat.string.clone()).collect()
14114}
14115
14116#[gpui::test]
14117async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14118    init_test(cx, |settings| {
14119        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14120            FormatterList(vec![Formatter::Prettier].into()),
14121        ))
14122    });
14123
14124    let fs = FakeFs::new(cx.executor());
14125    fs.insert_file(path!("/file.ts"), Default::default()).await;
14126
14127    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14128    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14129
14130    language_registry.add(Arc::new(Language::new(
14131        LanguageConfig {
14132            name: "TypeScript".into(),
14133            matcher: LanguageMatcher {
14134                path_suffixes: vec!["ts".to_string()],
14135                ..Default::default()
14136            },
14137            ..Default::default()
14138        },
14139        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14140    )));
14141    update_test_language_settings(cx, |settings| {
14142        settings.defaults.prettier = Some(PrettierSettings {
14143            allowed: true,
14144            ..PrettierSettings::default()
14145        });
14146    });
14147
14148    let test_plugin = "test_plugin";
14149    let _ = language_registry.register_fake_lsp(
14150        "TypeScript",
14151        FakeLspAdapter {
14152            prettier_plugins: vec![test_plugin],
14153            ..Default::default()
14154        },
14155    );
14156
14157    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14158    let buffer = project
14159        .update(cx, |project, cx| {
14160            project.open_local_buffer(path!("/file.ts"), cx)
14161        })
14162        .await
14163        .unwrap();
14164
14165    let buffer_text = "one\ntwo\nthree\n";
14166    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14167    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14168    editor.update_in(cx, |editor, window, cx| {
14169        editor.set_text(buffer_text, window, cx)
14170    });
14171
14172    editor
14173        .update_in(cx, |editor, window, cx| {
14174            editor.perform_format(
14175                project.clone(),
14176                FormatTrigger::Manual,
14177                FormatTarget::Buffers,
14178                window,
14179                cx,
14180            )
14181        })
14182        .unwrap()
14183        .await;
14184    assert_eq!(
14185        editor.update(cx, |editor, cx| editor.text(cx)),
14186        buffer_text.to_string() + prettier_format_suffix,
14187        "Test prettier formatting was not applied to the original buffer text",
14188    );
14189
14190    update_test_language_settings(cx, |settings| {
14191        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14192    });
14193    let format = editor.update_in(cx, |editor, window, cx| {
14194        editor.perform_format(
14195            project.clone(),
14196            FormatTrigger::Manual,
14197            FormatTarget::Buffers,
14198            window,
14199            cx,
14200        )
14201    });
14202    format.await.unwrap();
14203    assert_eq!(
14204        editor.update(cx, |editor, cx| editor.text(cx)),
14205        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14206        "Autoformatting (via test prettier) was not applied to the original buffer text",
14207    );
14208}
14209
14210#[gpui::test]
14211async fn test_addition_reverts(cx: &mut TestAppContext) {
14212    init_test(cx, |_| {});
14213    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14214    let base_text = indoc! {r#"
14215        struct Row;
14216        struct Row1;
14217        struct Row2;
14218
14219        struct Row4;
14220        struct Row5;
14221        struct Row6;
14222
14223        struct Row8;
14224        struct Row9;
14225        struct Row10;"#};
14226
14227    // When addition hunks are not adjacent to carets, no hunk revert is performed
14228    assert_hunk_revert(
14229        indoc! {r#"struct Row;
14230                   struct Row1;
14231                   struct Row1.1;
14232                   struct Row1.2;
14233                   struct Row2;ˇ
14234
14235                   struct Row4;
14236                   struct Row5;
14237                   struct Row6;
14238
14239                   struct Row8;
14240                   ˇstruct Row9;
14241                   struct Row9.1;
14242                   struct Row9.2;
14243                   struct Row9.3;
14244                   struct Row10;"#},
14245        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14246        indoc! {r#"struct Row;
14247                   struct Row1;
14248                   struct Row1.1;
14249                   struct Row1.2;
14250                   struct Row2;ˇ
14251
14252                   struct Row4;
14253                   struct Row5;
14254                   struct Row6;
14255
14256                   struct Row8;
14257                   ˇstruct Row9;
14258                   struct Row9.1;
14259                   struct Row9.2;
14260                   struct Row9.3;
14261                   struct Row10;"#},
14262        base_text,
14263        &mut cx,
14264    );
14265    // Same for selections
14266    assert_hunk_revert(
14267        indoc! {r#"struct Row;
14268                   struct Row1;
14269                   struct Row2;
14270                   struct Row2.1;
14271                   struct Row2.2;
14272                   «ˇ
14273                   struct Row4;
14274                   struct» Row5;
14275                   «struct Row6;
14276                   ˇ»
14277                   struct Row9.1;
14278                   struct Row9.2;
14279                   struct Row9.3;
14280                   struct Row8;
14281                   struct Row9;
14282                   struct Row10;"#},
14283        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14284        indoc! {r#"struct Row;
14285                   struct Row1;
14286                   struct Row2;
14287                   struct Row2.1;
14288                   struct Row2.2;
14289                   «ˇ
14290                   struct Row4;
14291                   struct» Row5;
14292                   «struct Row6;
14293                   ˇ»
14294                   struct Row9.1;
14295                   struct Row9.2;
14296                   struct Row9.3;
14297                   struct Row8;
14298                   struct Row9;
14299                   struct Row10;"#},
14300        base_text,
14301        &mut cx,
14302    );
14303
14304    // When carets and selections intersect the addition hunks, those are reverted.
14305    // Adjacent carets got merged.
14306    assert_hunk_revert(
14307        indoc! {r#"struct Row;
14308                   ˇ// something on the top
14309                   struct Row1;
14310                   struct Row2;
14311                   struct Roˇw3.1;
14312                   struct Row2.2;
14313                   struct Row2.3;ˇ
14314
14315                   struct Row4;
14316                   struct ˇRow5.1;
14317                   struct Row5.2;
14318                   struct «Rowˇ»5.3;
14319                   struct Row5;
14320                   struct Row6;
14321                   ˇ
14322                   struct Row9.1;
14323                   struct «Rowˇ»9.2;
14324                   struct «ˇRow»9.3;
14325                   struct Row8;
14326                   struct Row9;
14327                   «ˇ// something on bottom»
14328                   struct Row10;"#},
14329        vec![
14330            DiffHunkStatusKind::Added,
14331            DiffHunkStatusKind::Added,
14332            DiffHunkStatusKind::Added,
14333            DiffHunkStatusKind::Added,
14334            DiffHunkStatusKind::Added,
14335        ],
14336        indoc! {r#"struct Row;
14337                   ˇstruct Row1;
14338                   struct Row2;
14339                   ˇ
14340                   struct Row4;
14341                   ˇstruct Row5;
14342                   struct Row6;
14343                   ˇ
14344                   ˇstruct Row8;
14345                   struct Row9;
14346                   ˇstruct Row10;"#},
14347        base_text,
14348        &mut cx,
14349    );
14350}
14351
14352#[gpui::test]
14353async fn test_modification_reverts(cx: &mut TestAppContext) {
14354    init_test(cx, |_| {});
14355    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14356    let base_text = indoc! {r#"
14357        struct Row;
14358        struct Row1;
14359        struct Row2;
14360
14361        struct Row4;
14362        struct Row5;
14363        struct Row6;
14364
14365        struct Row8;
14366        struct Row9;
14367        struct Row10;"#};
14368
14369    // Modification hunks behave the same as the addition ones.
14370    assert_hunk_revert(
14371        indoc! {r#"struct Row;
14372                   struct Row1;
14373                   struct Row33;
14374                   ˇ
14375                   struct Row4;
14376                   struct Row5;
14377                   struct Row6;
14378                   ˇ
14379                   struct Row99;
14380                   struct Row9;
14381                   struct Row10;"#},
14382        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14383        indoc! {r#"struct Row;
14384                   struct Row1;
14385                   struct Row33;
14386                   ˇ
14387                   struct Row4;
14388                   struct Row5;
14389                   struct Row6;
14390                   ˇ
14391                   struct Row99;
14392                   struct Row9;
14393                   struct Row10;"#},
14394        base_text,
14395        &mut cx,
14396    );
14397    assert_hunk_revert(
14398        indoc! {r#"struct Row;
14399                   struct Row1;
14400                   struct Row33;
14401                   «ˇ
14402                   struct Row4;
14403                   struct» Row5;
14404                   «struct Row6;
14405                   ˇ»
14406                   struct Row99;
14407                   struct Row9;
14408                   struct Row10;"#},
14409        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14410        indoc! {r#"struct Row;
14411                   struct Row1;
14412                   struct Row33;
14413                   «ˇ
14414                   struct Row4;
14415                   struct» Row5;
14416                   «struct Row6;
14417                   ˇ»
14418                   struct Row99;
14419                   struct Row9;
14420                   struct Row10;"#},
14421        base_text,
14422        &mut cx,
14423    );
14424
14425    assert_hunk_revert(
14426        indoc! {r#"ˇstruct Row1.1;
14427                   struct Row1;
14428                   «ˇstr»uct Row22;
14429
14430                   struct ˇRow44;
14431                   struct Row5;
14432                   struct «Rˇ»ow66;ˇ
14433
14434                   «struˇ»ct Row88;
14435                   struct Row9;
14436                   struct Row1011;ˇ"#},
14437        vec![
14438            DiffHunkStatusKind::Modified,
14439            DiffHunkStatusKind::Modified,
14440            DiffHunkStatusKind::Modified,
14441            DiffHunkStatusKind::Modified,
14442            DiffHunkStatusKind::Modified,
14443            DiffHunkStatusKind::Modified,
14444        ],
14445        indoc! {r#"struct Row;
14446                   ˇstruct Row1;
14447                   struct Row2;
14448                   ˇ
14449                   struct Row4;
14450                   ˇstruct Row5;
14451                   struct Row6;
14452                   ˇ
14453                   struct Row8;
14454                   ˇstruct Row9;
14455                   struct Row10;ˇ"#},
14456        base_text,
14457        &mut cx,
14458    );
14459}
14460
14461#[gpui::test]
14462async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14463    init_test(cx, |_| {});
14464    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14465    let base_text = indoc! {r#"
14466        one
14467
14468        two
14469        three
14470        "#};
14471
14472    cx.set_head_text(base_text);
14473    cx.set_state("\nˇ\n");
14474    cx.executor().run_until_parked();
14475    cx.update_editor(|editor, _window, cx| {
14476        editor.expand_selected_diff_hunks(cx);
14477    });
14478    cx.executor().run_until_parked();
14479    cx.update_editor(|editor, window, cx| {
14480        editor.backspace(&Default::default(), window, cx);
14481    });
14482    cx.run_until_parked();
14483    cx.assert_state_with_diff(
14484        indoc! {r#"
14485
14486        - two
14487        - threeˇ
14488        +
14489        "#}
14490        .to_string(),
14491    );
14492}
14493
14494#[gpui::test]
14495async fn test_deletion_reverts(cx: &mut TestAppContext) {
14496    init_test(cx, |_| {});
14497    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14498    let base_text = indoc! {r#"struct Row;
14499struct Row1;
14500struct Row2;
14501
14502struct Row4;
14503struct Row5;
14504struct Row6;
14505
14506struct Row8;
14507struct Row9;
14508struct Row10;"#};
14509
14510    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14511    assert_hunk_revert(
14512        indoc! {r#"struct Row;
14513                   struct Row2;
14514
14515                   ˇstruct Row4;
14516                   struct Row5;
14517                   struct Row6;
14518                   ˇ
14519                   struct Row8;
14520                   struct Row10;"#},
14521        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14522        indoc! {r#"struct Row;
14523                   struct Row2;
14524
14525                   ˇstruct Row4;
14526                   struct Row5;
14527                   struct Row6;
14528                   ˇ
14529                   struct Row8;
14530                   struct Row10;"#},
14531        base_text,
14532        &mut cx,
14533    );
14534    assert_hunk_revert(
14535        indoc! {r#"struct Row;
14536                   struct Row2;
14537
14538                   «ˇstruct Row4;
14539                   struct» Row5;
14540                   «struct Row6;
14541                   ˇ»
14542                   struct Row8;
14543                   struct Row10;"#},
14544        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14545        indoc! {r#"struct Row;
14546                   struct Row2;
14547
14548                   «ˇstruct Row4;
14549                   struct» Row5;
14550                   «struct Row6;
14551                   ˇ»
14552                   struct Row8;
14553                   struct Row10;"#},
14554        base_text,
14555        &mut cx,
14556    );
14557
14558    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14559    assert_hunk_revert(
14560        indoc! {r#"struct Row;
14561                   ˇstruct Row2;
14562
14563                   struct Row4;
14564                   struct Row5;
14565                   struct Row6;
14566
14567                   struct Row8;ˇ
14568                   struct Row10;"#},
14569        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14570        indoc! {r#"struct Row;
14571                   struct Row1;
14572                   ˇstruct Row2;
14573
14574                   struct Row4;
14575                   struct Row5;
14576                   struct Row6;
14577
14578                   struct Row8;ˇ
14579                   struct Row9;
14580                   struct Row10;"#},
14581        base_text,
14582        &mut cx,
14583    );
14584    assert_hunk_revert(
14585        indoc! {r#"struct Row;
14586                   struct Row2«ˇ;
14587                   struct Row4;
14588                   struct» Row5;
14589                   «struct Row6;
14590
14591                   struct Row8;ˇ»
14592                   struct Row10;"#},
14593        vec![
14594            DiffHunkStatusKind::Deleted,
14595            DiffHunkStatusKind::Deleted,
14596            DiffHunkStatusKind::Deleted,
14597        ],
14598        indoc! {r#"struct Row;
14599                   struct Row1;
14600                   struct Row2«ˇ;
14601
14602                   struct Row4;
14603                   struct» Row5;
14604                   «struct Row6;
14605
14606                   struct Row8;ˇ»
14607                   struct Row9;
14608                   struct Row10;"#},
14609        base_text,
14610        &mut cx,
14611    );
14612}
14613
14614#[gpui::test]
14615async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14616    init_test(cx, |_| {});
14617
14618    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14619    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14620    let base_text_3 =
14621        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14622
14623    let text_1 = edit_first_char_of_every_line(base_text_1);
14624    let text_2 = edit_first_char_of_every_line(base_text_2);
14625    let text_3 = edit_first_char_of_every_line(base_text_3);
14626
14627    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14628    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14629    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14630
14631    let multibuffer = cx.new(|cx| {
14632        let mut multibuffer = MultiBuffer::new(ReadWrite);
14633        multibuffer.push_excerpts(
14634            buffer_1.clone(),
14635            [
14636                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14637                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14638                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14639            ],
14640            cx,
14641        );
14642        multibuffer.push_excerpts(
14643            buffer_2.clone(),
14644            [
14645                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14646                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14647                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14648            ],
14649            cx,
14650        );
14651        multibuffer.push_excerpts(
14652            buffer_3.clone(),
14653            [
14654                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14655                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14656                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14657            ],
14658            cx,
14659        );
14660        multibuffer
14661    });
14662
14663    let fs = FakeFs::new(cx.executor());
14664    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14665    let (editor, cx) = cx
14666        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14667    editor.update_in(cx, |editor, _window, cx| {
14668        for (buffer, diff_base) in [
14669            (buffer_1.clone(), base_text_1),
14670            (buffer_2.clone(), base_text_2),
14671            (buffer_3.clone(), base_text_3),
14672        ] {
14673            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14674            editor
14675                .buffer
14676                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14677        }
14678    });
14679    cx.executor().run_until_parked();
14680
14681    editor.update_in(cx, |editor, window, cx| {
14682        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}");
14683        editor.select_all(&SelectAll, window, cx);
14684        editor.git_restore(&Default::default(), window, cx);
14685    });
14686    cx.executor().run_until_parked();
14687
14688    // When all ranges are selected, all buffer hunks are reverted.
14689    editor.update(cx, |editor, cx| {
14690        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");
14691    });
14692    buffer_1.update(cx, |buffer, _| {
14693        assert_eq!(buffer.text(), base_text_1);
14694    });
14695    buffer_2.update(cx, |buffer, _| {
14696        assert_eq!(buffer.text(), base_text_2);
14697    });
14698    buffer_3.update(cx, |buffer, _| {
14699        assert_eq!(buffer.text(), base_text_3);
14700    });
14701
14702    editor.update_in(cx, |editor, window, cx| {
14703        editor.undo(&Default::default(), window, cx);
14704    });
14705
14706    editor.update_in(cx, |editor, window, cx| {
14707        editor.change_selections(None, window, cx, |s| {
14708            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14709        });
14710        editor.git_restore(&Default::default(), window, cx);
14711    });
14712
14713    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14714    // but not affect buffer_2 and its related excerpts.
14715    editor.update(cx, |editor, cx| {
14716        assert_eq!(
14717            editor.text(cx),
14718            "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}"
14719        );
14720    });
14721    buffer_1.update(cx, |buffer, _| {
14722        assert_eq!(buffer.text(), base_text_1);
14723    });
14724    buffer_2.update(cx, |buffer, _| {
14725        assert_eq!(
14726            buffer.text(),
14727            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14728        );
14729    });
14730    buffer_3.update(cx, |buffer, _| {
14731        assert_eq!(
14732            buffer.text(),
14733            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14734        );
14735    });
14736
14737    fn edit_first_char_of_every_line(text: &str) -> String {
14738        text.split('\n')
14739            .map(|line| format!("X{}", &line[1..]))
14740            .collect::<Vec<_>>()
14741            .join("\n")
14742    }
14743}
14744
14745#[gpui::test]
14746async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14747    init_test(cx, |_| {});
14748
14749    let cols = 4;
14750    let rows = 10;
14751    let sample_text_1 = sample_text(rows, cols, 'a');
14752    assert_eq!(
14753        sample_text_1,
14754        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14755    );
14756    let sample_text_2 = sample_text(rows, cols, 'l');
14757    assert_eq!(
14758        sample_text_2,
14759        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14760    );
14761    let sample_text_3 = sample_text(rows, cols, 'v');
14762    assert_eq!(
14763        sample_text_3,
14764        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14765    );
14766
14767    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14768    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14769    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14770
14771    let multi_buffer = cx.new(|cx| {
14772        let mut multibuffer = MultiBuffer::new(ReadWrite);
14773        multibuffer.push_excerpts(
14774            buffer_1.clone(),
14775            [
14776                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14777                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14778                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14779            ],
14780            cx,
14781        );
14782        multibuffer.push_excerpts(
14783            buffer_2.clone(),
14784            [
14785                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14786                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14787                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14788            ],
14789            cx,
14790        );
14791        multibuffer.push_excerpts(
14792            buffer_3.clone(),
14793            [
14794                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14795                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14796                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14797            ],
14798            cx,
14799        );
14800        multibuffer
14801    });
14802
14803    let fs = FakeFs::new(cx.executor());
14804    fs.insert_tree(
14805        "/a",
14806        json!({
14807            "main.rs": sample_text_1,
14808            "other.rs": sample_text_2,
14809            "lib.rs": sample_text_3,
14810        }),
14811    )
14812    .await;
14813    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14814    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14815    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14816    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14817        Editor::new(
14818            EditorMode::full(),
14819            multi_buffer,
14820            Some(project.clone()),
14821            window,
14822            cx,
14823        )
14824    });
14825    let multibuffer_item_id = workspace
14826        .update(cx, |workspace, window, cx| {
14827            assert!(
14828                workspace.active_item(cx).is_none(),
14829                "active item should be None before the first item is added"
14830            );
14831            workspace.add_item_to_active_pane(
14832                Box::new(multi_buffer_editor.clone()),
14833                None,
14834                true,
14835                window,
14836                cx,
14837            );
14838            let active_item = workspace
14839                .active_item(cx)
14840                .expect("should have an active item after adding the multi buffer");
14841            assert!(
14842                !active_item.is_singleton(cx),
14843                "A multi buffer was expected to active after adding"
14844            );
14845            active_item.item_id()
14846        })
14847        .unwrap();
14848    cx.executor().run_until_parked();
14849
14850    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14851        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14852            s.select_ranges(Some(1..2))
14853        });
14854        editor.open_excerpts(&OpenExcerpts, window, cx);
14855    });
14856    cx.executor().run_until_parked();
14857    let first_item_id = workspace
14858        .update(cx, |workspace, window, cx| {
14859            let active_item = workspace
14860                .active_item(cx)
14861                .expect("should have an active item after navigating into the 1st buffer");
14862            let first_item_id = active_item.item_id();
14863            assert_ne!(
14864                first_item_id, multibuffer_item_id,
14865                "Should navigate into the 1st buffer and activate it"
14866            );
14867            assert!(
14868                active_item.is_singleton(cx),
14869                "New active item should be a singleton buffer"
14870            );
14871            assert_eq!(
14872                active_item
14873                    .act_as::<Editor>(cx)
14874                    .expect("should have navigated into an editor for the 1st buffer")
14875                    .read(cx)
14876                    .text(cx),
14877                sample_text_1
14878            );
14879
14880            workspace
14881                .go_back(workspace.active_pane().downgrade(), window, cx)
14882                .detach_and_log_err(cx);
14883
14884            first_item_id
14885        })
14886        .unwrap();
14887    cx.executor().run_until_parked();
14888    workspace
14889        .update(cx, |workspace, _, cx| {
14890            let active_item = workspace
14891                .active_item(cx)
14892                .expect("should have an active item after navigating back");
14893            assert_eq!(
14894                active_item.item_id(),
14895                multibuffer_item_id,
14896                "Should navigate back to the multi buffer"
14897            );
14898            assert!(!active_item.is_singleton(cx));
14899        })
14900        .unwrap();
14901
14902    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14903        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14904            s.select_ranges(Some(39..40))
14905        });
14906        editor.open_excerpts(&OpenExcerpts, window, cx);
14907    });
14908    cx.executor().run_until_parked();
14909    let second_item_id = workspace
14910        .update(cx, |workspace, window, cx| {
14911            let active_item = workspace
14912                .active_item(cx)
14913                .expect("should have an active item after navigating into the 2nd buffer");
14914            let second_item_id = active_item.item_id();
14915            assert_ne!(
14916                second_item_id, multibuffer_item_id,
14917                "Should navigate away from the multibuffer"
14918            );
14919            assert_ne!(
14920                second_item_id, first_item_id,
14921                "Should navigate into the 2nd buffer and activate it"
14922            );
14923            assert!(
14924                active_item.is_singleton(cx),
14925                "New active item should be a singleton buffer"
14926            );
14927            assert_eq!(
14928                active_item
14929                    .act_as::<Editor>(cx)
14930                    .expect("should have navigated into an editor")
14931                    .read(cx)
14932                    .text(cx),
14933                sample_text_2
14934            );
14935
14936            workspace
14937                .go_back(workspace.active_pane().downgrade(), window, cx)
14938                .detach_and_log_err(cx);
14939
14940            second_item_id
14941        })
14942        .unwrap();
14943    cx.executor().run_until_parked();
14944    workspace
14945        .update(cx, |workspace, _, cx| {
14946            let active_item = workspace
14947                .active_item(cx)
14948                .expect("should have an active item after navigating back from the 2nd buffer");
14949            assert_eq!(
14950                active_item.item_id(),
14951                multibuffer_item_id,
14952                "Should navigate back from the 2nd buffer to the multi buffer"
14953            );
14954            assert!(!active_item.is_singleton(cx));
14955        })
14956        .unwrap();
14957
14958    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14959        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14960            s.select_ranges(Some(70..70))
14961        });
14962        editor.open_excerpts(&OpenExcerpts, window, cx);
14963    });
14964    cx.executor().run_until_parked();
14965    workspace
14966        .update(cx, |workspace, window, cx| {
14967            let active_item = workspace
14968                .active_item(cx)
14969                .expect("should have an active item after navigating into the 3rd buffer");
14970            let third_item_id = active_item.item_id();
14971            assert_ne!(
14972                third_item_id, multibuffer_item_id,
14973                "Should navigate into the 3rd buffer and activate it"
14974            );
14975            assert_ne!(third_item_id, first_item_id);
14976            assert_ne!(third_item_id, second_item_id);
14977            assert!(
14978                active_item.is_singleton(cx),
14979                "New active item should be a singleton buffer"
14980            );
14981            assert_eq!(
14982                active_item
14983                    .act_as::<Editor>(cx)
14984                    .expect("should have navigated into an editor")
14985                    .read(cx)
14986                    .text(cx),
14987                sample_text_3
14988            );
14989
14990            workspace
14991                .go_back(workspace.active_pane().downgrade(), window, cx)
14992                .detach_and_log_err(cx);
14993        })
14994        .unwrap();
14995    cx.executor().run_until_parked();
14996    workspace
14997        .update(cx, |workspace, _, cx| {
14998            let active_item = workspace
14999                .active_item(cx)
15000                .expect("should have an active item after navigating back from the 3rd buffer");
15001            assert_eq!(
15002                active_item.item_id(),
15003                multibuffer_item_id,
15004                "Should navigate back from the 3rd buffer to the multi buffer"
15005            );
15006            assert!(!active_item.is_singleton(cx));
15007        })
15008        .unwrap();
15009}
15010
15011#[gpui::test]
15012async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15013    init_test(cx, |_| {});
15014
15015    let mut cx = EditorTestContext::new(cx).await;
15016
15017    let diff_base = r#"
15018        use some::mod;
15019
15020        const A: u32 = 42;
15021
15022        fn main() {
15023            println!("hello");
15024
15025            println!("world");
15026        }
15027        "#
15028    .unindent();
15029
15030    cx.set_state(
15031        &r#"
15032        use some::modified;
15033
15034        ˇ
15035        fn main() {
15036            println!("hello there");
15037
15038            println!("around the");
15039            println!("world");
15040        }
15041        "#
15042        .unindent(),
15043    );
15044
15045    cx.set_head_text(&diff_base);
15046    executor.run_until_parked();
15047
15048    cx.update_editor(|editor, window, cx| {
15049        editor.go_to_next_hunk(&GoToHunk, window, cx);
15050        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15051    });
15052    executor.run_until_parked();
15053    cx.assert_state_with_diff(
15054        r#"
15055          use some::modified;
15056
15057
15058          fn main() {
15059        -     println!("hello");
15060        + ˇ    println!("hello there");
15061
15062              println!("around the");
15063              println!("world");
15064          }
15065        "#
15066        .unindent(),
15067    );
15068
15069    cx.update_editor(|editor, window, cx| {
15070        for _ in 0..2 {
15071            editor.go_to_next_hunk(&GoToHunk, window, cx);
15072            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15073        }
15074    });
15075    executor.run_until_parked();
15076    cx.assert_state_with_diff(
15077        r#"
15078        - use some::mod;
15079        + ˇuse some::modified;
15080
15081
15082          fn main() {
15083        -     println!("hello");
15084        +     println!("hello there");
15085
15086        +     println!("around the");
15087              println!("world");
15088          }
15089        "#
15090        .unindent(),
15091    );
15092
15093    cx.update_editor(|editor, window, cx| {
15094        editor.go_to_next_hunk(&GoToHunk, window, cx);
15095        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15096    });
15097    executor.run_until_parked();
15098    cx.assert_state_with_diff(
15099        r#"
15100        - use some::mod;
15101        + use some::modified;
15102
15103        - const A: u32 = 42;
15104          ˇ
15105          fn main() {
15106        -     println!("hello");
15107        +     println!("hello there");
15108
15109        +     println!("around the");
15110              println!("world");
15111          }
15112        "#
15113        .unindent(),
15114    );
15115
15116    cx.update_editor(|editor, window, cx| {
15117        editor.cancel(&Cancel, window, cx);
15118    });
15119
15120    cx.assert_state_with_diff(
15121        r#"
15122          use some::modified;
15123
15124          ˇ
15125          fn main() {
15126              println!("hello there");
15127
15128              println!("around the");
15129              println!("world");
15130          }
15131        "#
15132        .unindent(),
15133    );
15134}
15135
15136#[gpui::test]
15137async fn test_diff_base_change_with_expanded_diff_hunks(
15138    executor: BackgroundExecutor,
15139    cx: &mut TestAppContext,
15140) {
15141    init_test(cx, |_| {});
15142
15143    let mut cx = EditorTestContext::new(cx).await;
15144
15145    let diff_base = r#"
15146        use some::mod1;
15147        use some::mod2;
15148
15149        const A: u32 = 42;
15150        const B: u32 = 42;
15151        const C: u32 = 42;
15152
15153        fn main() {
15154            println!("hello");
15155
15156            println!("world");
15157        }
15158        "#
15159    .unindent();
15160
15161    cx.set_state(
15162        &r#"
15163        use some::mod2;
15164
15165        const A: u32 = 42;
15166        const C: u32 = 42;
15167
15168        fn main(ˇ) {
15169            //println!("hello");
15170
15171            println!("world");
15172            //
15173            //
15174        }
15175        "#
15176        .unindent(),
15177    );
15178
15179    cx.set_head_text(&diff_base);
15180    executor.run_until_parked();
15181
15182    cx.update_editor(|editor, window, cx| {
15183        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15184    });
15185    executor.run_until_parked();
15186    cx.assert_state_with_diff(
15187        r#"
15188        - use some::mod1;
15189          use some::mod2;
15190
15191          const A: u32 = 42;
15192        - const B: u32 = 42;
15193          const C: u32 = 42;
15194
15195          fn main(ˇ) {
15196        -     println!("hello");
15197        +     //println!("hello");
15198
15199              println!("world");
15200        +     //
15201        +     //
15202          }
15203        "#
15204        .unindent(),
15205    );
15206
15207    cx.set_head_text("new diff base!");
15208    executor.run_until_parked();
15209    cx.assert_state_with_diff(
15210        r#"
15211        - new diff base!
15212        + use some::mod2;
15213        +
15214        + const A: u32 = 42;
15215        + const C: u32 = 42;
15216        +
15217        + fn main(ˇ) {
15218        +     //println!("hello");
15219        +
15220        +     println!("world");
15221        +     //
15222        +     //
15223        + }
15224        "#
15225        .unindent(),
15226    );
15227}
15228
15229#[gpui::test]
15230async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15231    init_test(cx, |_| {});
15232
15233    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15234    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15235    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15236    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15237    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15238    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15239
15240    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15241    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15242    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15243
15244    let multi_buffer = cx.new(|cx| {
15245        let mut multibuffer = MultiBuffer::new(ReadWrite);
15246        multibuffer.push_excerpts(
15247            buffer_1.clone(),
15248            [
15249                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15250                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15251                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15252            ],
15253            cx,
15254        );
15255        multibuffer.push_excerpts(
15256            buffer_2.clone(),
15257            [
15258                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15259                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15260                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15261            ],
15262            cx,
15263        );
15264        multibuffer.push_excerpts(
15265            buffer_3.clone(),
15266            [
15267                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15268                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15269                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15270            ],
15271            cx,
15272        );
15273        multibuffer
15274    });
15275
15276    let editor =
15277        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15278    editor
15279        .update(cx, |editor, _window, cx| {
15280            for (buffer, diff_base) in [
15281                (buffer_1.clone(), file_1_old),
15282                (buffer_2.clone(), file_2_old),
15283                (buffer_3.clone(), file_3_old),
15284            ] {
15285                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15286                editor
15287                    .buffer
15288                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15289            }
15290        })
15291        .unwrap();
15292
15293    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15294    cx.run_until_parked();
15295
15296    cx.assert_editor_state(
15297        &"
15298            ˇaaa
15299            ccc
15300            ddd
15301
15302            ggg
15303            hhh
15304
15305
15306            lll
15307            mmm
15308            NNN
15309
15310            qqq
15311            rrr
15312
15313            uuu
15314            111
15315            222
15316            333
15317
15318            666
15319            777
15320
15321            000
15322            !!!"
15323        .unindent(),
15324    );
15325
15326    cx.update_editor(|editor, window, cx| {
15327        editor.select_all(&SelectAll, window, cx);
15328        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15329    });
15330    cx.executor().run_until_parked();
15331
15332    cx.assert_state_with_diff(
15333        "
15334            «aaa
15335          - bbb
15336            ccc
15337            ddd
15338
15339            ggg
15340            hhh
15341
15342
15343            lll
15344            mmm
15345          - nnn
15346          + NNN
15347
15348            qqq
15349            rrr
15350
15351            uuu
15352            111
15353            222
15354            333
15355
15356          + 666
15357            777
15358
15359            000
15360            !!!ˇ»"
15361            .unindent(),
15362    );
15363}
15364
15365#[gpui::test]
15366async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15367    init_test(cx, |_| {});
15368
15369    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15370    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15371
15372    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15373    let multi_buffer = cx.new(|cx| {
15374        let mut multibuffer = MultiBuffer::new(ReadWrite);
15375        multibuffer.push_excerpts(
15376            buffer.clone(),
15377            [
15378                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15379                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15380                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15381            ],
15382            cx,
15383        );
15384        multibuffer
15385    });
15386
15387    let editor =
15388        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15389    editor
15390        .update(cx, |editor, _window, cx| {
15391            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15392            editor
15393                .buffer
15394                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15395        })
15396        .unwrap();
15397
15398    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15399    cx.run_until_parked();
15400
15401    cx.update_editor(|editor, window, cx| {
15402        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15403    });
15404    cx.executor().run_until_parked();
15405
15406    // When the start of a hunk coincides with the start of its excerpt,
15407    // the hunk is expanded. When the start of a a hunk is earlier than
15408    // the start of its excerpt, the hunk is not expanded.
15409    cx.assert_state_with_diff(
15410        "
15411            ˇaaa
15412          - bbb
15413          + BBB
15414
15415          - ddd
15416          - eee
15417          + DDD
15418          + EEE
15419            fff
15420
15421            iii
15422        "
15423        .unindent(),
15424    );
15425}
15426
15427#[gpui::test]
15428async fn test_edits_around_expanded_insertion_hunks(
15429    executor: BackgroundExecutor,
15430    cx: &mut TestAppContext,
15431) {
15432    init_test(cx, |_| {});
15433
15434    let mut cx = EditorTestContext::new(cx).await;
15435
15436    let diff_base = r#"
15437        use some::mod1;
15438        use some::mod2;
15439
15440        const A: u32 = 42;
15441
15442        fn main() {
15443            println!("hello");
15444
15445            println!("world");
15446        }
15447        "#
15448    .unindent();
15449    executor.run_until_parked();
15450    cx.set_state(
15451        &r#"
15452        use some::mod1;
15453        use some::mod2;
15454
15455        const A: u32 = 42;
15456        const B: u32 = 42;
15457        const C: u32 = 42;
15458        ˇ
15459
15460        fn main() {
15461            println!("hello");
15462
15463            println!("world");
15464        }
15465        "#
15466        .unindent(),
15467    );
15468
15469    cx.set_head_text(&diff_base);
15470    executor.run_until_parked();
15471
15472    cx.update_editor(|editor, window, cx| {
15473        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15474    });
15475    executor.run_until_parked();
15476
15477    cx.assert_state_with_diff(
15478        r#"
15479        use some::mod1;
15480        use some::mod2;
15481
15482        const A: u32 = 42;
15483      + const B: u32 = 42;
15484      + const C: u32 = 42;
15485      + ˇ
15486
15487        fn main() {
15488            println!("hello");
15489
15490            println!("world");
15491        }
15492      "#
15493        .unindent(),
15494    );
15495
15496    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15497    executor.run_until_parked();
15498
15499    cx.assert_state_with_diff(
15500        r#"
15501        use some::mod1;
15502        use some::mod2;
15503
15504        const A: u32 = 42;
15505      + const B: u32 = 42;
15506      + const C: u32 = 42;
15507      + const D: u32 = 42;
15508      + ˇ
15509
15510        fn main() {
15511            println!("hello");
15512
15513            println!("world");
15514        }
15515      "#
15516        .unindent(),
15517    );
15518
15519    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15520    executor.run_until_parked();
15521
15522    cx.assert_state_with_diff(
15523        r#"
15524        use some::mod1;
15525        use some::mod2;
15526
15527        const A: u32 = 42;
15528      + const B: u32 = 42;
15529      + const C: u32 = 42;
15530      + const D: u32 = 42;
15531      + const E: u32 = 42;
15532      + ˇ
15533
15534        fn main() {
15535            println!("hello");
15536
15537            println!("world");
15538        }
15539      "#
15540        .unindent(),
15541    );
15542
15543    cx.update_editor(|editor, window, cx| {
15544        editor.delete_line(&DeleteLine, window, cx);
15545    });
15546    executor.run_until_parked();
15547
15548    cx.assert_state_with_diff(
15549        r#"
15550        use some::mod1;
15551        use some::mod2;
15552
15553        const A: u32 = 42;
15554      + const B: u32 = 42;
15555      + const C: u32 = 42;
15556      + const D: u32 = 42;
15557      + const E: u32 = 42;
15558        ˇ
15559        fn main() {
15560            println!("hello");
15561
15562            println!("world");
15563        }
15564      "#
15565        .unindent(),
15566    );
15567
15568    cx.update_editor(|editor, window, cx| {
15569        editor.move_up(&MoveUp, window, cx);
15570        editor.delete_line(&DeleteLine, window, cx);
15571        editor.move_up(&MoveUp, window, cx);
15572        editor.delete_line(&DeleteLine, window, cx);
15573        editor.move_up(&MoveUp, window, cx);
15574        editor.delete_line(&DeleteLine, window, cx);
15575    });
15576    executor.run_until_parked();
15577    cx.assert_state_with_diff(
15578        r#"
15579        use some::mod1;
15580        use some::mod2;
15581
15582        const A: u32 = 42;
15583      + const B: u32 = 42;
15584        ˇ
15585        fn main() {
15586            println!("hello");
15587
15588            println!("world");
15589        }
15590      "#
15591        .unindent(),
15592    );
15593
15594    cx.update_editor(|editor, window, cx| {
15595        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15596        editor.delete_line(&DeleteLine, window, cx);
15597    });
15598    executor.run_until_parked();
15599    cx.assert_state_with_diff(
15600        r#"
15601        ˇ
15602        fn main() {
15603            println!("hello");
15604
15605            println!("world");
15606        }
15607      "#
15608        .unindent(),
15609    );
15610}
15611
15612#[gpui::test]
15613async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15614    init_test(cx, |_| {});
15615
15616    let mut cx = EditorTestContext::new(cx).await;
15617    cx.set_head_text(indoc! { "
15618        one
15619        two
15620        three
15621        four
15622        five
15623        "
15624    });
15625    cx.set_state(indoc! { "
15626        one
15627        ˇthree
15628        five
15629    "});
15630    cx.run_until_parked();
15631    cx.update_editor(|editor, window, cx| {
15632        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15633    });
15634    cx.assert_state_with_diff(
15635        indoc! { "
15636        one
15637      - two
15638        ˇthree
15639      - four
15640        five
15641    "}
15642        .to_string(),
15643    );
15644    cx.update_editor(|editor, window, cx| {
15645        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15646    });
15647
15648    cx.assert_state_with_diff(
15649        indoc! { "
15650        one
15651        ˇthree
15652        five
15653    "}
15654        .to_string(),
15655    );
15656
15657    cx.set_state(indoc! { "
15658        one
15659        ˇTWO
15660        three
15661        four
15662        five
15663    "});
15664    cx.run_until_parked();
15665    cx.update_editor(|editor, window, cx| {
15666        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15667    });
15668
15669    cx.assert_state_with_diff(
15670        indoc! { "
15671            one
15672          - two
15673          + ˇTWO
15674            three
15675            four
15676            five
15677        "}
15678        .to_string(),
15679    );
15680    cx.update_editor(|editor, window, cx| {
15681        editor.move_up(&Default::default(), window, cx);
15682        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15683    });
15684    cx.assert_state_with_diff(
15685        indoc! { "
15686            one
15687            ˇTWO
15688            three
15689            four
15690            five
15691        "}
15692        .to_string(),
15693    );
15694}
15695
15696#[gpui::test]
15697async fn test_edits_around_expanded_deletion_hunks(
15698    executor: BackgroundExecutor,
15699    cx: &mut TestAppContext,
15700) {
15701    init_test(cx, |_| {});
15702
15703    let mut cx = EditorTestContext::new(cx).await;
15704
15705    let diff_base = r#"
15706        use some::mod1;
15707        use some::mod2;
15708
15709        const A: u32 = 42;
15710        const B: u32 = 42;
15711        const C: u32 = 42;
15712
15713
15714        fn main() {
15715            println!("hello");
15716
15717            println!("world");
15718        }
15719    "#
15720    .unindent();
15721    executor.run_until_parked();
15722    cx.set_state(
15723        &r#"
15724        use some::mod1;
15725        use some::mod2;
15726
15727        ˇconst B: u32 = 42;
15728        const C: u32 = 42;
15729
15730
15731        fn main() {
15732            println!("hello");
15733
15734            println!("world");
15735        }
15736        "#
15737        .unindent(),
15738    );
15739
15740    cx.set_head_text(&diff_base);
15741    executor.run_until_parked();
15742
15743    cx.update_editor(|editor, window, cx| {
15744        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15745    });
15746    executor.run_until_parked();
15747
15748    cx.assert_state_with_diff(
15749        r#"
15750        use some::mod1;
15751        use some::mod2;
15752
15753      - const A: u32 = 42;
15754        ˇconst B: u32 = 42;
15755        const C: u32 = 42;
15756
15757
15758        fn main() {
15759            println!("hello");
15760
15761            println!("world");
15762        }
15763      "#
15764        .unindent(),
15765    );
15766
15767    cx.update_editor(|editor, window, cx| {
15768        editor.delete_line(&DeleteLine, window, cx);
15769    });
15770    executor.run_until_parked();
15771    cx.assert_state_with_diff(
15772        r#"
15773        use some::mod1;
15774        use some::mod2;
15775
15776      - const A: u32 = 42;
15777      - const B: u32 = 42;
15778        ˇconst C: u32 = 42;
15779
15780
15781        fn main() {
15782            println!("hello");
15783
15784            println!("world");
15785        }
15786      "#
15787        .unindent(),
15788    );
15789
15790    cx.update_editor(|editor, window, cx| {
15791        editor.delete_line(&DeleteLine, window, cx);
15792    });
15793    executor.run_until_parked();
15794    cx.assert_state_with_diff(
15795        r#"
15796        use some::mod1;
15797        use some::mod2;
15798
15799      - const A: u32 = 42;
15800      - const B: u32 = 42;
15801      - const C: u32 = 42;
15802        ˇ
15803
15804        fn main() {
15805            println!("hello");
15806
15807            println!("world");
15808        }
15809      "#
15810        .unindent(),
15811    );
15812
15813    cx.update_editor(|editor, window, cx| {
15814        editor.handle_input("replacement", window, cx);
15815    });
15816    executor.run_until_parked();
15817    cx.assert_state_with_diff(
15818        r#"
15819        use some::mod1;
15820        use some::mod2;
15821
15822      - const A: u32 = 42;
15823      - const B: u32 = 42;
15824      - const C: u32 = 42;
15825      -
15826      + replacementˇ
15827
15828        fn main() {
15829            println!("hello");
15830
15831            println!("world");
15832        }
15833      "#
15834        .unindent(),
15835    );
15836}
15837
15838#[gpui::test]
15839async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15840    init_test(cx, |_| {});
15841
15842    let mut cx = EditorTestContext::new(cx).await;
15843
15844    let base_text = r#"
15845        one
15846        two
15847        three
15848        four
15849        five
15850    "#
15851    .unindent();
15852    executor.run_until_parked();
15853    cx.set_state(
15854        &r#"
15855        one
15856        two
15857        fˇour
15858        five
15859        "#
15860        .unindent(),
15861    );
15862
15863    cx.set_head_text(&base_text);
15864    executor.run_until_parked();
15865
15866    cx.update_editor(|editor, window, cx| {
15867        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15868    });
15869    executor.run_until_parked();
15870
15871    cx.assert_state_with_diff(
15872        r#"
15873          one
15874          two
15875        - three
15876          fˇour
15877          five
15878        "#
15879        .unindent(),
15880    );
15881
15882    cx.update_editor(|editor, window, cx| {
15883        editor.backspace(&Backspace, window, cx);
15884        editor.backspace(&Backspace, window, cx);
15885    });
15886    executor.run_until_parked();
15887    cx.assert_state_with_diff(
15888        r#"
15889          one
15890          two
15891        - threeˇ
15892        - four
15893        + our
15894          five
15895        "#
15896        .unindent(),
15897    );
15898}
15899
15900#[gpui::test]
15901async fn test_edit_after_expanded_modification_hunk(
15902    executor: BackgroundExecutor,
15903    cx: &mut TestAppContext,
15904) {
15905    init_test(cx, |_| {});
15906
15907    let mut cx = EditorTestContext::new(cx).await;
15908
15909    let diff_base = r#"
15910        use some::mod1;
15911        use some::mod2;
15912
15913        const A: u32 = 42;
15914        const B: u32 = 42;
15915        const C: u32 = 42;
15916        const D: u32 = 42;
15917
15918
15919        fn main() {
15920            println!("hello");
15921
15922            println!("world");
15923        }"#
15924    .unindent();
15925
15926    cx.set_state(
15927        &r#"
15928        use some::mod1;
15929        use some::mod2;
15930
15931        const A: u32 = 42;
15932        const B: u32 = 42;
15933        const C: u32 = 43ˇ
15934        const D: u32 = 42;
15935
15936
15937        fn main() {
15938            println!("hello");
15939
15940            println!("world");
15941        }"#
15942        .unindent(),
15943    );
15944
15945    cx.set_head_text(&diff_base);
15946    executor.run_until_parked();
15947    cx.update_editor(|editor, window, cx| {
15948        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15949    });
15950    executor.run_until_parked();
15951
15952    cx.assert_state_with_diff(
15953        r#"
15954        use some::mod1;
15955        use some::mod2;
15956
15957        const A: u32 = 42;
15958        const B: u32 = 42;
15959      - const C: u32 = 42;
15960      + const C: u32 = 43ˇ
15961        const D: u32 = 42;
15962
15963
15964        fn main() {
15965            println!("hello");
15966
15967            println!("world");
15968        }"#
15969        .unindent(),
15970    );
15971
15972    cx.update_editor(|editor, window, cx| {
15973        editor.handle_input("\nnew_line\n", window, cx);
15974    });
15975    executor.run_until_parked();
15976
15977    cx.assert_state_with_diff(
15978        r#"
15979        use some::mod1;
15980        use some::mod2;
15981
15982        const A: u32 = 42;
15983        const B: u32 = 42;
15984      - const C: u32 = 42;
15985      + const C: u32 = 43
15986      + new_line
15987      + ˇ
15988        const D: u32 = 42;
15989
15990
15991        fn main() {
15992            println!("hello");
15993
15994            println!("world");
15995        }"#
15996        .unindent(),
15997    );
15998}
15999
16000#[gpui::test]
16001async fn test_stage_and_unstage_added_file_hunk(
16002    executor: BackgroundExecutor,
16003    cx: &mut TestAppContext,
16004) {
16005    init_test(cx, |_| {});
16006
16007    let mut cx = EditorTestContext::new(cx).await;
16008    cx.update_editor(|editor, _, cx| {
16009        editor.set_expand_all_diff_hunks(cx);
16010    });
16011
16012    let working_copy = r#"
16013            ˇfn main() {
16014                println!("hello, world!");
16015            }
16016        "#
16017    .unindent();
16018
16019    cx.set_state(&working_copy);
16020    executor.run_until_parked();
16021
16022    cx.assert_state_with_diff(
16023        r#"
16024            + ˇfn main() {
16025            +     println!("hello, world!");
16026            + }
16027        "#
16028        .unindent(),
16029    );
16030    cx.assert_index_text(None);
16031
16032    cx.update_editor(|editor, window, cx| {
16033        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16034    });
16035    executor.run_until_parked();
16036    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16037    cx.assert_state_with_diff(
16038        r#"
16039            + ˇfn main() {
16040            +     println!("hello, world!");
16041            + }
16042        "#
16043        .unindent(),
16044    );
16045
16046    cx.update_editor(|editor, window, cx| {
16047        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16048    });
16049    executor.run_until_parked();
16050    cx.assert_index_text(None);
16051}
16052
16053async fn setup_indent_guides_editor(
16054    text: &str,
16055    cx: &mut TestAppContext,
16056) -> (BufferId, EditorTestContext) {
16057    init_test(cx, |_| {});
16058
16059    let mut cx = EditorTestContext::new(cx).await;
16060
16061    let buffer_id = cx.update_editor(|editor, window, cx| {
16062        editor.set_text(text, window, cx);
16063        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16064
16065        buffer_ids[0]
16066    });
16067
16068    (buffer_id, cx)
16069}
16070
16071fn assert_indent_guides(
16072    range: Range<u32>,
16073    expected: Vec<IndentGuide>,
16074    active_indices: Option<Vec<usize>>,
16075    cx: &mut EditorTestContext,
16076) {
16077    let indent_guides = cx.update_editor(|editor, window, cx| {
16078        let snapshot = editor.snapshot(window, cx).display_snapshot;
16079        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16080            editor,
16081            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16082            true,
16083            &snapshot,
16084            cx,
16085        );
16086
16087        indent_guides.sort_by(|a, b| {
16088            a.depth.cmp(&b.depth).then(
16089                a.start_row
16090                    .cmp(&b.start_row)
16091                    .then(a.end_row.cmp(&b.end_row)),
16092            )
16093        });
16094        indent_guides
16095    });
16096
16097    if let Some(expected) = active_indices {
16098        let active_indices = cx.update_editor(|editor, window, cx| {
16099            let snapshot = editor.snapshot(window, cx).display_snapshot;
16100            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16101        });
16102
16103        assert_eq!(
16104            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16105            expected,
16106            "Active indent guide indices do not match"
16107        );
16108    }
16109
16110    assert_eq!(indent_guides, expected, "Indent guides do not match");
16111}
16112
16113fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16114    IndentGuide {
16115        buffer_id,
16116        start_row: MultiBufferRow(start_row),
16117        end_row: MultiBufferRow(end_row),
16118        depth,
16119        tab_size: 4,
16120        settings: IndentGuideSettings {
16121            enabled: true,
16122            line_width: 1,
16123            active_line_width: 1,
16124            ..Default::default()
16125        },
16126    }
16127}
16128
16129#[gpui::test]
16130async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16131    let (buffer_id, mut cx) = setup_indent_guides_editor(
16132        &"
16133    fn main() {
16134        let a = 1;
16135    }"
16136        .unindent(),
16137        cx,
16138    )
16139    .await;
16140
16141    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16142}
16143
16144#[gpui::test]
16145async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16146    let (buffer_id, mut cx) = setup_indent_guides_editor(
16147        &"
16148    fn main() {
16149        let a = 1;
16150        let b = 2;
16151    }"
16152        .unindent(),
16153        cx,
16154    )
16155    .await;
16156
16157    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16158}
16159
16160#[gpui::test]
16161async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16162    let (buffer_id, mut cx) = setup_indent_guides_editor(
16163        &"
16164    fn main() {
16165        let a = 1;
16166        if a == 3 {
16167            let b = 2;
16168        } else {
16169            let c = 3;
16170        }
16171    }"
16172        .unindent(),
16173        cx,
16174    )
16175    .await;
16176
16177    assert_indent_guides(
16178        0..8,
16179        vec![
16180            indent_guide(buffer_id, 1, 6, 0),
16181            indent_guide(buffer_id, 3, 3, 1),
16182            indent_guide(buffer_id, 5, 5, 1),
16183        ],
16184        None,
16185        &mut cx,
16186    );
16187}
16188
16189#[gpui::test]
16190async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16191    let (buffer_id, mut cx) = setup_indent_guides_editor(
16192        &"
16193    fn main() {
16194        let a = 1;
16195            let b = 2;
16196        let c = 3;
16197    }"
16198        .unindent(),
16199        cx,
16200    )
16201    .await;
16202
16203    assert_indent_guides(
16204        0..5,
16205        vec![
16206            indent_guide(buffer_id, 1, 3, 0),
16207            indent_guide(buffer_id, 2, 2, 1),
16208        ],
16209        None,
16210        &mut cx,
16211    );
16212}
16213
16214#[gpui::test]
16215async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16216    let (buffer_id, mut cx) = setup_indent_guides_editor(
16217        &"
16218        fn main() {
16219            let a = 1;
16220
16221            let c = 3;
16222        }"
16223        .unindent(),
16224        cx,
16225    )
16226    .await;
16227
16228    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16229}
16230
16231#[gpui::test]
16232async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16233    let (buffer_id, mut cx) = setup_indent_guides_editor(
16234        &"
16235        fn main() {
16236            let a = 1;
16237
16238            let c = 3;
16239
16240            if a == 3 {
16241                let b = 2;
16242            } else {
16243                let c = 3;
16244            }
16245        }"
16246        .unindent(),
16247        cx,
16248    )
16249    .await;
16250
16251    assert_indent_guides(
16252        0..11,
16253        vec![
16254            indent_guide(buffer_id, 1, 9, 0),
16255            indent_guide(buffer_id, 6, 6, 1),
16256            indent_guide(buffer_id, 8, 8, 1),
16257        ],
16258        None,
16259        &mut cx,
16260    );
16261}
16262
16263#[gpui::test]
16264async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16265    let (buffer_id, mut cx) = setup_indent_guides_editor(
16266        &"
16267        fn main() {
16268            let a = 1;
16269
16270            let c = 3;
16271
16272            if a == 3 {
16273                let b = 2;
16274            } else {
16275                let c = 3;
16276            }
16277        }"
16278        .unindent(),
16279        cx,
16280    )
16281    .await;
16282
16283    assert_indent_guides(
16284        1..11,
16285        vec![
16286            indent_guide(buffer_id, 1, 9, 0),
16287            indent_guide(buffer_id, 6, 6, 1),
16288            indent_guide(buffer_id, 8, 8, 1),
16289        ],
16290        None,
16291        &mut cx,
16292    );
16293}
16294
16295#[gpui::test]
16296async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16297    let (buffer_id, mut cx) = setup_indent_guides_editor(
16298        &"
16299        fn main() {
16300            let a = 1;
16301
16302            let c = 3;
16303
16304            if a == 3 {
16305                let b = 2;
16306            } else {
16307                let c = 3;
16308            }
16309        }"
16310        .unindent(),
16311        cx,
16312    )
16313    .await;
16314
16315    assert_indent_guides(
16316        1..10,
16317        vec![
16318            indent_guide(buffer_id, 1, 9, 0),
16319            indent_guide(buffer_id, 6, 6, 1),
16320            indent_guide(buffer_id, 8, 8, 1),
16321        ],
16322        None,
16323        &mut cx,
16324    );
16325}
16326
16327#[gpui::test]
16328async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16329    let (buffer_id, mut cx) = setup_indent_guides_editor(
16330        &"
16331        block1
16332            block2
16333                block3
16334                    block4
16335            block2
16336        block1
16337        block1"
16338            .unindent(),
16339        cx,
16340    )
16341    .await;
16342
16343    assert_indent_guides(
16344        1..10,
16345        vec![
16346            indent_guide(buffer_id, 1, 4, 0),
16347            indent_guide(buffer_id, 2, 3, 1),
16348            indent_guide(buffer_id, 3, 3, 2),
16349        ],
16350        None,
16351        &mut cx,
16352    );
16353}
16354
16355#[gpui::test]
16356async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16357    let (buffer_id, mut cx) = setup_indent_guides_editor(
16358        &"
16359        block1
16360            block2
16361                block3
16362
16363        block1
16364        block1"
16365            .unindent(),
16366        cx,
16367    )
16368    .await;
16369
16370    assert_indent_guides(
16371        0..6,
16372        vec![
16373            indent_guide(buffer_id, 1, 2, 0),
16374            indent_guide(buffer_id, 2, 2, 1),
16375        ],
16376        None,
16377        &mut cx,
16378    );
16379}
16380
16381#[gpui::test]
16382async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16383    let (buffer_id, mut cx) = setup_indent_guides_editor(
16384        &"
16385        block1
16386
16387
16388
16389            block2
16390        "
16391        .unindent(),
16392        cx,
16393    )
16394    .await;
16395
16396    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16397}
16398
16399#[gpui::test]
16400async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16401    let (buffer_id, mut cx) = setup_indent_guides_editor(
16402        &"
16403        def a:
16404        \tb = 3
16405        \tif True:
16406        \t\tc = 4
16407        \t\td = 5
16408        \tprint(b)
16409        "
16410        .unindent(),
16411        cx,
16412    )
16413    .await;
16414
16415    assert_indent_guides(
16416        0..6,
16417        vec![
16418            indent_guide(buffer_id, 1, 6, 0),
16419            indent_guide(buffer_id, 3, 4, 1),
16420        ],
16421        None,
16422        &mut cx,
16423    );
16424}
16425
16426#[gpui::test]
16427async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16428    let (buffer_id, mut cx) = setup_indent_guides_editor(
16429        &"
16430    fn main() {
16431        let a = 1;
16432    }"
16433        .unindent(),
16434        cx,
16435    )
16436    .await;
16437
16438    cx.update_editor(|editor, window, cx| {
16439        editor.change_selections(None, window, cx, |s| {
16440            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16441        });
16442    });
16443
16444    assert_indent_guides(
16445        0..3,
16446        vec![indent_guide(buffer_id, 1, 1, 0)],
16447        Some(vec![0]),
16448        &mut cx,
16449    );
16450}
16451
16452#[gpui::test]
16453async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16454    let (buffer_id, mut cx) = setup_indent_guides_editor(
16455        &"
16456    fn main() {
16457        if 1 == 2 {
16458            let a = 1;
16459        }
16460    }"
16461        .unindent(),
16462        cx,
16463    )
16464    .await;
16465
16466    cx.update_editor(|editor, window, cx| {
16467        editor.change_selections(None, window, cx, |s| {
16468            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16469        });
16470    });
16471
16472    assert_indent_guides(
16473        0..4,
16474        vec![
16475            indent_guide(buffer_id, 1, 3, 0),
16476            indent_guide(buffer_id, 2, 2, 1),
16477        ],
16478        Some(vec![1]),
16479        &mut cx,
16480    );
16481
16482    cx.update_editor(|editor, window, cx| {
16483        editor.change_selections(None, window, cx, |s| {
16484            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16485        });
16486    });
16487
16488    assert_indent_guides(
16489        0..4,
16490        vec![
16491            indent_guide(buffer_id, 1, 3, 0),
16492            indent_guide(buffer_id, 2, 2, 1),
16493        ],
16494        Some(vec![1]),
16495        &mut cx,
16496    );
16497
16498    cx.update_editor(|editor, window, cx| {
16499        editor.change_selections(None, window, cx, |s| {
16500            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16501        });
16502    });
16503
16504    assert_indent_guides(
16505        0..4,
16506        vec![
16507            indent_guide(buffer_id, 1, 3, 0),
16508            indent_guide(buffer_id, 2, 2, 1),
16509        ],
16510        Some(vec![0]),
16511        &mut cx,
16512    );
16513}
16514
16515#[gpui::test]
16516async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16517    let (buffer_id, mut cx) = setup_indent_guides_editor(
16518        &"
16519    fn main() {
16520        let a = 1;
16521
16522        let b = 2;
16523    }"
16524        .unindent(),
16525        cx,
16526    )
16527    .await;
16528
16529    cx.update_editor(|editor, window, cx| {
16530        editor.change_selections(None, window, cx, |s| {
16531            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16532        });
16533    });
16534
16535    assert_indent_guides(
16536        0..5,
16537        vec![indent_guide(buffer_id, 1, 3, 0)],
16538        Some(vec![0]),
16539        &mut cx,
16540    );
16541}
16542
16543#[gpui::test]
16544async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16545    let (buffer_id, mut cx) = setup_indent_guides_editor(
16546        &"
16547    def m:
16548        a = 1
16549        pass"
16550            .unindent(),
16551        cx,
16552    )
16553    .await;
16554
16555    cx.update_editor(|editor, window, cx| {
16556        editor.change_selections(None, window, cx, |s| {
16557            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16558        });
16559    });
16560
16561    assert_indent_guides(
16562        0..3,
16563        vec![indent_guide(buffer_id, 1, 2, 0)],
16564        Some(vec![0]),
16565        &mut cx,
16566    );
16567}
16568
16569#[gpui::test]
16570async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16571    init_test(cx, |_| {});
16572    let mut cx = EditorTestContext::new(cx).await;
16573    let text = indoc! {
16574        "
16575        impl A {
16576            fn b() {
16577                0;
16578                3;
16579                5;
16580                6;
16581                7;
16582            }
16583        }
16584        "
16585    };
16586    let base_text = indoc! {
16587        "
16588        impl A {
16589            fn b() {
16590                0;
16591                1;
16592                2;
16593                3;
16594                4;
16595            }
16596            fn c() {
16597                5;
16598                6;
16599                7;
16600            }
16601        }
16602        "
16603    };
16604
16605    cx.update_editor(|editor, window, cx| {
16606        editor.set_text(text, window, cx);
16607
16608        editor.buffer().update(cx, |multibuffer, cx| {
16609            let buffer = multibuffer.as_singleton().unwrap();
16610            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16611
16612            multibuffer.set_all_diff_hunks_expanded(cx);
16613            multibuffer.add_diff(diff, cx);
16614
16615            buffer.read(cx).remote_id()
16616        })
16617    });
16618    cx.run_until_parked();
16619
16620    cx.assert_state_with_diff(
16621        indoc! { "
16622          impl A {
16623              fn b() {
16624                  0;
16625        -         1;
16626        -         2;
16627                  3;
16628        -         4;
16629        -     }
16630        -     fn c() {
16631                  5;
16632                  6;
16633                  7;
16634              }
16635          }
16636          ˇ"
16637        }
16638        .to_string(),
16639    );
16640
16641    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16642        editor
16643            .snapshot(window, cx)
16644            .buffer_snapshot
16645            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16646            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16647            .collect::<Vec<_>>()
16648    });
16649    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16650    assert_eq!(
16651        actual_guides,
16652        vec![
16653            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16654            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16655            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16656        ]
16657    );
16658}
16659
16660#[gpui::test]
16661async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16662    init_test(cx, |_| {});
16663    let mut cx = EditorTestContext::new(cx).await;
16664
16665    let diff_base = r#"
16666        a
16667        b
16668        c
16669        "#
16670    .unindent();
16671
16672    cx.set_state(
16673        &r#"
16674        ˇA
16675        b
16676        C
16677        "#
16678        .unindent(),
16679    );
16680    cx.set_head_text(&diff_base);
16681    cx.update_editor(|editor, window, cx| {
16682        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16683    });
16684    executor.run_until_parked();
16685
16686    let both_hunks_expanded = r#"
16687        - a
16688        + ˇA
16689          b
16690        - c
16691        + C
16692        "#
16693    .unindent();
16694
16695    cx.assert_state_with_diff(both_hunks_expanded.clone());
16696
16697    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16698        let snapshot = editor.snapshot(window, cx);
16699        let hunks = editor
16700            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16701            .collect::<Vec<_>>();
16702        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16703        let buffer_id = hunks[0].buffer_id;
16704        hunks
16705            .into_iter()
16706            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16707            .collect::<Vec<_>>()
16708    });
16709    assert_eq!(hunk_ranges.len(), 2);
16710
16711    cx.update_editor(|editor, _, cx| {
16712        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16713    });
16714    executor.run_until_parked();
16715
16716    let second_hunk_expanded = r#"
16717          ˇA
16718          b
16719        - c
16720        + C
16721        "#
16722    .unindent();
16723
16724    cx.assert_state_with_diff(second_hunk_expanded);
16725
16726    cx.update_editor(|editor, _, cx| {
16727        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16728    });
16729    executor.run_until_parked();
16730
16731    cx.assert_state_with_diff(both_hunks_expanded.clone());
16732
16733    cx.update_editor(|editor, _, cx| {
16734        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16735    });
16736    executor.run_until_parked();
16737
16738    let first_hunk_expanded = r#"
16739        - a
16740        + ˇA
16741          b
16742          C
16743        "#
16744    .unindent();
16745
16746    cx.assert_state_with_diff(first_hunk_expanded);
16747
16748    cx.update_editor(|editor, _, cx| {
16749        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16750    });
16751    executor.run_until_parked();
16752
16753    cx.assert_state_with_diff(both_hunks_expanded);
16754
16755    cx.set_state(
16756        &r#"
16757        ˇA
16758        b
16759        "#
16760        .unindent(),
16761    );
16762    cx.run_until_parked();
16763
16764    // TODO this cursor position seems bad
16765    cx.assert_state_with_diff(
16766        r#"
16767        - ˇa
16768        + A
16769          b
16770        "#
16771        .unindent(),
16772    );
16773
16774    cx.update_editor(|editor, window, cx| {
16775        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16776    });
16777
16778    cx.assert_state_with_diff(
16779        r#"
16780            - ˇa
16781            + A
16782              b
16783            - c
16784            "#
16785        .unindent(),
16786    );
16787
16788    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16789        let snapshot = editor.snapshot(window, cx);
16790        let hunks = editor
16791            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16792            .collect::<Vec<_>>();
16793        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16794        let buffer_id = hunks[0].buffer_id;
16795        hunks
16796            .into_iter()
16797            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16798            .collect::<Vec<_>>()
16799    });
16800    assert_eq!(hunk_ranges.len(), 2);
16801
16802    cx.update_editor(|editor, _, cx| {
16803        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16804    });
16805    executor.run_until_parked();
16806
16807    cx.assert_state_with_diff(
16808        r#"
16809        - ˇa
16810        + A
16811          b
16812        "#
16813        .unindent(),
16814    );
16815}
16816
16817#[gpui::test]
16818async fn test_toggle_deletion_hunk_at_start_of_file(
16819    executor: BackgroundExecutor,
16820    cx: &mut TestAppContext,
16821) {
16822    init_test(cx, |_| {});
16823    let mut cx = EditorTestContext::new(cx).await;
16824
16825    let diff_base = r#"
16826        a
16827        b
16828        c
16829        "#
16830    .unindent();
16831
16832    cx.set_state(
16833        &r#"
16834        ˇb
16835        c
16836        "#
16837        .unindent(),
16838    );
16839    cx.set_head_text(&diff_base);
16840    cx.update_editor(|editor, window, cx| {
16841        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16842    });
16843    executor.run_until_parked();
16844
16845    let hunk_expanded = r#"
16846        - a
16847          ˇb
16848          c
16849        "#
16850    .unindent();
16851
16852    cx.assert_state_with_diff(hunk_expanded.clone());
16853
16854    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16855        let snapshot = editor.snapshot(window, cx);
16856        let hunks = editor
16857            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16858            .collect::<Vec<_>>();
16859        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16860        let buffer_id = hunks[0].buffer_id;
16861        hunks
16862            .into_iter()
16863            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16864            .collect::<Vec<_>>()
16865    });
16866    assert_eq!(hunk_ranges.len(), 1);
16867
16868    cx.update_editor(|editor, _, cx| {
16869        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16870    });
16871    executor.run_until_parked();
16872
16873    let hunk_collapsed = r#"
16874          ˇb
16875          c
16876        "#
16877    .unindent();
16878
16879    cx.assert_state_with_diff(hunk_collapsed);
16880
16881    cx.update_editor(|editor, _, cx| {
16882        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16883    });
16884    executor.run_until_parked();
16885
16886    cx.assert_state_with_diff(hunk_expanded.clone());
16887}
16888
16889#[gpui::test]
16890async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16891    init_test(cx, |_| {});
16892
16893    let fs = FakeFs::new(cx.executor());
16894    fs.insert_tree(
16895        path!("/test"),
16896        json!({
16897            ".git": {},
16898            "file-1": "ONE\n",
16899            "file-2": "TWO\n",
16900            "file-3": "THREE\n",
16901        }),
16902    )
16903    .await;
16904
16905    fs.set_head_for_repo(
16906        path!("/test/.git").as_ref(),
16907        &[
16908            ("file-1".into(), "one\n".into()),
16909            ("file-2".into(), "two\n".into()),
16910            ("file-3".into(), "three\n".into()),
16911        ],
16912    );
16913
16914    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16915    let mut buffers = vec![];
16916    for i in 1..=3 {
16917        let buffer = project
16918            .update(cx, |project, cx| {
16919                let path = format!(path!("/test/file-{}"), i);
16920                project.open_local_buffer(path, cx)
16921            })
16922            .await
16923            .unwrap();
16924        buffers.push(buffer);
16925    }
16926
16927    let multibuffer = cx.new(|cx| {
16928        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16929        multibuffer.set_all_diff_hunks_expanded(cx);
16930        for buffer in &buffers {
16931            let snapshot = buffer.read(cx).snapshot();
16932            multibuffer.set_excerpts_for_path(
16933                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16934                buffer.clone(),
16935                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16936                DEFAULT_MULTIBUFFER_CONTEXT,
16937                cx,
16938            );
16939        }
16940        multibuffer
16941    });
16942
16943    let editor = cx.add_window(|window, cx| {
16944        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16945    });
16946    cx.run_until_parked();
16947
16948    let snapshot = editor
16949        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16950        .unwrap();
16951    let hunks = snapshot
16952        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16953        .map(|hunk| match hunk {
16954            DisplayDiffHunk::Unfolded {
16955                display_row_range, ..
16956            } => display_row_range,
16957            DisplayDiffHunk::Folded { .. } => unreachable!(),
16958        })
16959        .collect::<Vec<_>>();
16960    assert_eq!(
16961        hunks,
16962        [
16963            DisplayRow(2)..DisplayRow(4),
16964            DisplayRow(7)..DisplayRow(9),
16965            DisplayRow(12)..DisplayRow(14),
16966        ]
16967    );
16968}
16969
16970#[gpui::test]
16971async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16972    init_test(cx, |_| {});
16973
16974    let mut cx = EditorTestContext::new(cx).await;
16975    cx.set_head_text(indoc! { "
16976        one
16977        two
16978        three
16979        four
16980        five
16981        "
16982    });
16983    cx.set_index_text(indoc! { "
16984        one
16985        two
16986        three
16987        four
16988        five
16989        "
16990    });
16991    cx.set_state(indoc! {"
16992        one
16993        TWO
16994        ˇTHREE
16995        FOUR
16996        five
16997    "});
16998    cx.run_until_parked();
16999    cx.update_editor(|editor, window, cx| {
17000        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17001    });
17002    cx.run_until_parked();
17003    cx.assert_index_text(Some(indoc! {"
17004        one
17005        TWO
17006        THREE
17007        FOUR
17008        five
17009    "}));
17010    cx.set_state(indoc! { "
17011        one
17012        TWO
17013        ˇTHREE-HUNDRED
17014        FOUR
17015        five
17016    "});
17017    cx.run_until_parked();
17018    cx.update_editor(|editor, window, cx| {
17019        let snapshot = editor.snapshot(window, cx);
17020        let hunks = editor
17021            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17022            .collect::<Vec<_>>();
17023        assert_eq!(hunks.len(), 1);
17024        assert_eq!(
17025            hunks[0].status(),
17026            DiffHunkStatus {
17027                kind: DiffHunkStatusKind::Modified,
17028                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17029            }
17030        );
17031
17032        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17033    });
17034    cx.run_until_parked();
17035    cx.assert_index_text(Some(indoc! {"
17036        one
17037        TWO
17038        THREE-HUNDRED
17039        FOUR
17040        five
17041    "}));
17042}
17043
17044#[gpui::test]
17045fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17046    init_test(cx, |_| {});
17047
17048    let editor = cx.add_window(|window, cx| {
17049        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17050        build_editor(buffer, window, cx)
17051    });
17052
17053    let render_args = Arc::new(Mutex::new(None));
17054    let snapshot = editor
17055        .update(cx, |editor, window, cx| {
17056            let snapshot = editor.buffer().read(cx).snapshot(cx);
17057            let range =
17058                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17059
17060            struct RenderArgs {
17061                row: MultiBufferRow,
17062                folded: bool,
17063                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17064            }
17065
17066            let crease = Crease::inline(
17067                range,
17068                FoldPlaceholder::test(),
17069                {
17070                    let toggle_callback = render_args.clone();
17071                    move |row, folded, callback, _window, _cx| {
17072                        *toggle_callback.lock() = Some(RenderArgs {
17073                            row,
17074                            folded,
17075                            callback,
17076                        });
17077                        div()
17078                    }
17079                },
17080                |_row, _folded, _window, _cx| div(),
17081            );
17082
17083            editor.insert_creases(Some(crease), cx);
17084            let snapshot = editor.snapshot(window, cx);
17085            let _div = snapshot.render_crease_toggle(
17086                MultiBufferRow(1),
17087                false,
17088                cx.entity().clone(),
17089                window,
17090                cx,
17091            );
17092            snapshot
17093        })
17094        .unwrap();
17095
17096    let render_args = render_args.lock().take().unwrap();
17097    assert_eq!(render_args.row, MultiBufferRow(1));
17098    assert!(!render_args.folded);
17099    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17100
17101    cx.update_window(*editor, |_, window, cx| {
17102        (render_args.callback)(true, window, cx)
17103    })
17104    .unwrap();
17105    let snapshot = editor
17106        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17107        .unwrap();
17108    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17109
17110    cx.update_window(*editor, |_, window, cx| {
17111        (render_args.callback)(false, window, cx)
17112    })
17113    .unwrap();
17114    let snapshot = editor
17115        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17116        .unwrap();
17117    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17118}
17119
17120#[gpui::test]
17121async fn test_input_text(cx: &mut TestAppContext) {
17122    init_test(cx, |_| {});
17123    let mut cx = EditorTestContext::new(cx).await;
17124
17125    cx.set_state(
17126        &r#"ˇone
17127        two
17128
17129        three
17130        fourˇ
17131        five
17132
17133        siˇx"#
17134            .unindent(),
17135    );
17136
17137    cx.dispatch_action(HandleInput(String::new()));
17138    cx.assert_editor_state(
17139        &r#"ˇone
17140        two
17141
17142        three
17143        fourˇ
17144        five
17145
17146        siˇx"#
17147            .unindent(),
17148    );
17149
17150    cx.dispatch_action(HandleInput("AAAA".to_string()));
17151    cx.assert_editor_state(
17152        &r#"AAAAˇone
17153        two
17154
17155        three
17156        fourAAAAˇ
17157        five
17158
17159        siAAAAˇx"#
17160            .unindent(),
17161    );
17162}
17163
17164#[gpui::test]
17165async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17166    init_test(cx, |_| {});
17167
17168    let mut cx = EditorTestContext::new(cx).await;
17169    cx.set_state(
17170        r#"let foo = 1;
17171let foo = 2;
17172let foo = 3;
17173let fooˇ = 4;
17174let foo = 5;
17175let foo = 6;
17176let foo = 7;
17177let foo = 8;
17178let foo = 9;
17179let foo = 10;
17180let foo = 11;
17181let foo = 12;
17182let foo = 13;
17183let foo = 14;
17184let foo = 15;"#,
17185    );
17186
17187    cx.update_editor(|e, window, cx| {
17188        assert_eq!(
17189            e.next_scroll_position,
17190            NextScrollCursorCenterTopBottom::Center,
17191            "Default next scroll direction is center",
17192        );
17193
17194        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17195        assert_eq!(
17196            e.next_scroll_position,
17197            NextScrollCursorCenterTopBottom::Top,
17198            "After center, next scroll direction should be top",
17199        );
17200
17201        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17202        assert_eq!(
17203            e.next_scroll_position,
17204            NextScrollCursorCenterTopBottom::Bottom,
17205            "After top, next scroll direction should be bottom",
17206        );
17207
17208        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17209        assert_eq!(
17210            e.next_scroll_position,
17211            NextScrollCursorCenterTopBottom::Center,
17212            "After bottom, scrolling should start over",
17213        );
17214
17215        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17216        assert_eq!(
17217            e.next_scroll_position,
17218            NextScrollCursorCenterTopBottom::Top,
17219            "Scrolling continues if retriggered fast enough"
17220        );
17221    });
17222
17223    cx.executor()
17224        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17225    cx.executor().run_until_parked();
17226    cx.update_editor(|e, _, _| {
17227        assert_eq!(
17228            e.next_scroll_position,
17229            NextScrollCursorCenterTopBottom::Center,
17230            "If scrolling is not triggered fast enough, it should reset"
17231        );
17232    });
17233}
17234
17235#[gpui::test]
17236async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17237    init_test(cx, |_| {});
17238    let mut cx = EditorLspTestContext::new_rust(
17239        lsp::ServerCapabilities {
17240            definition_provider: Some(lsp::OneOf::Left(true)),
17241            references_provider: Some(lsp::OneOf::Left(true)),
17242            ..lsp::ServerCapabilities::default()
17243        },
17244        cx,
17245    )
17246    .await;
17247
17248    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17249        let go_to_definition = cx
17250            .lsp
17251            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17252                move |params, _| async move {
17253                    if empty_go_to_definition {
17254                        Ok(None)
17255                    } else {
17256                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17257                            uri: params.text_document_position_params.text_document.uri,
17258                            range: lsp::Range::new(
17259                                lsp::Position::new(4, 3),
17260                                lsp::Position::new(4, 6),
17261                            ),
17262                        })))
17263                    }
17264                },
17265            );
17266        let references = cx
17267            .lsp
17268            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17269                Ok(Some(vec![lsp::Location {
17270                    uri: params.text_document_position.text_document.uri,
17271                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17272                }]))
17273            });
17274        (go_to_definition, references)
17275    };
17276
17277    cx.set_state(
17278        &r#"fn one() {
17279            let mut a = ˇtwo();
17280        }
17281
17282        fn two() {}"#
17283            .unindent(),
17284    );
17285    set_up_lsp_handlers(false, &mut cx);
17286    let navigated = cx
17287        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17288        .await
17289        .expect("Failed to navigate to definition");
17290    assert_eq!(
17291        navigated,
17292        Navigated::Yes,
17293        "Should have navigated to definition from the GetDefinition response"
17294    );
17295    cx.assert_editor_state(
17296        &r#"fn one() {
17297            let mut a = two();
17298        }
17299
17300        fn «twoˇ»() {}"#
17301            .unindent(),
17302    );
17303
17304    let editors = cx.update_workspace(|workspace, _, cx| {
17305        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17306    });
17307    cx.update_editor(|_, _, test_editor_cx| {
17308        assert_eq!(
17309            editors.len(),
17310            1,
17311            "Initially, only one, test, editor should be open in the workspace"
17312        );
17313        assert_eq!(
17314            test_editor_cx.entity(),
17315            editors.last().expect("Asserted len is 1").clone()
17316        );
17317    });
17318
17319    set_up_lsp_handlers(true, &mut cx);
17320    let navigated = cx
17321        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17322        .await
17323        .expect("Failed to navigate to lookup references");
17324    assert_eq!(
17325        navigated,
17326        Navigated::Yes,
17327        "Should have navigated to references as a fallback after empty GoToDefinition response"
17328    );
17329    // We should not change the selections in the existing file,
17330    // if opening another milti buffer with the references
17331    cx.assert_editor_state(
17332        &r#"fn one() {
17333            let mut a = two();
17334        }
17335
17336        fn «twoˇ»() {}"#
17337            .unindent(),
17338    );
17339    let editors = cx.update_workspace(|workspace, _, cx| {
17340        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17341    });
17342    cx.update_editor(|_, _, test_editor_cx| {
17343        assert_eq!(
17344            editors.len(),
17345            2,
17346            "After falling back to references search, we open a new editor with the results"
17347        );
17348        let references_fallback_text = editors
17349            .into_iter()
17350            .find(|new_editor| *new_editor != test_editor_cx.entity())
17351            .expect("Should have one non-test editor now")
17352            .read(test_editor_cx)
17353            .text(test_editor_cx);
17354        assert_eq!(
17355            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17356            "Should use the range from the references response and not the GoToDefinition one"
17357        );
17358    });
17359}
17360
17361#[gpui::test]
17362async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17363    init_test(cx, |_| {});
17364    cx.update(|cx| {
17365        let mut editor_settings = EditorSettings::get_global(cx).clone();
17366        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17367        EditorSettings::override_global(editor_settings, cx);
17368    });
17369    let mut cx = EditorLspTestContext::new_rust(
17370        lsp::ServerCapabilities {
17371            definition_provider: Some(lsp::OneOf::Left(true)),
17372            references_provider: Some(lsp::OneOf::Left(true)),
17373            ..lsp::ServerCapabilities::default()
17374        },
17375        cx,
17376    )
17377    .await;
17378    let original_state = r#"fn one() {
17379        let mut a = ˇtwo();
17380    }
17381
17382    fn two() {}"#
17383        .unindent();
17384    cx.set_state(&original_state);
17385
17386    let mut go_to_definition = cx
17387        .lsp
17388        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17389            move |_, _| async move { Ok(None) },
17390        );
17391    let _references = cx
17392        .lsp
17393        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17394            panic!("Should not call for references with no go to definition fallback")
17395        });
17396
17397    let navigated = cx
17398        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17399        .await
17400        .expect("Failed to navigate to lookup references");
17401    go_to_definition
17402        .next()
17403        .await
17404        .expect("Should have called the go_to_definition handler");
17405
17406    assert_eq!(
17407        navigated,
17408        Navigated::No,
17409        "Should have navigated to references as a fallback after empty GoToDefinition response"
17410    );
17411    cx.assert_editor_state(&original_state);
17412    let editors = cx.update_workspace(|workspace, _, cx| {
17413        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17414    });
17415    cx.update_editor(|_, _, _| {
17416        assert_eq!(
17417            editors.len(),
17418            1,
17419            "After unsuccessful fallback, no other editor should have been opened"
17420        );
17421    });
17422}
17423
17424#[gpui::test]
17425async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17426    init_test(cx, |_| {});
17427
17428    let language = Arc::new(Language::new(
17429        LanguageConfig::default(),
17430        Some(tree_sitter_rust::LANGUAGE.into()),
17431    ));
17432
17433    let text = r#"
17434        #[cfg(test)]
17435        mod tests() {
17436            #[test]
17437            fn runnable_1() {
17438                let a = 1;
17439            }
17440
17441            #[test]
17442            fn runnable_2() {
17443                let a = 1;
17444                let b = 2;
17445            }
17446        }
17447    "#
17448    .unindent();
17449
17450    let fs = FakeFs::new(cx.executor());
17451    fs.insert_file("/file.rs", Default::default()).await;
17452
17453    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17454    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17455    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17456    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17457    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17458
17459    let editor = cx.new_window_entity(|window, cx| {
17460        Editor::new(
17461            EditorMode::full(),
17462            multi_buffer,
17463            Some(project.clone()),
17464            window,
17465            cx,
17466        )
17467    });
17468
17469    editor.update_in(cx, |editor, window, cx| {
17470        let snapshot = editor.buffer().read(cx).snapshot(cx);
17471        editor.tasks.insert(
17472            (buffer.read(cx).remote_id(), 3),
17473            RunnableTasks {
17474                templates: vec![],
17475                offset: snapshot.anchor_before(43),
17476                column: 0,
17477                extra_variables: HashMap::default(),
17478                context_range: BufferOffset(43)..BufferOffset(85),
17479            },
17480        );
17481        editor.tasks.insert(
17482            (buffer.read(cx).remote_id(), 8),
17483            RunnableTasks {
17484                templates: vec![],
17485                offset: snapshot.anchor_before(86),
17486                column: 0,
17487                extra_variables: HashMap::default(),
17488                context_range: BufferOffset(86)..BufferOffset(191),
17489            },
17490        );
17491
17492        // Test finding task when cursor is inside function body
17493        editor.change_selections(None, window, cx, |s| {
17494            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17495        });
17496        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17497        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17498
17499        // Test finding task when cursor is on function name
17500        editor.change_selections(None, window, cx, |s| {
17501            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17502        });
17503        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17504        assert_eq!(row, 8, "Should find task when cursor is on function name");
17505    });
17506}
17507
17508#[gpui::test]
17509async fn test_folding_buffers(cx: &mut TestAppContext) {
17510    init_test(cx, |_| {});
17511
17512    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17513    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17514    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17515
17516    let fs = FakeFs::new(cx.executor());
17517    fs.insert_tree(
17518        path!("/a"),
17519        json!({
17520            "first.rs": sample_text_1,
17521            "second.rs": sample_text_2,
17522            "third.rs": sample_text_3,
17523        }),
17524    )
17525    .await;
17526    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17527    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17528    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17529    let worktree = project.update(cx, |project, cx| {
17530        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17531        assert_eq!(worktrees.len(), 1);
17532        worktrees.pop().unwrap()
17533    });
17534    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17535
17536    let buffer_1 = project
17537        .update(cx, |project, cx| {
17538            project.open_buffer((worktree_id, "first.rs"), cx)
17539        })
17540        .await
17541        .unwrap();
17542    let buffer_2 = project
17543        .update(cx, |project, cx| {
17544            project.open_buffer((worktree_id, "second.rs"), cx)
17545        })
17546        .await
17547        .unwrap();
17548    let buffer_3 = project
17549        .update(cx, |project, cx| {
17550            project.open_buffer((worktree_id, "third.rs"), cx)
17551        })
17552        .await
17553        .unwrap();
17554
17555    let multi_buffer = cx.new(|cx| {
17556        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17557        multi_buffer.push_excerpts(
17558            buffer_1.clone(),
17559            [
17560                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17561                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17562                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17563            ],
17564            cx,
17565        );
17566        multi_buffer.push_excerpts(
17567            buffer_2.clone(),
17568            [
17569                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17570                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17571                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17572            ],
17573            cx,
17574        );
17575        multi_buffer.push_excerpts(
17576            buffer_3.clone(),
17577            [
17578                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17579                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17580                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17581            ],
17582            cx,
17583        );
17584        multi_buffer
17585    });
17586    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17587        Editor::new(
17588            EditorMode::full(),
17589            multi_buffer.clone(),
17590            Some(project.clone()),
17591            window,
17592            cx,
17593        )
17594    });
17595
17596    assert_eq!(
17597        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17598        "\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",
17599    );
17600
17601    multi_buffer_editor.update(cx, |editor, cx| {
17602        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17603    });
17604    assert_eq!(
17605        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17606        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17607        "After folding the first buffer, its text should not be displayed"
17608    );
17609
17610    multi_buffer_editor.update(cx, |editor, cx| {
17611        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17612    });
17613    assert_eq!(
17614        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17615        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17616        "After folding the second buffer, its text should not be displayed"
17617    );
17618
17619    multi_buffer_editor.update(cx, |editor, cx| {
17620        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17621    });
17622    assert_eq!(
17623        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17624        "\n\n\n\n\n",
17625        "After folding the third buffer, its text should not be displayed"
17626    );
17627
17628    // Emulate selection inside the fold logic, that should work
17629    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17630        editor
17631            .snapshot(window, cx)
17632            .next_line_boundary(Point::new(0, 4));
17633    });
17634
17635    multi_buffer_editor.update(cx, |editor, cx| {
17636        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17637    });
17638    assert_eq!(
17639        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17640        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17641        "After unfolding the second buffer, its text should be displayed"
17642    );
17643
17644    // Typing inside of buffer 1 causes that buffer to be unfolded.
17645    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17646        assert_eq!(
17647            multi_buffer
17648                .read(cx)
17649                .snapshot(cx)
17650                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17651                .collect::<String>(),
17652            "bbbb"
17653        );
17654        editor.change_selections(None, window, cx, |selections| {
17655            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17656        });
17657        editor.handle_input("B", window, cx);
17658    });
17659
17660    assert_eq!(
17661        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17662        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17663        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17664    );
17665
17666    multi_buffer_editor.update(cx, |editor, cx| {
17667        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17668    });
17669    assert_eq!(
17670        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17671        "\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",
17672        "After unfolding the all buffers, all original text should be displayed"
17673    );
17674}
17675
17676#[gpui::test]
17677async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17678    init_test(cx, |_| {});
17679
17680    let sample_text_1 = "1111\n2222\n3333".to_string();
17681    let sample_text_2 = "4444\n5555\n6666".to_string();
17682    let sample_text_3 = "7777\n8888\n9999".to_string();
17683
17684    let fs = FakeFs::new(cx.executor());
17685    fs.insert_tree(
17686        path!("/a"),
17687        json!({
17688            "first.rs": sample_text_1,
17689            "second.rs": sample_text_2,
17690            "third.rs": sample_text_3,
17691        }),
17692    )
17693    .await;
17694    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17695    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17696    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17697    let worktree = project.update(cx, |project, cx| {
17698        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17699        assert_eq!(worktrees.len(), 1);
17700        worktrees.pop().unwrap()
17701    });
17702    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17703
17704    let buffer_1 = project
17705        .update(cx, |project, cx| {
17706            project.open_buffer((worktree_id, "first.rs"), cx)
17707        })
17708        .await
17709        .unwrap();
17710    let buffer_2 = project
17711        .update(cx, |project, cx| {
17712            project.open_buffer((worktree_id, "second.rs"), cx)
17713        })
17714        .await
17715        .unwrap();
17716    let buffer_3 = project
17717        .update(cx, |project, cx| {
17718            project.open_buffer((worktree_id, "third.rs"), cx)
17719        })
17720        .await
17721        .unwrap();
17722
17723    let multi_buffer = cx.new(|cx| {
17724        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17725        multi_buffer.push_excerpts(
17726            buffer_1.clone(),
17727            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17728            cx,
17729        );
17730        multi_buffer.push_excerpts(
17731            buffer_2.clone(),
17732            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17733            cx,
17734        );
17735        multi_buffer.push_excerpts(
17736            buffer_3.clone(),
17737            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17738            cx,
17739        );
17740        multi_buffer
17741    });
17742
17743    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17744        Editor::new(
17745            EditorMode::full(),
17746            multi_buffer,
17747            Some(project.clone()),
17748            window,
17749            cx,
17750        )
17751    });
17752
17753    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17754    assert_eq!(
17755        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17756        full_text,
17757    );
17758
17759    multi_buffer_editor.update(cx, |editor, cx| {
17760        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17761    });
17762    assert_eq!(
17763        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17764        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17765        "After folding the first buffer, its text should not be displayed"
17766    );
17767
17768    multi_buffer_editor.update(cx, |editor, cx| {
17769        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17770    });
17771
17772    assert_eq!(
17773        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17774        "\n\n\n\n\n\n7777\n8888\n9999",
17775        "After folding the second buffer, its text should not be displayed"
17776    );
17777
17778    multi_buffer_editor.update(cx, |editor, cx| {
17779        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17780    });
17781    assert_eq!(
17782        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17783        "\n\n\n\n\n",
17784        "After folding the third buffer, its text should not be displayed"
17785    );
17786
17787    multi_buffer_editor.update(cx, |editor, cx| {
17788        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17789    });
17790    assert_eq!(
17791        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17792        "\n\n\n\n4444\n5555\n6666\n\n",
17793        "After unfolding the second buffer, its text should be displayed"
17794    );
17795
17796    multi_buffer_editor.update(cx, |editor, cx| {
17797        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17798    });
17799    assert_eq!(
17800        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17801        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17802        "After unfolding the first buffer, its text should be displayed"
17803    );
17804
17805    multi_buffer_editor.update(cx, |editor, cx| {
17806        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17807    });
17808    assert_eq!(
17809        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17810        full_text,
17811        "After unfolding all buffers, all original text should be displayed"
17812    );
17813}
17814
17815#[gpui::test]
17816async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17817    init_test(cx, |_| {});
17818
17819    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17820
17821    let fs = FakeFs::new(cx.executor());
17822    fs.insert_tree(
17823        path!("/a"),
17824        json!({
17825            "main.rs": sample_text,
17826        }),
17827    )
17828    .await;
17829    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17830    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17831    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17832    let worktree = project.update(cx, |project, cx| {
17833        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17834        assert_eq!(worktrees.len(), 1);
17835        worktrees.pop().unwrap()
17836    });
17837    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17838
17839    let buffer_1 = project
17840        .update(cx, |project, cx| {
17841            project.open_buffer((worktree_id, "main.rs"), cx)
17842        })
17843        .await
17844        .unwrap();
17845
17846    let multi_buffer = cx.new(|cx| {
17847        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17848        multi_buffer.push_excerpts(
17849            buffer_1.clone(),
17850            [ExcerptRange::new(
17851                Point::new(0, 0)
17852                    ..Point::new(
17853                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17854                        0,
17855                    ),
17856            )],
17857            cx,
17858        );
17859        multi_buffer
17860    });
17861    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17862        Editor::new(
17863            EditorMode::full(),
17864            multi_buffer,
17865            Some(project.clone()),
17866            window,
17867            cx,
17868        )
17869    });
17870
17871    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17872    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17873        enum TestHighlight {}
17874        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17875        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17876        editor.highlight_text::<TestHighlight>(
17877            vec![highlight_range.clone()],
17878            HighlightStyle::color(Hsla::green()),
17879            cx,
17880        );
17881        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17882    });
17883
17884    let full_text = format!("\n\n{sample_text}");
17885    assert_eq!(
17886        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17887        full_text,
17888    );
17889}
17890
17891#[gpui::test]
17892async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17893    init_test(cx, |_| {});
17894    cx.update(|cx| {
17895        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17896            "keymaps/default-linux.json",
17897            cx,
17898        )
17899        .unwrap();
17900        cx.bind_keys(default_key_bindings);
17901    });
17902
17903    let (editor, cx) = cx.add_window_view(|window, cx| {
17904        let multi_buffer = MultiBuffer::build_multi(
17905            [
17906                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17907                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17908                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17909                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17910            ],
17911            cx,
17912        );
17913        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17914
17915        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17916        // fold all but the second buffer, so that we test navigating between two
17917        // adjacent folded buffers, as well as folded buffers at the start and
17918        // end the multibuffer
17919        editor.fold_buffer(buffer_ids[0], cx);
17920        editor.fold_buffer(buffer_ids[2], cx);
17921        editor.fold_buffer(buffer_ids[3], cx);
17922
17923        editor
17924    });
17925    cx.simulate_resize(size(px(1000.), px(1000.)));
17926
17927    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17928    cx.assert_excerpts_with_selections(indoc! {"
17929        [EXCERPT]
17930        ˇ[FOLDED]
17931        [EXCERPT]
17932        a1
17933        b1
17934        [EXCERPT]
17935        [FOLDED]
17936        [EXCERPT]
17937        [FOLDED]
17938        "
17939    });
17940    cx.simulate_keystroke("down");
17941    cx.assert_excerpts_with_selections(indoc! {"
17942        [EXCERPT]
17943        [FOLDED]
17944        [EXCERPT]
17945        ˇa1
17946        b1
17947        [EXCERPT]
17948        [FOLDED]
17949        [EXCERPT]
17950        [FOLDED]
17951        "
17952    });
17953    cx.simulate_keystroke("down");
17954    cx.assert_excerpts_with_selections(indoc! {"
17955        [EXCERPT]
17956        [FOLDED]
17957        [EXCERPT]
17958        a1
17959        ˇb1
17960        [EXCERPT]
17961        [FOLDED]
17962        [EXCERPT]
17963        [FOLDED]
17964        "
17965    });
17966    cx.simulate_keystroke("down");
17967    cx.assert_excerpts_with_selections(indoc! {"
17968        [EXCERPT]
17969        [FOLDED]
17970        [EXCERPT]
17971        a1
17972        b1
17973        ˇ[EXCERPT]
17974        [FOLDED]
17975        [EXCERPT]
17976        [FOLDED]
17977        "
17978    });
17979    cx.simulate_keystroke("down");
17980    cx.assert_excerpts_with_selections(indoc! {"
17981        [EXCERPT]
17982        [FOLDED]
17983        [EXCERPT]
17984        a1
17985        b1
17986        [EXCERPT]
17987        ˇ[FOLDED]
17988        [EXCERPT]
17989        [FOLDED]
17990        "
17991    });
17992    for _ in 0..5 {
17993        cx.simulate_keystroke("down");
17994        cx.assert_excerpts_with_selections(indoc! {"
17995            [EXCERPT]
17996            [FOLDED]
17997            [EXCERPT]
17998            a1
17999            b1
18000            [EXCERPT]
18001            [FOLDED]
18002            [EXCERPT]
18003            ˇ[FOLDED]
18004            "
18005        });
18006    }
18007
18008    cx.simulate_keystroke("up");
18009    cx.assert_excerpts_with_selections(indoc! {"
18010        [EXCERPT]
18011        [FOLDED]
18012        [EXCERPT]
18013        a1
18014        b1
18015        [EXCERPT]
18016        ˇ[FOLDED]
18017        [EXCERPT]
18018        [FOLDED]
18019        "
18020    });
18021    cx.simulate_keystroke("up");
18022    cx.assert_excerpts_with_selections(indoc! {"
18023        [EXCERPT]
18024        [FOLDED]
18025        [EXCERPT]
18026        a1
18027        b1
18028        ˇ[EXCERPT]
18029        [FOLDED]
18030        [EXCERPT]
18031        [FOLDED]
18032        "
18033    });
18034    cx.simulate_keystroke("up");
18035    cx.assert_excerpts_with_selections(indoc! {"
18036        [EXCERPT]
18037        [FOLDED]
18038        [EXCERPT]
18039        a1
18040        ˇb1
18041        [EXCERPT]
18042        [FOLDED]
18043        [EXCERPT]
18044        [FOLDED]
18045        "
18046    });
18047    cx.simulate_keystroke("up");
18048    cx.assert_excerpts_with_selections(indoc! {"
18049        [EXCERPT]
18050        [FOLDED]
18051        [EXCERPT]
18052        ˇa1
18053        b1
18054        [EXCERPT]
18055        [FOLDED]
18056        [EXCERPT]
18057        [FOLDED]
18058        "
18059    });
18060    for _ in 0..5 {
18061        cx.simulate_keystroke("up");
18062        cx.assert_excerpts_with_selections(indoc! {"
18063            [EXCERPT]
18064            ˇ[FOLDED]
18065            [EXCERPT]
18066            a1
18067            b1
18068            [EXCERPT]
18069            [FOLDED]
18070            [EXCERPT]
18071            [FOLDED]
18072            "
18073        });
18074    }
18075}
18076
18077#[gpui::test]
18078async fn test_inline_completion_text(cx: &mut TestAppContext) {
18079    init_test(cx, |_| {});
18080
18081    // Simple insertion
18082    assert_highlighted_edits(
18083        "Hello, world!",
18084        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18085        true,
18086        cx,
18087        |highlighted_edits, cx| {
18088            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18089            assert_eq!(highlighted_edits.highlights.len(), 1);
18090            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18091            assert_eq!(
18092                highlighted_edits.highlights[0].1.background_color,
18093                Some(cx.theme().status().created_background)
18094            );
18095        },
18096    )
18097    .await;
18098
18099    // Replacement
18100    assert_highlighted_edits(
18101        "This is a test.",
18102        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18103        false,
18104        cx,
18105        |highlighted_edits, cx| {
18106            assert_eq!(highlighted_edits.text, "That is a test.");
18107            assert_eq!(highlighted_edits.highlights.len(), 1);
18108            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18109            assert_eq!(
18110                highlighted_edits.highlights[0].1.background_color,
18111                Some(cx.theme().status().created_background)
18112            );
18113        },
18114    )
18115    .await;
18116
18117    // Multiple edits
18118    assert_highlighted_edits(
18119        "Hello, world!",
18120        vec![
18121            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18122            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18123        ],
18124        false,
18125        cx,
18126        |highlighted_edits, cx| {
18127            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18128            assert_eq!(highlighted_edits.highlights.len(), 2);
18129            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18130            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18131            assert_eq!(
18132                highlighted_edits.highlights[0].1.background_color,
18133                Some(cx.theme().status().created_background)
18134            );
18135            assert_eq!(
18136                highlighted_edits.highlights[1].1.background_color,
18137                Some(cx.theme().status().created_background)
18138            );
18139        },
18140    )
18141    .await;
18142
18143    // Multiple lines with edits
18144    assert_highlighted_edits(
18145        "First line\nSecond line\nThird line\nFourth line",
18146        vec![
18147            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18148            (
18149                Point::new(2, 0)..Point::new(2, 10),
18150                "New third line".to_string(),
18151            ),
18152            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18153        ],
18154        false,
18155        cx,
18156        |highlighted_edits, cx| {
18157            assert_eq!(
18158                highlighted_edits.text,
18159                "Second modified\nNew third line\nFourth updated line"
18160            );
18161            assert_eq!(highlighted_edits.highlights.len(), 3);
18162            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18163            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18164            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18165            for highlight in &highlighted_edits.highlights {
18166                assert_eq!(
18167                    highlight.1.background_color,
18168                    Some(cx.theme().status().created_background)
18169                );
18170            }
18171        },
18172    )
18173    .await;
18174}
18175
18176#[gpui::test]
18177async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18178    init_test(cx, |_| {});
18179
18180    // Deletion
18181    assert_highlighted_edits(
18182        "Hello, world!",
18183        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18184        true,
18185        cx,
18186        |highlighted_edits, cx| {
18187            assert_eq!(highlighted_edits.text, "Hello, world!");
18188            assert_eq!(highlighted_edits.highlights.len(), 1);
18189            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18190            assert_eq!(
18191                highlighted_edits.highlights[0].1.background_color,
18192                Some(cx.theme().status().deleted_background)
18193            );
18194        },
18195    )
18196    .await;
18197
18198    // Insertion
18199    assert_highlighted_edits(
18200        "Hello, world!",
18201        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18202        true,
18203        cx,
18204        |highlighted_edits, cx| {
18205            assert_eq!(highlighted_edits.highlights.len(), 1);
18206            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18207            assert_eq!(
18208                highlighted_edits.highlights[0].1.background_color,
18209                Some(cx.theme().status().created_background)
18210            );
18211        },
18212    )
18213    .await;
18214}
18215
18216async fn assert_highlighted_edits(
18217    text: &str,
18218    edits: Vec<(Range<Point>, String)>,
18219    include_deletions: bool,
18220    cx: &mut TestAppContext,
18221    assertion_fn: impl Fn(HighlightedText, &App),
18222) {
18223    let window = cx.add_window(|window, cx| {
18224        let buffer = MultiBuffer::build_simple(text, cx);
18225        Editor::new(EditorMode::full(), buffer, None, window, cx)
18226    });
18227    let cx = &mut VisualTestContext::from_window(*window, cx);
18228
18229    let (buffer, snapshot) = window
18230        .update(cx, |editor, _window, cx| {
18231            (
18232                editor.buffer().clone(),
18233                editor.buffer().read(cx).snapshot(cx),
18234            )
18235        })
18236        .unwrap();
18237
18238    let edits = edits
18239        .into_iter()
18240        .map(|(range, edit)| {
18241            (
18242                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18243                edit,
18244            )
18245        })
18246        .collect::<Vec<_>>();
18247
18248    let text_anchor_edits = edits
18249        .clone()
18250        .into_iter()
18251        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18252        .collect::<Vec<_>>();
18253
18254    let edit_preview = window
18255        .update(cx, |_, _window, cx| {
18256            buffer
18257                .read(cx)
18258                .as_singleton()
18259                .unwrap()
18260                .read(cx)
18261                .preview_edits(text_anchor_edits.into(), cx)
18262        })
18263        .unwrap()
18264        .await;
18265
18266    cx.update(|_window, cx| {
18267        let highlighted_edits = inline_completion_edit_text(
18268            &snapshot.as_singleton().unwrap().2,
18269            &edits,
18270            &edit_preview,
18271            include_deletions,
18272            cx,
18273        );
18274        assertion_fn(highlighted_edits, cx)
18275    });
18276}
18277
18278#[track_caller]
18279fn assert_breakpoint(
18280    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18281    path: &Arc<Path>,
18282    expected: Vec<(u32, Breakpoint)>,
18283) {
18284    if expected.len() == 0usize {
18285        assert!(!breakpoints.contains_key(path), "{}", path.display());
18286    } else {
18287        let mut breakpoint = breakpoints
18288            .get(path)
18289            .unwrap()
18290            .into_iter()
18291            .map(|breakpoint| {
18292                (
18293                    breakpoint.row,
18294                    Breakpoint {
18295                        message: breakpoint.message.clone(),
18296                        state: breakpoint.state,
18297                        condition: breakpoint.condition.clone(),
18298                        hit_condition: breakpoint.hit_condition.clone(),
18299                    },
18300                )
18301            })
18302            .collect::<Vec<_>>();
18303
18304        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18305
18306        assert_eq!(expected, breakpoint);
18307    }
18308}
18309
18310fn add_log_breakpoint_at_cursor(
18311    editor: &mut Editor,
18312    log_message: &str,
18313    window: &mut Window,
18314    cx: &mut Context<Editor>,
18315) {
18316    let (anchor, bp) = editor
18317        .breakpoints_at_cursors(window, cx)
18318        .first()
18319        .and_then(|(anchor, bp)| {
18320            if let Some(bp) = bp {
18321                Some((*anchor, bp.clone()))
18322            } else {
18323                None
18324            }
18325        })
18326        .unwrap_or_else(|| {
18327            let cursor_position: Point = editor.selections.newest(cx).head();
18328
18329            let breakpoint_position = editor
18330                .snapshot(window, cx)
18331                .display_snapshot
18332                .buffer_snapshot
18333                .anchor_before(Point::new(cursor_position.row, 0));
18334
18335            (breakpoint_position, Breakpoint::new_log(&log_message))
18336        });
18337
18338    editor.edit_breakpoint_at_anchor(
18339        anchor,
18340        bp,
18341        BreakpointEditAction::EditLogMessage(log_message.into()),
18342        cx,
18343    );
18344}
18345
18346#[gpui::test]
18347async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18348    init_test(cx, |_| {});
18349
18350    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18351    let fs = FakeFs::new(cx.executor());
18352    fs.insert_tree(
18353        path!("/a"),
18354        json!({
18355            "main.rs": sample_text,
18356        }),
18357    )
18358    .await;
18359    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18360    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18361    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18362
18363    let fs = FakeFs::new(cx.executor());
18364    fs.insert_tree(
18365        path!("/a"),
18366        json!({
18367            "main.rs": sample_text,
18368        }),
18369    )
18370    .await;
18371    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18372    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18373    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18374    let worktree_id = workspace
18375        .update(cx, |workspace, _window, cx| {
18376            workspace.project().update(cx, |project, cx| {
18377                project.worktrees(cx).next().unwrap().read(cx).id()
18378            })
18379        })
18380        .unwrap();
18381
18382    let buffer = project
18383        .update(cx, |project, cx| {
18384            project.open_buffer((worktree_id, "main.rs"), cx)
18385        })
18386        .await
18387        .unwrap();
18388
18389    let (editor, cx) = cx.add_window_view(|window, cx| {
18390        Editor::new(
18391            EditorMode::full(),
18392            MultiBuffer::build_from_buffer(buffer, cx),
18393            Some(project.clone()),
18394            window,
18395            cx,
18396        )
18397    });
18398
18399    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18400    let abs_path = project.read_with(cx, |project, cx| {
18401        project
18402            .absolute_path(&project_path, cx)
18403            .map(|path_buf| Arc::from(path_buf.to_owned()))
18404            .unwrap()
18405    });
18406
18407    // assert we can add breakpoint on the first line
18408    editor.update_in(cx, |editor, window, cx| {
18409        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18410        editor.move_to_end(&MoveToEnd, window, cx);
18411        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18412    });
18413
18414    let breakpoints = editor.update(cx, |editor, cx| {
18415        editor
18416            .breakpoint_store()
18417            .as_ref()
18418            .unwrap()
18419            .read(cx)
18420            .all_breakpoints(cx)
18421            .clone()
18422    });
18423
18424    assert_eq!(1, breakpoints.len());
18425    assert_breakpoint(
18426        &breakpoints,
18427        &abs_path,
18428        vec![
18429            (0, Breakpoint::new_standard()),
18430            (3, Breakpoint::new_standard()),
18431        ],
18432    );
18433
18434    editor.update_in(cx, |editor, window, cx| {
18435        editor.move_to_beginning(&MoveToBeginning, window, cx);
18436        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18437    });
18438
18439    let breakpoints = editor.update(cx, |editor, cx| {
18440        editor
18441            .breakpoint_store()
18442            .as_ref()
18443            .unwrap()
18444            .read(cx)
18445            .all_breakpoints(cx)
18446            .clone()
18447    });
18448
18449    assert_eq!(1, breakpoints.len());
18450    assert_breakpoint(
18451        &breakpoints,
18452        &abs_path,
18453        vec![(3, Breakpoint::new_standard())],
18454    );
18455
18456    editor.update_in(cx, |editor, window, cx| {
18457        editor.move_to_end(&MoveToEnd, window, cx);
18458        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18459    });
18460
18461    let breakpoints = editor.update(cx, |editor, cx| {
18462        editor
18463            .breakpoint_store()
18464            .as_ref()
18465            .unwrap()
18466            .read(cx)
18467            .all_breakpoints(cx)
18468            .clone()
18469    });
18470
18471    assert_eq!(0, breakpoints.len());
18472    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18473}
18474
18475#[gpui::test]
18476async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18477    init_test(cx, |_| {});
18478
18479    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18480
18481    let fs = FakeFs::new(cx.executor());
18482    fs.insert_tree(
18483        path!("/a"),
18484        json!({
18485            "main.rs": sample_text,
18486        }),
18487    )
18488    .await;
18489    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18490    let (workspace, cx) =
18491        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18492
18493    let worktree_id = workspace.update(cx, |workspace, cx| {
18494        workspace.project().update(cx, |project, cx| {
18495            project.worktrees(cx).next().unwrap().read(cx).id()
18496        })
18497    });
18498
18499    let buffer = project
18500        .update(cx, |project, cx| {
18501            project.open_buffer((worktree_id, "main.rs"), cx)
18502        })
18503        .await
18504        .unwrap();
18505
18506    let (editor, cx) = cx.add_window_view(|window, cx| {
18507        Editor::new(
18508            EditorMode::full(),
18509            MultiBuffer::build_from_buffer(buffer, cx),
18510            Some(project.clone()),
18511            window,
18512            cx,
18513        )
18514    });
18515
18516    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18517    let abs_path = project.read_with(cx, |project, cx| {
18518        project
18519            .absolute_path(&project_path, cx)
18520            .map(|path_buf| Arc::from(path_buf.to_owned()))
18521            .unwrap()
18522    });
18523
18524    editor.update_in(cx, |editor, window, cx| {
18525        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18526    });
18527
18528    let breakpoints = editor.update(cx, |editor, cx| {
18529        editor
18530            .breakpoint_store()
18531            .as_ref()
18532            .unwrap()
18533            .read(cx)
18534            .all_breakpoints(cx)
18535            .clone()
18536    });
18537
18538    assert_breakpoint(
18539        &breakpoints,
18540        &abs_path,
18541        vec![(0, Breakpoint::new_log("hello world"))],
18542    );
18543
18544    // Removing a log message from a log breakpoint should remove it
18545    editor.update_in(cx, |editor, window, cx| {
18546        add_log_breakpoint_at_cursor(editor, "", window, cx);
18547    });
18548
18549    let breakpoints = editor.update(cx, |editor, cx| {
18550        editor
18551            .breakpoint_store()
18552            .as_ref()
18553            .unwrap()
18554            .read(cx)
18555            .all_breakpoints(cx)
18556            .clone()
18557    });
18558
18559    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18560
18561    editor.update_in(cx, |editor, window, cx| {
18562        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18563        editor.move_to_end(&MoveToEnd, window, cx);
18564        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18565        // Not adding a log message to a standard breakpoint shouldn't remove it
18566        add_log_breakpoint_at_cursor(editor, "", window, cx);
18567    });
18568
18569    let breakpoints = editor.update(cx, |editor, cx| {
18570        editor
18571            .breakpoint_store()
18572            .as_ref()
18573            .unwrap()
18574            .read(cx)
18575            .all_breakpoints(cx)
18576            .clone()
18577    });
18578
18579    assert_breakpoint(
18580        &breakpoints,
18581        &abs_path,
18582        vec![
18583            (0, Breakpoint::new_standard()),
18584            (3, Breakpoint::new_standard()),
18585        ],
18586    );
18587
18588    editor.update_in(cx, |editor, window, cx| {
18589        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18590    });
18591
18592    let breakpoints = editor.update(cx, |editor, cx| {
18593        editor
18594            .breakpoint_store()
18595            .as_ref()
18596            .unwrap()
18597            .read(cx)
18598            .all_breakpoints(cx)
18599            .clone()
18600    });
18601
18602    assert_breakpoint(
18603        &breakpoints,
18604        &abs_path,
18605        vec![
18606            (0, Breakpoint::new_standard()),
18607            (3, Breakpoint::new_log("hello world")),
18608        ],
18609    );
18610
18611    editor.update_in(cx, |editor, window, cx| {
18612        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18613    });
18614
18615    let breakpoints = editor.update(cx, |editor, cx| {
18616        editor
18617            .breakpoint_store()
18618            .as_ref()
18619            .unwrap()
18620            .read(cx)
18621            .all_breakpoints(cx)
18622            .clone()
18623    });
18624
18625    assert_breakpoint(
18626        &breakpoints,
18627        &abs_path,
18628        vec![
18629            (0, Breakpoint::new_standard()),
18630            (3, Breakpoint::new_log("hello Earth!!")),
18631        ],
18632    );
18633}
18634
18635/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18636/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18637/// or when breakpoints were placed out of order. This tests for a regression too
18638#[gpui::test]
18639async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18640    init_test(cx, |_| {});
18641
18642    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18643    let fs = FakeFs::new(cx.executor());
18644    fs.insert_tree(
18645        path!("/a"),
18646        json!({
18647            "main.rs": sample_text,
18648        }),
18649    )
18650    .await;
18651    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18652    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18653    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18654
18655    let fs = FakeFs::new(cx.executor());
18656    fs.insert_tree(
18657        path!("/a"),
18658        json!({
18659            "main.rs": sample_text,
18660        }),
18661    )
18662    .await;
18663    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18664    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18665    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18666    let worktree_id = workspace
18667        .update(cx, |workspace, _window, cx| {
18668            workspace.project().update(cx, |project, cx| {
18669                project.worktrees(cx).next().unwrap().read(cx).id()
18670            })
18671        })
18672        .unwrap();
18673
18674    let buffer = project
18675        .update(cx, |project, cx| {
18676            project.open_buffer((worktree_id, "main.rs"), cx)
18677        })
18678        .await
18679        .unwrap();
18680
18681    let (editor, cx) = cx.add_window_view(|window, cx| {
18682        Editor::new(
18683            EditorMode::full(),
18684            MultiBuffer::build_from_buffer(buffer, cx),
18685            Some(project.clone()),
18686            window,
18687            cx,
18688        )
18689    });
18690
18691    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18692    let abs_path = project.read_with(cx, |project, cx| {
18693        project
18694            .absolute_path(&project_path, cx)
18695            .map(|path_buf| Arc::from(path_buf.to_owned()))
18696            .unwrap()
18697    });
18698
18699    // assert we can add breakpoint on the first line
18700    editor.update_in(cx, |editor, window, cx| {
18701        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18702        editor.move_to_end(&MoveToEnd, window, cx);
18703        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18704        editor.move_up(&MoveUp, window, cx);
18705        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18706    });
18707
18708    let breakpoints = editor.update(cx, |editor, cx| {
18709        editor
18710            .breakpoint_store()
18711            .as_ref()
18712            .unwrap()
18713            .read(cx)
18714            .all_breakpoints(cx)
18715            .clone()
18716    });
18717
18718    assert_eq!(1, breakpoints.len());
18719    assert_breakpoint(
18720        &breakpoints,
18721        &abs_path,
18722        vec![
18723            (0, Breakpoint::new_standard()),
18724            (2, Breakpoint::new_standard()),
18725            (3, Breakpoint::new_standard()),
18726        ],
18727    );
18728
18729    editor.update_in(cx, |editor, window, cx| {
18730        editor.move_to_beginning(&MoveToBeginning, window, cx);
18731        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18732        editor.move_to_end(&MoveToEnd, window, cx);
18733        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18734        // Disabling a breakpoint that doesn't exist should do nothing
18735        editor.move_up(&MoveUp, window, cx);
18736        editor.move_up(&MoveUp, window, cx);
18737        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18738    });
18739
18740    let breakpoints = editor.update(cx, |editor, cx| {
18741        editor
18742            .breakpoint_store()
18743            .as_ref()
18744            .unwrap()
18745            .read(cx)
18746            .all_breakpoints(cx)
18747            .clone()
18748    });
18749
18750    let disable_breakpoint = {
18751        let mut bp = Breakpoint::new_standard();
18752        bp.state = BreakpointState::Disabled;
18753        bp
18754    };
18755
18756    assert_eq!(1, breakpoints.len());
18757    assert_breakpoint(
18758        &breakpoints,
18759        &abs_path,
18760        vec![
18761            (0, disable_breakpoint.clone()),
18762            (2, Breakpoint::new_standard()),
18763            (3, disable_breakpoint.clone()),
18764        ],
18765    );
18766
18767    editor.update_in(cx, |editor, window, cx| {
18768        editor.move_to_beginning(&MoveToBeginning, window, cx);
18769        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18770        editor.move_to_end(&MoveToEnd, window, cx);
18771        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18772        editor.move_up(&MoveUp, window, cx);
18773        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18774    });
18775
18776    let breakpoints = editor.update(cx, |editor, cx| {
18777        editor
18778            .breakpoint_store()
18779            .as_ref()
18780            .unwrap()
18781            .read(cx)
18782            .all_breakpoints(cx)
18783            .clone()
18784    });
18785
18786    assert_eq!(1, breakpoints.len());
18787    assert_breakpoint(
18788        &breakpoints,
18789        &abs_path,
18790        vec![
18791            (0, Breakpoint::new_standard()),
18792            (2, disable_breakpoint),
18793            (3, Breakpoint::new_standard()),
18794        ],
18795    );
18796}
18797
18798#[gpui::test]
18799async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18800    init_test(cx, |_| {});
18801    let capabilities = lsp::ServerCapabilities {
18802        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18803            prepare_provider: Some(true),
18804            work_done_progress_options: Default::default(),
18805        })),
18806        ..Default::default()
18807    };
18808    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18809
18810    cx.set_state(indoc! {"
18811        struct Fˇoo {}
18812    "});
18813
18814    cx.update_editor(|editor, _, cx| {
18815        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18816        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18817        editor.highlight_background::<DocumentHighlightRead>(
18818            &[highlight_range],
18819            |c| c.editor_document_highlight_read_background,
18820            cx,
18821        );
18822    });
18823
18824    let mut prepare_rename_handler = cx
18825        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18826            move |_, _, _| async move {
18827                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18828                    start: lsp::Position {
18829                        line: 0,
18830                        character: 7,
18831                    },
18832                    end: lsp::Position {
18833                        line: 0,
18834                        character: 10,
18835                    },
18836                })))
18837            },
18838        );
18839    let prepare_rename_task = cx
18840        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18841        .expect("Prepare rename was not started");
18842    prepare_rename_handler.next().await.unwrap();
18843    prepare_rename_task.await.expect("Prepare rename failed");
18844
18845    let mut rename_handler =
18846        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18847            let edit = lsp::TextEdit {
18848                range: lsp::Range {
18849                    start: lsp::Position {
18850                        line: 0,
18851                        character: 7,
18852                    },
18853                    end: lsp::Position {
18854                        line: 0,
18855                        character: 10,
18856                    },
18857                },
18858                new_text: "FooRenamed".to_string(),
18859            };
18860            Ok(Some(lsp::WorkspaceEdit::new(
18861                // Specify the same edit twice
18862                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18863            )))
18864        });
18865    let rename_task = cx
18866        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18867        .expect("Confirm rename was not started");
18868    rename_handler.next().await.unwrap();
18869    rename_task.await.expect("Confirm rename failed");
18870    cx.run_until_parked();
18871
18872    // Despite two edits, only one is actually applied as those are identical
18873    cx.assert_editor_state(indoc! {"
18874        struct FooRenamedˇ {}
18875    "});
18876}
18877
18878#[gpui::test]
18879async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18880    init_test(cx, |_| {});
18881    // These capabilities indicate that the server does not support prepare rename.
18882    let capabilities = lsp::ServerCapabilities {
18883        rename_provider: Some(lsp::OneOf::Left(true)),
18884        ..Default::default()
18885    };
18886    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18887
18888    cx.set_state(indoc! {"
18889        struct Fˇoo {}
18890    "});
18891
18892    cx.update_editor(|editor, _window, cx| {
18893        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18894        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18895        editor.highlight_background::<DocumentHighlightRead>(
18896            &[highlight_range],
18897            |c| c.editor_document_highlight_read_background,
18898            cx,
18899        );
18900    });
18901
18902    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18903        .expect("Prepare rename was not started")
18904        .await
18905        .expect("Prepare rename failed");
18906
18907    let mut rename_handler =
18908        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18909            let edit = lsp::TextEdit {
18910                range: lsp::Range {
18911                    start: lsp::Position {
18912                        line: 0,
18913                        character: 7,
18914                    },
18915                    end: lsp::Position {
18916                        line: 0,
18917                        character: 10,
18918                    },
18919                },
18920                new_text: "FooRenamed".to_string(),
18921            };
18922            Ok(Some(lsp::WorkspaceEdit::new(
18923                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18924            )))
18925        });
18926    let rename_task = cx
18927        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18928        .expect("Confirm rename was not started");
18929    rename_handler.next().await.unwrap();
18930    rename_task.await.expect("Confirm rename failed");
18931    cx.run_until_parked();
18932
18933    // Correct range is renamed, as `surrounding_word` is used to find it.
18934    cx.assert_editor_state(indoc! {"
18935        struct FooRenamedˇ {}
18936    "});
18937}
18938
18939#[gpui::test]
18940async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18941    init_test(cx, |_| {});
18942    let mut cx = EditorTestContext::new(cx).await;
18943
18944    let language = Arc::new(
18945        Language::new(
18946            LanguageConfig::default(),
18947            Some(tree_sitter_html::LANGUAGE.into()),
18948        )
18949        .with_brackets_query(
18950            r#"
18951            ("<" @open "/>" @close)
18952            ("</" @open ">" @close)
18953            ("<" @open ">" @close)
18954            ("\"" @open "\"" @close)
18955            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18956        "#,
18957        )
18958        .unwrap(),
18959    );
18960    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18961
18962    cx.set_state(indoc! {"
18963        <span>ˇ</span>
18964    "});
18965    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18966    cx.assert_editor_state(indoc! {"
18967        <span>
18968        ˇ
18969        </span>
18970    "});
18971
18972    cx.set_state(indoc! {"
18973        <span><span></span>ˇ</span>
18974    "});
18975    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18976    cx.assert_editor_state(indoc! {"
18977        <span><span></span>
18978        ˇ</span>
18979    "});
18980
18981    cx.set_state(indoc! {"
18982        <span>ˇ
18983        </span>
18984    "});
18985    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18986    cx.assert_editor_state(indoc! {"
18987        <span>
18988        ˇ
18989        </span>
18990    "});
18991}
18992
18993#[gpui::test(iterations = 10)]
18994async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18995    init_test(cx, |_| {});
18996
18997    let fs = FakeFs::new(cx.executor());
18998    fs.insert_tree(
18999        path!("/dir"),
19000        json!({
19001            "a.ts": "a",
19002        }),
19003    )
19004    .await;
19005
19006    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19007    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19008    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19009
19010    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19011    language_registry.add(Arc::new(Language::new(
19012        LanguageConfig {
19013            name: "TypeScript".into(),
19014            matcher: LanguageMatcher {
19015                path_suffixes: vec!["ts".to_string()],
19016                ..Default::default()
19017            },
19018            ..Default::default()
19019        },
19020        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19021    )));
19022    let mut fake_language_servers = language_registry.register_fake_lsp(
19023        "TypeScript",
19024        FakeLspAdapter {
19025            capabilities: lsp::ServerCapabilities {
19026                code_lens_provider: Some(lsp::CodeLensOptions {
19027                    resolve_provider: Some(true),
19028                }),
19029                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19030                    commands: vec!["_the/command".to_string()],
19031                    ..lsp::ExecuteCommandOptions::default()
19032                }),
19033                ..lsp::ServerCapabilities::default()
19034            },
19035            ..FakeLspAdapter::default()
19036        },
19037    );
19038
19039    let (buffer, _handle) = project
19040        .update(cx, |p, cx| {
19041            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19042        })
19043        .await
19044        .unwrap();
19045    cx.executor().run_until_parked();
19046
19047    let fake_server = fake_language_servers.next().await.unwrap();
19048
19049    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19050    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19051    drop(buffer_snapshot);
19052    let actions = cx
19053        .update_window(*workspace, |_, window, cx| {
19054            project.code_actions(&buffer, anchor..anchor, window, cx)
19055        })
19056        .unwrap();
19057
19058    fake_server
19059        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19060            Ok(Some(vec![
19061                lsp::CodeLens {
19062                    range: lsp::Range::default(),
19063                    command: Some(lsp::Command {
19064                        title: "Code lens command".to_owned(),
19065                        command: "_the/command".to_owned(),
19066                        arguments: None,
19067                    }),
19068                    data: None,
19069                },
19070                lsp::CodeLens {
19071                    range: lsp::Range::default(),
19072                    command: Some(lsp::Command {
19073                        title: "Command not in capabilities".to_owned(),
19074                        command: "not in capabilities".to_owned(),
19075                        arguments: None,
19076                    }),
19077                    data: None,
19078                },
19079                lsp::CodeLens {
19080                    range: lsp::Range {
19081                        start: lsp::Position {
19082                            line: 1,
19083                            character: 1,
19084                        },
19085                        end: lsp::Position {
19086                            line: 1,
19087                            character: 1,
19088                        },
19089                    },
19090                    command: Some(lsp::Command {
19091                        title: "Command not in range".to_owned(),
19092                        command: "_the/command".to_owned(),
19093                        arguments: None,
19094                    }),
19095                    data: None,
19096                },
19097            ]))
19098        })
19099        .next()
19100        .await;
19101
19102    let actions = actions.await.unwrap();
19103    assert_eq!(
19104        actions.len(),
19105        1,
19106        "Should have only one valid action for the 0..0 range"
19107    );
19108    let action = actions[0].clone();
19109    let apply = project.update(cx, |project, cx| {
19110        project.apply_code_action(buffer.clone(), action, true, cx)
19111    });
19112
19113    // Resolving the code action does not populate its edits. In absence of
19114    // edits, we must execute the given command.
19115    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19116        |mut lens, _| async move {
19117            let lens_command = lens.command.as_mut().expect("should have a command");
19118            assert_eq!(lens_command.title, "Code lens command");
19119            lens_command.arguments = Some(vec![json!("the-argument")]);
19120            Ok(lens)
19121        },
19122    );
19123
19124    // While executing the command, the language server sends the editor
19125    // a `workspaceEdit` request.
19126    fake_server
19127        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19128            let fake = fake_server.clone();
19129            move |params, _| {
19130                assert_eq!(params.command, "_the/command");
19131                let fake = fake.clone();
19132                async move {
19133                    fake.server
19134                        .request::<lsp::request::ApplyWorkspaceEdit>(
19135                            lsp::ApplyWorkspaceEditParams {
19136                                label: None,
19137                                edit: lsp::WorkspaceEdit {
19138                                    changes: Some(
19139                                        [(
19140                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19141                                            vec![lsp::TextEdit {
19142                                                range: lsp::Range::new(
19143                                                    lsp::Position::new(0, 0),
19144                                                    lsp::Position::new(0, 0),
19145                                                ),
19146                                                new_text: "X".into(),
19147                                            }],
19148                                        )]
19149                                        .into_iter()
19150                                        .collect(),
19151                                    ),
19152                                    ..Default::default()
19153                                },
19154                            },
19155                        )
19156                        .await
19157                        .into_response()
19158                        .unwrap();
19159                    Ok(Some(json!(null)))
19160                }
19161            }
19162        })
19163        .next()
19164        .await;
19165
19166    // Applying the code lens command returns a project transaction containing the edits
19167    // sent by the language server in its `workspaceEdit` request.
19168    let transaction = apply.await.unwrap();
19169    assert!(transaction.0.contains_key(&buffer));
19170    buffer.update(cx, |buffer, cx| {
19171        assert_eq!(buffer.text(), "Xa");
19172        buffer.undo(cx);
19173        assert_eq!(buffer.text(), "a");
19174    });
19175}
19176
19177#[gpui::test]
19178async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19179    init_test(cx, |_| {});
19180
19181    let fs = FakeFs::new(cx.executor());
19182    let main_text = r#"fn main() {
19183println!("1");
19184println!("2");
19185println!("3");
19186println!("4");
19187println!("5");
19188}"#;
19189    let lib_text = "mod foo {}";
19190    fs.insert_tree(
19191        path!("/a"),
19192        json!({
19193            "lib.rs": lib_text,
19194            "main.rs": main_text,
19195        }),
19196    )
19197    .await;
19198
19199    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19200    let (workspace, cx) =
19201        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19202    let worktree_id = workspace.update(cx, |workspace, cx| {
19203        workspace.project().update(cx, |project, cx| {
19204            project.worktrees(cx).next().unwrap().read(cx).id()
19205        })
19206    });
19207
19208    let expected_ranges = vec![
19209        Point::new(0, 0)..Point::new(0, 0),
19210        Point::new(1, 0)..Point::new(1, 1),
19211        Point::new(2, 0)..Point::new(2, 2),
19212        Point::new(3, 0)..Point::new(3, 3),
19213    ];
19214
19215    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19216    let editor_1 = workspace
19217        .update_in(cx, |workspace, window, cx| {
19218            workspace.open_path(
19219                (worktree_id, "main.rs"),
19220                Some(pane_1.downgrade()),
19221                true,
19222                window,
19223                cx,
19224            )
19225        })
19226        .unwrap()
19227        .await
19228        .downcast::<Editor>()
19229        .unwrap();
19230    pane_1.update(cx, |pane, cx| {
19231        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19232        open_editor.update(cx, |editor, cx| {
19233            assert_eq!(
19234                editor.display_text(cx),
19235                main_text,
19236                "Original main.rs text on initial open",
19237            );
19238            assert_eq!(
19239                editor
19240                    .selections
19241                    .all::<Point>(cx)
19242                    .into_iter()
19243                    .map(|s| s.range())
19244                    .collect::<Vec<_>>(),
19245                vec![Point::zero()..Point::zero()],
19246                "Default selections on initial open",
19247            );
19248        })
19249    });
19250    editor_1.update_in(cx, |editor, window, cx| {
19251        editor.change_selections(None, window, cx, |s| {
19252            s.select_ranges(expected_ranges.clone());
19253        });
19254    });
19255
19256    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19257        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19258    });
19259    let editor_2 = workspace
19260        .update_in(cx, |workspace, window, cx| {
19261            workspace.open_path(
19262                (worktree_id, "main.rs"),
19263                Some(pane_2.downgrade()),
19264                true,
19265                window,
19266                cx,
19267            )
19268        })
19269        .unwrap()
19270        .await
19271        .downcast::<Editor>()
19272        .unwrap();
19273    pane_2.update(cx, |pane, cx| {
19274        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19275        open_editor.update(cx, |editor, cx| {
19276            assert_eq!(
19277                editor.display_text(cx),
19278                main_text,
19279                "Original main.rs text on initial open in another panel",
19280            );
19281            assert_eq!(
19282                editor
19283                    .selections
19284                    .all::<Point>(cx)
19285                    .into_iter()
19286                    .map(|s| s.range())
19287                    .collect::<Vec<_>>(),
19288                vec![Point::zero()..Point::zero()],
19289                "Default selections on initial open in another panel",
19290            );
19291        })
19292    });
19293
19294    editor_2.update_in(cx, |editor, window, cx| {
19295        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19296    });
19297
19298    let _other_editor_1 = workspace
19299        .update_in(cx, |workspace, window, cx| {
19300            workspace.open_path(
19301                (worktree_id, "lib.rs"),
19302                Some(pane_1.downgrade()),
19303                true,
19304                window,
19305                cx,
19306            )
19307        })
19308        .unwrap()
19309        .await
19310        .downcast::<Editor>()
19311        .unwrap();
19312    pane_1
19313        .update_in(cx, |pane, window, cx| {
19314            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19315                .unwrap()
19316        })
19317        .await
19318        .unwrap();
19319    drop(editor_1);
19320    pane_1.update(cx, |pane, cx| {
19321        pane.active_item()
19322            .unwrap()
19323            .downcast::<Editor>()
19324            .unwrap()
19325            .update(cx, |editor, cx| {
19326                assert_eq!(
19327                    editor.display_text(cx),
19328                    lib_text,
19329                    "Other file should be open and active",
19330                );
19331            });
19332        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19333    });
19334
19335    let _other_editor_2 = workspace
19336        .update_in(cx, |workspace, window, cx| {
19337            workspace.open_path(
19338                (worktree_id, "lib.rs"),
19339                Some(pane_2.downgrade()),
19340                true,
19341                window,
19342                cx,
19343            )
19344        })
19345        .unwrap()
19346        .await
19347        .downcast::<Editor>()
19348        .unwrap();
19349    pane_2
19350        .update_in(cx, |pane, window, cx| {
19351            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19352                .unwrap()
19353        })
19354        .await
19355        .unwrap();
19356    drop(editor_2);
19357    pane_2.update(cx, |pane, cx| {
19358        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19359        open_editor.update(cx, |editor, cx| {
19360            assert_eq!(
19361                editor.display_text(cx),
19362                lib_text,
19363                "Other file should be open and active in another panel too",
19364            );
19365        });
19366        assert_eq!(
19367            pane.items().count(),
19368            1,
19369            "No other editors should be open in another pane",
19370        );
19371    });
19372
19373    let _editor_1_reopened = workspace
19374        .update_in(cx, |workspace, window, cx| {
19375            workspace.open_path(
19376                (worktree_id, "main.rs"),
19377                Some(pane_1.downgrade()),
19378                true,
19379                window,
19380                cx,
19381            )
19382        })
19383        .unwrap()
19384        .await
19385        .downcast::<Editor>()
19386        .unwrap();
19387    let _editor_2_reopened = workspace
19388        .update_in(cx, |workspace, window, cx| {
19389            workspace.open_path(
19390                (worktree_id, "main.rs"),
19391                Some(pane_2.downgrade()),
19392                true,
19393                window,
19394                cx,
19395            )
19396        })
19397        .unwrap()
19398        .await
19399        .downcast::<Editor>()
19400        .unwrap();
19401    pane_1.update(cx, |pane, cx| {
19402        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19403        open_editor.update(cx, |editor, cx| {
19404            assert_eq!(
19405                editor.display_text(cx),
19406                main_text,
19407                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19408            );
19409            assert_eq!(
19410                editor
19411                    .selections
19412                    .all::<Point>(cx)
19413                    .into_iter()
19414                    .map(|s| s.range())
19415                    .collect::<Vec<_>>(),
19416                expected_ranges,
19417                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19418            );
19419        })
19420    });
19421    pane_2.update(cx, |pane, cx| {
19422        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19423        open_editor.update(cx, |editor, cx| {
19424            assert_eq!(
19425                editor.display_text(cx),
19426                r#"fn main() {
19427⋯rintln!("1");
19428⋯intln!("2");
19429⋯ntln!("3");
19430println!("4");
19431println!("5");
19432}"#,
19433                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19434            );
19435            assert_eq!(
19436                editor
19437                    .selections
19438                    .all::<Point>(cx)
19439                    .into_iter()
19440                    .map(|s| s.range())
19441                    .collect::<Vec<_>>(),
19442                vec![Point::zero()..Point::zero()],
19443                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19444            );
19445        })
19446    });
19447}
19448
19449#[gpui::test]
19450async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19451    init_test(cx, |_| {});
19452
19453    let fs = FakeFs::new(cx.executor());
19454    let main_text = r#"fn main() {
19455println!("1");
19456println!("2");
19457println!("3");
19458println!("4");
19459println!("5");
19460}"#;
19461    let lib_text = "mod foo {}";
19462    fs.insert_tree(
19463        path!("/a"),
19464        json!({
19465            "lib.rs": lib_text,
19466            "main.rs": main_text,
19467        }),
19468    )
19469    .await;
19470
19471    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19472    let (workspace, cx) =
19473        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19474    let worktree_id = workspace.update(cx, |workspace, cx| {
19475        workspace.project().update(cx, |project, cx| {
19476            project.worktrees(cx).next().unwrap().read(cx).id()
19477        })
19478    });
19479
19480    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19481    let editor = workspace
19482        .update_in(cx, |workspace, window, cx| {
19483            workspace.open_path(
19484                (worktree_id, "main.rs"),
19485                Some(pane.downgrade()),
19486                true,
19487                window,
19488                cx,
19489            )
19490        })
19491        .unwrap()
19492        .await
19493        .downcast::<Editor>()
19494        .unwrap();
19495    pane.update(cx, |pane, cx| {
19496        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19497        open_editor.update(cx, |editor, cx| {
19498            assert_eq!(
19499                editor.display_text(cx),
19500                main_text,
19501                "Original main.rs text on initial open",
19502            );
19503        })
19504    });
19505    editor.update_in(cx, |editor, window, cx| {
19506        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19507    });
19508
19509    cx.update_global(|store: &mut SettingsStore, cx| {
19510        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19511            s.restore_on_file_reopen = Some(false);
19512        });
19513    });
19514    editor.update_in(cx, |editor, window, cx| {
19515        editor.fold_ranges(
19516            vec![
19517                Point::new(1, 0)..Point::new(1, 1),
19518                Point::new(2, 0)..Point::new(2, 2),
19519                Point::new(3, 0)..Point::new(3, 3),
19520            ],
19521            false,
19522            window,
19523            cx,
19524        );
19525    });
19526    pane.update_in(cx, |pane, window, cx| {
19527        pane.close_all_items(&CloseAllItems::default(), window, cx)
19528            .unwrap()
19529    })
19530    .await
19531    .unwrap();
19532    pane.update(cx, |pane, _| {
19533        assert!(pane.active_item().is_none());
19534    });
19535    cx.update_global(|store: &mut SettingsStore, cx| {
19536        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19537            s.restore_on_file_reopen = Some(true);
19538        });
19539    });
19540
19541    let _editor_reopened = workspace
19542        .update_in(cx, |workspace, window, cx| {
19543            workspace.open_path(
19544                (worktree_id, "main.rs"),
19545                Some(pane.downgrade()),
19546                true,
19547                window,
19548                cx,
19549            )
19550        })
19551        .unwrap()
19552        .await
19553        .downcast::<Editor>()
19554        .unwrap();
19555    pane.update(cx, |pane, cx| {
19556        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19557        open_editor.update(cx, |editor, cx| {
19558            assert_eq!(
19559                editor.display_text(cx),
19560                main_text,
19561                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19562            );
19563        })
19564    });
19565}
19566
19567#[gpui::test]
19568async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19569    struct EmptyModalView {
19570        focus_handle: gpui::FocusHandle,
19571    }
19572    impl EventEmitter<DismissEvent> for EmptyModalView {}
19573    impl Render for EmptyModalView {
19574        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19575            div()
19576        }
19577    }
19578    impl Focusable for EmptyModalView {
19579        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19580            self.focus_handle.clone()
19581        }
19582    }
19583    impl workspace::ModalView for EmptyModalView {}
19584    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19585        EmptyModalView {
19586            focus_handle: cx.focus_handle(),
19587        }
19588    }
19589
19590    init_test(cx, |_| {});
19591
19592    let fs = FakeFs::new(cx.executor());
19593    let project = Project::test(fs, [], cx).await;
19594    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19595    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19596    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19597    let editor = cx.new_window_entity(|window, cx| {
19598        Editor::new(
19599            EditorMode::full(),
19600            buffer,
19601            Some(project.clone()),
19602            window,
19603            cx,
19604        )
19605    });
19606    workspace
19607        .update(cx, |workspace, window, cx| {
19608            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19609        })
19610        .unwrap();
19611    editor.update_in(cx, |editor, window, cx| {
19612        editor.open_context_menu(&OpenContextMenu, window, cx);
19613        assert!(editor.mouse_context_menu.is_some());
19614    });
19615    workspace
19616        .update(cx, |workspace, window, cx| {
19617            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19618        })
19619        .unwrap();
19620    cx.read(|cx| {
19621        assert!(editor.read(cx).mouse_context_menu.is_none());
19622    });
19623}
19624
19625#[gpui::test]
19626async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19627    init_test(cx, |_| {});
19628
19629    let fs = FakeFs::new(cx.executor());
19630    fs.insert_file(path!("/file.html"), Default::default())
19631        .await;
19632
19633    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19634
19635    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19636    let html_language = Arc::new(Language::new(
19637        LanguageConfig {
19638            name: "HTML".into(),
19639            matcher: LanguageMatcher {
19640                path_suffixes: vec!["html".to_string()],
19641                ..LanguageMatcher::default()
19642            },
19643            brackets: BracketPairConfig {
19644                pairs: vec![BracketPair {
19645                    start: "<".into(),
19646                    end: ">".into(),
19647                    close: true,
19648                    ..Default::default()
19649                }],
19650                ..Default::default()
19651            },
19652            ..Default::default()
19653        },
19654        Some(tree_sitter_html::LANGUAGE.into()),
19655    ));
19656    language_registry.add(html_language);
19657    let mut fake_servers = language_registry.register_fake_lsp(
19658        "HTML",
19659        FakeLspAdapter {
19660            capabilities: lsp::ServerCapabilities {
19661                completion_provider: Some(lsp::CompletionOptions {
19662                    resolve_provider: Some(true),
19663                    ..Default::default()
19664                }),
19665                ..Default::default()
19666            },
19667            ..Default::default()
19668        },
19669    );
19670
19671    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19672    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19673
19674    let worktree_id = workspace
19675        .update(cx, |workspace, _window, cx| {
19676            workspace.project().update(cx, |project, cx| {
19677                project.worktrees(cx).next().unwrap().read(cx).id()
19678            })
19679        })
19680        .unwrap();
19681    project
19682        .update(cx, |project, cx| {
19683            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19684        })
19685        .await
19686        .unwrap();
19687    let editor = workspace
19688        .update(cx, |workspace, window, cx| {
19689            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19690        })
19691        .unwrap()
19692        .await
19693        .unwrap()
19694        .downcast::<Editor>()
19695        .unwrap();
19696
19697    let fake_server = fake_servers.next().await.unwrap();
19698    editor.update_in(cx, |editor, window, cx| {
19699        editor.set_text("<ad></ad>", window, cx);
19700        editor.change_selections(None, window, cx, |selections| {
19701            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19702        });
19703        let Some((buffer, _)) = editor
19704            .buffer
19705            .read(cx)
19706            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19707        else {
19708            panic!("Failed to get buffer for selection position");
19709        };
19710        let buffer = buffer.read(cx);
19711        let buffer_id = buffer.remote_id();
19712        let opening_range =
19713            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19714        let closing_range =
19715            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19716        let mut linked_ranges = HashMap::default();
19717        linked_ranges.insert(
19718            buffer_id,
19719            vec![(opening_range.clone(), vec![closing_range.clone()])],
19720        );
19721        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19722    });
19723    let mut completion_handle =
19724        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19725            Ok(Some(lsp::CompletionResponse::Array(vec![
19726                lsp::CompletionItem {
19727                    label: "head".to_string(),
19728                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19729                        lsp::InsertReplaceEdit {
19730                            new_text: "head".to_string(),
19731                            insert: lsp::Range::new(
19732                                lsp::Position::new(0, 1),
19733                                lsp::Position::new(0, 3),
19734                            ),
19735                            replace: lsp::Range::new(
19736                                lsp::Position::new(0, 1),
19737                                lsp::Position::new(0, 3),
19738                            ),
19739                        },
19740                    )),
19741                    ..Default::default()
19742                },
19743            ])))
19744        });
19745    editor.update_in(cx, |editor, window, cx| {
19746        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19747    });
19748    cx.run_until_parked();
19749    completion_handle.next().await.unwrap();
19750    editor.update(cx, |editor, _| {
19751        assert!(
19752            editor.context_menu_visible(),
19753            "Completion menu should be visible"
19754        );
19755    });
19756    editor.update_in(cx, |editor, window, cx| {
19757        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19758    });
19759    cx.executor().run_until_parked();
19760    editor.update(cx, |editor, cx| {
19761        assert_eq!(editor.text(cx), "<head></head>");
19762    });
19763}
19764
19765fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19766    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19767    point..point
19768}
19769
19770fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19771    let (text, ranges) = marked_text_ranges(marked_text, true);
19772    assert_eq!(editor.text(cx), text);
19773    assert_eq!(
19774        editor.selections.ranges(cx),
19775        ranges,
19776        "Assert selections are {}",
19777        marked_text
19778    );
19779}
19780
19781pub fn handle_signature_help_request(
19782    cx: &mut EditorLspTestContext,
19783    mocked_response: lsp::SignatureHelp,
19784) -> impl Future<Output = ()> + use<> {
19785    let mut request =
19786        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19787            let mocked_response = mocked_response.clone();
19788            async move { Ok(Some(mocked_response)) }
19789        });
19790
19791    async move {
19792        request.next().await;
19793    }
19794}
19795
19796/// Handle completion request passing a marked string specifying where the completion
19797/// should be triggered from using '|' character, what range should be replaced, and what completions
19798/// should be returned using '<' and '>' to delimit the range.
19799///
19800/// Also see `handle_completion_request_with_insert_and_replace`.
19801#[track_caller]
19802pub fn handle_completion_request(
19803    cx: &mut EditorLspTestContext,
19804    marked_string: &str,
19805    completions: Vec<&'static str>,
19806    counter: Arc<AtomicUsize>,
19807) -> impl Future<Output = ()> {
19808    let complete_from_marker: TextRangeMarker = '|'.into();
19809    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19810    let (_, mut marked_ranges) = marked_text_ranges_by(
19811        marked_string,
19812        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19813    );
19814
19815    let complete_from_position =
19816        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19817    let replace_range =
19818        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19819
19820    let mut request =
19821        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19822            let completions = completions.clone();
19823            counter.fetch_add(1, atomic::Ordering::Release);
19824            async move {
19825                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19826                assert_eq!(
19827                    params.text_document_position.position,
19828                    complete_from_position
19829                );
19830                Ok(Some(lsp::CompletionResponse::Array(
19831                    completions
19832                        .iter()
19833                        .map(|completion_text| lsp::CompletionItem {
19834                            label: completion_text.to_string(),
19835                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19836                                range: replace_range,
19837                                new_text: completion_text.to_string(),
19838                            })),
19839                            ..Default::default()
19840                        })
19841                        .collect(),
19842                )))
19843            }
19844        });
19845
19846    async move {
19847        request.next().await;
19848    }
19849}
19850
19851/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19852/// given instead, which also contains an `insert` range.
19853///
19854/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19855/// that is, `replace_range.start..cursor_pos`.
19856pub fn handle_completion_request_with_insert_and_replace(
19857    cx: &mut EditorLspTestContext,
19858    marked_string: &str,
19859    completions: Vec<&'static str>,
19860    counter: Arc<AtomicUsize>,
19861) -> impl Future<Output = ()> {
19862    let complete_from_marker: TextRangeMarker = '|'.into();
19863    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19864    let (_, mut marked_ranges) = marked_text_ranges_by(
19865        marked_string,
19866        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19867    );
19868
19869    let complete_from_position =
19870        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19871    let replace_range =
19872        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19873
19874    let mut request =
19875        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19876            let completions = completions.clone();
19877            counter.fetch_add(1, atomic::Ordering::Release);
19878            async move {
19879                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19880                assert_eq!(
19881                    params.text_document_position.position, complete_from_position,
19882                    "marker `|` position doesn't match",
19883                );
19884                Ok(Some(lsp::CompletionResponse::Array(
19885                    completions
19886                        .iter()
19887                        .map(|completion_text| lsp::CompletionItem {
19888                            label: completion_text.to_string(),
19889                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19890                                lsp::InsertReplaceEdit {
19891                                    insert: lsp::Range {
19892                                        start: replace_range.start,
19893                                        end: complete_from_position,
19894                                    },
19895                                    replace: replace_range,
19896                                    new_text: completion_text.to_string(),
19897                                },
19898                            )),
19899                            ..Default::default()
19900                        })
19901                        .collect(),
19902                )))
19903            }
19904        });
19905
19906    async move {
19907        request.next().await;
19908    }
19909}
19910
19911fn handle_resolve_completion_request(
19912    cx: &mut EditorLspTestContext,
19913    edits: Option<Vec<(&'static str, &'static str)>>,
19914) -> impl Future<Output = ()> {
19915    let edits = edits.map(|edits| {
19916        edits
19917            .iter()
19918            .map(|(marked_string, new_text)| {
19919                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19920                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19921                lsp::TextEdit::new(replace_range, new_text.to_string())
19922            })
19923            .collect::<Vec<_>>()
19924    });
19925
19926    let mut request =
19927        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19928            let edits = edits.clone();
19929            async move {
19930                Ok(lsp::CompletionItem {
19931                    additional_text_edits: edits,
19932                    ..Default::default()
19933                })
19934            }
19935        });
19936
19937    async move {
19938        request.next().await;
19939    }
19940}
19941
19942pub(crate) fn update_test_language_settings(
19943    cx: &mut TestAppContext,
19944    f: impl Fn(&mut AllLanguageSettingsContent),
19945) {
19946    cx.update(|cx| {
19947        SettingsStore::update_global(cx, |store, cx| {
19948            store.update_user_settings::<AllLanguageSettings>(cx, f);
19949        });
19950    });
19951}
19952
19953pub(crate) fn update_test_project_settings(
19954    cx: &mut TestAppContext,
19955    f: impl Fn(&mut ProjectSettings),
19956) {
19957    cx.update(|cx| {
19958        SettingsStore::update_global(cx, |store, cx| {
19959            store.update_user_settings::<ProjectSettings>(cx, f);
19960        });
19961    });
19962}
19963
19964pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19965    cx.update(|cx| {
19966        assets::Assets.load_test_fonts(cx);
19967        let store = SettingsStore::test(cx);
19968        cx.set_global(store);
19969        theme::init(theme::LoadThemes::JustBase, cx);
19970        release_channel::init(SemanticVersion::default(), cx);
19971        client::init_settings(cx);
19972        language::init(cx);
19973        Project::init_settings(cx);
19974        workspace::init_settings(cx);
19975        crate::init(cx);
19976    });
19977
19978    update_test_language_settings(cx, f);
19979}
19980
19981#[track_caller]
19982fn assert_hunk_revert(
19983    not_reverted_text_with_selections: &str,
19984    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19985    expected_reverted_text_with_selections: &str,
19986    base_text: &str,
19987    cx: &mut EditorLspTestContext,
19988) {
19989    cx.set_state(not_reverted_text_with_selections);
19990    cx.set_head_text(base_text);
19991    cx.executor().run_until_parked();
19992
19993    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19994        let snapshot = editor.snapshot(window, cx);
19995        let reverted_hunk_statuses = snapshot
19996            .buffer_snapshot
19997            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19998            .map(|hunk| hunk.status().kind)
19999            .collect::<Vec<_>>();
20000
20001        editor.git_restore(&Default::default(), window, cx);
20002        reverted_hunk_statuses
20003    });
20004    cx.executor().run_until_parked();
20005    cx.assert_editor_state(expected_reverted_text_with_selections);
20006    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20007}