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                    .unwrap();
 8904                Ok(Some(json!(null)))
 8905            }
 8906        }
 8907    });
 8908
 8909    cx.executor().start_waiting();
 8910    editor
 8911        .update_in(cx, |editor, window, cx| {
 8912            editor.perform_format(
 8913                project.clone(),
 8914                FormatTrigger::Manual,
 8915                FormatTarget::Buffers,
 8916                window,
 8917                cx,
 8918            )
 8919        })
 8920        .unwrap()
 8921        .await;
 8922    editor.update(cx, |editor, cx| {
 8923        assert_eq!(
 8924            editor.text(cx),
 8925            r#"
 8926                applied-code-action-2-edit
 8927                applied-code-action-1-command
 8928                applied-code-action-1-edit
 8929                applied-formatting
 8930                one
 8931                two
 8932                three
 8933            "#
 8934            .unindent()
 8935        );
 8936    });
 8937
 8938    editor.update_in(cx, |editor, window, cx| {
 8939        editor.undo(&Default::default(), window, cx);
 8940        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8941    });
 8942
 8943    // Perform a manual edit while waiting for an LSP command
 8944    // that's being run as part of a formatting code action.
 8945    let lock_guard = command_lock.lock().await;
 8946    let format = editor
 8947        .update_in(cx, |editor, window, cx| {
 8948            editor.perform_format(
 8949                project.clone(),
 8950                FormatTrigger::Manual,
 8951                FormatTarget::Buffers,
 8952                window,
 8953                cx,
 8954            )
 8955        })
 8956        .unwrap();
 8957    cx.run_until_parked();
 8958    editor.update(cx, |editor, cx| {
 8959        assert_eq!(
 8960            editor.text(cx),
 8961            r#"
 8962                applied-code-action-1-edit
 8963                applied-formatting
 8964                one
 8965                two
 8966                three
 8967            "#
 8968            .unindent()
 8969        );
 8970
 8971        editor.buffer.update(cx, |buffer, cx| {
 8972            let ix = buffer.len(cx);
 8973            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8974        });
 8975    });
 8976
 8977    // Allow the LSP command to proceed. Because the buffer was edited,
 8978    // the second code action will not be run.
 8979    drop(lock_guard);
 8980    format.await;
 8981    editor.update_in(cx, |editor, window, cx| {
 8982        assert_eq!(
 8983            editor.text(cx),
 8984            r#"
 8985                applied-code-action-1-command
 8986                applied-code-action-1-edit
 8987                applied-formatting
 8988                one
 8989                two
 8990                three
 8991                edited
 8992            "#
 8993            .unindent()
 8994        );
 8995
 8996        // The manual edit is undone first, because it is the last thing the user did
 8997        // (even though the command completed afterwards).
 8998        editor.undo(&Default::default(), window, cx);
 8999        assert_eq!(
 9000            editor.text(cx),
 9001            r#"
 9002                applied-code-action-1-command
 9003                applied-code-action-1-edit
 9004                applied-formatting
 9005                one
 9006                two
 9007                three
 9008            "#
 9009            .unindent()
 9010        );
 9011
 9012        // All the formatting (including the command, which completed after the manual edit)
 9013        // is undone together.
 9014        editor.undo(&Default::default(), window, cx);
 9015        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9016    });
 9017}
 9018
 9019#[gpui::test]
 9020async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9021    init_test(cx, |settings| {
 9022        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9023            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9024        ))
 9025    });
 9026
 9027    let fs = FakeFs::new(cx.executor());
 9028    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9029
 9030    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9031
 9032    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9033    language_registry.add(Arc::new(Language::new(
 9034        LanguageConfig {
 9035            name: "TypeScript".into(),
 9036            matcher: LanguageMatcher {
 9037                path_suffixes: vec!["ts".to_string()],
 9038                ..Default::default()
 9039            },
 9040            ..LanguageConfig::default()
 9041        },
 9042        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9043    )));
 9044    update_test_language_settings(cx, |settings| {
 9045        settings.defaults.prettier = Some(PrettierSettings {
 9046            allowed: true,
 9047            ..PrettierSettings::default()
 9048        });
 9049    });
 9050    let mut fake_servers = language_registry.register_fake_lsp(
 9051        "TypeScript",
 9052        FakeLspAdapter {
 9053            capabilities: lsp::ServerCapabilities {
 9054                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9055                ..Default::default()
 9056            },
 9057            ..Default::default()
 9058        },
 9059    );
 9060
 9061    let buffer = project
 9062        .update(cx, |project, cx| {
 9063            project.open_local_buffer(path!("/file.ts"), cx)
 9064        })
 9065        .await
 9066        .unwrap();
 9067
 9068    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9069    let (editor, cx) = cx.add_window_view(|window, cx| {
 9070        build_editor_with_project(project.clone(), buffer, window, cx)
 9071    });
 9072    editor.update_in(cx, |editor, window, cx| {
 9073        editor.set_text(
 9074            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9075            window,
 9076            cx,
 9077        )
 9078    });
 9079
 9080    cx.executor().start_waiting();
 9081    let fake_server = fake_servers.next().await.unwrap();
 9082
 9083    let format = editor
 9084        .update_in(cx, |editor, window, cx| {
 9085            editor.perform_code_action_kind(
 9086                project.clone(),
 9087                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9088                window,
 9089                cx,
 9090            )
 9091        })
 9092        .unwrap();
 9093    fake_server
 9094        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9095            assert_eq!(
 9096                params.text_document.uri,
 9097                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9098            );
 9099            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9100                lsp::CodeAction {
 9101                    title: "Organize Imports".to_string(),
 9102                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9103                    edit: Some(lsp::WorkspaceEdit {
 9104                        changes: Some(
 9105                            [(
 9106                                params.text_document.uri.clone(),
 9107                                vec![lsp::TextEdit::new(
 9108                                    lsp::Range::new(
 9109                                        lsp::Position::new(1, 0),
 9110                                        lsp::Position::new(2, 0),
 9111                                    ),
 9112                                    "".to_string(),
 9113                                )],
 9114                            )]
 9115                            .into_iter()
 9116                            .collect(),
 9117                        ),
 9118                        ..Default::default()
 9119                    }),
 9120                    ..Default::default()
 9121                },
 9122            )]))
 9123        })
 9124        .next()
 9125        .await;
 9126    cx.executor().start_waiting();
 9127    format.await;
 9128    assert_eq!(
 9129        editor.update(cx, |editor, cx| editor.text(cx)),
 9130        "import { a } from 'module';\n\nconst x = a;\n"
 9131    );
 9132
 9133    editor.update_in(cx, |editor, window, cx| {
 9134        editor.set_text(
 9135            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9136            window,
 9137            cx,
 9138        )
 9139    });
 9140    // Ensure we don't lock if code action hangs.
 9141    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9142        move |params, _| async move {
 9143            assert_eq!(
 9144                params.text_document.uri,
 9145                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9146            );
 9147            futures::future::pending::<()>().await;
 9148            unreachable!()
 9149        },
 9150    );
 9151    let format = editor
 9152        .update_in(cx, |editor, window, cx| {
 9153            editor.perform_code_action_kind(
 9154                project,
 9155                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9156                window,
 9157                cx,
 9158            )
 9159        })
 9160        .unwrap();
 9161    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9162    cx.executor().start_waiting();
 9163    format.await;
 9164    assert_eq!(
 9165        editor.update(cx, |editor, cx| editor.text(cx)),
 9166        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9167    );
 9168}
 9169
 9170#[gpui::test]
 9171async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9172    init_test(cx, |_| {});
 9173
 9174    let mut cx = EditorLspTestContext::new_rust(
 9175        lsp::ServerCapabilities {
 9176            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9177            ..Default::default()
 9178        },
 9179        cx,
 9180    )
 9181    .await;
 9182
 9183    cx.set_state(indoc! {"
 9184        one.twoˇ
 9185    "});
 9186
 9187    // The format request takes a long time. When it completes, it inserts
 9188    // a newline and an indent before the `.`
 9189    cx.lsp
 9190        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9191            let executor = cx.background_executor().clone();
 9192            async move {
 9193                executor.timer(Duration::from_millis(100)).await;
 9194                Ok(Some(vec![lsp::TextEdit {
 9195                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9196                    new_text: "\n    ".into(),
 9197                }]))
 9198            }
 9199        });
 9200
 9201    // Submit a format request.
 9202    let format_1 = cx
 9203        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9204        .unwrap();
 9205    cx.executor().run_until_parked();
 9206
 9207    // Submit a second format request.
 9208    let format_2 = cx
 9209        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9210        .unwrap();
 9211    cx.executor().run_until_parked();
 9212
 9213    // Wait for both format requests to complete
 9214    cx.executor().advance_clock(Duration::from_millis(200));
 9215    cx.executor().start_waiting();
 9216    format_1.await.unwrap();
 9217    cx.executor().start_waiting();
 9218    format_2.await.unwrap();
 9219
 9220    // The formatting edits only happens once.
 9221    cx.assert_editor_state(indoc! {"
 9222        one
 9223            .twoˇ
 9224    "});
 9225}
 9226
 9227#[gpui::test]
 9228async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9229    init_test(cx, |settings| {
 9230        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9231    });
 9232
 9233    let mut cx = EditorLspTestContext::new_rust(
 9234        lsp::ServerCapabilities {
 9235            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9236            ..Default::default()
 9237        },
 9238        cx,
 9239    )
 9240    .await;
 9241
 9242    // Set up a buffer white some trailing whitespace and no trailing newline.
 9243    cx.set_state(
 9244        &[
 9245            "one ",   //
 9246            "twoˇ",   //
 9247            "three ", //
 9248            "four",   //
 9249        ]
 9250        .join("\n"),
 9251    );
 9252
 9253    // Submit a format request.
 9254    let format = cx
 9255        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9256        .unwrap();
 9257
 9258    // Record which buffer changes have been sent to the language server
 9259    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9260    cx.lsp
 9261        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9262            let buffer_changes = buffer_changes.clone();
 9263            move |params, _| {
 9264                buffer_changes.lock().extend(
 9265                    params
 9266                        .content_changes
 9267                        .into_iter()
 9268                        .map(|e| (e.range.unwrap(), e.text)),
 9269                );
 9270            }
 9271        });
 9272
 9273    // Handle formatting requests to the language server.
 9274    cx.lsp
 9275        .set_request_handler::<lsp::request::Formatting, _, _>({
 9276            let buffer_changes = buffer_changes.clone();
 9277            move |_, _| {
 9278                // When formatting is requested, trailing whitespace has already been stripped,
 9279                // and the trailing newline has already been added.
 9280                assert_eq!(
 9281                    &buffer_changes.lock()[1..],
 9282                    &[
 9283                        (
 9284                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9285                            "".into()
 9286                        ),
 9287                        (
 9288                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9289                            "".into()
 9290                        ),
 9291                        (
 9292                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9293                            "\n".into()
 9294                        ),
 9295                    ]
 9296                );
 9297
 9298                // Insert blank lines between each line of the buffer.
 9299                async move {
 9300                    Ok(Some(vec![
 9301                        lsp::TextEdit {
 9302                            range: lsp::Range::new(
 9303                                lsp::Position::new(1, 0),
 9304                                lsp::Position::new(1, 0),
 9305                            ),
 9306                            new_text: "\n".into(),
 9307                        },
 9308                        lsp::TextEdit {
 9309                            range: lsp::Range::new(
 9310                                lsp::Position::new(2, 0),
 9311                                lsp::Position::new(2, 0),
 9312                            ),
 9313                            new_text: "\n".into(),
 9314                        },
 9315                    ]))
 9316                }
 9317            }
 9318        });
 9319
 9320    // After formatting the buffer, the trailing whitespace is stripped,
 9321    // a newline is appended, and the edits provided by the language server
 9322    // have been applied.
 9323    format.await.unwrap();
 9324    cx.assert_editor_state(
 9325        &[
 9326            "one",   //
 9327            "",      //
 9328            "twoˇ",  //
 9329            "",      //
 9330            "three", //
 9331            "four",  //
 9332            "",      //
 9333        ]
 9334        .join("\n"),
 9335    );
 9336
 9337    // Undoing the formatting undoes the trailing whitespace removal, the
 9338    // trailing newline, and the LSP edits.
 9339    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9340    cx.assert_editor_state(
 9341        &[
 9342            "one ",   //
 9343            "twoˇ",   //
 9344            "three ", //
 9345            "four",   //
 9346        ]
 9347        .join("\n"),
 9348    );
 9349}
 9350
 9351#[gpui::test]
 9352async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9353    cx: &mut TestAppContext,
 9354) {
 9355    init_test(cx, |_| {});
 9356
 9357    cx.update(|cx| {
 9358        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9359            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9360                settings.auto_signature_help = Some(true);
 9361            });
 9362        });
 9363    });
 9364
 9365    let mut cx = EditorLspTestContext::new_rust(
 9366        lsp::ServerCapabilities {
 9367            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9368                ..Default::default()
 9369            }),
 9370            ..Default::default()
 9371        },
 9372        cx,
 9373    )
 9374    .await;
 9375
 9376    let language = Language::new(
 9377        LanguageConfig {
 9378            name: "Rust".into(),
 9379            brackets: BracketPairConfig {
 9380                pairs: vec![
 9381                    BracketPair {
 9382                        start: "{".to_string(),
 9383                        end: "}".to_string(),
 9384                        close: true,
 9385                        surround: true,
 9386                        newline: true,
 9387                    },
 9388                    BracketPair {
 9389                        start: "(".to_string(),
 9390                        end: ")".to_string(),
 9391                        close: true,
 9392                        surround: true,
 9393                        newline: true,
 9394                    },
 9395                    BracketPair {
 9396                        start: "/*".to_string(),
 9397                        end: " */".to_string(),
 9398                        close: true,
 9399                        surround: true,
 9400                        newline: true,
 9401                    },
 9402                    BracketPair {
 9403                        start: "[".to_string(),
 9404                        end: "]".to_string(),
 9405                        close: false,
 9406                        surround: false,
 9407                        newline: true,
 9408                    },
 9409                    BracketPair {
 9410                        start: "\"".to_string(),
 9411                        end: "\"".to_string(),
 9412                        close: true,
 9413                        surround: true,
 9414                        newline: false,
 9415                    },
 9416                    BracketPair {
 9417                        start: "<".to_string(),
 9418                        end: ">".to_string(),
 9419                        close: false,
 9420                        surround: true,
 9421                        newline: true,
 9422                    },
 9423                ],
 9424                ..Default::default()
 9425            },
 9426            autoclose_before: "})]".to_string(),
 9427            ..Default::default()
 9428        },
 9429        Some(tree_sitter_rust::LANGUAGE.into()),
 9430    );
 9431    let language = Arc::new(language);
 9432
 9433    cx.language_registry().add(language.clone());
 9434    cx.update_buffer(|buffer, cx| {
 9435        buffer.set_language(Some(language), cx);
 9436    });
 9437
 9438    cx.set_state(
 9439        &r#"
 9440            fn main() {
 9441                sampleˇ
 9442            }
 9443        "#
 9444        .unindent(),
 9445    );
 9446
 9447    cx.update_editor(|editor, window, cx| {
 9448        editor.handle_input("(", window, cx);
 9449    });
 9450    cx.assert_editor_state(
 9451        &"
 9452            fn main() {
 9453                sample(ˇ)
 9454            }
 9455        "
 9456        .unindent(),
 9457    );
 9458
 9459    let mocked_response = lsp::SignatureHelp {
 9460        signatures: vec![lsp::SignatureInformation {
 9461            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9462            documentation: None,
 9463            parameters: Some(vec![
 9464                lsp::ParameterInformation {
 9465                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9466                    documentation: None,
 9467                },
 9468                lsp::ParameterInformation {
 9469                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9470                    documentation: None,
 9471                },
 9472            ]),
 9473            active_parameter: None,
 9474        }],
 9475        active_signature: Some(0),
 9476        active_parameter: Some(0),
 9477    };
 9478    handle_signature_help_request(&mut cx, mocked_response).await;
 9479
 9480    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9481        .await;
 9482
 9483    cx.editor(|editor, _, _| {
 9484        let signature_help_state = editor.signature_help_state.popover().cloned();
 9485        assert_eq!(
 9486            signature_help_state.unwrap().label,
 9487            "param1: u8, param2: u8"
 9488        );
 9489    });
 9490}
 9491
 9492#[gpui::test]
 9493async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9494    init_test(cx, |_| {});
 9495
 9496    cx.update(|cx| {
 9497        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9498            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9499                settings.auto_signature_help = Some(false);
 9500                settings.show_signature_help_after_edits = Some(false);
 9501            });
 9502        });
 9503    });
 9504
 9505    let mut cx = EditorLspTestContext::new_rust(
 9506        lsp::ServerCapabilities {
 9507            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9508                ..Default::default()
 9509            }),
 9510            ..Default::default()
 9511        },
 9512        cx,
 9513    )
 9514    .await;
 9515
 9516    let language = Language::new(
 9517        LanguageConfig {
 9518            name: "Rust".into(),
 9519            brackets: BracketPairConfig {
 9520                pairs: vec![
 9521                    BracketPair {
 9522                        start: "{".to_string(),
 9523                        end: "}".to_string(),
 9524                        close: true,
 9525                        surround: true,
 9526                        newline: true,
 9527                    },
 9528                    BracketPair {
 9529                        start: "(".to_string(),
 9530                        end: ")".to_string(),
 9531                        close: true,
 9532                        surround: true,
 9533                        newline: true,
 9534                    },
 9535                    BracketPair {
 9536                        start: "/*".to_string(),
 9537                        end: " */".to_string(),
 9538                        close: true,
 9539                        surround: true,
 9540                        newline: true,
 9541                    },
 9542                    BracketPair {
 9543                        start: "[".to_string(),
 9544                        end: "]".to_string(),
 9545                        close: false,
 9546                        surround: false,
 9547                        newline: true,
 9548                    },
 9549                    BracketPair {
 9550                        start: "\"".to_string(),
 9551                        end: "\"".to_string(),
 9552                        close: true,
 9553                        surround: true,
 9554                        newline: false,
 9555                    },
 9556                    BracketPair {
 9557                        start: "<".to_string(),
 9558                        end: ">".to_string(),
 9559                        close: false,
 9560                        surround: true,
 9561                        newline: true,
 9562                    },
 9563                ],
 9564                ..Default::default()
 9565            },
 9566            autoclose_before: "})]".to_string(),
 9567            ..Default::default()
 9568        },
 9569        Some(tree_sitter_rust::LANGUAGE.into()),
 9570    );
 9571    let language = Arc::new(language);
 9572
 9573    cx.language_registry().add(language.clone());
 9574    cx.update_buffer(|buffer, cx| {
 9575        buffer.set_language(Some(language), cx);
 9576    });
 9577
 9578    // Ensure that signature_help is not called when no signature help is enabled.
 9579    cx.set_state(
 9580        &r#"
 9581            fn main() {
 9582                sampleˇ
 9583            }
 9584        "#
 9585        .unindent(),
 9586    );
 9587    cx.update_editor(|editor, window, cx| {
 9588        editor.handle_input("(", window, cx);
 9589    });
 9590    cx.assert_editor_state(
 9591        &"
 9592            fn main() {
 9593                sample(ˇ)
 9594            }
 9595        "
 9596        .unindent(),
 9597    );
 9598    cx.editor(|editor, _, _| {
 9599        assert!(editor.signature_help_state.task().is_none());
 9600    });
 9601
 9602    let mocked_response = lsp::SignatureHelp {
 9603        signatures: vec![lsp::SignatureInformation {
 9604            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9605            documentation: None,
 9606            parameters: Some(vec![
 9607                lsp::ParameterInformation {
 9608                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9609                    documentation: None,
 9610                },
 9611                lsp::ParameterInformation {
 9612                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9613                    documentation: None,
 9614                },
 9615            ]),
 9616            active_parameter: None,
 9617        }],
 9618        active_signature: Some(0),
 9619        active_parameter: Some(0),
 9620    };
 9621
 9622    // Ensure that signature_help is called when enabled afte edits
 9623    cx.update(|_, cx| {
 9624        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9625            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9626                settings.auto_signature_help = Some(false);
 9627                settings.show_signature_help_after_edits = Some(true);
 9628            });
 9629        });
 9630    });
 9631    cx.set_state(
 9632        &r#"
 9633            fn main() {
 9634                sampleˇ
 9635            }
 9636        "#
 9637        .unindent(),
 9638    );
 9639    cx.update_editor(|editor, window, cx| {
 9640        editor.handle_input("(", window, cx);
 9641    });
 9642    cx.assert_editor_state(
 9643        &"
 9644            fn main() {
 9645                sample(ˇ)
 9646            }
 9647        "
 9648        .unindent(),
 9649    );
 9650    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9651    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9652        .await;
 9653    cx.update_editor(|editor, _, _| {
 9654        let signature_help_state = editor.signature_help_state.popover().cloned();
 9655        assert!(signature_help_state.is_some());
 9656        assert_eq!(
 9657            signature_help_state.unwrap().label,
 9658            "param1: u8, param2: u8"
 9659        );
 9660        editor.signature_help_state = SignatureHelpState::default();
 9661    });
 9662
 9663    // Ensure that signature_help is called when auto signature help override is enabled
 9664    cx.update(|_, cx| {
 9665        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9666            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9667                settings.auto_signature_help = Some(true);
 9668                settings.show_signature_help_after_edits = Some(false);
 9669            });
 9670        });
 9671    });
 9672    cx.set_state(
 9673        &r#"
 9674            fn main() {
 9675                sampleˇ
 9676            }
 9677        "#
 9678        .unindent(),
 9679    );
 9680    cx.update_editor(|editor, window, cx| {
 9681        editor.handle_input("(", window, cx);
 9682    });
 9683    cx.assert_editor_state(
 9684        &"
 9685            fn main() {
 9686                sample(ˇ)
 9687            }
 9688        "
 9689        .unindent(),
 9690    );
 9691    handle_signature_help_request(&mut cx, mocked_response).await;
 9692    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9693        .await;
 9694    cx.editor(|editor, _, _| {
 9695        let signature_help_state = editor.signature_help_state.popover().cloned();
 9696        assert!(signature_help_state.is_some());
 9697        assert_eq!(
 9698            signature_help_state.unwrap().label,
 9699            "param1: u8, param2: u8"
 9700        );
 9701    });
 9702}
 9703
 9704#[gpui::test]
 9705async fn test_signature_help(cx: &mut TestAppContext) {
 9706    init_test(cx, |_| {});
 9707    cx.update(|cx| {
 9708        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9709            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9710                settings.auto_signature_help = Some(true);
 9711            });
 9712        });
 9713    });
 9714
 9715    let mut cx = EditorLspTestContext::new_rust(
 9716        lsp::ServerCapabilities {
 9717            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9718                ..Default::default()
 9719            }),
 9720            ..Default::default()
 9721        },
 9722        cx,
 9723    )
 9724    .await;
 9725
 9726    // A test that directly calls `show_signature_help`
 9727    cx.update_editor(|editor, window, cx| {
 9728        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9729    });
 9730
 9731    let mocked_response = lsp::SignatureHelp {
 9732        signatures: vec![lsp::SignatureInformation {
 9733            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9734            documentation: None,
 9735            parameters: Some(vec![
 9736                lsp::ParameterInformation {
 9737                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9738                    documentation: None,
 9739                },
 9740                lsp::ParameterInformation {
 9741                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9742                    documentation: None,
 9743                },
 9744            ]),
 9745            active_parameter: None,
 9746        }],
 9747        active_signature: Some(0),
 9748        active_parameter: Some(0),
 9749    };
 9750    handle_signature_help_request(&mut cx, mocked_response).await;
 9751
 9752    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9753        .await;
 9754
 9755    cx.editor(|editor, _, _| {
 9756        let signature_help_state = editor.signature_help_state.popover().cloned();
 9757        assert!(signature_help_state.is_some());
 9758        assert_eq!(
 9759            signature_help_state.unwrap().label,
 9760            "param1: u8, param2: u8"
 9761        );
 9762    });
 9763
 9764    // When exiting outside from inside the brackets, `signature_help` is closed.
 9765    cx.set_state(indoc! {"
 9766        fn main() {
 9767            sample(ˇ);
 9768        }
 9769
 9770        fn sample(param1: u8, param2: u8) {}
 9771    "});
 9772
 9773    cx.update_editor(|editor, window, cx| {
 9774        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9775    });
 9776
 9777    let mocked_response = lsp::SignatureHelp {
 9778        signatures: Vec::new(),
 9779        active_signature: None,
 9780        active_parameter: None,
 9781    };
 9782    handle_signature_help_request(&mut cx, mocked_response).await;
 9783
 9784    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9785        .await;
 9786
 9787    cx.editor(|editor, _, _| {
 9788        assert!(!editor.signature_help_state.is_shown());
 9789    });
 9790
 9791    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9792    cx.set_state(indoc! {"
 9793        fn main() {
 9794            sample(ˇ);
 9795        }
 9796
 9797        fn sample(param1: u8, param2: u8) {}
 9798    "});
 9799
 9800    let mocked_response = lsp::SignatureHelp {
 9801        signatures: vec![lsp::SignatureInformation {
 9802            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9803            documentation: None,
 9804            parameters: Some(vec![
 9805                lsp::ParameterInformation {
 9806                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9807                    documentation: None,
 9808                },
 9809                lsp::ParameterInformation {
 9810                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9811                    documentation: None,
 9812                },
 9813            ]),
 9814            active_parameter: None,
 9815        }],
 9816        active_signature: Some(0),
 9817        active_parameter: Some(0),
 9818    };
 9819    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9820    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9821        .await;
 9822    cx.editor(|editor, _, _| {
 9823        assert!(editor.signature_help_state.is_shown());
 9824    });
 9825
 9826    // Restore the popover with more parameter input
 9827    cx.set_state(indoc! {"
 9828        fn main() {
 9829            sample(param1, param2ˇ);
 9830        }
 9831
 9832        fn sample(param1: u8, param2: u8) {}
 9833    "});
 9834
 9835    let mocked_response = lsp::SignatureHelp {
 9836        signatures: vec![lsp::SignatureInformation {
 9837            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9838            documentation: None,
 9839            parameters: Some(vec![
 9840                lsp::ParameterInformation {
 9841                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9842                    documentation: None,
 9843                },
 9844                lsp::ParameterInformation {
 9845                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9846                    documentation: None,
 9847                },
 9848            ]),
 9849            active_parameter: None,
 9850        }],
 9851        active_signature: Some(0),
 9852        active_parameter: Some(1),
 9853    };
 9854    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9855    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9856        .await;
 9857
 9858    // When selecting a range, the popover is gone.
 9859    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9860    cx.update_editor(|editor, window, cx| {
 9861        editor.change_selections(None, window, cx, |s| {
 9862            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9863        })
 9864    });
 9865    cx.assert_editor_state(indoc! {"
 9866        fn main() {
 9867            sample(param1, «ˇparam2»);
 9868        }
 9869
 9870        fn sample(param1: u8, param2: u8) {}
 9871    "});
 9872    cx.editor(|editor, _, _| {
 9873        assert!(!editor.signature_help_state.is_shown());
 9874    });
 9875
 9876    // When unselecting again, the popover is back if within the brackets.
 9877    cx.update_editor(|editor, window, cx| {
 9878        editor.change_selections(None, window, cx, |s| {
 9879            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9880        })
 9881    });
 9882    cx.assert_editor_state(indoc! {"
 9883        fn main() {
 9884            sample(param1, ˇparam2);
 9885        }
 9886
 9887        fn sample(param1: u8, param2: u8) {}
 9888    "});
 9889    handle_signature_help_request(&mut cx, mocked_response).await;
 9890    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9891        .await;
 9892    cx.editor(|editor, _, _| {
 9893        assert!(editor.signature_help_state.is_shown());
 9894    });
 9895
 9896    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9897    cx.update_editor(|editor, window, cx| {
 9898        editor.change_selections(None, window, cx, |s| {
 9899            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9900            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9901        })
 9902    });
 9903    cx.assert_editor_state(indoc! {"
 9904        fn main() {
 9905            sample(param1, ˇparam2);
 9906        }
 9907
 9908        fn sample(param1: u8, param2: u8) {}
 9909    "});
 9910
 9911    let mocked_response = lsp::SignatureHelp {
 9912        signatures: vec![lsp::SignatureInformation {
 9913            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9914            documentation: None,
 9915            parameters: Some(vec![
 9916                lsp::ParameterInformation {
 9917                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9918                    documentation: None,
 9919                },
 9920                lsp::ParameterInformation {
 9921                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9922                    documentation: None,
 9923                },
 9924            ]),
 9925            active_parameter: None,
 9926        }],
 9927        active_signature: Some(0),
 9928        active_parameter: Some(1),
 9929    };
 9930    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9931    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9932        .await;
 9933    cx.update_editor(|editor, _, cx| {
 9934        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9935    });
 9936    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9937        .await;
 9938    cx.update_editor(|editor, window, cx| {
 9939        editor.change_selections(None, window, cx, |s| {
 9940            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9941        })
 9942    });
 9943    cx.assert_editor_state(indoc! {"
 9944        fn main() {
 9945            sample(param1, «ˇparam2»);
 9946        }
 9947
 9948        fn sample(param1: u8, param2: u8) {}
 9949    "});
 9950    cx.update_editor(|editor, window, cx| {
 9951        editor.change_selections(None, window, cx, |s| {
 9952            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9953        })
 9954    });
 9955    cx.assert_editor_state(indoc! {"
 9956        fn main() {
 9957            sample(param1, ˇparam2);
 9958        }
 9959
 9960        fn sample(param1: u8, param2: u8) {}
 9961    "});
 9962    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9963        .await;
 9964}
 9965
 9966#[gpui::test]
 9967async fn test_completion_mode(cx: &mut TestAppContext) {
 9968    init_test(cx, |_| {});
 9969    let mut cx = EditorLspTestContext::new_rust(
 9970        lsp::ServerCapabilities {
 9971            completion_provider: Some(lsp::CompletionOptions {
 9972                resolve_provider: Some(true),
 9973                ..Default::default()
 9974            }),
 9975            ..Default::default()
 9976        },
 9977        cx,
 9978    )
 9979    .await;
 9980
 9981    struct Run {
 9982        run_description: &'static str,
 9983        initial_state: String,
 9984        buffer_marked_text: String,
 9985        completion_text: &'static str,
 9986        expected_with_insert_mode: String,
 9987        expected_with_replace_mode: String,
 9988        expected_with_replace_subsequence_mode: String,
 9989        expected_with_replace_suffix_mode: String,
 9990    }
 9991
 9992    let runs = [
 9993        Run {
 9994            run_description: "Start of word matches completion text",
 9995            initial_state: "before ediˇ after".into(),
 9996            buffer_marked_text: "before <edi|> after".into(),
 9997            completion_text: "editor",
 9998            expected_with_insert_mode: "before editorˇ after".into(),
 9999            expected_with_replace_mode: "before editorˇ after".into(),
10000            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10001            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10002        },
10003        Run {
10004            run_description: "Accept same text at the middle of the word",
10005            initial_state: "before ediˇtor after".into(),
10006            buffer_marked_text: "before <edi|tor> after".into(),
10007            completion_text: "editor",
10008            expected_with_insert_mode: "before editorˇtor after".into(),
10009            expected_with_replace_mode: "before editorˇ after".into(),
10010            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10011            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10012        },
10013        Run {
10014            run_description: "End of word matches completion text -- cursor at end",
10015            initial_state: "before torˇ after".into(),
10016            buffer_marked_text: "before <tor|> after".into(),
10017            completion_text: "editor",
10018            expected_with_insert_mode: "before editorˇ after".into(),
10019            expected_with_replace_mode: "before editorˇ after".into(),
10020            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10021            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10022        },
10023        Run {
10024            run_description: "End of word matches completion text -- cursor at start",
10025            initial_state: "before ˇtor after".into(),
10026            buffer_marked_text: "before <|tor> after".into(),
10027            completion_text: "editor",
10028            expected_with_insert_mode: "before editorˇtor after".into(),
10029            expected_with_replace_mode: "before editorˇ after".into(),
10030            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10031            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10032        },
10033        Run {
10034            run_description: "Prepend text containing whitespace",
10035            initial_state: "pˇfield: bool".into(),
10036            buffer_marked_text: "<p|field>: bool".into(),
10037            completion_text: "pub ",
10038            expected_with_insert_mode: "pub ˇfield: bool".into(),
10039            expected_with_replace_mode: "pub ˇ: bool".into(),
10040            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10041            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10042        },
10043        Run {
10044            run_description: "Add element to start of list",
10045            initial_state: "[element_ˇelement_2]".into(),
10046            buffer_marked_text: "[<element_|element_2>]".into(),
10047            completion_text: "element_1",
10048            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10049            expected_with_replace_mode: "[element_1ˇ]".into(),
10050            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10051            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10052        },
10053        Run {
10054            run_description: "Add element to start of list -- first and second elements are equal",
10055            initial_state: "[elˇelement]".into(),
10056            buffer_marked_text: "[<el|element>]".into(),
10057            completion_text: "element",
10058            expected_with_insert_mode: "[elementˇelement]".into(),
10059            expected_with_replace_mode: "[elementˇ]".into(),
10060            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10061            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10062        },
10063        Run {
10064            run_description: "Ends with matching suffix",
10065            initial_state: "SubˇError".into(),
10066            buffer_marked_text: "<Sub|Error>".into(),
10067            completion_text: "SubscriptionError",
10068            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10069            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10070            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10071            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10072        },
10073        Run {
10074            run_description: "Suffix is a subsequence -- contiguous",
10075            initial_state: "SubˇErr".into(),
10076            buffer_marked_text: "<Sub|Err>".into(),
10077            completion_text: "SubscriptionError",
10078            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10079            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10080            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10081            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10082        },
10083        Run {
10084            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10085            initial_state: "Suˇscrirr".into(),
10086            buffer_marked_text: "<Su|scrirr>".into(),
10087            completion_text: "SubscriptionError",
10088            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10089            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10090            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10091            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10092        },
10093        Run {
10094            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10095            initial_state: "foo(indˇix)".into(),
10096            buffer_marked_text: "foo(<ind|ix>)".into(),
10097            completion_text: "node_index",
10098            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10099            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10100            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10101            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10102        },
10103    ];
10104
10105    for run in runs {
10106        let run_variations = [
10107            (LspInsertMode::Insert, run.expected_with_insert_mode),
10108            (LspInsertMode::Replace, run.expected_with_replace_mode),
10109            (
10110                LspInsertMode::ReplaceSubsequence,
10111                run.expected_with_replace_subsequence_mode,
10112            ),
10113            (
10114                LspInsertMode::ReplaceSuffix,
10115                run.expected_with_replace_suffix_mode,
10116            ),
10117        ];
10118
10119        for (lsp_insert_mode, expected_text) in run_variations {
10120            eprintln!(
10121                "run = {:?}, mode = {lsp_insert_mode:.?}",
10122                run.run_description,
10123            );
10124
10125            update_test_language_settings(&mut cx, |settings| {
10126                settings.defaults.completions = Some(CompletionSettings {
10127                    lsp_insert_mode,
10128                    words: WordsCompletionMode::Disabled,
10129                    lsp: true,
10130                    lsp_fetch_timeout_ms: 0,
10131                });
10132            });
10133
10134            cx.set_state(&run.initial_state);
10135            cx.update_editor(|editor, window, cx| {
10136                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10137            });
10138
10139            let counter = Arc::new(AtomicUsize::new(0));
10140            handle_completion_request_with_insert_and_replace(
10141                &mut cx,
10142                &run.buffer_marked_text,
10143                vec![run.completion_text],
10144                counter.clone(),
10145            )
10146            .await;
10147            cx.condition(|editor, _| editor.context_menu_visible())
10148                .await;
10149            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10150
10151            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10152                editor
10153                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10154                    .unwrap()
10155            });
10156            cx.assert_editor_state(&expected_text);
10157            handle_resolve_completion_request(&mut cx, None).await;
10158            apply_additional_edits.await.unwrap();
10159        }
10160    }
10161}
10162
10163#[gpui::test]
10164async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10165    init_test(cx, |_| {});
10166    let mut cx = EditorLspTestContext::new_rust(
10167        lsp::ServerCapabilities {
10168            completion_provider: Some(lsp::CompletionOptions {
10169                resolve_provider: Some(true),
10170                ..Default::default()
10171            }),
10172            ..Default::default()
10173        },
10174        cx,
10175    )
10176    .await;
10177
10178    let initial_state = "SubˇError";
10179    let buffer_marked_text = "<Sub|Error>";
10180    let completion_text = "SubscriptionError";
10181    let expected_with_insert_mode = "SubscriptionErrorˇError";
10182    let expected_with_replace_mode = "SubscriptionErrorˇ";
10183
10184    update_test_language_settings(&mut cx, |settings| {
10185        settings.defaults.completions = Some(CompletionSettings {
10186            words: WordsCompletionMode::Disabled,
10187            // set the opposite here to ensure that the action is overriding the default behavior
10188            lsp_insert_mode: LspInsertMode::Insert,
10189            lsp: true,
10190            lsp_fetch_timeout_ms: 0,
10191        });
10192    });
10193
10194    cx.set_state(initial_state);
10195    cx.update_editor(|editor, window, cx| {
10196        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10197    });
10198
10199    let counter = Arc::new(AtomicUsize::new(0));
10200    handle_completion_request_with_insert_and_replace(
10201        &mut cx,
10202        &buffer_marked_text,
10203        vec![completion_text],
10204        counter.clone(),
10205    )
10206    .await;
10207    cx.condition(|editor, _| editor.context_menu_visible())
10208        .await;
10209    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10210
10211    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10212        editor
10213            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10214            .unwrap()
10215    });
10216    cx.assert_editor_state(&expected_with_replace_mode);
10217    handle_resolve_completion_request(&mut cx, None).await;
10218    apply_additional_edits.await.unwrap();
10219
10220    update_test_language_settings(&mut cx, |settings| {
10221        settings.defaults.completions = Some(CompletionSettings {
10222            words: WordsCompletionMode::Disabled,
10223            // set the opposite here to ensure that the action is overriding the default behavior
10224            lsp_insert_mode: LspInsertMode::Replace,
10225            lsp: true,
10226            lsp_fetch_timeout_ms: 0,
10227        });
10228    });
10229
10230    cx.set_state(initial_state);
10231    cx.update_editor(|editor, window, cx| {
10232        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10233    });
10234    handle_completion_request_with_insert_and_replace(
10235        &mut cx,
10236        &buffer_marked_text,
10237        vec![completion_text],
10238        counter.clone(),
10239    )
10240    .await;
10241    cx.condition(|editor, _| editor.context_menu_visible())
10242        .await;
10243    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10244
10245    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10246        editor
10247            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10248            .unwrap()
10249    });
10250    cx.assert_editor_state(&expected_with_insert_mode);
10251    handle_resolve_completion_request(&mut cx, None).await;
10252    apply_additional_edits.await.unwrap();
10253}
10254
10255#[gpui::test]
10256async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10257    init_test(cx, |_| {});
10258    let mut cx = EditorLspTestContext::new_rust(
10259        lsp::ServerCapabilities {
10260            completion_provider: Some(lsp::CompletionOptions {
10261                resolve_provider: Some(true),
10262                ..Default::default()
10263            }),
10264            ..Default::default()
10265        },
10266        cx,
10267    )
10268    .await;
10269
10270    // scenario: surrounding text matches completion text
10271    let completion_text = "to_offset";
10272    let initial_state = indoc! {"
10273        1. buf.to_offˇsuffix
10274        2. buf.to_offˇsuf
10275        3. buf.to_offˇfix
10276        4. buf.to_offˇ
10277        5. into_offˇensive
10278        6. ˇsuffix
10279        7. let ˇ //
10280        8. aaˇzz
10281        9. buf.to_off«zzzzzˇ»suffix
10282        10. buf.«ˇzzzzz»suffix
10283        11. to_off«ˇzzzzz»
10284
10285        buf.to_offˇsuffix  // newest cursor
10286    "};
10287    let completion_marked_buffer = indoc! {"
10288        1. buf.to_offsuffix
10289        2. buf.to_offsuf
10290        3. buf.to_offfix
10291        4. buf.to_off
10292        5. into_offensive
10293        6. suffix
10294        7. let  //
10295        8. aazz
10296        9. buf.to_offzzzzzsuffix
10297        10. buf.zzzzzsuffix
10298        11. to_offzzzzz
10299
10300        buf.<to_off|suffix>  // newest cursor
10301    "};
10302    let expected = indoc! {"
10303        1. buf.to_offsetˇ
10304        2. buf.to_offsetˇsuf
10305        3. buf.to_offsetˇfix
10306        4. buf.to_offsetˇ
10307        5. into_offsetˇensive
10308        6. to_offsetˇsuffix
10309        7. let to_offsetˇ //
10310        8. aato_offsetˇzz
10311        9. buf.to_offsetˇ
10312        10. buf.to_offsetˇsuffix
10313        11. to_offsetˇ
10314
10315        buf.to_offsetˇ  // newest cursor
10316    "};
10317    cx.set_state(initial_state);
10318    cx.update_editor(|editor, window, cx| {
10319        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10320    });
10321    handle_completion_request_with_insert_and_replace(
10322        &mut cx,
10323        completion_marked_buffer,
10324        vec![completion_text],
10325        Arc::new(AtomicUsize::new(0)),
10326    )
10327    .await;
10328    cx.condition(|editor, _| editor.context_menu_visible())
10329        .await;
10330    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10331        editor
10332            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10333            .unwrap()
10334    });
10335    cx.assert_editor_state(expected);
10336    handle_resolve_completion_request(&mut cx, None).await;
10337    apply_additional_edits.await.unwrap();
10338
10339    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10340    let completion_text = "foo_and_bar";
10341    let initial_state = indoc! {"
10342        1. ooanbˇ
10343        2. zooanbˇ
10344        3. ooanbˇz
10345        4. zooanbˇz
10346        5. ooanˇ
10347        6. oanbˇ
10348
10349        ooanbˇ
10350    "};
10351    let completion_marked_buffer = indoc! {"
10352        1. ooanb
10353        2. zooanb
10354        3. ooanbz
10355        4. zooanbz
10356        5. ooan
10357        6. oanb
10358
10359        <ooanb|>
10360    "};
10361    let expected = indoc! {"
10362        1. foo_and_barˇ
10363        2. zfoo_and_barˇ
10364        3. foo_and_barˇz
10365        4. zfoo_and_barˇz
10366        5. ooanfoo_and_barˇ
10367        6. oanbfoo_and_barˇ
10368
10369        foo_and_barˇ
10370    "};
10371    cx.set_state(initial_state);
10372    cx.update_editor(|editor, window, cx| {
10373        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10374    });
10375    handle_completion_request_with_insert_and_replace(
10376        &mut cx,
10377        completion_marked_buffer,
10378        vec![completion_text],
10379        Arc::new(AtomicUsize::new(0)),
10380    )
10381    .await;
10382    cx.condition(|editor, _| editor.context_menu_visible())
10383        .await;
10384    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10385        editor
10386            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10387            .unwrap()
10388    });
10389    cx.assert_editor_state(expected);
10390    handle_resolve_completion_request(&mut cx, None).await;
10391    apply_additional_edits.await.unwrap();
10392
10393    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10394    // (expects the same as if it was inserted at the end)
10395    let completion_text = "foo_and_bar";
10396    let initial_state = indoc! {"
10397        1. ooˇanb
10398        2. zooˇanb
10399        3. ooˇanbz
10400        4. zooˇanbz
10401
10402        ooˇanb
10403    "};
10404    let completion_marked_buffer = indoc! {"
10405        1. ooanb
10406        2. zooanb
10407        3. ooanbz
10408        4. zooanbz
10409
10410        <oo|anb>
10411    "};
10412    let expected = indoc! {"
10413        1. foo_and_barˇ
10414        2. zfoo_and_barˇ
10415        3. foo_and_barˇz
10416        4. zfoo_and_barˇz
10417
10418        foo_and_barˇ
10419    "};
10420    cx.set_state(initial_state);
10421    cx.update_editor(|editor, window, cx| {
10422        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10423    });
10424    handle_completion_request_with_insert_and_replace(
10425        &mut cx,
10426        completion_marked_buffer,
10427        vec![completion_text],
10428        Arc::new(AtomicUsize::new(0)),
10429    )
10430    .await;
10431    cx.condition(|editor, _| editor.context_menu_visible())
10432        .await;
10433    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10434        editor
10435            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10436            .unwrap()
10437    });
10438    cx.assert_editor_state(expected);
10439    handle_resolve_completion_request(&mut cx, None).await;
10440    apply_additional_edits.await.unwrap();
10441}
10442
10443// This used to crash
10444#[gpui::test]
10445async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10446    init_test(cx, |_| {});
10447
10448    let buffer_text = indoc! {"
10449        fn main() {
10450            10.satu;
10451
10452            //
10453            // separate cursors so they open in different excerpts (manually reproducible)
10454            //
10455
10456            10.satu20;
10457        }
10458    "};
10459    let multibuffer_text_with_selections = indoc! {"
10460        fn main() {
10461            10.satuˇ;
10462
10463            //
10464
10465            //
10466
10467            10.satuˇ20;
10468        }
10469    "};
10470    let expected_multibuffer = indoc! {"
10471        fn main() {
10472            10.saturating_sub()ˇ;
10473
10474            //
10475
10476            //
10477
10478            10.saturating_sub()ˇ;
10479        }
10480    "};
10481
10482    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10483    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10484
10485    let fs = FakeFs::new(cx.executor());
10486    fs.insert_tree(
10487        path!("/a"),
10488        json!({
10489            "main.rs": buffer_text,
10490        }),
10491    )
10492    .await;
10493
10494    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10495    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10496    language_registry.add(rust_lang());
10497    let mut fake_servers = language_registry.register_fake_lsp(
10498        "Rust",
10499        FakeLspAdapter {
10500            capabilities: lsp::ServerCapabilities {
10501                completion_provider: Some(lsp::CompletionOptions {
10502                    resolve_provider: None,
10503                    ..lsp::CompletionOptions::default()
10504                }),
10505                ..lsp::ServerCapabilities::default()
10506            },
10507            ..FakeLspAdapter::default()
10508        },
10509    );
10510    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10511    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10512    let buffer = project
10513        .update(cx, |project, cx| {
10514            project.open_local_buffer(path!("/a/main.rs"), cx)
10515        })
10516        .await
10517        .unwrap();
10518
10519    let multi_buffer = cx.new(|cx| {
10520        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10521        multi_buffer.push_excerpts(
10522            buffer.clone(),
10523            [ExcerptRange::new(0..first_excerpt_end)],
10524            cx,
10525        );
10526        multi_buffer.push_excerpts(
10527            buffer.clone(),
10528            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10529            cx,
10530        );
10531        multi_buffer
10532    });
10533
10534    let editor = workspace
10535        .update(cx, |_, window, cx| {
10536            cx.new(|cx| {
10537                Editor::new(
10538                    EditorMode::Full {
10539                        scale_ui_elements_with_buffer_font_size: false,
10540                        show_active_line_background: false,
10541                        sized_by_content: false,
10542                    },
10543                    multi_buffer.clone(),
10544                    Some(project.clone()),
10545                    window,
10546                    cx,
10547                )
10548            })
10549        })
10550        .unwrap();
10551
10552    let pane = workspace
10553        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10554        .unwrap();
10555    pane.update_in(cx, |pane, window, cx| {
10556        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10557    });
10558
10559    let fake_server = fake_servers.next().await.unwrap();
10560
10561    editor.update_in(cx, |editor, window, cx| {
10562        editor.change_selections(None, window, cx, |s| {
10563            s.select_ranges([
10564                Point::new(1, 11)..Point::new(1, 11),
10565                Point::new(7, 11)..Point::new(7, 11),
10566            ])
10567        });
10568
10569        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10570    });
10571
10572    editor.update_in(cx, |editor, window, cx| {
10573        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10574    });
10575
10576    fake_server
10577        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10578            let completion_item = lsp::CompletionItem {
10579                label: "saturating_sub()".into(),
10580                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10581                    lsp::InsertReplaceEdit {
10582                        new_text: "saturating_sub()".to_owned(),
10583                        insert: lsp::Range::new(
10584                            lsp::Position::new(7, 7),
10585                            lsp::Position::new(7, 11),
10586                        ),
10587                        replace: lsp::Range::new(
10588                            lsp::Position::new(7, 7),
10589                            lsp::Position::new(7, 13),
10590                        ),
10591                    },
10592                )),
10593                ..lsp::CompletionItem::default()
10594            };
10595
10596            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10597        })
10598        .next()
10599        .await
10600        .unwrap();
10601
10602    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10603        .await;
10604
10605    editor
10606        .update_in(cx, |editor, window, cx| {
10607            editor
10608                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10609                .unwrap()
10610        })
10611        .await
10612        .unwrap();
10613
10614    editor.update(cx, |editor, cx| {
10615        assert_text_with_selections(editor, expected_multibuffer, cx);
10616    })
10617}
10618
10619#[gpui::test]
10620async fn test_completion(cx: &mut TestAppContext) {
10621    init_test(cx, |_| {});
10622
10623    let mut cx = EditorLspTestContext::new_rust(
10624        lsp::ServerCapabilities {
10625            completion_provider: Some(lsp::CompletionOptions {
10626                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10627                resolve_provider: Some(true),
10628                ..Default::default()
10629            }),
10630            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10631            ..Default::default()
10632        },
10633        cx,
10634    )
10635    .await;
10636    let counter = Arc::new(AtomicUsize::new(0));
10637
10638    cx.set_state(indoc! {"
10639        oneˇ
10640        two
10641        three
10642    "});
10643    cx.simulate_keystroke(".");
10644    handle_completion_request(
10645        &mut cx,
10646        indoc! {"
10647            one.|<>
10648            two
10649            three
10650        "},
10651        vec!["first_completion", "second_completion"],
10652        counter.clone(),
10653    )
10654    .await;
10655    cx.condition(|editor, _| editor.context_menu_visible())
10656        .await;
10657    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10658
10659    let _handler = handle_signature_help_request(
10660        &mut cx,
10661        lsp::SignatureHelp {
10662            signatures: vec![lsp::SignatureInformation {
10663                label: "test signature".to_string(),
10664                documentation: None,
10665                parameters: Some(vec![lsp::ParameterInformation {
10666                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10667                    documentation: None,
10668                }]),
10669                active_parameter: None,
10670            }],
10671            active_signature: None,
10672            active_parameter: None,
10673        },
10674    );
10675    cx.update_editor(|editor, window, cx| {
10676        assert!(
10677            !editor.signature_help_state.is_shown(),
10678            "No signature help was called for"
10679        );
10680        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10681    });
10682    cx.run_until_parked();
10683    cx.update_editor(|editor, _, _| {
10684        assert!(
10685            !editor.signature_help_state.is_shown(),
10686            "No signature help should be shown when completions menu is open"
10687        );
10688    });
10689
10690    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10691        editor.context_menu_next(&Default::default(), window, cx);
10692        editor
10693            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10694            .unwrap()
10695    });
10696    cx.assert_editor_state(indoc! {"
10697        one.second_completionˇ
10698        two
10699        three
10700    "});
10701
10702    handle_resolve_completion_request(
10703        &mut cx,
10704        Some(vec![
10705            (
10706                //This overlaps with the primary completion edit which is
10707                //misbehavior from the LSP spec, test that we filter it out
10708                indoc! {"
10709                    one.second_ˇcompletion
10710                    two
10711                    threeˇ
10712                "},
10713                "overlapping additional edit",
10714            ),
10715            (
10716                indoc! {"
10717                    one.second_completion
10718                    two
10719                    threeˇ
10720                "},
10721                "\nadditional edit",
10722            ),
10723        ]),
10724    )
10725    .await;
10726    apply_additional_edits.await.unwrap();
10727    cx.assert_editor_state(indoc! {"
10728        one.second_completionˇ
10729        two
10730        three
10731        additional edit
10732    "});
10733
10734    cx.set_state(indoc! {"
10735        one.second_completion
10736        twoˇ
10737        threeˇ
10738        additional edit
10739    "});
10740    cx.simulate_keystroke(" ");
10741    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10742    cx.simulate_keystroke("s");
10743    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10744
10745    cx.assert_editor_state(indoc! {"
10746        one.second_completion
10747        two sˇ
10748        three sˇ
10749        additional edit
10750    "});
10751    handle_completion_request(
10752        &mut cx,
10753        indoc! {"
10754            one.second_completion
10755            two s
10756            three <s|>
10757            additional edit
10758        "},
10759        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10760        counter.clone(),
10761    )
10762    .await;
10763    cx.condition(|editor, _| editor.context_menu_visible())
10764        .await;
10765    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10766
10767    cx.simulate_keystroke("i");
10768
10769    handle_completion_request(
10770        &mut cx,
10771        indoc! {"
10772            one.second_completion
10773            two si
10774            three <si|>
10775            additional edit
10776        "},
10777        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10778        counter.clone(),
10779    )
10780    .await;
10781    cx.condition(|editor, _| editor.context_menu_visible())
10782        .await;
10783    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10784
10785    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10786        editor
10787            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10788            .unwrap()
10789    });
10790    cx.assert_editor_state(indoc! {"
10791        one.second_completion
10792        two sixth_completionˇ
10793        three sixth_completionˇ
10794        additional edit
10795    "});
10796
10797    apply_additional_edits.await.unwrap();
10798
10799    update_test_language_settings(&mut cx, |settings| {
10800        settings.defaults.show_completions_on_input = Some(false);
10801    });
10802    cx.set_state("editorˇ");
10803    cx.simulate_keystroke(".");
10804    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10805    cx.simulate_keystrokes("c l o");
10806    cx.assert_editor_state("editor.cloˇ");
10807    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10808    cx.update_editor(|editor, window, cx| {
10809        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10810    });
10811    handle_completion_request(
10812        &mut cx,
10813        "editor.<clo|>",
10814        vec!["close", "clobber"],
10815        counter.clone(),
10816    )
10817    .await;
10818    cx.condition(|editor, _| editor.context_menu_visible())
10819        .await;
10820    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10821
10822    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10823        editor
10824            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10825            .unwrap()
10826    });
10827    cx.assert_editor_state("editor.closeˇ");
10828    handle_resolve_completion_request(&mut cx, None).await;
10829    apply_additional_edits.await.unwrap();
10830}
10831
10832#[gpui::test]
10833async fn test_word_completion(cx: &mut TestAppContext) {
10834    let lsp_fetch_timeout_ms = 10;
10835    init_test(cx, |language_settings| {
10836        language_settings.defaults.completions = Some(CompletionSettings {
10837            words: WordsCompletionMode::Fallback,
10838            lsp: true,
10839            lsp_fetch_timeout_ms: 10,
10840            lsp_insert_mode: LspInsertMode::Insert,
10841        });
10842    });
10843
10844    let mut cx = EditorLspTestContext::new_rust(
10845        lsp::ServerCapabilities {
10846            completion_provider: Some(lsp::CompletionOptions {
10847                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10848                ..lsp::CompletionOptions::default()
10849            }),
10850            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10851            ..lsp::ServerCapabilities::default()
10852        },
10853        cx,
10854    )
10855    .await;
10856
10857    let throttle_completions = Arc::new(AtomicBool::new(false));
10858
10859    let lsp_throttle_completions = throttle_completions.clone();
10860    let _completion_requests_handler =
10861        cx.lsp
10862            .server
10863            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10864                let lsp_throttle_completions = lsp_throttle_completions.clone();
10865                let cx = cx.clone();
10866                async move {
10867                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10868                        cx.background_executor()
10869                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10870                            .await;
10871                    }
10872                    Ok(Some(lsp::CompletionResponse::Array(vec![
10873                        lsp::CompletionItem {
10874                            label: "first".into(),
10875                            ..lsp::CompletionItem::default()
10876                        },
10877                        lsp::CompletionItem {
10878                            label: "last".into(),
10879                            ..lsp::CompletionItem::default()
10880                        },
10881                    ])))
10882                }
10883            });
10884
10885    cx.set_state(indoc! {"
10886        oneˇ
10887        two
10888        three
10889    "});
10890    cx.simulate_keystroke(".");
10891    cx.executor().run_until_parked();
10892    cx.condition(|editor, _| editor.context_menu_visible())
10893        .await;
10894    cx.update_editor(|editor, window, cx| {
10895        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10896        {
10897            assert_eq!(
10898                completion_menu_entries(&menu),
10899                &["first", "last"],
10900                "When LSP server is fast to reply, no fallback word completions are used"
10901            );
10902        } else {
10903            panic!("expected completion menu to be open");
10904        }
10905        editor.cancel(&Cancel, window, cx);
10906    });
10907    cx.executor().run_until_parked();
10908    cx.condition(|editor, _| !editor.context_menu_visible())
10909        .await;
10910
10911    throttle_completions.store(true, atomic::Ordering::Release);
10912    cx.simulate_keystroke(".");
10913    cx.executor()
10914        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10915    cx.executor().run_until_parked();
10916    cx.condition(|editor, _| editor.context_menu_visible())
10917        .await;
10918    cx.update_editor(|editor, _, _| {
10919        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10920        {
10921            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10922                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10923        } else {
10924            panic!("expected completion menu to be open");
10925        }
10926    });
10927}
10928
10929#[gpui::test]
10930async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10931    init_test(cx, |language_settings| {
10932        language_settings.defaults.completions = Some(CompletionSettings {
10933            words: WordsCompletionMode::Enabled,
10934            lsp: true,
10935            lsp_fetch_timeout_ms: 0,
10936            lsp_insert_mode: LspInsertMode::Insert,
10937        });
10938    });
10939
10940    let mut cx = EditorLspTestContext::new_rust(
10941        lsp::ServerCapabilities {
10942            completion_provider: Some(lsp::CompletionOptions {
10943                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10944                ..lsp::CompletionOptions::default()
10945            }),
10946            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10947            ..lsp::ServerCapabilities::default()
10948        },
10949        cx,
10950    )
10951    .await;
10952
10953    let _completion_requests_handler =
10954        cx.lsp
10955            .server
10956            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10957                Ok(Some(lsp::CompletionResponse::Array(vec![
10958                    lsp::CompletionItem {
10959                        label: "first".into(),
10960                        ..lsp::CompletionItem::default()
10961                    },
10962                    lsp::CompletionItem {
10963                        label: "last".into(),
10964                        ..lsp::CompletionItem::default()
10965                    },
10966                ])))
10967            });
10968
10969    cx.set_state(indoc! {"ˇ
10970        first
10971        last
10972        second
10973    "});
10974    cx.simulate_keystroke(".");
10975    cx.executor().run_until_parked();
10976    cx.condition(|editor, _| editor.context_menu_visible())
10977        .await;
10978    cx.update_editor(|editor, _, _| {
10979        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10980        {
10981            assert_eq!(
10982                completion_menu_entries(&menu),
10983                &["first", "last", "second"],
10984                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10985            );
10986        } else {
10987            panic!("expected completion menu to be open");
10988        }
10989    });
10990}
10991
10992#[gpui::test]
10993async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10994    init_test(cx, |language_settings| {
10995        language_settings.defaults.completions = Some(CompletionSettings {
10996            words: WordsCompletionMode::Disabled,
10997            lsp: true,
10998            lsp_fetch_timeout_ms: 0,
10999            lsp_insert_mode: LspInsertMode::Insert,
11000        });
11001    });
11002
11003    let mut cx = EditorLspTestContext::new_rust(
11004        lsp::ServerCapabilities {
11005            completion_provider: Some(lsp::CompletionOptions {
11006                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11007                ..lsp::CompletionOptions::default()
11008            }),
11009            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11010            ..lsp::ServerCapabilities::default()
11011        },
11012        cx,
11013    )
11014    .await;
11015
11016    let _completion_requests_handler =
11017        cx.lsp
11018            .server
11019            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11020                panic!("LSP completions should not be queried when dealing with word completions")
11021            });
11022
11023    cx.set_state(indoc! {"ˇ
11024        first
11025        last
11026        second
11027    "});
11028    cx.update_editor(|editor, window, cx| {
11029        editor.show_word_completions(&ShowWordCompletions, window, cx);
11030    });
11031    cx.executor().run_until_parked();
11032    cx.condition(|editor, _| editor.context_menu_visible())
11033        .await;
11034    cx.update_editor(|editor, _, _| {
11035        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11036        {
11037            assert_eq!(
11038                completion_menu_entries(&menu),
11039                &["first", "last", "second"],
11040                "`ShowWordCompletions` action should show word completions"
11041            );
11042        } else {
11043            panic!("expected completion menu to be open");
11044        }
11045    });
11046
11047    cx.simulate_keystroke("l");
11048    cx.executor().run_until_parked();
11049    cx.condition(|editor, _| editor.context_menu_visible())
11050        .await;
11051    cx.update_editor(|editor, _, _| {
11052        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11053        {
11054            assert_eq!(
11055                completion_menu_entries(&menu),
11056                &["last"],
11057                "After showing word completions, further editing should filter them and not query the LSP"
11058            );
11059        } else {
11060            panic!("expected completion menu to be open");
11061        }
11062    });
11063}
11064
11065#[gpui::test]
11066async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11067    init_test(cx, |language_settings| {
11068        language_settings.defaults.completions = Some(CompletionSettings {
11069            words: WordsCompletionMode::Fallback,
11070            lsp: false,
11071            lsp_fetch_timeout_ms: 0,
11072            lsp_insert_mode: LspInsertMode::Insert,
11073        });
11074    });
11075
11076    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11077
11078    cx.set_state(indoc! {"ˇ
11079        0_usize
11080        let
11081        33
11082        4.5f32
11083    "});
11084    cx.update_editor(|editor, window, cx| {
11085        editor.show_completions(&ShowCompletions::default(), window, cx);
11086    });
11087    cx.executor().run_until_parked();
11088    cx.condition(|editor, _| editor.context_menu_visible())
11089        .await;
11090    cx.update_editor(|editor, window, cx| {
11091        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11092        {
11093            assert_eq!(
11094                completion_menu_entries(&menu),
11095                &["let"],
11096                "With no digits in the completion query, no digits should be in the word completions"
11097            );
11098        } else {
11099            panic!("expected completion menu to be open");
11100        }
11101        editor.cancel(&Cancel, window, cx);
11102    });
11103
11104    cx.set_state(indoc! {"11105        0_usize
11106        let
11107        3
11108        33.35f32
11109    "});
11110    cx.update_editor(|editor, window, cx| {
11111        editor.show_completions(&ShowCompletions::default(), window, cx);
11112    });
11113    cx.executor().run_until_parked();
11114    cx.condition(|editor, _| editor.context_menu_visible())
11115        .await;
11116    cx.update_editor(|editor, _, _| {
11117        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11118        {
11119            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11120                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11121        } else {
11122            panic!("expected completion menu to be open");
11123        }
11124    });
11125}
11126
11127fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11128    let position = || lsp::Position {
11129        line: params.text_document_position.position.line,
11130        character: params.text_document_position.position.character,
11131    };
11132    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11133        range: lsp::Range {
11134            start: position(),
11135            end: position(),
11136        },
11137        new_text: text.to_string(),
11138    }))
11139}
11140
11141#[gpui::test]
11142async fn test_multiline_completion(cx: &mut TestAppContext) {
11143    init_test(cx, |_| {});
11144
11145    let fs = FakeFs::new(cx.executor());
11146    fs.insert_tree(
11147        path!("/a"),
11148        json!({
11149            "main.ts": "a",
11150        }),
11151    )
11152    .await;
11153
11154    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11155    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11156    let typescript_language = Arc::new(Language::new(
11157        LanguageConfig {
11158            name: "TypeScript".into(),
11159            matcher: LanguageMatcher {
11160                path_suffixes: vec!["ts".to_string()],
11161                ..LanguageMatcher::default()
11162            },
11163            line_comments: vec!["// ".into()],
11164            ..LanguageConfig::default()
11165        },
11166        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11167    ));
11168    language_registry.add(typescript_language.clone());
11169    let mut fake_servers = language_registry.register_fake_lsp(
11170        "TypeScript",
11171        FakeLspAdapter {
11172            capabilities: lsp::ServerCapabilities {
11173                completion_provider: Some(lsp::CompletionOptions {
11174                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11175                    ..lsp::CompletionOptions::default()
11176                }),
11177                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11178                ..lsp::ServerCapabilities::default()
11179            },
11180            // Emulate vtsls label generation
11181            label_for_completion: Some(Box::new(|item, _| {
11182                let text = if let Some(description) = item
11183                    .label_details
11184                    .as_ref()
11185                    .and_then(|label_details| label_details.description.as_ref())
11186                {
11187                    format!("{} {}", item.label, description)
11188                } else if let Some(detail) = &item.detail {
11189                    format!("{} {}", item.label, detail)
11190                } else {
11191                    item.label.clone()
11192                };
11193                let len = text.len();
11194                Some(language::CodeLabel {
11195                    text,
11196                    runs: Vec::new(),
11197                    filter_range: 0..len,
11198                })
11199            })),
11200            ..FakeLspAdapter::default()
11201        },
11202    );
11203    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11204    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11205    let worktree_id = workspace
11206        .update(cx, |workspace, _window, cx| {
11207            workspace.project().update(cx, |project, cx| {
11208                project.worktrees(cx).next().unwrap().read(cx).id()
11209            })
11210        })
11211        .unwrap();
11212    let _buffer = project
11213        .update(cx, |project, cx| {
11214            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11215        })
11216        .await
11217        .unwrap();
11218    let editor = workspace
11219        .update(cx, |workspace, window, cx| {
11220            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11221        })
11222        .unwrap()
11223        .await
11224        .unwrap()
11225        .downcast::<Editor>()
11226        .unwrap();
11227    let fake_server = fake_servers.next().await.unwrap();
11228
11229    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11230    let multiline_label_2 = "a\nb\nc\n";
11231    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11232    let multiline_description = "d\ne\nf\n";
11233    let multiline_detail_2 = "g\nh\ni\n";
11234
11235    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11236        move |params, _| async move {
11237            Ok(Some(lsp::CompletionResponse::Array(vec![
11238                lsp::CompletionItem {
11239                    label: multiline_label.to_string(),
11240                    text_edit: gen_text_edit(&params, "new_text_1"),
11241                    ..lsp::CompletionItem::default()
11242                },
11243                lsp::CompletionItem {
11244                    label: "single line label 1".to_string(),
11245                    detail: Some(multiline_detail.to_string()),
11246                    text_edit: gen_text_edit(&params, "new_text_2"),
11247                    ..lsp::CompletionItem::default()
11248                },
11249                lsp::CompletionItem {
11250                    label: "single line label 2".to_string(),
11251                    label_details: Some(lsp::CompletionItemLabelDetails {
11252                        description: Some(multiline_description.to_string()),
11253                        detail: None,
11254                    }),
11255                    text_edit: gen_text_edit(&params, "new_text_2"),
11256                    ..lsp::CompletionItem::default()
11257                },
11258                lsp::CompletionItem {
11259                    label: multiline_label_2.to_string(),
11260                    detail: Some(multiline_detail_2.to_string()),
11261                    text_edit: gen_text_edit(&params, "new_text_3"),
11262                    ..lsp::CompletionItem::default()
11263                },
11264                lsp::CompletionItem {
11265                    label: "Label with many     spaces and \t but without newlines".to_string(),
11266                    detail: Some(
11267                        "Details with many     spaces and \t but without newlines".to_string(),
11268                    ),
11269                    text_edit: gen_text_edit(&params, "new_text_4"),
11270                    ..lsp::CompletionItem::default()
11271                },
11272            ])))
11273        },
11274    );
11275
11276    editor.update_in(cx, |editor, window, cx| {
11277        cx.focus_self(window);
11278        editor.move_to_end(&MoveToEnd, window, cx);
11279        editor.handle_input(".", window, cx);
11280    });
11281    cx.run_until_parked();
11282    completion_handle.next().await.unwrap();
11283
11284    editor.update(cx, |editor, _| {
11285        assert!(editor.context_menu_visible());
11286        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11287        {
11288            let completion_labels = menu
11289                .completions
11290                .borrow()
11291                .iter()
11292                .map(|c| c.label.text.clone())
11293                .collect::<Vec<_>>();
11294            assert_eq!(
11295                completion_labels,
11296                &[
11297                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11298                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11299                    "single line label 2 d e f ",
11300                    "a b c g h i ",
11301                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11302                ],
11303                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11304            );
11305
11306            for completion in menu
11307                .completions
11308                .borrow()
11309                .iter() {
11310                    assert_eq!(
11311                        completion.label.filter_range,
11312                        0..completion.label.text.len(),
11313                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11314                    );
11315                }
11316        } else {
11317            panic!("expected completion menu to be open");
11318        }
11319    });
11320}
11321
11322#[gpui::test]
11323async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11324    init_test(cx, |_| {});
11325    let mut cx = EditorLspTestContext::new_rust(
11326        lsp::ServerCapabilities {
11327            completion_provider: Some(lsp::CompletionOptions {
11328                trigger_characters: Some(vec![".".to_string()]),
11329                ..Default::default()
11330            }),
11331            ..Default::default()
11332        },
11333        cx,
11334    )
11335    .await;
11336    cx.lsp
11337        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11338            Ok(Some(lsp::CompletionResponse::Array(vec![
11339                lsp::CompletionItem {
11340                    label: "first".into(),
11341                    ..Default::default()
11342                },
11343                lsp::CompletionItem {
11344                    label: "last".into(),
11345                    ..Default::default()
11346                },
11347            ])))
11348        });
11349    cx.set_state("variableˇ");
11350    cx.simulate_keystroke(".");
11351    cx.executor().run_until_parked();
11352
11353    cx.update_editor(|editor, _, _| {
11354        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11355        {
11356            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11357        } else {
11358            panic!("expected completion menu to be open");
11359        }
11360    });
11361
11362    cx.update_editor(|editor, window, cx| {
11363        editor.move_page_down(&MovePageDown::default(), window, cx);
11364        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11365        {
11366            assert!(
11367                menu.selected_item == 1,
11368                "expected PageDown to select the last item from the context menu"
11369            );
11370        } else {
11371            panic!("expected completion menu to stay open after PageDown");
11372        }
11373    });
11374
11375    cx.update_editor(|editor, window, cx| {
11376        editor.move_page_up(&MovePageUp::default(), window, cx);
11377        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11378        {
11379            assert!(
11380                menu.selected_item == 0,
11381                "expected PageUp to select the first item from the context menu"
11382            );
11383        } else {
11384            panic!("expected completion menu to stay open after PageUp");
11385        }
11386    });
11387}
11388
11389#[gpui::test]
11390async fn test_as_is_completions(cx: &mut TestAppContext) {
11391    init_test(cx, |_| {});
11392    let mut cx = EditorLspTestContext::new_rust(
11393        lsp::ServerCapabilities {
11394            completion_provider: Some(lsp::CompletionOptions {
11395                ..Default::default()
11396            }),
11397            ..Default::default()
11398        },
11399        cx,
11400    )
11401    .await;
11402    cx.lsp
11403        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11404            Ok(Some(lsp::CompletionResponse::Array(vec![
11405                lsp::CompletionItem {
11406                    label: "unsafe".into(),
11407                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11408                        range: lsp::Range {
11409                            start: lsp::Position {
11410                                line: 1,
11411                                character: 2,
11412                            },
11413                            end: lsp::Position {
11414                                line: 1,
11415                                character: 3,
11416                            },
11417                        },
11418                        new_text: "unsafe".to_string(),
11419                    })),
11420                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11421                    ..Default::default()
11422                },
11423            ])))
11424        });
11425    cx.set_state("fn a() {}\n");
11426    cx.executor().run_until_parked();
11427    cx.update_editor(|editor, window, cx| {
11428        editor.show_completions(
11429            &ShowCompletions {
11430                trigger: Some("\n".into()),
11431            },
11432            window,
11433            cx,
11434        );
11435    });
11436    cx.executor().run_until_parked();
11437
11438    cx.update_editor(|editor, window, cx| {
11439        editor.confirm_completion(&Default::default(), window, cx)
11440    });
11441    cx.executor().run_until_parked();
11442    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11443}
11444
11445#[gpui::test]
11446async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11447    init_test(cx, |_| {});
11448
11449    let mut cx = EditorLspTestContext::new_rust(
11450        lsp::ServerCapabilities {
11451            completion_provider: Some(lsp::CompletionOptions {
11452                trigger_characters: Some(vec![".".to_string()]),
11453                resolve_provider: Some(true),
11454                ..Default::default()
11455            }),
11456            ..Default::default()
11457        },
11458        cx,
11459    )
11460    .await;
11461
11462    cx.set_state("fn main() { let a = 2ˇ; }");
11463    cx.simulate_keystroke(".");
11464    let completion_item = lsp::CompletionItem {
11465        label: "Some".into(),
11466        kind: Some(lsp::CompletionItemKind::SNIPPET),
11467        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11468        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11469            kind: lsp::MarkupKind::Markdown,
11470            value: "```rust\nSome(2)\n```".to_string(),
11471        })),
11472        deprecated: Some(false),
11473        sort_text: Some("Some".to_string()),
11474        filter_text: Some("Some".to_string()),
11475        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11476        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11477            range: lsp::Range {
11478                start: lsp::Position {
11479                    line: 0,
11480                    character: 22,
11481                },
11482                end: lsp::Position {
11483                    line: 0,
11484                    character: 22,
11485                },
11486            },
11487            new_text: "Some(2)".to_string(),
11488        })),
11489        additional_text_edits: Some(vec![lsp::TextEdit {
11490            range: lsp::Range {
11491                start: lsp::Position {
11492                    line: 0,
11493                    character: 20,
11494                },
11495                end: lsp::Position {
11496                    line: 0,
11497                    character: 22,
11498                },
11499            },
11500            new_text: "".to_string(),
11501        }]),
11502        ..Default::default()
11503    };
11504
11505    let closure_completion_item = completion_item.clone();
11506    let counter = Arc::new(AtomicUsize::new(0));
11507    let counter_clone = counter.clone();
11508    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11509        let task_completion_item = closure_completion_item.clone();
11510        counter_clone.fetch_add(1, atomic::Ordering::Release);
11511        async move {
11512            Ok(Some(lsp::CompletionResponse::Array(vec![
11513                task_completion_item,
11514            ])))
11515        }
11516    });
11517
11518    cx.condition(|editor, _| editor.context_menu_visible())
11519        .await;
11520    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11521    assert!(request.next().await.is_some());
11522    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11523
11524    cx.simulate_keystrokes("S o m");
11525    cx.condition(|editor, _| editor.context_menu_visible())
11526        .await;
11527    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11528    assert!(request.next().await.is_some());
11529    assert!(request.next().await.is_some());
11530    assert!(request.next().await.is_some());
11531    request.close();
11532    assert!(request.next().await.is_none());
11533    assert_eq!(
11534        counter.load(atomic::Ordering::Acquire),
11535        4,
11536        "With the completions menu open, only one LSP request should happen per input"
11537    );
11538}
11539
11540#[gpui::test]
11541async fn test_toggle_comment(cx: &mut TestAppContext) {
11542    init_test(cx, |_| {});
11543    let mut cx = EditorTestContext::new(cx).await;
11544    let language = Arc::new(Language::new(
11545        LanguageConfig {
11546            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11547            ..Default::default()
11548        },
11549        Some(tree_sitter_rust::LANGUAGE.into()),
11550    ));
11551    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11552
11553    // If multiple selections intersect a line, the line is only toggled once.
11554    cx.set_state(indoc! {"
11555        fn a() {
11556            «//b();
11557            ˇ»// «c();
11558            //ˇ»  d();
11559        }
11560    "});
11561
11562    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11563
11564    cx.assert_editor_state(indoc! {"
11565        fn a() {
11566            «b();
11567            c();
11568            ˇ» d();
11569        }
11570    "});
11571
11572    // The comment prefix is inserted at the same column for every line in a
11573    // selection.
11574    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11575
11576    cx.assert_editor_state(indoc! {"
11577        fn a() {
11578            // «b();
11579            // c();
11580            ˇ»//  d();
11581        }
11582    "});
11583
11584    // If a selection ends at the beginning of a line, that line is not toggled.
11585    cx.set_selections_state(indoc! {"
11586        fn a() {
11587            // b();
11588            «// c();
11589        ˇ»    //  d();
11590        }
11591    "});
11592
11593    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11594
11595    cx.assert_editor_state(indoc! {"
11596        fn a() {
11597            // b();
11598            «c();
11599        ˇ»    //  d();
11600        }
11601    "});
11602
11603    // If a selection span a single line and is empty, the line is toggled.
11604    cx.set_state(indoc! {"
11605        fn a() {
11606            a();
11607            b();
11608        ˇ
11609        }
11610    "});
11611
11612    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11613
11614    cx.assert_editor_state(indoc! {"
11615        fn a() {
11616            a();
11617            b();
11618        //•ˇ
11619        }
11620    "});
11621
11622    // If a selection span multiple lines, empty lines are not toggled.
11623    cx.set_state(indoc! {"
11624        fn a() {
11625            «a();
11626
11627            c();ˇ»
11628        }
11629    "});
11630
11631    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11632
11633    cx.assert_editor_state(indoc! {"
11634        fn a() {
11635            // «a();
11636
11637            // c();ˇ»
11638        }
11639    "});
11640
11641    // If a selection includes multiple comment prefixes, all lines are uncommented.
11642    cx.set_state(indoc! {"
11643        fn a() {
11644            «// a();
11645            /// b();
11646            //! c();ˇ»
11647        }
11648    "});
11649
11650    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11651
11652    cx.assert_editor_state(indoc! {"
11653        fn a() {
11654            «a();
11655            b();
11656            c();ˇ»
11657        }
11658    "});
11659}
11660
11661#[gpui::test]
11662async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11663    init_test(cx, |_| {});
11664    let mut cx = EditorTestContext::new(cx).await;
11665    let language = Arc::new(Language::new(
11666        LanguageConfig {
11667            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11668            ..Default::default()
11669        },
11670        Some(tree_sitter_rust::LANGUAGE.into()),
11671    ));
11672    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11673
11674    let toggle_comments = &ToggleComments {
11675        advance_downwards: false,
11676        ignore_indent: true,
11677    };
11678
11679    // If multiple selections intersect a line, the line is only toggled once.
11680    cx.set_state(indoc! {"
11681        fn a() {
11682        //    «b();
11683        //    c();
11684        //    ˇ» d();
11685        }
11686    "});
11687
11688    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11689
11690    cx.assert_editor_state(indoc! {"
11691        fn a() {
11692            «b();
11693            c();
11694            ˇ» d();
11695        }
11696    "});
11697
11698    // The comment prefix is inserted at the beginning of each line
11699    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11700
11701    cx.assert_editor_state(indoc! {"
11702        fn a() {
11703        //    «b();
11704        //    c();
11705        //    ˇ» d();
11706        }
11707    "});
11708
11709    // If a selection ends at the beginning of a line, that line is not toggled.
11710    cx.set_selections_state(indoc! {"
11711        fn a() {
11712        //    b();
11713        //    «c();
11714        ˇ»//     d();
11715        }
11716    "});
11717
11718    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11719
11720    cx.assert_editor_state(indoc! {"
11721        fn a() {
11722        //    b();
11723            «c();
11724        ˇ»//     d();
11725        }
11726    "});
11727
11728    // If a selection span a single line and is empty, the line is toggled.
11729    cx.set_state(indoc! {"
11730        fn a() {
11731            a();
11732            b();
11733        ˇ
11734        }
11735    "});
11736
11737    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11738
11739    cx.assert_editor_state(indoc! {"
11740        fn a() {
11741            a();
11742            b();
11743        //ˇ
11744        }
11745    "});
11746
11747    // If a selection span multiple lines, empty lines are not toggled.
11748    cx.set_state(indoc! {"
11749        fn a() {
11750            «a();
11751
11752            c();ˇ»
11753        }
11754    "});
11755
11756    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11757
11758    cx.assert_editor_state(indoc! {"
11759        fn a() {
11760        //    «a();
11761
11762        //    c();ˇ»
11763        }
11764    "});
11765
11766    // If a selection includes multiple comment prefixes, all lines are uncommented.
11767    cx.set_state(indoc! {"
11768        fn a() {
11769        //    «a();
11770        ///    b();
11771        //!    c();ˇ»
11772        }
11773    "});
11774
11775    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11776
11777    cx.assert_editor_state(indoc! {"
11778        fn a() {
11779            «a();
11780            b();
11781            c();ˇ»
11782        }
11783    "});
11784}
11785
11786#[gpui::test]
11787async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11788    init_test(cx, |_| {});
11789
11790    let language = Arc::new(Language::new(
11791        LanguageConfig {
11792            line_comments: vec!["// ".into()],
11793            ..Default::default()
11794        },
11795        Some(tree_sitter_rust::LANGUAGE.into()),
11796    ));
11797
11798    let mut cx = EditorTestContext::new(cx).await;
11799
11800    cx.language_registry().add(language.clone());
11801    cx.update_buffer(|buffer, cx| {
11802        buffer.set_language(Some(language), cx);
11803    });
11804
11805    let toggle_comments = &ToggleComments {
11806        advance_downwards: true,
11807        ignore_indent: false,
11808    };
11809
11810    // Single cursor on one line -> advance
11811    // Cursor moves horizontally 3 characters as well on non-blank line
11812    cx.set_state(indoc!(
11813        "fn a() {
11814             ˇdog();
11815             cat();
11816        }"
11817    ));
11818    cx.update_editor(|editor, window, cx| {
11819        editor.toggle_comments(toggle_comments, window, cx);
11820    });
11821    cx.assert_editor_state(indoc!(
11822        "fn a() {
11823             // dog();
11824             catˇ();
11825        }"
11826    ));
11827
11828    // Single selection on one line -> don't advance
11829    cx.set_state(indoc!(
11830        "fn a() {
11831             «dog()ˇ»;
11832             cat();
11833        }"
11834    ));
11835    cx.update_editor(|editor, window, cx| {
11836        editor.toggle_comments(toggle_comments, window, cx);
11837    });
11838    cx.assert_editor_state(indoc!(
11839        "fn a() {
11840             // «dog()ˇ»;
11841             cat();
11842        }"
11843    ));
11844
11845    // Multiple cursors on one line -> advance
11846    cx.set_state(indoc!(
11847        "fn a() {
11848             ˇdˇog();
11849             cat();
11850        }"
11851    ));
11852    cx.update_editor(|editor, window, cx| {
11853        editor.toggle_comments(toggle_comments, window, cx);
11854    });
11855    cx.assert_editor_state(indoc!(
11856        "fn a() {
11857             // dog();
11858             catˇ(ˇ);
11859        }"
11860    ));
11861
11862    // Multiple cursors on one line, with selection -> don't advance
11863    cx.set_state(indoc!(
11864        "fn a() {
11865             ˇdˇog«()ˇ»;
11866             cat();
11867        }"
11868    ));
11869    cx.update_editor(|editor, window, cx| {
11870        editor.toggle_comments(toggle_comments, window, cx);
11871    });
11872    cx.assert_editor_state(indoc!(
11873        "fn a() {
11874             // ˇdˇog«()ˇ»;
11875             cat();
11876        }"
11877    ));
11878
11879    // Single cursor on one line -> advance
11880    // Cursor moves to column 0 on blank line
11881    cx.set_state(indoc!(
11882        "fn a() {
11883             ˇdog();
11884
11885             cat();
11886        }"
11887    ));
11888    cx.update_editor(|editor, window, cx| {
11889        editor.toggle_comments(toggle_comments, window, cx);
11890    });
11891    cx.assert_editor_state(indoc!(
11892        "fn a() {
11893             // dog();
11894        ˇ
11895             cat();
11896        }"
11897    ));
11898
11899    // Single cursor on one line -> advance
11900    // Cursor starts and ends at column 0
11901    cx.set_state(indoc!(
11902        "fn a() {
11903         ˇ    dog();
11904             cat();
11905        }"
11906    ));
11907    cx.update_editor(|editor, window, cx| {
11908        editor.toggle_comments(toggle_comments, window, cx);
11909    });
11910    cx.assert_editor_state(indoc!(
11911        "fn a() {
11912             // dog();
11913         ˇ    cat();
11914        }"
11915    ));
11916}
11917
11918#[gpui::test]
11919async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11920    init_test(cx, |_| {});
11921
11922    let mut cx = EditorTestContext::new(cx).await;
11923
11924    let html_language = Arc::new(
11925        Language::new(
11926            LanguageConfig {
11927                name: "HTML".into(),
11928                block_comment: Some(("<!-- ".into(), " -->".into())),
11929                ..Default::default()
11930            },
11931            Some(tree_sitter_html::LANGUAGE.into()),
11932        )
11933        .with_injection_query(
11934            r#"
11935            (script_element
11936                (raw_text) @injection.content
11937                (#set! injection.language "javascript"))
11938            "#,
11939        )
11940        .unwrap(),
11941    );
11942
11943    let javascript_language = Arc::new(Language::new(
11944        LanguageConfig {
11945            name: "JavaScript".into(),
11946            line_comments: vec!["// ".into()],
11947            ..Default::default()
11948        },
11949        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11950    ));
11951
11952    cx.language_registry().add(html_language.clone());
11953    cx.language_registry().add(javascript_language.clone());
11954    cx.update_buffer(|buffer, cx| {
11955        buffer.set_language(Some(html_language), cx);
11956    });
11957
11958    // Toggle comments for empty selections
11959    cx.set_state(
11960        &r#"
11961            <p>A</p>ˇ
11962            <p>B</p>ˇ
11963            <p>C</p>ˇ
11964        "#
11965        .unindent(),
11966    );
11967    cx.update_editor(|editor, window, cx| {
11968        editor.toggle_comments(&ToggleComments::default(), window, cx)
11969    });
11970    cx.assert_editor_state(
11971        &r#"
11972            <!-- <p>A</p>ˇ -->
11973            <!-- <p>B</p>ˇ -->
11974            <!-- <p>C</p>ˇ -->
11975        "#
11976        .unindent(),
11977    );
11978    cx.update_editor(|editor, window, cx| {
11979        editor.toggle_comments(&ToggleComments::default(), window, cx)
11980    });
11981    cx.assert_editor_state(
11982        &r#"
11983            <p>A</p>ˇ
11984            <p>B</p>ˇ
11985            <p>C</p>ˇ
11986        "#
11987        .unindent(),
11988    );
11989
11990    // Toggle comments for mixture of empty and non-empty selections, where
11991    // multiple selections occupy a given line.
11992    cx.set_state(
11993        &r#"
11994            <p>A«</p>
11995            <p>ˇ»B</p>ˇ
11996            <p>C«</p>
11997            <p>ˇ»D</p>ˇ
11998        "#
11999        .unindent(),
12000    );
12001
12002    cx.update_editor(|editor, window, cx| {
12003        editor.toggle_comments(&ToggleComments::default(), window, cx)
12004    });
12005    cx.assert_editor_state(
12006        &r#"
12007            <!-- <p>A«</p>
12008            <p>ˇ»B</p>ˇ -->
12009            <!-- <p>C«</p>
12010            <p>ˇ»D</p>ˇ -->
12011        "#
12012        .unindent(),
12013    );
12014    cx.update_editor(|editor, window, cx| {
12015        editor.toggle_comments(&ToggleComments::default(), window, cx)
12016    });
12017    cx.assert_editor_state(
12018        &r#"
12019            <p>A«</p>
12020            <p>ˇ»B</p>ˇ
12021            <p>C«</p>
12022            <p>ˇ»D</p>ˇ
12023        "#
12024        .unindent(),
12025    );
12026
12027    // Toggle comments when different languages are active for different
12028    // selections.
12029    cx.set_state(
12030        &r#"
12031            ˇ<script>
12032                ˇvar x = new Y();
12033            ˇ</script>
12034        "#
12035        .unindent(),
12036    );
12037    cx.executor().run_until_parked();
12038    cx.update_editor(|editor, window, cx| {
12039        editor.toggle_comments(&ToggleComments::default(), window, cx)
12040    });
12041    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12042    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12043    cx.assert_editor_state(
12044        &r#"
12045            <!-- ˇ<script> -->
12046                // ˇvar x = new Y();
12047            <!-- ˇ</script> -->
12048        "#
12049        .unindent(),
12050    );
12051}
12052
12053#[gpui::test]
12054fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12055    init_test(cx, |_| {});
12056
12057    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12058    let multibuffer = cx.new(|cx| {
12059        let mut multibuffer = MultiBuffer::new(ReadWrite);
12060        multibuffer.push_excerpts(
12061            buffer.clone(),
12062            [
12063                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12064                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12065            ],
12066            cx,
12067        );
12068        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12069        multibuffer
12070    });
12071
12072    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12073    editor.update_in(cx, |editor, window, cx| {
12074        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12075        editor.change_selections(None, window, cx, |s| {
12076            s.select_ranges([
12077                Point::new(0, 0)..Point::new(0, 0),
12078                Point::new(1, 0)..Point::new(1, 0),
12079            ])
12080        });
12081
12082        editor.handle_input("X", window, cx);
12083        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12084        assert_eq!(
12085            editor.selections.ranges(cx),
12086            [
12087                Point::new(0, 1)..Point::new(0, 1),
12088                Point::new(1, 1)..Point::new(1, 1),
12089            ]
12090        );
12091
12092        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12093        editor.change_selections(None, window, cx, |s| {
12094            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12095        });
12096        editor.backspace(&Default::default(), window, cx);
12097        assert_eq!(editor.text(cx), "Xa\nbbb");
12098        assert_eq!(
12099            editor.selections.ranges(cx),
12100            [Point::new(1, 0)..Point::new(1, 0)]
12101        );
12102
12103        editor.change_selections(None, window, cx, |s| {
12104            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12105        });
12106        editor.backspace(&Default::default(), window, cx);
12107        assert_eq!(editor.text(cx), "X\nbb");
12108        assert_eq!(
12109            editor.selections.ranges(cx),
12110            [Point::new(0, 1)..Point::new(0, 1)]
12111        );
12112    });
12113}
12114
12115#[gpui::test]
12116fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12117    init_test(cx, |_| {});
12118
12119    let markers = vec![('[', ']').into(), ('(', ')').into()];
12120    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12121        indoc! {"
12122            [aaaa
12123            (bbbb]
12124            cccc)",
12125        },
12126        markers.clone(),
12127    );
12128    let excerpt_ranges = markers.into_iter().map(|marker| {
12129        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12130        ExcerptRange::new(context.clone())
12131    });
12132    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12133    let multibuffer = cx.new(|cx| {
12134        let mut multibuffer = MultiBuffer::new(ReadWrite);
12135        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12136        multibuffer
12137    });
12138
12139    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12140    editor.update_in(cx, |editor, window, cx| {
12141        let (expected_text, selection_ranges) = marked_text_ranges(
12142            indoc! {"
12143                aaaa
12144                bˇbbb
12145                bˇbbˇb
12146                cccc"
12147            },
12148            true,
12149        );
12150        assert_eq!(editor.text(cx), expected_text);
12151        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12152
12153        editor.handle_input("X", window, cx);
12154
12155        let (expected_text, expected_selections) = marked_text_ranges(
12156            indoc! {"
12157                aaaa
12158                bXˇbbXb
12159                bXˇbbXˇb
12160                cccc"
12161            },
12162            false,
12163        );
12164        assert_eq!(editor.text(cx), expected_text);
12165        assert_eq!(editor.selections.ranges(cx), expected_selections);
12166
12167        editor.newline(&Newline, window, cx);
12168        let (expected_text, expected_selections) = marked_text_ranges(
12169            indoc! {"
12170                aaaa
12171                bX
12172                ˇbbX
12173                b
12174                bX
12175                ˇbbX
12176                ˇb
12177                cccc"
12178            },
12179            false,
12180        );
12181        assert_eq!(editor.text(cx), expected_text);
12182        assert_eq!(editor.selections.ranges(cx), expected_selections);
12183    });
12184}
12185
12186#[gpui::test]
12187fn test_refresh_selections(cx: &mut TestAppContext) {
12188    init_test(cx, |_| {});
12189
12190    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12191    let mut excerpt1_id = None;
12192    let multibuffer = cx.new(|cx| {
12193        let mut multibuffer = MultiBuffer::new(ReadWrite);
12194        excerpt1_id = multibuffer
12195            .push_excerpts(
12196                buffer.clone(),
12197                [
12198                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12199                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12200                ],
12201                cx,
12202            )
12203            .into_iter()
12204            .next();
12205        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12206        multibuffer
12207    });
12208
12209    let editor = cx.add_window(|window, cx| {
12210        let mut editor = build_editor(multibuffer.clone(), window, cx);
12211        let snapshot = editor.snapshot(window, cx);
12212        editor.change_selections(None, window, cx, |s| {
12213            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12214        });
12215        editor.begin_selection(
12216            Point::new(2, 1).to_display_point(&snapshot),
12217            true,
12218            1,
12219            window,
12220            cx,
12221        );
12222        assert_eq!(
12223            editor.selections.ranges(cx),
12224            [
12225                Point::new(1, 3)..Point::new(1, 3),
12226                Point::new(2, 1)..Point::new(2, 1),
12227            ]
12228        );
12229        editor
12230    });
12231
12232    // Refreshing selections is a no-op when excerpts haven't changed.
12233    _ = editor.update(cx, |editor, window, cx| {
12234        editor.change_selections(None, window, cx, |s| s.refresh());
12235        assert_eq!(
12236            editor.selections.ranges(cx),
12237            [
12238                Point::new(1, 3)..Point::new(1, 3),
12239                Point::new(2, 1)..Point::new(2, 1),
12240            ]
12241        );
12242    });
12243
12244    multibuffer.update(cx, |multibuffer, cx| {
12245        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12246    });
12247    _ = editor.update(cx, |editor, window, cx| {
12248        // Removing an excerpt causes the first selection to become degenerate.
12249        assert_eq!(
12250            editor.selections.ranges(cx),
12251            [
12252                Point::new(0, 0)..Point::new(0, 0),
12253                Point::new(0, 1)..Point::new(0, 1)
12254            ]
12255        );
12256
12257        // Refreshing selections will relocate the first selection to the original buffer
12258        // location.
12259        editor.change_selections(None, window, cx, |s| s.refresh());
12260        assert_eq!(
12261            editor.selections.ranges(cx),
12262            [
12263                Point::new(0, 1)..Point::new(0, 1),
12264                Point::new(0, 3)..Point::new(0, 3)
12265            ]
12266        );
12267        assert!(editor.selections.pending_anchor().is_some());
12268    });
12269}
12270
12271#[gpui::test]
12272fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12273    init_test(cx, |_| {});
12274
12275    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12276    let mut excerpt1_id = None;
12277    let multibuffer = cx.new(|cx| {
12278        let mut multibuffer = MultiBuffer::new(ReadWrite);
12279        excerpt1_id = multibuffer
12280            .push_excerpts(
12281                buffer.clone(),
12282                [
12283                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12284                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12285                ],
12286                cx,
12287            )
12288            .into_iter()
12289            .next();
12290        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12291        multibuffer
12292    });
12293
12294    let editor = cx.add_window(|window, cx| {
12295        let mut editor = build_editor(multibuffer.clone(), window, cx);
12296        let snapshot = editor.snapshot(window, cx);
12297        editor.begin_selection(
12298            Point::new(1, 3).to_display_point(&snapshot),
12299            false,
12300            1,
12301            window,
12302            cx,
12303        );
12304        assert_eq!(
12305            editor.selections.ranges(cx),
12306            [Point::new(1, 3)..Point::new(1, 3)]
12307        );
12308        editor
12309    });
12310
12311    multibuffer.update(cx, |multibuffer, cx| {
12312        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12313    });
12314    _ = editor.update(cx, |editor, window, cx| {
12315        assert_eq!(
12316            editor.selections.ranges(cx),
12317            [Point::new(0, 0)..Point::new(0, 0)]
12318        );
12319
12320        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12321        editor.change_selections(None, window, cx, |s| s.refresh());
12322        assert_eq!(
12323            editor.selections.ranges(cx),
12324            [Point::new(0, 3)..Point::new(0, 3)]
12325        );
12326        assert!(editor.selections.pending_anchor().is_some());
12327    });
12328}
12329
12330#[gpui::test]
12331async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12332    init_test(cx, |_| {});
12333
12334    let language = Arc::new(
12335        Language::new(
12336            LanguageConfig {
12337                brackets: BracketPairConfig {
12338                    pairs: vec![
12339                        BracketPair {
12340                            start: "{".to_string(),
12341                            end: "}".to_string(),
12342                            close: true,
12343                            surround: true,
12344                            newline: true,
12345                        },
12346                        BracketPair {
12347                            start: "/* ".to_string(),
12348                            end: " */".to_string(),
12349                            close: true,
12350                            surround: true,
12351                            newline: true,
12352                        },
12353                    ],
12354                    ..Default::default()
12355                },
12356                ..Default::default()
12357            },
12358            Some(tree_sitter_rust::LANGUAGE.into()),
12359        )
12360        .with_indents_query("")
12361        .unwrap(),
12362    );
12363
12364    let text = concat!(
12365        "{   }\n",     //
12366        "  x\n",       //
12367        "  /*   */\n", //
12368        "x\n",         //
12369        "{{} }\n",     //
12370    );
12371
12372    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12373    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12374    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12375    editor
12376        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12377        .await;
12378
12379    editor.update_in(cx, |editor, window, cx| {
12380        editor.change_selections(None, window, cx, |s| {
12381            s.select_display_ranges([
12382                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12383                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12384                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12385            ])
12386        });
12387        editor.newline(&Newline, window, cx);
12388
12389        assert_eq!(
12390            editor.buffer().read(cx).read(cx).text(),
12391            concat!(
12392                "{ \n",    // Suppress rustfmt
12393                "\n",      //
12394                "}\n",     //
12395                "  x\n",   //
12396                "  /* \n", //
12397                "  \n",    //
12398                "  */\n",  //
12399                "x\n",     //
12400                "{{} \n",  //
12401                "}\n",     //
12402            )
12403        );
12404    });
12405}
12406
12407#[gpui::test]
12408fn test_highlighted_ranges(cx: &mut TestAppContext) {
12409    init_test(cx, |_| {});
12410
12411    let editor = cx.add_window(|window, cx| {
12412        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12413        build_editor(buffer.clone(), window, cx)
12414    });
12415
12416    _ = editor.update(cx, |editor, window, cx| {
12417        struct Type1;
12418        struct Type2;
12419
12420        let buffer = editor.buffer.read(cx).snapshot(cx);
12421
12422        let anchor_range =
12423            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12424
12425        editor.highlight_background::<Type1>(
12426            &[
12427                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12428                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12429                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12430                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12431            ],
12432            |_| Hsla::red(),
12433            cx,
12434        );
12435        editor.highlight_background::<Type2>(
12436            &[
12437                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12438                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12439                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12440                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12441            ],
12442            |_| Hsla::green(),
12443            cx,
12444        );
12445
12446        let snapshot = editor.snapshot(window, cx);
12447        let mut highlighted_ranges = editor.background_highlights_in_range(
12448            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12449            &snapshot,
12450            cx.theme().colors(),
12451        );
12452        // Enforce a consistent ordering based on color without relying on the ordering of the
12453        // highlight's `TypeId` which is non-executor.
12454        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12455        assert_eq!(
12456            highlighted_ranges,
12457            &[
12458                (
12459                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12460                    Hsla::red(),
12461                ),
12462                (
12463                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12464                    Hsla::red(),
12465                ),
12466                (
12467                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12468                    Hsla::green(),
12469                ),
12470                (
12471                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12472                    Hsla::green(),
12473                ),
12474            ]
12475        );
12476        assert_eq!(
12477            editor.background_highlights_in_range(
12478                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12479                &snapshot,
12480                cx.theme().colors(),
12481            ),
12482            &[(
12483                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12484                Hsla::red(),
12485            )]
12486        );
12487    });
12488}
12489
12490#[gpui::test]
12491async fn test_following(cx: &mut TestAppContext) {
12492    init_test(cx, |_| {});
12493
12494    let fs = FakeFs::new(cx.executor());
12495    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12496
12497    let buffer = project.update(cx, |project, cx| {
12498        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12499        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12500    });
12501    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12502    let follower = cx.update(|cx| {
12503        cx.open_window(
12504            WindowOptions {
12505                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12506                    gpui::Point::new(px(0.), px(0.)),
12507                    gpui::Point::new(px(10.), px(80.)),
12508                ))),
12509                ..Default::default()
12510            },
12511            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12512        )
12513        .unwrap()
12514    });
12515
12516    let is_still_following = Rc::new(RefCell::new(true));
12517    let follower_edit_event_count = Rc::new(RefCell::new(0));
12518    let pending_update = Rc::new(RefCell::new(None));
12519    let leader_entity = leader.root(cx).unwrap();
12520    let follower_entity = follower.root(cx).unwrap();
12521    _ = follower.update(cx, {
12522        let update = pending_update.clone();
12523        let is_still_following = is_still_following.clone();
12524        let follower_edit_event_count = follower_edit_event_count.clone();
12525        |_, window, cx| {
12526            cx.subscribe_in(
12527                &leader_entity,
12528                window,
12529                move |_, leader, event, window, cx| {
12530                    leader.read(cx).add_event_to_update_proto(
12531                        event,
12532                        &mut update.borrow_mut(),
12533                        window,
12534                        cx,
12535                    );
12536                },
12537            )
12538            .detach();
12539
12540            cx.subscribe_in(
12541                &follower_entity,
12542                window,
12543                move |_, _, event: &EditorEvent, _window, _cx| {
12544                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12545                        *is_still_following.borrow_mut() = false;
12546                    }
12547
12548                    if let EditorEvent::BufferEdited = event {
12549                        *follower_edit_event_count.borrow_mut() += 1;
12550                    }
12551                },
12552            )
12553            .detach();
12554        }
12555    });
12556
12557    // Update the selections only
12558    _ = leader.update(cx, |leader, window, cx| {
12559        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12560    });
12561    follower
12562        .update(cx, |follower, window, cx| {
12563            follower.apply_update_proto(
12564                &project,
12565                pending_update.borrow_mut().take().unwrap(),
12566                window,
12567                cx,
12568            )
12569        })
12570        .unwrap()
12571        .await
12572        .unwrap();
12573    _ = follower.update(cx, |follower, _, cx| {
12574        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12575    });
12576    assert!(*is_still_following.borrow());
12577    assert_eq!(*follower_edit_event_count.borrow(), 0);
12578
12579    // Update the scroll position only
12580    _ = leader.update(cx, |leader, window, cx| {
12581        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12582    });
12583    follower
12584        .update(cx, |follower, window, cx| {
12585            follower.apply_update_proto(
12586                &project,
12587                pending_update.borrow_mut().take().unwrap(),
12588                window,
12589                cx,
12590            )
12591        })
12592        .unwrap()
12593        .await
12594        .unwrap();
12595    assert_eq!(
12596        follower
12597            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12598            .unwrap(),
12599        gpui::Point::new(1.5, 3.5)
12600    );
12601    assert!(*is_still_following.borrow());
12602    assert_eq!(*follower_edit_event_count.borrow(), 0);
12603
12604    // Update the selections and scroll position. The follower's scroll position is updated
12605    // via autoscroll, not via the leader's exact scroll position.
12606    _ = leader.update(cx, |leader, window, cx| {
12607        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12608        leader.request_autoscroll(Autoscroll::newest(), cx);
12609        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12610    });
12611    follower
12612        .update(cx, |follower, window, cx| {
12613            follower.apply_update_proto(
12614                &project,
12615                pending_update.borrow_mut().take().unwrap(),
12616                window,
12617                cx,
12618            )
12619        })
12620        .unwrap()
12621        .await
12622        .unwrap();
12623    _ = follower.update(cx, |follower, _, cx| {
12624        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12625        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12626    });
12627    assert!(*is_still_following.borrow());
12628
12629    // Creating a pending selection that precedes another selection
12630    _ = leader.update(cx, |leader, window, cx| {
12631        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12632        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12633    });
12634    follower
12635        .update(cx, |follower, window, cx| {
12636            follower.apply_update_proto(
12637                &project,
12638                pending_update.borrow_mut().take().unwrap(),
12639                window,
12640                cx,
12641            )
12642        })
12643        .unwrap()
12644        .await
12645        .unwrap();
12646    _ = follower.update(cx, |follower, _, cx| {
12647        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12648    });
12649    assert!(*is_still_following.borrow());
12650
12651    // Extend the pending selection so that it surrounds another selection
12652    _ = leader.update(cx, |leader, window, cx| {
12653        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12654    });
12655    follower
12656        .update(cx, |follower, window, cx| {
12657            follower.apply_update_proto(
12658                &project,
12659                pending_update.borrow_mut().take().unwrap(),
12660                window,
12661                cx,
12662            )
12663        })
12664        .unwrap()
12665        .await
12666        .unwrap();
12667    _ = follower.update(cx, |follower, _, cx| {
12668        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12669    });
12670
12671    // Scrolling locally breaks the follow
12672    _ = follower.update(cx, |follower, window, cx| {
12673        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12674        follower.set_scroll_anchor(
12675            ScrollAnchor {
12676                anchor: top_anchor,
12677                offset: gpui::Point::new(0.0, 0.5),
12678            },
12679            window,
12680            cx,
12681        );
12682    });
12683    assert!(!(*is_still_following.borrow()));
12684}
12685
12686#[gpui::test]
12687async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12688    init_test(cx, |_| {});
12689
12690    let fs = FakeFs::new(cx.executor());
12691    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12692    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12693    let pane = workspace
12694        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12695        .unwrap();
12696
12697    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12698
12699    let leader = pane.update_in(cx, |_, window, cx| {
12700        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12701        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12702    });
12703
12704    // Start following the editor when it has no excerpts.
12705    let mut state_message =
12706        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12707    let workspace_entity = workspace.root(cx).unwrap();
12708    let follower_1 = cx
12709        .update_window(*workspace.deref(), |_, window, cx| {
12710            Editor::from_state_proto(
12711                workspace_entity,
12712                ViewId {
12713                    creator: CollaboratorId::PeerId(PeerId::default()),
12714                    id: 0,
12715                },
12716                &mut state_message,
12717                window,
12718                cx,
12719            )
12720        })
12721        .unwrap()
12722        .unwrap()
12723        .await
12724        .unwrap();
12725
12726    let update_message = Rc::new(RefCell::new(None));
12727    follower_1.update_in(cx, {
12728        let update = update_message.clone();
12729        |_, window, cx| {
12730            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12731                leader.read(cx).add_event_to_update_proto(
12732                    event,
12733                    &mut update.borrow_mut(),
12734                    window,
12735                    cx,
12736                );
12737            })
12738            .detach();
12739        }
12740    });
12741
12742    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12743        (
12744            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12745            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12746        )
12747    });
12748
12749    // Insert some excerpts.
12750    leader.update(cx, |leader, cx| {
12751        leader.buffer.update(cx, |multibuffer, cx| {
12752            multibuffer.set_excerpts_for_path(
12753                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12754                buffer_1.clone(),
12755                vec![
12756                    Point::row_range(0..3),
12757                    Point::row_range(1..6),
12758                    Point::row_range(12..15),
12759                ],
12760                0,
12761                cx,
12762            );
12763            multibuffer.set_excerpts_for_path(
12764                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12765                buffer_2.clone(),
12766                vec![Point::row_range(0..6), Point::row_range(8..12)],
12767                0,
12768                cx,
12769            );
12770        });
12771    });
12772
12773    // Apply the update of adding the excerpts.
12774    follower_1
12775        .update_in(cx, |follower, window, cx| {
12776            follower.apply_update_proto(
12777                &project,
12778                update_message.borrow().clone().unwrap(),
12779                window,
12780                cx,
12781            )
12782        })
12783        .await
12784        .unwrap();
12785    assert_eq!(
12786        follower_1.update(cx, |editor, cx| editor.text(cx)),
12787        leader.update(cx, |editor, cx| editor.text(cx))
12788    );
12789    update_message.borrow_mut().take();
12790
12791    // Start following separately after it already has excerpts.
12792    let mut state_message =
12793        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12794    let workspace_entity = workspace.root(cx).unwrap();
12795    let follower_2 = cx
12796        .update_window(*workspace.deref(), |_, window, cx| {
12797            Editor::from_state_proto(
12798                workspace_entity,
12799                ViewId {
12800                    creator: CollaboratorId::PeerId(PeerId::default()),
12801                    id: 0,
12802                },
12803                &mut state_message,
12804                window,
12805                cx,
12806            )
12807        })
12808        .unwrap()
12809        .unwrap()
12810        .await
12811        .unwrap();
12812    assert_eq!(
12813        follower_2.update(cx, |editor, cx| editor.text(cx)),
12814        leader.update(cx, |editor, cx| editor.text(cx))
12815    );
12816
12817    // Remove some excerpts.
12818    leader.update(cx, |leader, cx| {
12819        leader.buffer.update(cx, |multibuffer, cx| {
12820            let excerpt_ids = multibuffer.excerpt_ids();
12821            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12822            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12823        });
12824    });
12825
12826    // Apply the update of removing the excerpts.
12827    follower_1
12828        .update_in(cx, |follower, window, cx| {
12829            follower.apply_update_proto(
12830                &project,
12831                update_message.borrow().clone().unwrap(),
12832                window,
12833                cx,
12834            )
12835        })
12836        .await
12837        .unwrap();
12838    follower_2
12839        .update_in(cx, |follower, window, cx| {
12840            follower.apply_update_proto(
12841                &project,
12842                update_message.borrow().clone().unwrap(),
12843                window,
12844                cx,
12845            )
12846        })
12847        .await
12848        .unwrap();
12849    update_message.borrow_mut().take();
12850    assert_eq!(
12851        follower_1.update(cx, |editor, cx| editor.text(cx)),
12852        leader.update(cx, |editor, cx| editor.text(cx))
12853    );
12854}
12855
12856#[gpui::test]
12857async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12858    init_test(cx, |_| {});
12859
12860    let mut cx = EditorTestContext::new(cx).await;
12861    let lsp_store =
12862        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12863
12864    cx.set_state(indoc! {"
12865        ˇfn func(abc def: i32) -> u32 {
12866        }
12867    "});
12868
12869    cx.update(|_, cx| {
12870        lsp_store.update(cx, |lsp_store, cx| {
12871            lsp_store
12872                .update_diagnostics(
12873                    LanguageServerId(0),
12874                    lsp::PublishDiagnosticsParams {
12875                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12876                        version: None,
12877                        diagnostics: vec![
12878                            lsp::Diagnostic {
12879                                range: lsp::Range::new(
12880                                    lsp::Position::new(0, 11),
12881                                    lsp::Position::new(0, 12),
12882                                ),
12883                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12884                                ..Default::default()
12885                            },
12886                            lsp::Diagnostic {
12887                                range: lsp::Range::new(
12888                                    lsp::Position::new(0, 12),
12889                                    lsp::Position::new(0, 15),
12890                                ),
12891                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12892                                ..Default::default()
12893                            },
12894                            lsp::Diagnostic {
12895                                range: lsp::Range::new(
12896                                    lsp::Position::new(0, 25),
12897                                    lsp::Position::new(0, 28),
12898                                ),
12899                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12900                                ..Default::default()
12901                            },
12902                        ],
12903                    },
12904                    &[],
12905                    cx,
12906                )
12907                .unwrap()
12908        });
12909    });
12910
12911    executor.run_until_parked();
12912
12913    cx.update_editor(|editor, window, cx| {
12914        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12915    });
12916
12917    cx.assert_editor_state(indoc! {"
12918        fn func(abc def: i32) -> ˇu32 {
12919        }
12920    "});
12921
12922    cx.update_editor(|editor, window, cx| {
12923        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12924    });
12925
12926    cx.assert_editor_state(indoc! {"
12927        fn func(abc ˇdef: i32) -> u32 {
12928        }
12929    "});
12930
12931    cx.update_editor(|editor, window, cx| {
12932        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12933    });
12934
12935    cx.assert_editor_state(indoc! {"
12936        fn func(abcˇ def: i32) -> u32 {
12937        }
12938    "});
12939
12940    cx.update_editor(|editor, window, cx| {
12941        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12942    });
12943
12944    cx.assert_editor_state(indoc! {"
12945        fn func(abc def: i32) -> ˇu32 {
12946        }
12947    "});
12948}
12949
12950#[gpui::test]
12951async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12952    init_test(cx, |_| {});
12953
12954    let mut cx = EditorTestContext::new(cx).await;
12955
12956    let diff_base = r#"
12957        use some::mod;
12958
12959        const A: u32 = 42;
12960
12961        fn main() {
12962            println!("hello");
12963
12964            println!("world");
12965        }
12966        "#
12967    .unindent();
12968
12969    // Edits are modified, removed, modified, added
12970    cx.set_state(
12971        &r#"
12972        use some::modified;
12973
12974        ˇ
12975        fn main() {
12976            println!("hello there");
12977
12978            println!("around the");
12979            println!("world");
12980        }
12981        "#
12982        .unindent(),
12983    );
12984
12985    cx.set_head_text(&diff_base);
12986    executor.run_until_parked();
12987
12988    cx.update_editor(|editor, window, cx| {
12989        //Wrap around the bottom of the buffer
12990        for _ in 0..3 {
12991            editor.go_to_next_hunk(&GoToHunk, window, cx);
12992        }
12993    });
12994
12995    cx.assert_editor_state(
12996        &r#"
12997        ˇuse some::modified;
12998
12999
13000        fn main() {
13001            println!("hello there");
13002
13003            println!("around the");
13004            println!("world");
13005        }
13006        "#
13007        .unindent(),
13008    );
13009
13010    cx.update_editor(|editor, window, cx| {
13011        //Wrap around the top of the buffer
13012        for _ in 0..2 {
13013            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13014        }
13015    });
13016
13017    cx.assert_editor_state(
13018        &r#"
13019        use some::modified;
13020
13021
13022        fn main() {
13023        ˇ    println!("hello there");
13024
13025            println!("around the");
13026            println!("world");
13027        }
13028        "#
13029        .unindent(),
13030    );
13031
13032    cx.update_editor(|editor, window, cx| {
13033        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13034    });
13035
13036    cx.assert_editor_state(
13037        &r#"
13038        use some::modified;
13039
13040        ˇ
13041        fn main() {
13042            println!("hello there");
13043
13044            println!("around the");
13045            println!("world");
13046        }
13047        "#
13048        .unindent(),
13049    );
13050
13051    cx.update_editor(|editor, window, cx| {
13052        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13053    });
13054
13055    cx.assert_editor_state(
13056        &r#"
13057        ˇuse some::modified;
13058
13059
13060        fn main() {
13061            println!("hello there");
13062
13063            println!("around the");
13064            println!("world");
13065        }
13066        "#
13067        .unindent(),
13068    );
13069
13070    cx.update_editor(|editor, window, cx| {
13071        for _ in 0..2 {
13072            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13073        }
13074    });
13075
13076    cx.assert_editor_state(
13077        &r#"
13078        use some::modified;
13079
13080
13081        fn main() {
13082        ˇ    println!("hello there");
13083
13084            println!("around the");
13085            println!("world");
13086        }
13087        "#
13088        .unindent(),
13089    );
13090
13091    cx.update_editor(|editor, window, cx| {
13092        editor.fold(&Fold, window, cx);
13093    });
13094
13095    cx.update_editor(|editor, window, cx| {
13096        editor.go_to_next_hunk(&GoToHunk, window, cx);
13097    });
13098
13099    cx.assert_editor_state(
13100        &r#"
13101        ˇuse some::modified;
13102
13103
13104        fn main() {
13105            println!("hello there");
13106
13107            println!("around the");
13108            println!("world");
13109        }
13110        "#
13111        .unindent(),
13112    );
13113}
13114
13115#[test]
13116fn test_split_words() {
13117    fn split(text: &str) -> Vec<&str> {
13118        split_words(text).collect()
13119    }
13120
13121    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13122    assert_eq!(split("hello_world"), &["hello_", "world"]);
13123    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13124    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13125    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13126    assert_eq!(split("helloworld"), &["helloworld"]);
13127
13128    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13129}
13130
13131#[gpui::test]
13132async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13133    init_test(cx, |_| {});
13134
13135    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13136    let mut assert = |before, after| {
13137        let _state_context = cx.set_state(before);
13138        cx.run_until_parked();
13139        cx.update_editor(|editor, window, cx| {
13140            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13141        });
13142        cx.run_until_parked();
13143        cx.assert_editor_state(after);
13144    };
13145
13146    // Outside bracket jumps to outside of matching bracket
13147    assert("console.logˇ(var);", "console.log(var)ˇ;");
13148    assert("console.log(var)ˇ;", "console.logˇ(var);");
13149
13150    // Inside bracket jumps to inside of matching bracket
13151    assert("console.log(ˇvar);", "console.log(varˇ);");
13152    assert("console.log(varˇ);", "console.log(ˇvar);");
13153
13154    // When outside a bracket and inside, favor jumping to the inside bracket
13155    assert(
13156        "console.log('foo', [1, 2, 3]ˇ);",
13157        "console.log(ˇ'foo', [1, 2, 3]);",
13158    );
13159    assert(
13160        "console.log(ˇ'foo', [1, 2, 3]);",
13161        "console.log('foo', [1, 2, 3]ˇ);",
13162    );
13163
13164    // Bias forward if two options are equally likely
13165    assert(
13166        "let result = curried_fun()ˇ();",
13167        "let result = curried_fun()()ˇ;",
13168    );
13169
13170    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13171    assert(
13172        indoc! {"
13173            function test() {
13174                console.log('test')ˇ
13175            }"},
13176        indoc! {"
13177            function test() {
13178                console.logˇ('test')
13179            }"},
13180    );
13181}
13182
13183#[gpui::test]
13184async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13185    init_test(cx, |_| {});
13186
13187    let fs = FakeFs::new(cx.executor());
13188    fs.insert_tree(
13189        path!("/a"),
13190        json!({
13191            "main.rs": "fn main() { let a = 5; }",
13192            "other.rs": "// Test file",
13193        }),
13194    )
13195    .await;
13196    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13197
13198    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13199    language_registry.add(Arc::new(Language::new(
13200        LanguageConfig {
13201            name: "Rust".into(),
13202            matcher: LanguageMatcher {
13203                path_suffixes: vec!["rs".to_string()],
13204                ..Default::default()
13205            },
13206            brackets: BracketPairConfig {
13207                pairs: vec![BracketPair {
13208                    start: "{".to_string(),
13209                    end: "}".to_string(),
13210                    close: true,
13211                    surround: true,
13212                    newline: true,
13213                }],
13214                disabled_scopes_by_bracket_ix: Vec::new(),
13215            },
13216            ..Default::default()
13217        },
13218        Some(tree_sitter_rust::LANGUAGE.into()),
13219    )));
13220    let mut fake_servers = language_registry.register_fake_lsp(
13221        "Rust",
13222        FakeLspAdapter {
13223            capabilities: lsp::ServerCapabilities {
13224                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13225                    first_trigger_character: "{".to_string(),
13226                    more_trigger_character: None,
13227                }),
13228                ..Default::default()
13229            },
13230            ..Default::default()
13231        },
13232    );
13233
13234    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13235
13236    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13237
13238    let worktree_id = workspace
13239        .update(cx, |workspace, _, cx| {
13240            workspace.project().update(cx, |project, cx| {
13241                project.worktrees(cx).next().unwrap().read(cx).id()
13242            })
13243        })
13244        .unwrap();
13245
13246    let buffer = project
13247        .update(cx, |project, cx| {
13248            project.open_local_buffer(path!("/a/main.rs"), cx)
13249        })
13250        .await
13251        .unwrap();
13252    let editor_handle = workspace
13253        .update(cx, |workspace, window, cx| {
13254            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13255        })
13256        .unwrap()
13257        .await
13258        .unwrap()
13259        .downcast::<Editor>()
13260        .unwrap();
13261
13262    cx.executor().start_waiting();
13263    let fake_server = fake_servers.next().await.unwrap();
13264
13265    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13266        |params, _| async move {
13267            assert_eq!(
13268                params.text_document_position.text_document.uri,
13269                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13270            );
13271            assert_eq!(
13272                params.text_document_position.position,
13273                lsp::Position::new(0, 21),
13274            );
13275
13276            Ok(Some(vec![lsp::TextEdit {
13277                new_text: "]".to_string(),
13278                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13279            }]))
13280        },
13281    );
13282
13283    editor_handle.update_in(cx, |editor, window, cx| {
13284        window.focus(&editor.focus_handle(cx));
13285        editor.change_selections(None, window, cx, |s| {
13286            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13287        });
13288        editor.handle_input("{", window, cx);
13289    });
13290
13291    cx.executor().run_until_parked();
13292
13293    buffer.update(cx, |buffer, _| {
13294        assert_eq!(
13295            buffer.text(),
13296            "fn main() { let a = {5}; }",
13297            "No extra braces from on type formatting should appear in the buffer"
13298        )
13299    });
13300}
13301
13302#[gpui::test]
13303async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13304    init_test(cx, |_| {});
13305
13306    let fs = FakeFs::new(cx.executor());
13307    fs.insert_tree(
13308        path!("/a"),
13309        json!({
13310            "main.rs": "fn main() { let a = 5; }",
13311            "other.rs": "// Test file",
13312        }),
13313    )
13314    .await;
13315
13316    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13317
13318    let server_restarts = Arc::new(AtomicUsize::new(0));
13319    let closure_restarts = Arc::clone(&server_restarts);
13320    let language_server_name = "test language server";
13321    let language_name: LanguageName = "Rust".into();
13322
13323    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13324    language_registry.add(Arc::new(Language::new(
13325        LanguageConfig {
13326            name: language_name.clone(),
13327            matcher: LanguageMatcher {
13328                path_suffixes: vec!["rs".to_string()],
13329                ..Default::default()
13330            },
13331            ..Default::default()
13332        },
13333        Some(tree_sitter_rust::LANGUAGE.into()),
13334    )));
13335    let mut fake_servers = language_registry.register_fake_lsp(
13336        "Rust",
13337        FakeLspAdapter {
13338            name: language_server_name,
13339            initialization_options: Some(json!({
13340                "testOptionValue": true
13341            })),
13342            initializer: Some(Box::new(move |fake_server| {
13343                let task_restarts = Arc::clone(&closure_restarts);
13344                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13345                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13346                    futures::future::ready(Ok(()))
13347                });
13348            })),
13349            ..Default::default()
13350        },
13351    );
13352
13353    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13354    let _buffer = project
13355        .update(cx, |project, cx| {
13356            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13357        })
13358        .await
13359        .unwrap();
13360    let _fake_server = fake_servers.next().await.unwrap();
13361    update_test_language_settings(cx, |language_settings| {
13362        language_settings.languages.insert(
13363            language_name.clone(),
13364            LanguageSettingsContent {
13365                tab_size: NonZeroU32::new(8),
13366                ..Default::default()
13367            },
13368        );
13369    });
13370    cx.executor().run_until_parked();
13371    assert_eq!(
13372        server_restarts.load(atomic::Ordering::Acquire),
13373        0,
13374        "Should not restart LSP server on an unrelated change"
13375    );
13376
13377    update_test_project_settings(cx, |project_settings| {
13378        project_settings.lsp.insert(
13379            "Some other server name".into(),
13380            LspSettings {
13381                binary: None,
13382                settings: None,
13383                initialization_options: Some(json!({
13384                    "some other init value": false
13385                })),
13386                enable_lsp_tasks: false,
13387            },
13388        );
13389    });
13390    cx.executor().run_until_parked();
13391    assert_eq!(
13392        server_restarts.load(atomic::Ordering::Acquire),
13393        0,
13394        "Should not restart LSP server on an unrelated LSP settings change"
13395    );
13396
13397    update_test_project_settings(cx, |project_settings| {
13398        project_settings.lsp.insert(
13399            language_server_name.into(),
13400            LspSettings {
13401                binary: None,
13402                settings: None,
13403                initialization_options: Some(json!({
13404                    "anotherInitValue": false
13405                })),
13406                enable_lsp_tasks: false,
13407            },
13408        );
13409    });
13410    cx.executor().run_until_parked();
13411    assert_eq!(
13412        server_restarts.load(atomic::Ordering::Acquire),
13413        1,
13414        "Should restart LSP server on a related LSP settings change"
13415    );
13416
13417    update_test_project_settings(cx, |project_settings| {
13418        project_settings.lsp.insert(
13419            language_server_name.into(),
13420            LspSettings {
13421                binary: None,
13422                settings: None,
13423                initialization_options: Some(json!({
13424                    "anotherInitValue": false
13425                })),
13426                enable_lsp_tasks: false,
13427            },
13428        );
13429    });
13430    cx.executor().run_until_parked();
13431    assert_eq!(
13432        server_restarts.load(atomic::Ordering::Acquire),
13433        1,
13434        "Should not restart LSP server on a related LSP settings change that is the same"
13435    );
13436
13437    update_test_project_settings(cx, |project_settings| {
13438        project_settings.lsp.insert(
13439            language_server_name.into(),
13440            LspSettings {
13441                binary: None,
13442                settings: None,
13443                initialization_options: None,
13444                enable_lsp_tasks: false,
13445            },
13446        );
13447    });
13448    cx.executor().run_until_parked();
13449    assert_eq!(
13450        server_restarts.load(atomic::Ordering::Acquire),
13451        2,
13452        "Should restart LSP server on another related LSP settings change"
13453    );
13454}
13455
13456#[gpui::test]
13457async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13458    init_test(cx, |_| {});
13459
13460    let mut cx = EditorLspTestContext::new_rust(
13461        lsp::ServerCapabilities {
13462            completion_provider: Some(lsp::CompletionOptions {
13463                trigger_characters: Some(vec![".".to_string()]),
13464                resolve_provider: Some(true),
13465                ..Default::default()
13466            }),
13467            ..Default::default()
13468        },
13469        cx,
13470    )
13471    .await;
13472
13473    cx.set_state("fn main() { let a = 2ˇ; }");
13474    cx.simulate_keystroke(".");
13475    let completion_item = lsp::CompletionItem {
13476        label: "some".into(),
13477        kind: Some(lsp::CompletionItemKind::SNIPPET),
13478        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13479        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13480            kind: lsp::MarkupKind::Markdown,
13481            value: "```rust\nSome(2)\n```".to_string(),
13482        })),
13483        deprecated: Some(false),
13484        sort_text: Some("fffffff2".to_string()),
13485        filter_text: Some("some".to_string()),
13486        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13487        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13488            range: lsp::Range {
13489                start: lsp::Position {
13490                    line: 0,
13491                    character: 22,
13492                },
13493                end: lsp::Position {
13494                    line: 0,
13495                    character: 22,
13496                },
13497            },
13498            new_text: "Some(2)".to_string(),
13499        })),
13500        additional_text_edits: Some(vec![lsp::TextEdit {
13501            range: lsp::Range {
13502                start: lsp::Position {
13503                    line: 0,
13504                    character: 20,
13505                },
13506                end: lsp::Position {
13507                    line: 0,
13508                    character: 22,
13509                },
13510            },
13511            new_text: "".to_string(),
13512        }]),
13513        ..Default::default()
13514    };
13515
13516    let closure_completion_item = completion_item.clone();
13517    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13518        let task_completion_item = closure_completion_item.clone();
13519        async move {
13520            Ok(Some(lsp::CompletionResponse::Array(vec![
13521                task_completion_item,
13522            ])))
13523        }
13524    });
13525
13526    request.next().await;
13527
13528    cx.condition(|editor, _| editor.context_menu_visible())
13529        .await;
13530    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13531        editor
13532            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13533            .unwrap()
13534    });
13535    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13536
13537    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13538        let task_completion_item = completion_item.clone();
13539        async move { Ok(task_completion_item) }
13540    })
13541    .next()
13542    .await
13543    .unwrap();
13544    apply_additional_edits.await.unwrap();
13545    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13546}
13547
13548#[gpui::test]
13549async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13550    init_test(cx, |_| {});
13551
13552    let mut cx = EditorLspTestContext::new_rust(
13553        lsp::ServerCapabilities {
13554            completion_provider: Some(lsp::CompletionOptions {
13555                trigger_characters: Some(vec![".".to_string()]),
13556                resolve_provider: Some(true),
13557                ..Default::default()
13558            }),
13559            ..Default::default()
13560        },
13561        cx,
13562    )
13563    .await;
13564
13565    cx.set_state("fn main() { let a = 2ˇ; }");
13566    cx.simulate_keystroke(".");
13567
13568    let item1 = lsp::CompletionItem {
13569        label: "method id()".to_string(),
13570        filter_text: Some("id".to_string()),
13571        detail: None,
13572        documentation: None,
13573        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13574            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13575            new_text: ".id".to_string(),
13576        })),
13577        ..lsp::CompletionItem::default()
13578    };
13579
13580    let item2 = lsp::CompletionItem {
13581        label: "other".to_string(),
13582        filter_text: Some("other".to_string()),
13583        detail: None,
13584        documentation: None,
13585        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13586            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13587            new_text: ".other".to_string(),
13588        })),
13589        ..lsp::CompletionItem::default()
13590    };
13591
13592    let item1 = item1.clone();
13593    cx.set_request_handler::<lsp::request::Completion, _, _>({
13594        let item1 = item1.clone();
13595        move |_, _, _| {
13596            let item1 = item1.clone();
13597            let item2 = item2.clone();
13598            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13599        }
13600    })
13601    .next()
13602    .await;
13603
13604    cx.condition(|editor, _| editor.context_menu_visible())
13605        .await;
13606    cx.update_editor(|editor, _, _| {
13607        let context_menu = editor.context_menu.borrow_mut();
13608        let context_menu = context_menu
13609            .as_ref()
13610            .expect("Should have the context menu deployed");
13611        match context_menu {
13612            CodeContextMenu::Completions(completions_menu) => {
13613                let completions = completions_menu.completions.borrow_mut();
13614                assert_eq!(
13615                    completions
13616                        .iter()
13617                        .map(|completion| &completion.label.text)
13618                        .collect::<Vec<_>>(),
13619                    vec!["method id()", "other"]
13620                )
13621            }
13622            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13623        }
13624    });
13625
13626    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13627        let item1 = item1.clone();
13628        move |_, item_to_resolve, _| {
13629            let item1 = item1.clone();
13630            async move {
13631                if item1 == item_to_resolve {
13632                    Ok(lsp::CompletionItem {
13633                        label: "method id()".to_string(),
13634                        filter_text: Some("id".to_string()),
13635                        detail: Some("Now resolved!".to_string()),
13636                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13637                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13638                            range: lsp::Range::new(
13639                                lsp::Position::new(0, 22),
13640                                lsp::Position::new(0, 22),
13641                            ),
13642                            new_text: ".id".to_string(),
13643                        })),
13644                        ..lsp::CompletionItem::default()
13645                    })
13646                } else {
13647                    Ok(item_to_resolve)
13648                }
13649            }
13650        }
13651    })
13652    .next()
13653    .await
13654    .unwrap();
13655    cx.run_until_parked();
13656
13657    cx.update_editor(|editor, window, cx| {
13658        editor.context_menu_next(&Default::default(), window, cx);
13659    });
13660
13661    cx.update_editor(|editor, _, _| {
13662        let context_menu = editor.context_menu.borrow_mut();
13663        let context_menu = context_menu
13664            .as_ref()
13665            .expect("Should have the context menu deployed");
13666        match context_menu {
13667            CodeContextMenu::Completions(completions_menu) => {
13668                let completions = completions_menu.completions.borrow_mut();
13669                assert_eq!(
13670                    completions
13671                        .iter()
13672                        .map(|completion| &completion.label.text)
13673                        .collect::<Vec<_>>(),
13674                    vec!["method id() Now resolved!", "other"],
13675                    "Should update first completion label, but not second as the filter text did not match."
13676                );
13677            }
13678            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13679        }
13680    });
13681}
13682
13683#[gpui::test]
13684async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13685    init_test(cx, |_| {});
13686
13687    let mut cx = EditorLspTestContext::new_rust(
13688        lsp::ServerCapabilities {
13689            completion_provider: Some(lsp::CompletionOptions {
13690                trigger_characters: Some(vec![".".to_string()]),
13691                resolve_provider: Some(true),
13692                ..Default::default()
13693            }),
13694            ..Default::default()
13695        },
13696        cx,
13697    )
13698    .await;
13699
13700    cx.set_state("fn main() { let a = 2ˇ; }");
13701    cx.simulate_keystroke(".");
13702
13703    let unresolved_item_1 = lsp::CompletionItem {
13704        label: "id".to_string(),
13705        filter_text: Some("id".to_string()),
13706        detail: None,
13707        documentation: None,
13708        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13709            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13710            new_text: ".id".to_string(),
13711        })),
13712        ..lsp::CompletionItem::default()
13713    };
13714    let resolved_item_1 = lsp::CompletionItem {
13715        additional_text_edits: Some(vec![lsp::TextEdit {
13716            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13717            new_text: "!!".to_string(),
13718        }]),
13719        ..unresolved_item_1.clone()
13720    };
13721    let unresolved_item_2 = lsp::CompletionItem {
13722        label: "other".to_string(),
13723        filter_text: Some("other".to_string()),
13724        detail: None,
13725        documentation: None,
13726        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13727            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13728            new_text: ".other".to_string(),
13729        })),
13730        ..lsp::CompletionItem::default()
13731    };
13732    let resolved_item_2 = lsp::CompletionItem {
13733        additional_text_edits: Some(vec![lsp::TextEdit {
13734            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13735            new_text: "??".to_string(),
13736        }]),
13737        ..unresolved_item_2.clone()
13738    };
13739
13740    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13741    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13742    cx.lsp
13743        .server
13744        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13745            let unresolved_item_1 = unresolved_item_1.clone();
13746            let resolved_item_1 = resolved_item_1.clone();
13747            let unresolved_item_2 = unresolved_item_2.clone();
13748            let resolved_item_2 = resolved_item_2.clone();
13749            let resolve_requests_1 = resolve_requests_1.clone();
13750            let resolve_requests_2 = resolve_requests_2.clone();
13751            move |unresolved_request, _| {
13752                let unresolved_item_1 = unresolved_item_1.clone();
13753                let resolved_item_1 = resolved_item_1.clone();
13754                let unresolved_item_2 = unresolved_item_2.clone();
13755                let resolved_item_2 = resolved_item_2.clone();
13756                let resolve_requests_1 = resolve_requests_1.clone();
13757                let resolve_requests_2 = resolve_requests_2.clone();
13758                async move {
13759                    if unresolved_request == unresolved_item_1 {
13760                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13761                        Ok(resolved_item_1.clone())
13762                    } else if unresolved_request == unresolved_item_2 {
13763                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13764                        Ok(resolved_item_2.clone())
13765                    } else {
13766                        panic!("Unexpected completion item {unresolved_request:?}")
13767                    }
13768                }
13769            }
13770        })
13771        .detach();
13772
13773    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13774        let unresolved_item_1 = unresolved_item_1.clone();
13775        let unresolved_item_2 = unresolved_item_2.clone();
13776        async move {
13777            Ok(Some(lsp::CompletionResponse::Array(vec![
13778                unresolved_item_1,
13779                unresolved_item_2,
13780            ])))
13781        }
13782    })
13783    .next()
13784    .await;
13785
13786    cx.condition(|editor, _| editor.context_menu_visible())
13787        .await;
13788    cx.update_editor(|editor, _, _| {
13789        let context_menu = editor.context_menu.borrow_mut();
13790        let context_menu = context_menu
13791            .as_ref()
13792            .expect("Should have the context menu deployed");
13793        match context_menu {
13794            CodeContextMenu::Completions(completions_menu) => {
13795                let completions = completions_menu.completions.borrow_mut();
13796                assert_eq!(
13797                    completions
13798                        .iter()
13799                        .map(|completion| &completion.label.text)
13800                        .collect::<Vec<_>>(),
13801                    vec!["id", "other"]
13802                )
13803            }
13804            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13805        }
13806    });
13807    cx.run_until_parked();
13808
13809    cx.update_editor(|editor, window, cx| {
13810        editor.context_menu_next(&ContextMenuNext, window, cx);
13811    });
13812    cx.run_until_parked();
13813    cx.update_editor(|editor, window, cx| {
13814        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13815    });
13816    cx.run_until_parked();
13817    cx.update_editor(|editor, window, cx| {
13818        editor.context_menu_next(&ContextMenuNext, window, cx);
13819    });
13820    cx.run_until_parked();
13821    cx.update_editor(|editor, window, cx| {
13822        editor
13823            .compose_completion(&ComposeCompletion::default(), window, cx)
13824            .expect("No task returned")
13825    })
13826    .await
13827    .expect("Completion failed");
13828    cx.run_until_parked();
13829
13830    cx.update_editor(|editor, _, cx| {
13831        assert_eq!(
13832            resolve_requests_1.load(atomic::Ordering::Acquire),
13833            1,
13834            "Should always resolve once despite multiple selections"
13835        );
13836        assert_eq!(
13837            resolve_requests_2.load(atomic::Ordering::Acquire),
13838            1,
13839            "Should always resolve once after multiple selections and applying the completion"
13840        );
13841        assert_eq!(
13842            editor.text(cx),
13843            "fn main() { let a = ??.other; }",
13844            "Should use resolved data when applying the completion"
13845        );
13846    });
13847}
13848
13849#[gpui::test]
13850async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13851    init_test(cx, |_| {});
13852
13853    let item_0 = lsp::CompletionItem {
13854        label: "abs".into(),
13855        insert_text: Some("abs".into()),
13856        data: Some(json!({ "very": "special"})),
13857        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13858        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13859            lsp::InsertReplaceEdit {
13860                new_text: "abs".to_string(),
13861                insert: lsp::Range::default(),
13862                replace: lsp::Range::default(),
13863            },
13864        )),
13865        ..lsp::CompletionItem::default()
13866    };
13867    let items = iter::once(item_0.clone())
13868        .chain((11..51).map(|i| lsp::CompletionItem {
13869            label: format!("item_{}", i),
13870            insert_text: Some(format!("item_{}", i)),
13871            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13872            ..lsp::CompletionItem::default()
13873        }))
13874        .collect::<Vec<_>>();
13875
13876    let default_commit_characters = vec!["?".to_string()];
13877    let default_data = json!({ "default": "data"});
13878    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13879    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13880    let default_edit_range = lsp::Range {
13881        start: lsp::Position {
13882            line: 0,
13883            character: 5,
13884        },
13885        end: lsp::Position {
13886            line: 0,
13887            character: 5,
13888        },
13889    };
13890
13891    let mut cx = EditorLspTestContext::new_rust(
13892        lsp::ServerCapabilities {
13893            completion_provider: Some(lsp::CompletionOptions {
13894                trigger_characters: Some(vec![".".to_string()]),
13895                resolve_provider: Some(true),
13896                ..Default::default()
13897            }),
13898            ..Default::default()
13899        },
13900        cx,
13901    )
13902    .await;
13903
13904    cx.set_state("fn main() { let a = 2ˇ; }");
13905    cx.simulate_keystroke(".");
13906
13907    let completion_data = default_data.clone();
13908    let completion_characters = default_commit_characters.clone();
13909    let completion_items = items.clone();
13910    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13911        let default_data = completion_data.clone();
13912        let default_commit_characters = completion_characters.clone();
13913        let items = completion_items.clone();
13914        async move {
13915            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13916                items,
13917                item_defaults: Some(lsp::CompletionListItemDefaults {
13918                    data: Some(default_data.clone()),
13919                    commit_characters: Some(default_commit_characters.clone()),
13920                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13921                        default_edit_range,
13922                    )),
13923                    insert_text_format: Some(default_insert_text_format),
13924                    insert_text_mode: Some(default_insert_text_mode),
13925                }),
13926                ..lsp::CompletionList::default()
13927            })))
13928        }
13929    })
13930    .next()
13931    .await;
13932
13933    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13934    cx.lsp
13935        .server
13936        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13937            let closure_resolved_items = resolved_items.clone();
13938            move |item_to_resolve, _| {
13939                let closure_resolved_items = closure_resolved_items.clone();
13940                async move {
13941                    closure_resolved_items.lock().push(item_to_resolve.clone());
13942                    Ok(item_to_resolve)
13943                }
13944            }
13945        })
13946        .detach();
13947
13948    cx.condition(|editor, _| editor.context_menu_visible())
13949        .await;
13950    cx.run_until_parked();
13951    cx.update_editor(|editor, _, _| {
13952        let menu = editor.context_menu.borrow_mut();
13953        match menu.as_ref().expect("should have the completions menu") {
13954            CodeContextMenu::Completions(completions_menu) => {
13955                assert_eq!(
13956                    completions_menu
13957                        .entries
13958                        .borrow()
13959                        .iter()
13960                        .map(|mat| mat.string.clone())
13961                        .collect::<Vec<String>>(),
13962                    items
13963                        .iter()
13964                        .map(|completion| completion.label.clone())
13965                        .collect::<Vec<String>>()
13966                );
13967            }
13968            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13969        }
13970    });
13971    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13972    // with 4 from the end.
13973    assert_eq!(
13974        *resolved_items.lock(),
13975        [&items[0..16], &items[items.len() - 4..items.len()]]
13976            .concat()
13977            .iter()
13978            .cloned()
13979            .map(|mut item| {
13980                if item.data.is_none() {
13981                    item.data = Some(default_data.clone());
13982                }
13983                item
13984            })
13985            .collect::<Vec<lsp::CompletionItem>>(),
13986        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13987    );
13988    resolved_items.lock().clear();
13989
13990    cx.update_editor(|editor, window, cx| {
13991        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13992    });
13993    cx.run_until_parked();
13994    // Completions that have already been resolved are skipped.
13995    assert_eq!(
13996        *resolved_items.lock(),
13997        items[items.len() - 16..items.len() - 4]
13998            .iter()
13999            .cloned()
14000            .map(|mut item| {
14001                if item.data.is_none() {
14002                    item.data = Some(default_data.clone());
14003                }
14004                item
14005            })
14006            .collect::<Vec<lsp::CompletionItem>>()
14007    );
14008    resolved_items.lock().clear();
14009}
14010
14011#[gpui::test]
14012async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14013    init_test(cx, |_| {});
14014
14015    let mut cx = EditorLspTestContext::new(
14016        Language::new(
14017            LanguageConfig {
14018                matcher: LanguageMatcher {
14019                    path_suffixes: vec!["jsx".into()],
14020                    ..Default::default()
14021                },
14022                overrides: [(
14023                    "element".into(),
14024                    LanguageConfigOverride {
14025                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14026                        ..Default::default()
14027                    },
14028                )]
14029                .into_iter()
14030                .collect(),
14031                ..Default::default()
14032            },
14033            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14034        )
14035        .with_override_query("(jsx_self_closing_element) @element")
14036        .unwrap(),
14037        lsp::ServerCapabilities {
14038            completion_provider: Some(lsp::CompletionOptions {
14039                trigger_characters: Some(vec![":".to_string()]),
14040                ..Default::default()
14041            }),
14042            ..Default::default()
14043        },
14044        cx,
14045    )
14046    .await;
14047
14048    cx.lsp
14049        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14050            Ok(Some(lsp::CompletionResponse::Array(vec![
14051                lsp::CompletionItem {
14052                    label: "bg-blue".into(),
14053                    ..Default::default()
14054                },
14055                lsp::CompletionItem {
14056                    label: "bg-red".into(),
14057                    ..Default::default()
14058                },
14059                lsp::CompletionItem {
14060                    label: "bg-yellow".into(),
14061                    ..Default::default()
14062                },
14063            ])))
14064        });
14065
14066    cx.set_state(r#"<p class="bgˇ" />"#);
14067
14068    // Trigger completion when typing a dash, because the dash is an extra
14069    // word character in the 'element' scope, which contains the cursor.
14070    cx.simulate_keystroke("-");
14071    cx.executor().run_until_parked();
14072    cx.update_editor(|editor, _, _| {
14073        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14074        {
14075            assert_eq!(
14076                completion_menu_entries(&menu),
14077                &["bg-red", "bg-blue", "bg-yellow"]
14078            );
14079        } else {
14080            panic!("expected completion menu to be open");
14081        }
14082    });
14083
14084    cx.simulate_keystroke("l");
14085    cx.executor().run_until_parked();
14086    cx.update_editor(|editor, _, _| {
14087        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14088        {
14089            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14090        } else {
14091            panic!("expected completion menu to be open");
14092        }
14093    });
14094
14095    // When filtering completions, consider the character after the '-' to
14096    // be the start of a subword.
14097    cx.set_state(r#"<p class="yelˇ" />"#);
14098    cx.simulate_keystroke("l");
14099    cx.executor().run_until_parked();
14100    cx.update_editor(|editor, _, _| {
14101        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14102        {
14103            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14104        } else {
14105            panic!("expected completion menu to be open");
14106        }
14107    });
14108}
14109
14110fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14111    let entries = menu.entries.borrow();
14112    entries.iter().map(|mat| mat.string.clone()).collect()
14113}
14114
14115#[gpui::test]
14116async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14117    init_test(cx, |settings| {
14118        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14119            FormatterList(vec![Formatter::Prettier].into()),
14120        ))
14121    });
14122
14123    let fs = FakeFs::new(cx.executor());
14124    fs.insert_file(path!("/file.ts"), Default::default()).await;
14125
14126    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14127    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14128
14129    language_registry.add(Arc::new(Language::new(
14130        LanguageConfig {
14131            name: "TypeScript".into(),
14132            matcher: LanguageMatcher {
14133                path_suffixes: vec!["ts".to_string()],
14134                ..Default::default()
14135            },
14136            ..Default::default()
14137        },
14138        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14139    )));
14140    update_test_language_settings(cx, |settings| {
14141        settings.defaults.prettier = Some(PrettierSettings {
14142            allowed: true,
14143            ..PrettierSettings::default()
14144        });
14145    });
14146
14147    let test_plugin = "test_plugin";
14148    let _ = language_registry.register_fake_lsp(
14149        "TypeScript",
14150        FakeLspAdapter {
14151            prettier_plugins: vec![test_plugin],
14152            ..Default::default()
14153        },
14154    );
14155
14156    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14157    let buffer = project
14158        .update(cx, |project, cx| {
14159            project.open_local_buffer(path!("/file.ts"), cx)
14160        })
14161        .await
14162        .unwrap();
14163
14164    let buffer_text = "one\ntwo\nthree\n";
14165    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14166    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14167    editor.update_in(cx, |editor, window, cx| {
14168        editor.set_text(buffer_text, window, cx)
14169    });
14170
14171    editor
14172        .update_in(cx, |editor, window, cx| {
14173            editor.perform_format(
14174                project.clone(),
14175                FormatTrigger::Manual,
14176                FormatTarget::Buffers,
14177                window,
14178                cx,
14179            )
14180        })
14181        .unwrap()
14182        .await;
14183    assert_eq!(
14184        editor.update(cx, |editor, cx| editor.text(cx)),
14185        buffer_text.to_string() + prettier_format_suffix,
14186        "Test prettier formatting was not applied to the original buffer text",
14187    );
14188
14189    update_test_language_settings(cx, |settings| {
14190        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14191    });
14192    let format = editor.update_in(cx, |editor, window, cx| {
14193        editor.perform_format(
14194            project.clone(),
14195            FormatTrigger::Manual,
14196            FormatTarget::Buffers,
14197            window,
14198            cx,
14199        )
14200    });
14201    format.await.unwrap();
14202    assert_eq!(
14203        editor.update(cx, |editor, cx| editor.text(cx)),
14204        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14205        "Autoformatting (via test prettier) was not applied to the original buffer text",
14206    );
14207}
14208
14209#[gpui::test]
14210async fn test_addition_reverts(cx: &mut TestAppContext) {
14211    init_test(cx, |_| {});
14212    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14213    let base_text = indoc! {r#"
14214        struct Row;
14215        struct Row1;
14216        struct Row2;
14217
14218        struct Row4;
14219        struct Row5;
14220        struct Row6;
14221
14222        struct Row8;
14223        struct Row9;
14224        struct Row10;"#};
14225
14226    // When addition hunks are not adjacent to carets, no hunk revert is performed
14227    assert_hunk_revert(
14228        indoc! {r#"struct Row;
14229                   struct Row1;
14230                   struct Row1.1;
14231                   struct Row1.2;
14232                   struct Row2;ˇ
14233
14234                   struct Row4;
14235                   struct Row5;
14236                   struct Row6;
14237
14238                   struct Row8;
14239                   ˇstruct Row9;
14240                   struct Row9.1;
14241                   struct Row9.2;
14242                   struct Row9.3;
14243                   struct Row10;"#},
14244        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14245        indoc! {r#"struct Row;
14246                   struct Row1;
14247                   struct Row1.1;
14248                   struct Row1.2;
14249                   struct Row2;ˇ
14250
14251                   struct Row4;
14252                   struct Row5;
14253                   struct Row6;
14254
14255                   struct Row8;
14256                   ˇstruct Row9;
14257                   struct Row9.1;
14258                   struct Row9.2;
14259                   struct Row9.3;
14260                   struct Row10;"#},
14261        base_text,
14262        &mut cx,
14263    );
14264    // Same for selections
14265    assert_hunk_revert(
14266        indoc! {r#"struct Row;
14267                   struct Row1;
14268                   struct Row2;
14269                   struct Row2.1;
14270                   struct Row2.2;
14271                   «ˇ
14272                   struct Row4;
14273                   struct» Row5;
14274                   «struct Row6;
14275                   ˇ»
14276                   struct Row9.1;
14277                   struct Row9.2;
14278                   struct Row9.3;
14279                   struct Row8;
14280                   struct Row9;
14281                   struct Row10;"#},
14282        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14283        indoc! {r#"struct Row;
14284                   struct Row1;
14285                   struct Row2;
14286                   struct Row2.1;
14287                   struct Row2.2;
14288                   «ˇ
14289                   struct Row4;
14290                   struct» Row5;
14291                   «struct Row6;
14292                   ˇ»
14293                   struct Row9.1;
14294                   struct Row9.2;
14295                   struct Row9.3;
14296                   struct Row8;
14297                   struct Row9;
14298                   struct Row10;"#},
14299        base_text,
14300        &mut cx,
14301    );
14302
14303    // When carets and selections intersect the addition hunks, those are reverted.
14304    // Adjacent carets got merged.
14305    assert_hunk_revert(
14306        indoc! {r#"struct Row;
14307                   ˇ// something on the top
14308                   struct Row1;
14309                   struct Row2;
14310                   struct Roˇw3.1;
14311                   struct Row2.2;
14312                   struct Row2.3;ˇ
14313
14314                   struct Row4;
14315                   struct ˇRow5.1;
14316                   struct Row5.2;
14317                   struct «Rowˇ»5.3;
14318                   struct Row5;
14319                   struct Row6;
14320                   ˇ
14321                   struct Row9.1;
14322                   struct «Rowˇ»9.2;
14323                   struct «ˇRow»9.3;
14324                   struct Row8;
14325                   struct Row9;
14326                   «ˇ// something on bottom»
14327                   struct Row10;"#},
14328        vec![
14329            DiffHunkStatusKind::Added,
14330            DiffHunkStatusKind::Added,
14331            DiffHunkStatusKind::Added,
14332            DiffHunkStatusKind::Added,
14333            DiffHunkStatusKind::Added,
14334        ],
14335        indoc! {r#"struct Row;
14336                   ˇstruct Row1;
14337                   struct Row2;
14338                   ˇ
14339                   struct Row4;
14340                   ˇstruct Row5;
14341                   struct Row6;
14342                   ˇ
14343                   ˇstruct Row8;
14344                   struct Row9;
14345                   ˇstruct Row10;"#},
14346        base_text,
14347        &mut cx,
14348    );
14349}
14350
14351#[gpui::test]
14352async fn test_modification_reverts(cx: &mut TestAppContext) {
14353    init_test(cx, |_| {});
14354    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14355    let base_text = indoc! {r#"
14356        struct Row;
14357        struct Row1;
14358        struct Row2;
14359
14360        struct Row4;
14361        struct Row5;
14362        struct Row6;
14363
14364        struct Row8;
14365        struct Row9;
14366        struct Row10;"#};
14367
14368    // Modification hunks behave the same as the addition ones.
14369    assert_hunk_revert(
14370        indoc! {r#"struct Row;
14371                   struct Row1;
14372                   struct Row33;
14373                   ˇ
14374                   struct Row4;
14375                   struct Row5;
14376                   struct Row6;
14377                   ˇ
14378                   struct Row99;
14379                   struct Row9;
14380                   struct Row10;"#},
14381        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14382        indoc! {r#"struct Row;
14383                   struct Row1;
14384                   struct Row33;
14385                   ˇ
14386                   struct Row4;
14387                   struct Row5;
14388                   struct Row6;
14389                   ˇ
14390                   struct Row99;
14391                   struct Row9;
14392                   struct Row10;"#},
14393        base_text,
14394        &mut cx,
14395    );
14396    assert_hunk_revert(
14397        indoc! {r#"struct Row;
14398                   struct Row1;
14399                   struct Row33;
14400                   «ˇ
14401                   struct Row4;
14402                   struct» Row5;
14403                   «struct Row6;
14404                   ˇ»
14405                   struct Row99;
14406                   struct Row9;
14407                   struct Row10;"#},
14408        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14409        indoc! {r#"struct Row;
14410                   struct Row1;
14411                   struct Row33;
14412                   «ˇ
14413                   struct Row4;
14414                   struct» Row5;
14415                   «struct Row6;
14416                   ˇ»
14417                   struct Row99;
14418                   struct Row9;
14419                   struct Row10;"#},
14420        base_text,
14421        &mut cx,
14422    );
14423
14424    assert_hunk_revert(
14425        indoc! {r#"ˇstruct Row1.1;
14426                   struct Row1;
14427                   «ˇstr»uct Row22;
14428
14429                   struct ˇRow44;
14430                   struct Row5;
14431                   struct «Rˇ»ow66;ˇ
14432
14433                   «struˇ»ct Row88;
14434                   struct Row9;
14435                   struct Row1011;ˇ"#},
14436        vec![
14437            DiffHunkStatusKind::Modified,
14438            DiffHunkStatusKind::Modified,
14439            DiffHunkStatusKind::Modified,
14440            DiffHunkStatusKind::Modified,
14441            DiffHunkStatusKind::Modified,
14442            DiffHunkStatusKind::Modified,
14443        ],
14444        indoc! {r#"struct Row;
14445                   ˇstruct Row1;
14446                   struct Row2;
14447                   ˇ
14448                   struct Row4;
14449                   ˇstruct Row5;
14450                   struct Row6;
14451                   ˇ
14452                   struct Row8;
14453                   ˇstruct Row9;
14454                   struct Row10;ˇ"#},
14455        base_text,
14456        &mut cx,
14457    );
14458}
14459
14460#[gpui::test]
14461async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14462    init_test(cx, |_| {});
14463    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14464    let base_text = indoc! {r#"
14465        one
14466
14467        two
14468        three
14469        "#};
14470
14471    cx.set_head_text(base_text);
14472    cx.set_state("\nˇ\n");
14473    cx.executor().run_until_parked();
14474    cx.update_editor(|editor, _window, cx| {
14475        editor.expand_selected_diff_hunks(cx);
14476    });
14477    cx.executor().run_until_parked();
14478    cx.update_editor(|editor, window, cx| {
14479        editor.backspace(&Default::default(), window, cx);
14480    });
14481    cx.run_until_parked();
14482    cx.assert_state_with_diff(
14483        indoc! {r#"
14484
14485        - two
14486        - threeˇ
14487        +
14488        "#}
14489        .to_string(),
14490    );
14491}
14492
14493#[gpui::test]
14494async fn test_deletion_reverts(cx: &mut TestAppContext) {
14495    init_test(cx, |_| {});
14496    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14497    let base_text = indoc! {r#"struct Row;
14498struct Row1;
14499struct Row2;
14500
14501struct Row4;
14502struct Row5;
14503struct Row6;
14504
14505struct Row8;
14506struct Row9;
14507struct Row10;"#};
14508
14509    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14510    assert_hunk_revert(
14511        indoc! {r#"struct Row;
14512                   struct Row2;
14513
14514                   ˇstruct Row4;
14515                   struct Row5;
14516                   struct Row6;
14517                   ˇ
14518                   struct Row8;
14519                   struct Row10;"#},
14520        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14521        indoc! {r#"struct Row;
14522                   struct Row2;
14523
14524                   ˇstruct Row4;
14525                   struct Row5;
14526                   struct Row6;
14527                   ˇ
14528                   struct Row8;
14529                   struct Row10;"#},
14530        base_text,
14531        &mut cx,
14532    );
14533    assert_hunk_revert(
14534        indoc! {r#"struct Row;
14535                   struct Row2;
14536
14537                   «ˇstruct Row4;
14538                   struct» Row5;
14539                   «struct Row6;
14540                   ˇ»
14541                   struct Row8;
14542                   struct Row10;"#},
14543        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14544        indoc! {r#"struct Row;
14545                   struct Row2;
14546
14547                   «ˇstruct Row4;
14548                   struct» Row5;
14549                   «struct Row6;
14550                   ˇ»
14551                   struct Row8;
14552                   struct Row10;"#},
14553        base_text,
14554        &mut cx,
14555    );
14556
14557    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14558    assert_hunk_revert(
14559        indoc! {r#"struct Row;
14560                   ˇstruct Row2;
14561
14562                   struct Row4;
14563                   struct Row5;
14564                   struct Row6;
14565
14566                   struct Row8;ˇ
14567                   struct Row10;"#},
14568        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14569        indoc! {r#"struct Row;
14570                   struct Row1;
14571                   ˇstruct Row2;
14572
14573                   struct Row4;
14574                   struct Row5;
14575                   struct Row6;
14576
14577                   struct Row8;ˇ
14578                   struct Row9;
14579                   struct Row10;"#},
14580        base_text,
14581        &mut cx,
14582    );
14583    assert_hunk_revert(
14584        indoc! {r#"struct Row;
14585                   struct Row2«ˇ;
14586                   struct Row4;
14587                   struct» Row5;
14588                   «struct Row6;
14589
14590                   struct Row8;ˇ»
14591                   struct Row10;"#},
14592        vec![
14593            DiffHunkStatusKind::Deleted,
14594            DiffHunkStatusKind::Deleted,
14595            DiffHunkStatusKind::Deleted,
14596        ],
14597        indoc! {r#"struct Row;
14598                   struct Row1;
14599                   struct Row2«ˇ;
14600
14601                   struct Row4;
14602                   struct» Row5;
14603                   «struct Row6;
14604
14605                   struct Row8;ˇ»
14606                   struct Row9;
14607                   struct Row10;"#},
14608        base_text,
14609        &mut cx,
14610    );
14611}
14612
14613#[gpui::test]
14614async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14615    init_test(cx, |_| {});
14616
14617    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14618    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14619    let base_text_3 =
14620        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14621
14622    let text_1 = edit_first_char_of_every_line(base_text_1);
14623    let text_2 = edit_first_char_of_every_line(base_text_2);
14624    let text_3 = edit_first_char_of_every_line(base_text_3);
14625
14626    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14627    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14628    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14629
14630    let multibuffer = cx.new(|cx| {
14631        let mut multibuffer = MultiBuffer::new(ReadWrite);
14632        multibuffer.push_excerpts(
14633            buffer_1.clone(),
14634            [
14635                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14636                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14637                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14638            ],
14639            cx,
14640        );
14641        multibuffer.push_excerpts(
14642            buffer_2.clone(),
14643            [
14644                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14645                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14646                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14647            ],
14648            cx,
14649        );
14650        multibuffer.push_excerpts(
14651            buffer_3.clone(),
14652            [
14653                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14654                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14655                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14656            ],
14657            cx,
14658        );
14659        multibuffer
14660    });
14661
14662    let fs = FakeFs::new(cx.executor());
14663    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14664    let (editor, cx) = cx
14665        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14666    editor.update_in(cx, |editor, _window, cx| {
14667        for (buffer, diff_base) in [
14668            (buffer_1.clone(), base_text_1),
14669            (buffer_2.clone(), base_text_2),
14670            (buffer_3.clone(), base_text_3),
14671        ] {
14672            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14673            editor
14674                .buffer
14675                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14676        }
14677    });
14678    cx.executor().run_until_parked();
14679
14680    editor.update_in(cx, |editor, window, cx| {
14681        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}");
14682        editor.select_all(&SelectAll, window, cx);
14683        editor.git_restore(&Default::default(), window, cx);
14684    });
14685    cx.executor().run_until_parked();
14686
14687    // When all ranges are selected, all buffer hunks are reverted.
14688    editor.update(cx, |editor, cx| {
14689        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");
14690    });
14691    buffer_1.update(cx, |buffer, _| {
14692        assert_eq!(buffer.text(), base_text_1);
14693    });
14694    buffer_2.update(cx, |buffer, _| {
14695        assert_eq!(buffer.text(), base_text_2);
14696    });
14697    buffer_3.update(cx, |buffer, _| {
14698        assert_eq!(buffer.text(), base_text_3);
14699    });
14700
14701    editor.update_in(cx, |editor, window, cx| {
14702        editor.undo(&Default::default(), window, cx);
14703    });
14704
14705    editor.update_in(cx, |editor, window, cx| {
14706        editor.change_selections(None, window, cx, |s| {
14707            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14708        });
14709        editor.git_restore(&Default::default(), window, cx);
14710    });
14711
14712    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14713    // but not affect buffer_2 and its related excerpts.
14714    editor.update(cx, |editor, cx| {
14715        assert_eq!(
14716            editor.text(cx),
14717            "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}"
14718        );
14719    });
14720    buffer_1.update(cx, |buffer, _| {
14721        assert_eq!(buffer.text(), base_text_1);
14722    });
14723    buffer_2.update(cx, |buffer, _| {
14724        assert_eq!(
14725            buffer.text(),
14726            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14727        );
14728    });
14729    buffer_3.update(cx, |buffer, _| {
14730        assert_eq!(
14731            buffer.text(),
14732            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14733        );
14734    });
14735
14736    fn edit_first_char_of_every_line(text: &str) -> String {
14737        text.split('\n')
14738            .map(|line| format!("X{}", &line[1..]))
14739            .collect::<Vec<_>>()
14740            .join("\n")
14741    }
14742}
14743
14744#[gpui::test]
14745async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14746    init_test(cx, |_| {});
14747
14748    let cols = 4;
14749    let rows = 10;
14750    let sample_text_1 = sample_text(rows, cols, 'a');
14751    assert_eq!(
14752        sample_text_1,
14753        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14754    );
14755    let sample_text_2 = sample_text(rows, cols, 'l');
14756    assert_eq!(
14757        sample_text_2,
14758        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14759    );
14760    let sample_text_3 = sample_text(rows, cols, 'v');
14761    assert_eq!(
14762        sample_text_3,
14763        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14764    );
14765
14766    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14767    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14768    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14769
14770    let multi_buffer = cx.new(|cx| {
14771        let mut multibuffer = MultiBuffer::new(ReadWrite);
14772        multibuffer.push_excerpts(
14773            buffer_1.clone(),
14774            [
14775                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14776                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14777                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14778            ],
14779            cx,
14780        );
14781        multibuffer.push_excerpts(
14782            buffer_2.clone(),
14783            [
14784                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14785                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14786                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14787            ],
14788            cx,
14789        );
14790        multibuffer.push_excerpts(
14791            buffer_3.clone(),
14792            [
14793                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14794                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14795                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14796            ],
14797            cx,
14798        );
14799        multibuffer
14800    });
14801
14802    let fs = FakeFs::new(cx.executor());
14803    fs.insert_tree(
14804        "/a",
14805        json!({
14806            "main.rs": sample_text_1,
14807            "other.rs": sample_text_2,
14808            "lib.rs": sample_text_3,
14809        }),
14810    )
14811    .await;
14812    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14813    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14814    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14815    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14816        Editor::new(
14817            EditorMode::full(),
14818            multi_buffer,
14819            Some(project.clone()),
14820            window,
14821            cx,
14822        )
14823    });
14824    let multibuffer_item_id = workspace
14825        .update(cx, |workspace, window, cx| {
14826            assert!(
14827                workspace.active_item(cx).is_none(),
14828                "active item should be None before the first item is added"
14829            );
14830            workspace.add_item_to_active_pane(
14831                Box::new(multi_buffer_editor.clone()),
14832                None,
14833                true,
14834                window,
14835                cx,
14836            );
14837            let active_item = workspace
14838                .active_item(cx)
14839                .expect("should have an active item after adding the multi buffer");
14840            assert!(
14841                !active_item.is_singleton(cx),
14842                "A multi buffer was expected to active after adding"
14843            );
14844            active_item.item_id()
14845        })
14846        .unwrap();
14847    cx.executor().run_until_parked();
14848
14849    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14850        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14851            s.select_ranges(Some(1..2))
14852        });
14853        editor.open_excerpts(&OpenExcerpts, window, cx);
14854    });
14855    cx.executor().run_until_parked();
14856    let first_item_id = workspace
14857        .update(cx, |workspace, window, cx| {
14858            let active_item = workspace
14859                .active_item(cx)
14860                .expect("should have an active item after navigating into the 1st buffer");
14861            let first_item_id = active_item.item_id();
14862            assert_ne!(
14863                first_item_id, multibuffer_item_id,
14864                "Should navigate into the 1st buffer and activate it"
14865            );
14866            assert!(
14867                active_item.is_singleton(cx),
14868                "New active item should be a singleton buffer"
14869            );
14870            assert_eq!(
14871                active_item
14872                    .act_as::<Editor>(cx)
14873                    .expect("should have navigated into an editor for the 1st buffer")
14874                    .read(cx)
14875                    .text(cx),
14876                sample_text_1
14877            );
14878
14879            workspace
14880                .go_back(workspace.active_pane().downgrade(), window, cx)
14881                .detach_and_log_err(cx);
14882
14883            first_item_id
14884        })
14885        .unwrap();
14886    cx.executor().run_until_parked();
14887    workspace
14888        .update(cx, |workspace, _, cx| {
14889            let active_item = workspace
14890                .active_item(cx)
14891                .expect("should have an active item after navigating back");
14892            assert_eq!(
14893                active_item.item_id(),
14894                multibuffer_item_id,
14895                "Should navigate back to the multi buffer"
14896            );
14897            assert!(!active_item.is_singleton(cx));
14898        })
14899        .unwrap();
14900
14901    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14902        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14903            s.select_ranges(Some(39..40))
14904        });
14905        editor.open_excerpts(&OpenExcerpts, window, cx);
14906    });
14907    cx.executor().run_until_parked();
14908    let second_item_id = workspace
14909        .update(cx, |workspace, window, cx| {
14910            let active_item = workspace
14911                .active_item(cx)
14912                .expect("should have an active item after navigating into the 2nd buffer");
14913            let second_item_id = active_item.item_id();
14914            assert_ne!(
14915                second_item_id, multibuffer_item_id,
14916                "Should navigate away from the multibuffer"
14917            );
14918            assert_ne!(
14919                second_item_id, first_item_id,
14920                "Should navigate into the 2nd buffer and activate it"
14921            );
14922            assert!(
14923                active_item.is_singleton(cx),
14924                "New active item should be a singleton buffer"
14925            );
14926            assert_eq!(
14927                active_item
14928                    .act_as::<Editor>(cx)
14929                    .expect("should have navigated into an editor")
14930                    .read(cx)
14931                    .text(cx),
14932                sample_text_2
14933            );
14934
14935            workspace
14936                .go_back(workspace.active_pane().downgrade(), window, cx)
14937                .detach_and_log_err(cx);
14938
14939            second_item_id
14940        })
14941        .unwrap();
14942    cx.executor().run_until_parked();
14943    workspace
14944        .update(cx, |workspace, _, cx| {
14945            let active_item = workspace
14946                .active_item(cx)
14947                .expect("should have an active item after navigating back from the 2nd buffer");
14948            assert_eq!(
14949                active_item.item_id(),
14950                multibuffer_item_id,
14951                "Should navigate back from the 2nd buffer to the multi buffer"
14952            );
14953            assert!(!active_item.is_singleton(cx));
14954        })
14955        .unwrap();
14956
14957    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14958        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14959            s.select_ranges(Some(70..70))
14960        });
14961        editor.open_excerpts(&OpenExcerpts, window, cx);
14962    });
14963    cx.executor().run_until_parked();
14964    workspace
14965        .update(cx, |workspace, window, cx| {
14966            let active_item = workspace
14967                .active_item(cx)
14968                .expect("should have an active item after navigating into the 3rd buffer");
14969            let third_item_id = active_item.item_id();
14970            assert_ne!(
14971                third_item_id, multibuffer_item_id,
14972                "Should navigate into the 3rd buffer and activate it"
14973            );
14974            assert_ne!(third_item_id, first_item_id);
14975            assert_ne!(third_item_id, second_item_id);
14976            assert!(
14977                active_item.is_singleton(cx),
14978                "New active item should be a singleton buffer"
14979            );
14980            assert_eq!(
14981                active_item
14982                    .act_as::<Editor>(cx)
14983                    .expect("should have navigated into an editor")
14984                    .read(cx)
14985                    .text(cx),
14986                sample_text_3
14987            );
14988
14989            workspace
14990                .go_back(workspace.active_pane().downgrade(), window, cx)
14991                .detach_and_log_err(cx);
14992        })
14993        .unwrap();
14994    cx.executor().run_until_parked();
14995    workspace
14996        .update(cx, |workspace, _, cx| {
14997            let active_item = workspace
14998                .active_item(cx)
14999                .expect("should have an active item after navigating back from the 3rd buffer");
15000            assert_eq!(
15001                active_item.item_id(),
15002                multibuffer_item_id,
15003                "Should navigate back from the 3rd buffer to the multi buffer"
15004            );
15005            assert!(!active_item.is_singleton(cx));
15006        })
15007        .unwrap();
15008}
15009
15010#[gpui::test]
15011async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15012    init_test(cx, |_| {});
15013
15014    let mut cx = EditorTestContext::new(cx).await;
15015
15016    let diff_base = r#"
15017        use some::mod;
15018
15019        const A: u32 = 42;
15020
15021        fn main() {
15022            println!("hello");
15023
15024            println!("world");
15025        }
15026        "#
15027    .unindent();
15028
15029    cx.set_state(
15030        &r#"
15031        use some::modified;
15032
15033        ˇ
15034        fn main() {
15035            println!("hello there");
15036
15037            println!("around the");
15038            println!("world");
15039        }
15040        "#
15041        .unindent(),
15042    );
15043
15044    cx.set_head_text(&diff_base);
15045    executor.run_until_parked();
15046
15047    cx.update_editor(|editor, window, cx| {
15048        editor.go_to_next_hunk(&GoToHunk, window, cx);
15049        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15050    });
15051    executor.run_until_parked();
15052    cx.assert_state_with_diff(
15053        r#"
15054          use some::modified;
15055
15056
15057          fn main() {
15058        -     println!("hello");
15059        + ˇ    println!("hello there");
15060
15061              println!("around the");
15062              println!("world");
15063          }
15064        "#
15065        .unindent(),
15066    );
15067
15068    cx.update_editor(|editor, window, cx| {
15069        for _ in 0..2 {
15070            editor.go_to_next_hunk(&GoToHunk, window, cx);
15071            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15072        }
15073    });
15074    executor.run_until_parked();
15075    cx.assert_state_with_diff(
15076        r#"
15077        - use some::mod;
15078        + ˇuse some::modified;
15079
15080
15081          fn main() {
15082        -     println!("hello");
15083        +     println!("hello there");
15084
15085        +     println!("around the");
15086              println!("world");
15087          }
15088        "#
15089        .unindent(),
15090    );
15091
15092    cx.update_editor(|editor, window, cx| {
15093        editor.go_to_next_hunk(&GoToHunk, window, cx);
15094        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15095    });
15096    executor.run_until_parked();
15097    cx.assert_state_with_diff(
15098        r#"
15099        - use some::mod;
15100        + use some::modified;
15101
15102        - const A: u32 = 42;
15103          ˇ
15104          fn main() {
15105        -     println!("hello");
15106        +     println!("hello there");
15107
15108        +     println!("around the");
15109              println!("world");
15110          }
15111        "#
15112        .unindent(),
15113    );
15114
15115    cx.update_editor(|editor, window, cx| {
15116        editor.cancel(&Cancel, window, cx);
15117    });
15118
15119    cx.assert_state_with_diff(
15120        r#"
15121          use some::modified;
15122
15123          ˇ
15124          fn main() {
15125              println!("hello there");
15126
15127              println!("around the");
15128              println!("world");
15129          }
15130        "#
15131        .unindent(),
15132    );
15133}
15134
15135#[gpui::test]
15136async fn test_diff_base_change_with_expanded_diff_hunks(
15137    executor: BackgroundExecutor,
15138    cx: &mut TestAppContext,
15139) {
15140    init_test(cx, |_| {});
15141
15142    let mut cx = EditorTestContext::new(cx).await;
15143
15144    let diff_base = r#"
15145        use some::mod1;
15146        use some::mod2;
15147
15148        const A: u32 = 42;
15149        const B: u32 = 42;
15150        const C: u32 = 42;
15151
15152        fn main() {
15153            println!("hello");
15154
15155            println!("world");
15156        }
15157        "#
15158    .unindent();
15159
15160    cx.set_state(
15161        &r#"
15162        use some::mod2;
15163
15164        const A: u32 = 42;
15165        const C: u32 = 42;
15166
15167        fn main(ˇ) {
15168            //println!("hello");
15169
15170            println!("world");
15171            //
15172            //
15173        }
15174        "#
15175        .unindent(),
15176    );
15177
15178    cx.set_head_text(&diff_base);
15179    executor.run_until_parked();
15180
15181    cx.update_editor(|editor, window, cx| {
15182        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15183    });
15184    executor.run_until_parked();
15185    cx.assert_state_with_diff(
15186        r#"
15187        - use some::mod1;
15188          use some::mod2;
15189
15190          const A: u32 = 42;
15191        - const B: u32 = 42;
15192          const C: u32 = 42;
15193
15194          fn main(ˇ) {
15195        -     println!("hello");
15196        +     //println!("hello");
15197
15198              println!("world");
15199        +     //
15200        +     //
15201          }
15202        "#
15203        .unindent(),
15204    );
15205
15206    cx.set_head_text("new diff base!");
15207    executor.run_until_parked();
15208    cx.assert_state_with_diff(
15209        r#"
15210        - new diff base!
15211        + use some::mod2;
15212        +
15213        + const A: u32 = 42;
15214        + const C: u32 = 42;
15215        +
15216        + fn main(ˇ) {
15217        +     //println!("hello");
15218        +
15219        +     println!("world");
15220        +     //
15221        +     //
15222        + }
15223        "#
15224        .unindent(),
15225    );
15226}
15227
15228#[gpui::test]
15229async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15230    init_test(cx, |_| {});
15231
15232    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15233    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15234    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15235    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15236    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15237    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15238
15239    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15240    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15241    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15242
15243    let multi_buffer = cx.new(|cx| {
15244        let mut multibuffer = MultiBuffer::new(ReadWrite);
15245        multibuffer.push_excerpts(
15246            buffer_1.clone(),
15247            [
15248                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15249                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15250                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15251            ],
15252            cx,
15253        );
15254        multibuffer.push_excerpts(
15255            buffer_2.clone(),
15256            [
15257                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15258                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15259                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15260            ],
15261            cx,
15262        );
15263        multibuffer.push_excerpts(
15264            buffer_3.clone(),
15265            [
15266                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15267                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15268                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15269            ],
15270            cx,
15271        );
15272        multibuffer
15273    });
15274
15275    let editor =
15276        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15277    editor
15278        .update(cx, |editor, _window, cx| {
15279            for (buffer, diff_base) in [
15280                (buffer_1.clone(), file_1_old),
15281                (buffer_2.clone(), file_2_old),
15282                (buffer_3.clone(), file_3_old),
15283            ] {
15284                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15285                editor
15286                    .buffer
15287                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15288            }
15289        })
15290        .unwrap();
15291
15292    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15293    cx.run_until_parked();
15294
15295    cx.assert_editor_state(
15296        &"
15297            ˇaaa
15298            ccc
15299            ddd
15300
15301            ggg
15302            hhh
15303
15304
15305            lll
15306            mmm
15307            NNN
15308
15309            qqq
15310            rrr
15311
15312            uuu
15313            111
15314            222
15315            333
15316
15317            666
15318            777
15319
15320            000
15321            !!!"
15322        .unindent(),
15323    );
15324
15325    cx.update_editor(|editor, window, cx| {
15326        editor.select_all(&SelectAll, window, cx);
15327        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15328    });
15329    cx.executor().run_until_parked();
15330
15331    cx.assert_state_with_diff(
15332        "
15333            «aaa
15334          - bbb
15335            ccc
15336            ddd
15337
15338            ggg
15339            hhh
15340
15341
15342            lll
15343            mmm
15344          - nnn
15345          + NNN
15346
15347            qqq
15348            rrr
15349
15350            uuu
15351            111
15352            222
15353            333
15354
15355          + 666
15356            777
15357
15358            000
15359            !!!ˇ»"
15360            .unindent(),
15361    );
15362}
15363
15364#[gpui::test]
15365async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15366    init_test(cx, |_| {});
15367
15368    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15369    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15370
15371    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15372    let multi_buffer = cx.new(|cx| {
15373        let mut multibuffer = MultiBuffer::new(ReadWrite);
15374        multibuffer.push_excerpts(
15375            buffer.clone(),
15376            [
15377                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15378                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15379                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15380            ],
15381            cx,
15382        );
15383        multibuffer
15384    });
15385
15386    let editor =
15387        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15388    editor
15389        .update(cx, |editor, _window, cx| {
15390            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15391            editor
15392                .buffer
15393                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15394        })
15395        .unwrap();
15396
15397    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15398    cx.run_until_parked();
15399
15400    cx.update_editor(|editor, window, cx| {
15401        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15402    });
15403    cx.executor().run_until_parked();
15404
15405    // When the start of a hunk coincides with the start of its excerpt,
15406    // the hunk is expanded. When the start of a a hunk is earlier than
15407    // the start of its excerpt, the hunk is not expanded.
15408    cx.assert_state_with_diff(
15409        "
15410            ˇaaa
15411          - bbb
15412          + BBB
15413
15414          - ddd
15415          - eee
15416          + DDD
15417          + EEE
15418            fff
15419
15420            iii
15421        "
15422        .unindent(),
15423    );
15424}
15425
15426#[gpui::test]
15427async fn test_edits_around_expanded_insertion_hunks(
15428    executor: BackgroundExecutor,
15429    cx: &mut TestAppContext,
15430) {
15431    init_test(cx, |_| {});
15432
15433    let mut cx = EditorTestContext::new(cx).await;
15434
15435    let diff_base = r#"
15436        use some::mod1;
15437        use some::mod2;
15438
15439        const A: u32 = 42;
15440
15441        fn main() {
15442            println!("hello");
15443
15444            println!("world");
15445        }
15446        "#
15447    .unindent();
15448    executor.run_until_parked();
15449    cx.set_state(
15450        &r#"
15451        use some::mod1;
15452        use some::mod2;
15453
15454        const A: u32 = 42;
15455        const B: u32 = 42;
15456        const C: u32 = 42;
15457        ˇ
15458
15459        fn main() {
15460            println!("hello");
15461
15462            println!("world");
15463        }
15464        "#
15465        .unindent(),
15466    );
15467
15468    cx.set_head_text(&diff_base);
15469    executor.run_until_parked();
15470
15471    cx.update_editor(|editor, window, cx| {
15472        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15473    });
15474    executor.run_until_parked();
15475
15476    cx.assert_state_with_diff(
15477        r#"
15478        use some::mod1;
15479        use some::mod2;
15480
15481        const A: u32 = 42;
15482      + const B: u32 = 42;
15483      + const C: u32 = 42;
15484      + ˇ
15485
15486        fn main() {
15487            println!("hello");
15488
15489            println!("world");
15490        }
15491      "#
15492        .unindent(),
15493    );
15494
15495    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15496    executor.run_until_parked();
15497
15498    cx.assert_state_with_diff(
15499        r#"
15500        use some::mod1;
15501        use some::mod2;
15502
15503        const A: u32 = 42;
15504      + const B: u32 = 42;
15505      + const C: u32 = 42;
15506      + const D: u32 = 42;
15507      + ˇ
15508
15509        fn main() {
15510            println!("hello");
15511
15512            println!("world");
15513        }
15514      "#
15515        .unindent(),
15516    );
15517
15518    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15519    executor.run_until_parked();
15520
15521    cx.assert_state_with_diff(
15522        r#"
15523        use some::mod1;
15524        use some::mod2;
15525
15526        const A: u32 = 42;
15527      + const B: u32 = 42;
15528      + const C: u32 = 42;
15529      + const D: u32 = 42;
15530      + const E: u32 = 42;
15531      + ˇ
15532
15533        fn main() {
15534            println!("hello");
15535
15536            println!("world");
15537        }
15538      "#
15539        .unindent(),
15540    );
15541
15542    cx.update_editor(|editor, window, cx| {
15543        editor.delete_line(&DeleteLine, window, cx);
15544    });
15545    executor.run_until_parked();
15546
15547    cx.assert_state_with_diff(
15548        r#"
15549        use some::mod1;
15550        use some::mod2;
15551
15552        const A: u32 = 42;
15553      + const B: u32 = 42;
15554      + const C: u32 = 42;
15555      + const D: u32 = 42;
15556      + const E: u32 = 42;
15557        ˇ
15558        fn main() {
15559            println!("hello");
15560
15561            println!("world");
15562        }
15563      "#
15564        .unindent(),
15565    );
15566
15567    cx.update_editor(|editor, window, cx| {
15568        editor.move_up(&MoveUp, window, cx);
15569        editor.delete_line(&DeleteLine, window, cx);
15570        editor.move_up(&MoveUp, window, cx);
15571        editor.delete_line(&DeleteLine, window, cx);
15572        editor.move_up(&MoveUp, window, cx);
15573        editor.delete_line(&DeleteLine, window, cx);
15574    });
15575    executor.run_until_parked();
15576    cx.assert_state_with_diff(
15577        r#"
15578        use some::mod1;
15579        use some::mod2;
15580
15581        const A: u32 = 42;
15582      + const B: u32 = 42;
15583        ˇ
15584        fn main() {
15585            println!("hello");
15586
15587            println!("world");
15588        }
15589      "#
15590        .unindent(),
15591    );
15592
15593    cx.update_editor(|editor, window, cx| {
15594        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15595        editor.delete_line(&DeleteLine, window, cx);
15596    });
15597    executor.run_until_parked();
15598    cx.assert_state_with_diff(
15599        r#"
15600        ˇ
15601        fn main() {
15602            println!("hello");
15603
15604            println!("world");
15605        }
15606      "#
15607        .unindent(),
15608    );
15609}
15610
15611#[gpui::test]
15612async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15613    init_test(cx, |_| {});
15614
15615    let mut cx = EditorTestContext::new(cx).await;
15616    cx.set_head_text(indoc! { "
15617        one
15618        two
15619        three
15620        four
15621        five
15622        "
15623    });
15624    cx.set_state(indoc! { "
15625        one
15626        ˇthree
15627        five
15628    "});
15629    cx.run_until_parked();
15630    cx.update_editor(|editor, window, cx| {
15631        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15632    });
15633    cx.assert_state_with_diff(
15634        indoc! { "
15635        one
15636      - two
15637        ˇthree
15638      - four
15639        five
15640    "}
15641        .to_string(),
15642    );
15643    cx.update_editor(|editor, window, cx| {
15644        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15645    });
15646
15647    cx.assert_state_with_diff(
15648        indoc! { "
15649        one
15650        ˇthree
15651        five
15652    "}
15653        .to_string(),
15654    );
15655
15656    cx.set_state(indoc! { "
15657        one
15658        ˇTWO
15659        three
15660        four
15661        five
15662    "});
15663    cx.run_until_parked();
15664    cx.update_editor(|editor, window, cx| {
15665        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15666    });
15667
15668    cx.assert_state_with_diff(
15669        indoc! { "
15670            one
15671          - two
15672          + ˇTWO
15673            three
15674            four
15675            five
15676        "}
15677        .to_string(),
15678    );
15679    cx.update_editor(|editor, window, cx| {
15680        editor.move_up(&Default::default(), window, cx);
15681        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15682    });
15683    cx.assert_state_with_diff(
15684        indoc! { "
15685            one
15686            ˇTWO
15687            three
15688            four
15689            five
15690        "}
15691        .to_string(),
15692    );
15693}
15694
15695#[gpui::test]
15696async fn test_edits_around_expanded_deletion_hunks(
15697    executor: BackgroundExecutor,
15698    cx: &mut TestAppContext,
15699) {
15700    init_test(cx, |_| {});
15701
15702    let mut cx = EditorTestContext::new(cx).await;
15703
15704    let diff_base = r#"
15705        use some::mod1;
15706        use some::mod2;
15707
15708        const A: u32 = 42;
15709        const B: u32 = 42;
15710        const C: u32 = 42;
15711
15712
15713        fn main() {
15714            println!("hello");
15715
15716            println!("world");
15717        }
15718    "#
15719    .unindent();
15720    executor.run_until_parked();
15721    cx.set_state(
15722        &r#"
15723        use some::mod1;
15724        use some::mod2;
15725
15726        ˇconst B: u32 = 42;
15727        const C: u32 = 42;
15728
15729
15730        fn main() {
15731            println!("hello");
15732
15733            println!("world");
15734        }
15735        "#
15736        .unindent(),
15737    );
15738
15739    cx.set_head_text(&diff_base);
15740    executor.run_until_parked();
15741
15742    cx.update_editor(|editor, window, cx| {
15743        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15744    });
15745    executor.run_until_parked();
15746
15747    cx.assert_state_with_diff(
15748        r#"
15749        use some::mod1;
15750        use some::mod2;
15751
15752      - const A: u32 = 42;
15753        ˇconst B: u32 = 42;
15754        const C: u32 = 42;
15755
15756
15757        fn main() {
15758            println!("hello");
15759
15760            println!("world");
15761        }
15762      "#
15763        .unindent(),
15764    );
15765
15766    cx.update_editor(|editor, window, cx| {
15767        editor.delete_line(&DeleteLine, window, cx);
15768    });
15769    executor.run_until_parked();
15770    cx.assert_state_with_diff(
15771        r#"
15772        use some::mod1;
15773        use some::mod2;
15774
15775      - const A: u32 = 42;
15776      - const B: u32 = 42;
15777        ˇconst C: u32 = 42;
15778
15779
15780        fn main() {
15781            println!("hello");
15782
15783            println!("world");
15784        }
15785      "#
15786        .unindent(),
15787    );
15788
15789    cx.update_editor(|editor, window, cx| {
15790        editor.delete_line(&DeleteLine, window, cx);
15791    });
15792    executor.run_until_parked();
15793    cx.assert_state_with_diff(
15794        r#"
15795        use some::mod1;
15796        use some::mod2;
15797
15798      - const A: u32 = 42;
15799      - const B: u32 = 42;
15800      - const C: u32 = 42;
15801        ˇ
15802
15803        fn main() {
15804            println!("hello");
15805
15806            println!("world");
15807        }
15808      "#
15809        .unindent(),
15810    );
15811
15812    cx.update_editor(|editor, window, cx| {
15813        editor.handle_input("replacement", window, cx);
15814    });
15815    executor.run_until_parked();
15816    cx.assert_state_with_diff(
15817        r#"
15818        use some::mod1;
15819        use some::mod2;
15820
15821      - const A: u32 = 42;
15822      - const B: u32 = 42;
15823      - const C: u32 = 42;
15824      -
15825      + replacementˇ
15826
15827        fn main() {
15828            println!("hello");
15829
15830            println!("world");
15831        }
15832      "#
15833        .unindent(),
15834    );
15835}
15836
15837#[gpui::test]
15838async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15839    init_test(cx, |_| {});
15840
15841    let mut cx = EditorTestContext::new(cx).await;
15842
15843    let base_text = r#"
15844        one
15845        two
15846        three
15847        four
15848        five
15849    "#
15850    .unindent();
15851    executor.run_until_parked();
15852    cx.set_state(
15853        &r#"
15854        one
15855        two
15856        fˇour
15857        five
15858        "#
15859        .unindent(),
15860    );
15861
15862    cx.set_head_text(&base_text);
15863    executor.run_until_parked();
15864
15865    cx.update_editor(|editor, window, cx| {
15866        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15867    });
15868    executor.run_until_parked();
15869
15870    cx.assert_state_with_diff(
15871        r#"
15872          one
15873          two
15874        - three
15875          fˇour
15876          five
15877        "#
15878        .unindent(),
15879    );
15880
15881    cx.update_editor(|editor, window, cx| {
15882        editor.backspace(&Backspace, window, cx);
15883        editor.backspace(&Backspace, window, cx);
15884    });
15885    executor.run_until_parked();
15886    cx.assert_state_with_diff(
15887        r#"
15888          one
15889          two
15890        - threeˇ
15891        - four
15892        + our
15893          five
15894        "#
15895        .unindent(),
15896    );
15897}
15898
15899#[gpui::test]
15900async fn test_edit_after_expanded_modification_hunk(
15901    executor: BackgroundExecutor,
15902    cx: &mut TestAppContext,
15903) {
15904    init_test(cx, |_| {});
15905
15906    let mut cx = EditorTestContext::new(cx).await;
15907
15908    let diff_base = r#"
15909        use some::mod1;
15910        use some::mod2;
15911
15912        const A: u32 = 42;
15913        const B: u32 = 42;
15914        const C: u32 = 42;
15915        const D: u32 = 42;
15916
15917
15918        fn main() {
15919            println!("hello");
15920
15921            println!("world");
15922        }"#
15923    .unindent();
15924
15925    cx.set_state(
15926        &r#"
15927        use some::mod1;
15928        use some::mod2;
15929
15930        const A: u32 = 42;
15931        const B: u32 = 42;
15932        const C: u32 = 43ˇ
15933        const D: u32 = 42;
15934
15935
15936        fn main() {
15937            println!("hello");
15938
15939            println!("world");
15940        }"#
15941        .unindent(),
15942    );
15943
15944    cx.set_head_text(&diff_base);
15945    executor.run_until_parked();
15946    cx.update_editor(|editor, window, cx| {
15947        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15948    });
15949    executor.run_until_parked();
15950
15951    cx.assert_state_with_diff(
15952        r#"
15953        use some::mod1;
15954        use some::mod2;
15955
15956        const A: u32 = 42;
15957        const B: u32 = 42;
15958      - const C: u32 = 42;
15959      + const C: u32 = 43ˇ
15960        const D: u32 = 42;
15961
15962
15963        fn main() {
15964            println!("hello");
15965
15966            println!("world");
15967        }"#
15968        .unindent(),
15969    );
15970
15971    cx.update_editor(|editor, window, cx| {
15972        editor.handle_input("\nnew_line\n", window, cx);
15973    });
15974    executor.run_until_parked();
15975
15976    cx.assert_state_with_diff(
15977        r#"
15978        use some::mod1;
15979        use some::mod2;
15980
15981        const A: u32 = 42;
15982        const B: u32 = 42;
15983      - const C: u32 = 42;
15984      + const C: u32 = 43
15985      + new_line
15986      + ˇ
15987        const D: u32 = 42;
15988
15989
15990        fn main() {
15991            println!("hello");
15992
15993            println!("world");
15994        }"#
15995        .unindent(),
15996    );
15997}
15998
15999#[gpui::test]
16000async fn test_stage_and_unstage_added_file_hunk(
16001    executor: BackgroundExecutor,
16002    cx: &mut TestAppContext,
16003) {
16004    init_test(cx, |_| {});
16005
16006    let mut cx = EditorTestContext::new(cx).await;
16007    cx.update_editor(|editor, _, cx| {
16008        editor.set_expand_all_diff_hunks(cx);
16009    });
16010
16011    let working_copy = r#"
16012            ˇfn main() {
16013                println!("hello, world!");
16014            }
16015        "#
16016    .unindent();
16017
16018    cx.set_state(&working_copy);
16019    executor.run_until_parked();
16020
16021    cx.assert_state_with_diff(
16022        r#"
16023            + ˇfn main() {
16024            +     println!("hello, world!");
16025            + }
16026        "#
16027        .unindent(),
16028    );
16029    cx.assert_index_text(None);
16030
16031    cx.update_editor(|editor, window, cx| {
16032        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16033    });
16034    executor.run_until_parked();
16035    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16036    cx.assert_state_with_diff(
16037        r#"
16038            + ˇfn main() {
16039            +     println!("hello, world!");
16040            + }
16041        "#
16042        .unindent(),
16043    );
16044
16045    cx.update_editor(|editor, window, cx| {
16046        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16047    });
16048    executor.run_until_parked();
16049    cx.assert_index_text(None);
16050}
16051
16052async fn setup_indent_guides_editor(
16053    text: &str,
16054    cx: &mut TestAppContext,
16055) -> (BufferId, EditorTestContext) {
16056    init_test(cx, |_| {});
16057
16058    let mut cx = EditorTestContext::new(cx).await;
16059
16060    let buffer_id = cx.update_editor(|editor, window, cx| {
16061        editor.set_text(text, window, cx);
16062        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16063
16064        buffer_ids[0]
16065    });
16066
16067    (buffer_id, cx)
16068}
16069
16070fn assert_indent_guides(
16071    range: Range<u32>,
16072    expected: Vec<IndentGuide>,
16073    active_indices: Option<Vec<usize>>,
16074    cx: &mut EditorTestContext,
16075) {
16076    let indent_guides = cx.update_editor(|editor, window, cx| {
16077        let snapshot = editor.snapshot(window, cx).display_snapshot;
16078        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16079            editor,
16080            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16081            true,
16082            &snapshot,
16083            cx,
16084        );
16085
16086        indent_guides.sort_by(|a, b| {
16087            a.depth.cmp(&b.depth).then(
16088                a.start_row
16089                    .cmp(&b.start_row)
16090                    .then(a.end_row.cmp(&b.end_row)),
16091            )
16092        });
16093        indent_guides
16094    });
16095
16096    if let Some(expected) = active_indices {
16097        let active_indices = cx.update_editor(|editor, window, cx| {
16098            let snapshot = editor.snapshot(window, cx).display_snapshot;
16099            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16100        });
16101
16102        assert_eq!(
16103            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16104            expected,
16105            "Active indent guide indices do not match"
16106        );
16107    }
16108
16109    assert_eq!(indent_guides, expected, "Indent guides do not match");
16110}
16111
16112fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16113    IndentGuide {
16114        buffer_id,
16115        start_row: MultiBufferRow(start_row),
16116        end_row: MultiBufferRow(end_row),
16117        depth,
16118        tab_size: 4,
16119        settings: IndentGuideSettings {
16120            enabled: true,
16121            line_width: 1,
16122            active_line_width: 1,
16123            ..Default::default()
16124        },
16125    }
16126}
16127
16128#[gpui::test]
16129async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16130    let (buffer_id, mut cx) = setup_indent_guides_editor(
16131        &"
16132    fn main() {
16133        let a = 1;
16134    }"
16135        .unindent(),
16136        cx,
16137    )
16138    .await;
16139
16140    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16141}
16142
16143#[gpui::test]
16144async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16145    let (buffer_id, mut cx) = setup_indent_guides_editor(
16146        &"
16147    fn main() {
16148        let a = 1;
16149        let b = 2;
16150    }"
16151        .unindent(),
16152        cx,
16153    )
16154    .await;
16155
16156    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16157}
16158
16159#[gpui::test]
16160async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16161    let (buffer_id, mut cx) = setup_indent_guides_editor(
16162        &"
16163    fn main() {
16164        let a = 1;
16165        if a == 3 {
16166            let b = 2;
16167        } else {
16168            let c = 3;
16169        }
16170    }"
16171        .unindent(),
16172        cx,
16173    )
16174    .await;
16175
16176    assert_indent_guides(
16177        0..8,
16178        vec![
16179            indent_guide(buffer_id, 1, 6, 0),
16180            indent_guide(buffer_id, 3, 3, 1),
16181            indent_guide(buffer_id, 5, 5, 1),
16182        ],
16183        None,
16184        &mut cx,
16185    );
16186}
16187
16188#[gpui::test]
16189async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16190    let (buffer_id, mut cx) = setup_indent_guides_editor(
16191        &"
16192    fn main() {
16193        let a = 1;
16194            let b = 2;
16195        let c = 3;
16196    }"
16197        .unindent(),
16198        cx,
16199    )
16200    .await;
16201
16202    assert_indent_guides(
16203        0..5,
16204        vec![
16205            indent_guide(buffer_id, 1, 3, 0),
16206            indent_guide(buffer_id, 2, 2, 1),
16207        ],
16208        None,
16209        &mut cx,
16210    );
16211}
16212
16213#[gpui::test]
16214async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16215    let (buffer_id, mut cx) = setup_indent_guides_editor(
16216        &"
16217        fn main() {
16218            let a = 1;
16219
16220            let c = 3;
16221        }"
16222        .unindent(),
16223        cx,
16224    )
16225    .await;
16226
16227    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16228}
16229
16230#[gpui::test]
16231async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16232    let (buffer_id, mut cx) = setup_indent_guides_editor(
16233        &"
16234        fn main() {
16235            let a = 1;
16236
16237            let c = 3;
16238
16239            if a == 3 {
16240                let b = 2;
16241            } else {
16242                let c = 3;
16243            }
16244        }"
16245        .unindent(),
16246        cx,
16247    )
16248    .await;
16249
16250    assert_indent_guides(
16251        0..11,
16252        vec![
16253            indent_guide(buffer_id, 1, 9, 0),
16254            indent_guide(buffer_id, 6, 6, 1),
16255            indent_guide(buffer_id, 8, 8, 1),
16256        ],
16257        None,
16258        &mut cx,
16259    );
16260}
16261
16262#[gpui::test]
16263async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16264    let (buffer_id, mut cx) = setup_indent_guides_editor(
16265        &"
16266        fn main() {
16267            let a = 1;
16268
16269            let c = 3;
16270
16271            if a == 3 {
16272                let b = 2;
16273            } else {
16274                let c = 3;
16275            }
16276        }"
16277        .unindent(),
16278        cx,
16279    )
16280    .await;
16281
16282    assert_indent_guides(
16283        1..11,
16284        vec![
16285            indent_guide(buffer_id, 1, 9, 0),
16286            indent_guide(buffer_id, 6, 6, 1),
16287            indent_guide(buffer_id, 8, 8, 1),
16288        ],
16289        None,
16290        &mut cx,
16291    );
16292}
16293
16294#[gpui::test]
16295async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16296    let (buffer_id, mut cx) = setup_indent_guides_editor(
16297        &"
16298        fn main() {
16299            let a = 1;
16300
16301            let c = 3;
16302
16303            if a == 3 {
16304                let b = 2;
16305            } else {
16306                let c = 3;
16307            }
16308        }"
16309        .unindent(),
16310        cx,
16311    )
16312    .await;
16313
16314    assert_indent_guides(
16315        1..10,
16316        vec![
16317            indent_guide(buffer_id, 1, 9, 0),
16318            indent_guide(buffer_id, 6, 6, 1),
16319            indent_guide(buffer_id, 8, 8, 1),
16320        ],
16321        None,
16322        &mut cx,
16323    );
16324}
16325
16326#[gpui::test]
16327async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16328    let (buffer_id, mut cx) = setup_indent_guides_editor(
16329        &"
16330        block1
16331            block2
16332                block3
16333                    block4
16334            block2
16335        block1
16336        block1"
16337            .unindent(),
16338        cx,
16339    )
16340    .await;
16341
16342    assert_indent_guides(
16343        1..10,
16344        vec![
16345            indent_guide(buffer_id, 1, 4, 0),
16346            indent_guide(buffer_id, 2, 3, 1),
16347            indent_guide(buffer_id, 3, 3, 2),
16348        ],
16349        None,
16350        &mut cx,
16351    );
16352}
16353
16354#[gpui::test]
16355async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16356    let (buffer_id, mut cx) = setup_indent_guides_editor(
16357        &"
16358        block1
16359            block2
16360                block3
16361
16362        block1
16363        block1"
16364            .unindent(),
16365        cx,
16366    )
16367    .await;
16368
16369    assert_indent_guides(
16370        0..6,
16371        vec![
16372            indent_guide(buffer_id, 1, 2, 0),
16373            indent_guide(buffer_id, 2, 2, 1),
16374        ],
16375        None,
16376        &mut cx,
16377    );
16378}
16379
16380#[gpui::test]
16381async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16382    let (buffer_id, mut cx) = setup_indent_guides_editor(
16383        &"
16384        block1
16385
16386
16387
16388            block2
16389        "
16390        .unindent(),
16391        cx,
16392    )
16393    .await;
16394
16395    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16396}
16397
16398#[gpui::test]
16399async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16400    let (buffer_id, mut cx) = setup_indent_guides_editor(
16401        &"
16402        def a:
16403        \tb = 3
16404        \tif True:
16405        \t\tc = 4
16406        \t\td = 5
16407        \tprint(b)
16408        "
16409        .unindent(),
16410        cx,
16411    )
16412    .await;
16413
16414    assert_indent_guides(
16415        0..6,
16416        vec![
16417            indent_guide(buffer_id, 1, 6, 0),
16418            indent_guide(buffer_id, 3, 4, 1),
16419        ],
16420        None,
16421        &mut cx,
16422    );
16423}
16424
16425#[gpui::test]
16426async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16427    let (buffer_id, mut cx) = setup_indent_guides_editor(
16428        &"
16429    fn main() {
16430        let a = 1;
16431    }"
16432        .unindent(),
16433        cx,
16434    )
16435    .await;
16436
16437    cx.update_editor(|editor, window, cx| {
16438        editor.change_selections(None, window, cx, |s| {
16439            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16440        });
16441    });
16442
16443    assert_indent_guides(
16444        0..3,
16445        vec![indent_guide(buffer_id, 1, 1, 0)],
16446        Some(vec![0]),
16447        &mut cx,
16448    );
16449}
16450
16451#[gpui::test]
16452async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16453    let (buffer_id, mut cx) = setup_indent_guides_editor(
16454        &"
16455    fn main() {
16456        if 1 == 2 {
16457            let a = 1;
16458        }
16459    }"
16460        .unindent(),
16461        cx,
16462    )
16463    .await;
16464
16465    cx.update_editor(|editor, window, cx| {
16466        editor.change_selections(None, window, cx, |s| {
16467            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16468        });
16469    });
16470
16471    assert_indent_guides(
16472        0..4,
16473        vec![
16474            indent_guide(buffer_id, 1, 3, 0),
16475            indent_guide(buffer_id, 2, 2, 1),
16476        ],
16477        Some(vec![1]),
16478        &mut cx,
16479    );
16480
16481    cx.update_editor(|editor, window, cx| {
16482        editor.change_selections(None, window, cx, |s| {
16483            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16484        });
16485    });
16486
16487    assert_indent_guides(
16488        0..4,
16489        vec![
16490            indent_guide(buffer_id, 1, 3, 0),
16491            indent_guide(buffer_id, 2, 2, 1),
16492        ],
16493        Some(vec![1]),
16494        &mut cx,
16495    );
16496
16497    cx.update_editor(|editor, window, cx| {
16498        editor.change_selections(None, window, cx, |s| {
16499            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16500        });
16501    });
16502
16503    assert_indent_guides(
16504        0..4,
16505        vec![
16506            indent_guide(buffer_id, 1, 3, 0),
16507            indent_guide(buffer_id, 2, 2, 1),
16508        ],
16509        Some(vec![0]),
16510        &mut cx,
16511    );
16512}
16513
16514#[gpui::test]
16515async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16516    let (buffer_id, mut cx) = setup_indent_guides_editor(
16517        &"
16518    fn main() {
16519        let a = 1;
16520
16521        let b = 2;
16522    }"
16523        .unindent(),
16524        cx,
16525    )
16526    .await;
16527
16528    cx.update_editor(|editor, window, cx| {
16529        editor.change_selections(None, window, cx, |s| {
16530            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16531        });
16532    });
16533
16534    assert_indent_guides(
16535        0..5,
16536        vec![indent_guide(buffer_id, 1, 3, 0)],
16537        Some(vec![0]),
16538        &mut cx,
16539    );
16540}
16541
16542#[gpui::test]
16543async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16544    let (buffer_id, mut cx) = setup_indent_guides_editor(
16545        &"
16546    def m:
16547        a = 1
16548        pass"
16549            .unindent(),
16550        cx,
16551    )
16552    .await;
16553
16554    cx.update_editor(|editor, window, cx| {
16555        editor.change_selections(None, window, cx, |s| {
16556            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16557        });
16558    });
16559
16560    assert_indent_guides(
16561        0..3,
16562        vec![indent_guide(buffer_id, 1, 2, 0)],
16563        Some(vec![0]),
16564        &mut cx,
16565    );
16566}
16567
16568#[gpui::test]
16569async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16570    init_test(cx, |_| {});
16571    let mut cx = EditorTestContext::new(cx).await;
16572    let text = indoc! {
16573        "
16574        impl A {
16575            fn b() {
16576                0;
16577                3;
16578                5;
16579                6;
16580                7;
16581            }
16582        }
16583        "
16584    };
16585    let base_text = indoc! {
16586        "
16587        impl A {
16588            fn b() {
16589                0;
16590                1;
16591                2;
16592                3;
16593                4;
16594            }
16595            fn c() {
16596                5;
16597                6;
16598                7;
16599            }
16600        }
16601        "
16602    };
16603
16604    cx.update_editor(|editor, window, cx| {
16605        editor.set_text(text, window, cx);
16606
16607        editor.buffer().update(cx, |multibuffer, cx| {
16608            let buffer = multibuffer.as_singleton().unwrap();
16609            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16610
16611            multibuffer.set_all_diff_hunks_expanded(cx);
16612            multibuffer.add_diff(diff, cx);
16613
16614            buffer.read(cx).remote_id()
16615        })
16616    });
16617    cx.run_until_parked();
16618
16619    cx.assert_state_with_diff(
16620        indoc! { "
16621          impl A {
16622              fn b() {
16623                  0;
16624        -         1;
16625        -         2;
16626                  3;
16627        -         4;
16628        -     }
16629        -     fn c() {
16630                  5;
16631                  6;
16632                  7;
16633              }
16634          }
16635          ˇ"
16636        }
16637        .to_string(),
16638    );
16639
16640    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16641        editor
16642            .snapshot(window, cx)
16643            .buffer_snapshot
16644            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16645            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16646            .collect::<Vec<_>>()
16647    });
16648    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16649    assert_eq!(
16650        actual_guides,
16651        vec![
16652            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16653            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16654            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16655        ]
16656    );
16657}
16658
16659#[gpui::test]
16660async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16661    init_test(cx, |_| {});
16662    let mut cx = EditorTestContext::new(cx).await;
16663
16664    let diff_base = r#"
16665        a
16666        b
16667        c
16668        "#
16669    .unindent();
16670
16671    cx.set_state(
16672        &r#"
16673        ˇA
16674        b
16675        C
16676        "#
16677        .unindent(),
16678    );
16679    cx.set_head_text(&diff_base);
16680    cx.update_editor(|editor, window, cx| {
16681        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16682    });
16683    executor.run_until_parked();
16684
16685    let both_hunks_expanded = r#"
16686        - a
16687        + ˇA
16688          b
16689        - c
16690        + C
16691        "#
16692    .unindent();
16693
16694    cx.assert_state_with_diff(both_hunks_expanded.clone());
16695
16696    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16697        let snapshot = editor.snapshot(window, cx);
16698        let hunks = editor
16699            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16700            .collect::<Vec<_>>();
16701        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16702        let buffer_id = hunks[0].buffer_id;
16703        hunks
16704            .into_iter()
16705            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16706            .collect::<Vec<_>>()
16707    });
16708    assert_eq!(hunk_ranges.len(), 2);
16709
16710    cx.update_editor(|editor, _, cx| {
16711        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16712    });
16713    executor.run_until_parked();
16714
16715    let second_hunk_expanded = r#"
16716          ˇA
16717          b
16718        - c
16719        + C
16720        "#
16721    .unindent();
16722
16723    cx.assert_state_with_diff(second_hunk_expanded);
16724
16725    cx.update_editor(|editor, _, cx| {
16726        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16727    });
16728    executor.run_until_parked();
16729
16730    cx.assert_state_with_diff(both_hunks_expanded.clone());
16731
16732    cx.update_editor(|editor, _, cx| {
16733        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16734    });
16735    executor.run_until_parked();
16736
16737    let first_hunk_expanded = r#"
16738        - a
16739        + ˇA
16740          b
16741          C
16742        "#
16743    .unindent();
16744
16745    cx.assert_state_with_diff(first_hunk_expanded);
16746
16747    cx.update_editor(|editor, _, cx| {
16748        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16749    });
16750    executor.run_until_parked();
16751
16752    cx.assert_state_with_diff(both_hunks_expanded);
16753
16754    cx.set_state(
16755        &r#"
16756        ˇA
16757        b
16758        "#
16759        .unindent(),
16760    );
16761    cx.run_until_parked();
16762
16763    // TODO this cursor position seems bad
16764    cx.assert_state_with_diff(
16765        r#"
16766        - ˇa
16767        + A
16768          b
16769        "#
16770        .unindent(),
16771    );
16772
16773    cx.update_editor(|editor, window, cx| {
16774        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16775    });
16776
16777    cx.assert_state_with_diff(
16778        r#"
16779            - ˇa
16780            + A
16781              b
16782            - c
16783            "#
16784        .unindent(),
16785    );
16786
16787    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16788        let snapshot = editor.snapshot(window, cx);
16789        let hunks = editor
16790            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16791            .collect::<Vec<_>>();
16792        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16793        let buffer_id = hunks[0].buffer_id;
16794        hunks
16795            .into_iter()
16796            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16797            .collect::<Vec<_>>()
16798    });
16799    assert_eq!(hunk_ranges.len(), 2);
16800
16801    cx.update_editor(|editor, _, cx| {
16802        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16803    });
16804    executor.run_until_parked();
16805
16806    cx.assert_state_with_diff(
16807        r#"
16808        - ˇa
16809        + A
16810          b
16811        "#
16812        .unindent(),
16813    );
16814}
16815
16816#[gpui::test]
16817async fn test_toggle_deletion_hunk_at_start_of_file(
16818    executor: BackgroundExecutor,
16819    cx: &mut TestAppContext,
16820) {
16821    init_test(cx, |_| {});
16822    let mut cx = EditorTestContext::new(cx).await;
16823
16824    let diff_base = r#"
16825        a
16826        b
16827        c
16828        "#
16829    .unindent();
16830
16831    cx.set_state(
16832        &r#"
16833        ˇb
16834        c
16835        "#
16836        .unindent(),
16837    );
16838    cx.set_head_text(&diff_base);
16839    cx.update_editor(|editor, window, cx| {
16840        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16841    });
16842    executor.run_until_parked();
16843
16844    let hunk_expanded = r#"
16845        - a
16846          ˇb
16847          c
16848        "#
16849    .unindent();
16850
16851    cx.assert_state_with_diff(hunk_expanded.clone());
16852
16853    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16854        let snapshot = editor.snapshot(window, cx);
16855        let hunks = editor
16856            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16857            .collect::<Vec<_>>();
16858        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16859        let buffer_id = hunks[0].buffer_id;
16860        hunks
16861            .into_iter()
16862            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16863            .collect::<Vec<_>>()
16864    });
16865    assert_eq!(hunk_ranges.len(), 1);
16866
16867    cx.update_editor(|editor, _, cx| {
16868        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16869    });
16870    executor.run_until_parked();
16871
16872    let hunk_collapsed = r#"
16873          ˇb
16874          c
16875        "#
16876    .unindent();
16877
16878    cx.assert_state_with_diff(hunk_collapsed);
16879
16880    cx.update_editor(|editor, _, cx| {
16881        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16882    });
16883    executor.run_until_parked();
16884
16885    cx.assert_state_with_diff(hunk_expanded.clone());
16886}
16887
16888#[gpui::test]
16889async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16890    init_test(cx, |_| {});
16891
16892    let fs = FakeFs::new(cx.executor());
16893    fs.insert_tree(
16894        path!("/test"),
16895        json!({
16896            ".git": {},
16897            "file-1": "ONE\n",
16898            "file-2": "TWO\n",
16899            "file-3": "THREE\n",
16900        }),
16901    )
16902    .await;
16903
16904    fs.set_head_for_repo(
16905        path!("/test/.git").as_ref(),
16906        &[
16907            ("file-1".into(), "one\n".into()),
16908            ("file-2".into(), "two\n".into()),
16909            ("file-3".into(), "three\n".into()),
16910        ],
16911    );
16912
16913    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16914    let mut buffers = vec![];
16915    for i in 1..=3 {
16916        let buffer = project
16917            .update(cx, |project, cx| {
16918                let path = format!(path!("/test/file-{}"), i);
16919                project.open_local_buffer(path, cx)
16920            })
16921            .await
16922            .unwrap();
16923        buffers.push(buffer);
16924    }
16925
16926    let multibuffer = cx.new(|cx| {
16927        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16928        multibuffer.set_all_diff_hunks_expanded(cx);
16929        for buffer in &buffers {
16930            let snapshot = buffer.read(cx).snapshot();
16931            multibuffer.set_excerpts_for_path(
16932                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16933                buffer.clone(),
16934                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16935                DEFAULT_MULTIBUFFER_CONTEXT,
16936                cx,
16937            );
16938        }
16939        multibuffer
16940    });
16941
16942    let editor = cx.add_window(|window, cx| {
16943        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16944    });
16945    cx.run_until_parked();
16946
16947    let snapshot = editor
16948        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16949        .unwrap();
16950    let hunks = snapshot
16951        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16952        .map(|hunk| match hunk {
16953            DisplayDiffHunk::Unfolded {
16954                display_row_range, ..
16955            } => display_row_range,
16956            DisplayDiffHunk::Folded { .. } => unreachable!(),
16957        })
16958        .collect::<Vec<_>>();
16959    assert_eq!(
16960        hunks,
16961        [
16962            DisplayRow(2)..DisplayRow(4),
16963            DisplayRow(7)..DisplayRow(9),
16964            DisplayRow(12)..DisplayRow(14),
16965        ]
16966    );
16967}
16968
16969#[gpui::test]
16970async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16971    init_test(cx, |_| {});
16972
16973    let mut cx = EditorTestContext::new(cx).await;
16974    cx.set_head_text(indoc! { "
16975        one
16976        two
16977        three
16978        four
16979        five
16980        "
16981    });
16982    cx.set_index_text(indoc! { "
16983        one
16984        two
16985        three
16986        four
16987        five
16988        "
16989    });
16990    cx.set_state(indoc! {"
16991        one
16992        TWO
16993        ˇTHREE
16994        FOUR
16995        five
16996    "});
16997    cx.run_until_parked();
16998    cx.update_editor(|editor, window, cx| {
16999        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17000    });
17001    cx.run_until_parked();
17002    cx.assert_index_text(Some(indoc! {"
17003        one
17004        TWO
17005        THREE
17006        FOUR
17007        five
17008    "}));
17009    cx.set_state(indoc! { "
17010        one
17011        TWO
17012        ˇTHREE-HUNDRED
17013        FOUR
17014        five
17015    "});
17016    cx.run_until_parked();
17017    cx.update_editor(|editor, window, cx| {
17018        let snapshot = editor.snapshot(window, cx);
17019        let hunks = editor
17020            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17021            .collect::<Vec<_>>();
17022        assert_eq!(hunks.len(), 1);
17023        assert_eq!(
17024            hunks[0].status(),
17025            DiffHunkStatus {
17026                kind: DiffHunkStatusKind::Modified,
17027                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17028            }
17029        );
17030
17031        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17032    });
17033    cx.run_until_parked();
17034    cx.assert_index_text(Some(indoc! {"
17035        one
17036        TWO
17037        THREE-HUNDRED
17038        FOUR
17039        five
17040    "}));
17041}
17042
17043#[gpui::test]
17044fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17045    init_test(cx, |_| {});
17046
17047    let editor = cx.add_window(|window, cx| {
17048        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17049        build_editor(buffer, window, cx)
17050    });
17051
17052    let render_args = Arc::new(Mutex::new(None));
17053    let snapshot = editor
17054        .update(cx, |editor, window, cx| {
17055            let snapshot = editor.buffer().read(cx).snapshot(cx);
17056            let range =
17057                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17058
17059            struct RenderArgs {
17060                row: MultiBufferRow,
17061                folded: bool,
17062                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17063            }
17064
17065            let crease = Crease::inline(
17066                range,
17067                FoldPlaceholder::test(),
17068                {
17069                    let toggle_callback = render_args.clone();
17070                    move |row, folded, callback, _window, _cx| {
17071                        *toggle_callback.lock() = Some(RenderArgs {
17072                            row,
17073                            folded,
17074                            callback,
17075                        });
17076                        div()
17077                    }
17078                },
17079                |_row, _folded, _window, _cx| div(),
17080            );
17081
17082            editor.insert_creases(Some(crease), cx);
17083            let snapshot = editor.snapshot(window, cx);
17084            let _div = snapshot.render_crease_toggle(
17085                MultiBufferRow(1),
17086                false,
17087                cx.entity().clone(),
17088                window,
17089                cx,
17090            );
17091            snapshot
17092        })
17093        .unwrap();
17094
17095    let render_args = render_args.lock().take().unwrap();
17096    assert_eq!(render_args.row, MultiBufferRow(1));
17097    assert!(!render_args.folded);
17098    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17099
17100    cx.update_window(*editor, |_, window, cx| {
17101        (render_args.callback)(true, window, cx)
17102    })
17103    .unwrap();
17104    let snapshot = editor
17105        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17106        .unwrap();
17107    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17108
17109    cx.update_window(*editor, |_, window, cx| {
17110        (render_args.callback)(false, window, cx)
17111    })
17112    .unwrap();
17113    let snapshot = editor
17114        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17115        .unwrap();
17116    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17117}
17118
17119#[gpui::test]
17120async fn test_input_text(cx: &mut TestAppContext) {
17121    init_test(cx, |_| {});
17122    let mut cx = EditorTestContext::new(cx).await;
17123
17124    cx.set_state(
17125        &r#"ˇone
17126        two
17127
17128        three
17129        fourˇ
17130        five
17131
17132        siˇx"#
17133            .unindent(),
17134    );
17135
17136    cx.dispatch_action(HandleInput(String::new()));
17137    cx.assert_editor_state(
17138        &r#"ˇone
17139        two
17140
17141        three
17142        fourˇ
17143        five
17144
17145        siˇx"#
17146            .unindent(),
17147    );
17148
17149    cx.dispatch_action(HandleInput("AAAA".to_string()));
17150    cx.assert_editor_state(
17151        &r#"AAAAˇone
17152        two
17153
17154        three
17155        fourAAAAˇ
17156        five
17157
17158        siAAAAˇx"#
17159            .unindent(),
17160    );
17161}
17162
17163#[gpui::test]
17164async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17165    init_test(cx, |_| {});
17166
17167    let mut cx = EditorTestContext::new(cx).await;
17168    cx.set_state(
17169        r#"let foo = 1;
17170let foo = 2;
17171let foo = 3;
17172let fooˇ = 4;
17173let foo = 5;
17174let foo = 6;
17175let foo = 7;
17176let foo = 8;
17177let foo = 9;
17178let foo = 10;
17179let foo = 11;
17180let foo = 12;
17181let foo = 13;
17182let foo = 14;
17183let foo = 15;"#,
17184    );
17185
17186    cx.update_editor(|e, window, cx| {
17187        assert_eq!(
17188            e.next_scroll_position,
17189            NextScrollCursorCenterTopBottom::Center,
17190            "Default next scroll direction is center",
17191        );
17192
17193        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17194        assert_eq!(
17195            e.next_scroll_position,
17196            NextScrollCursorCenterTopBottom::Top,
17197            "After center, next scroll direction should be top",
17198        );
17199
17200        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17201        assert_eq!(
17202            e.next_scroll_position,
17203            NextScrollCursorCenterTopBottom::Bottom,
17204            "After top, next scroll direction should be bottom",
17205        );
17206
17207        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17208        assert_eq!(
17209            e.next_scroll_position,
17210            NextScrollCursorCenterTopBottom::Center,
17211            "After bottom, scrolling should start over",
17212        );
17213
17214        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17215        assert_eq!(
17216            e.next_scroll_position,
17217            NextScrollCursorCenterTopBottom::Top,
17218            "Scrolling continues if retriggered fast enough"
17219        );
17220    });
17221
17222    cx.executor()
17223        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17224    cx.executor().run_until_parked();
17225    cx.update_editor(|e, _, _| {
17226        assert_eq!(
17227            e.next_scroll_position,
17228            NextScrollCursorCenterTopBottom::Center,
17229            "If scrolling is not triggered fast enough, it should reset"
17230        );
17231    });
17232}
17233
17234#[gpui::test]
17235async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17236    init_test(cx, |_| {});
17237    let mut cx = EditorLspTestContext::new_rust(
17238        lsp::ServerCapabilities {
17239            definition_provider: Some(lsp::OneOf::Left(true)),
17240            references_provider: Some(lsp::OneOf::Left(true)),
17241            ..lsp::ServerCapabilities::default()
17242        },
17243        cx,
17244    )
17245    .await;
17246
17247    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17248        let go_to_definition = cx
17249            .lsp
17250            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17251                move |params, _| async move {
17252                    if empty_go_to_definition {
17253                        Ok(None)
17254                    } else {
17255                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17256                            uri: params.text_document_position_params.text_document.uri,
17257                            range: lsp::Range::new(
17258                                lsp::Position::new(4, 3),
17259                                lsp::Position::new(4, 6),
17260                            ),
17261                        })))
17262                    }
17263                },
17264            );
17265        let references = cx
17266            .lsp
17267            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17268                Ok(Some(vec![lsp::Location {
17269                    uri: params.text_document_position.text_document.uri,
17270                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17271                }]))
17272            });
17273        (go_to_definition, references)
17274    };
17275
17276    cx.set_state(
17277        &r#"fn one() {
17278            let mut a = ˇtwo();
17279        }
17280
17281        fn two() {}"#
17282            .unindent(),
17283    );
17284    set_up_lsp_handlers(false, &mut cx);
17285    let navigated = cx
17286        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17287        .await
17288        .expect("Failed to navigate to definition");
17289    assert_eq!(
17290        navigated,
17291        Navigated::Yes,
17292        "Should have navigated to definition from the GetDefinition response"
17293    );
17294    cx.assert_editor_state(
17295        &r#"fn one() {
17296            let mut a = two();
17297        }
17298
17299        fn «twoˇ»() {}"#
17300            .unindent(),
17301    );
17302
17303    let editors = cx.update_workspace(|workspace, _, cx| {
17304        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17305    });
17306    cx.update_editor(|_, _, test_editor_cx| {
17307        assert_eq!(
17308            editors.len(),
17309            1,
17310            "Initially, only one, test, editor should be open in the workspace"
17311        );
17312        assert_eq!(
17313            test_editor_cx.entity(),
17314            editors.last().expect("Asserted len is 1").clone()
17315        );
17316    });
17317
17318    set_up_lsp_handlers(true, &mut cx);
17319    let navigated = cx
17320        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17321        .await
17322        .expect("Failed to navigate to lookup references");
17323    assert_eq!(
17324        navigated,
17325        Navigated::Yes,
17326        "Should have navigated to references as a fallback after empty GoToDefinition response"
17327    );
17328    // We should not change the selections in the existing file,
17329    // if opening another milti buffer with the references
17330    cx.assert_editor_state(
17331        &r#"fn one() {
17332            let mut a = two();
17333        }
17334
17335        fn «twoˇ»() {}"#
17336            .unindent(),
17337    );
17338    let editors = cx.update_workspace(|workspace, _, cx| {
17339        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17340    });
17341    cx.update_editor(|_, _, test_editor_cx| {
17342        assert_eq!(
17343            editors.len(),
17344            2,
17345            "After falling back to references search, we open a new editor with the results"
17346        );
17347        let references_fallback_text = editors
17348            .into_iter()
17349            .find(|new_editor| *new_editor != test_editor_cx.entity())
17350            .expect("Should have one non-test editor now")
17351            .read(test_editor_cx)
17352            .text(test_editor_cx);
17353        assert_eq!(
17354            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17355            "Should use the range from the references response and not the GoToDefinition one"
17356        );
17357    });
17358}
17359
17360#[gpui::test]
17361async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17362    init_test(cx, |_| {});
17363    cx.update(|cx| {
17364        let mut editor_settings = EditorSettings::get_global(cx).clone();
17365        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17366        EditorSettings::override_global(editor_settings, cx);
17367    });
17368    let mut cx = EditorLspTestContext::new_rust(
17369        lsp::ServerCapabilities {
17370            definition_provider: Some(lsp::OneOf::Left(true)),
17371            references_provider: Some(lsp::OneOf::Left(true)),
17372            ..lsp::ServerCapabilities::default()
17373        },
17374        cx,
17375    )
17376    .await;
17377    let original_state = r#"fn one() {
17378        let mut a = ˇtwo();
17379    }
17380
17381    fn two() {}"#
17382        .unindent();
17383    cx.set_state(&original_state);
17384
17385    let mut go_to_definition = cx
17386        .lsp
17387        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17388            move |_, _| async move { Ok(None) },
17389        );
17390    let _references = cx
17391        .lsp
17392        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17393            panic!("Should not call for references with no go to definition fallback")
17394        });
17395
17396    let navigated = cx
17397        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17398        .await
17399        .expect("Failed to navigate to lookup references");
17400    go_to_definition
17401        .next()
17402        .await
17403        .expect("Should have called the go_to_definition handler");
17404
17405    assert_eq!(
17406        navigated,
17407        Navigated::No,
17408        "Should have navigated to references as a fallback after empty GoToDefinition response"
17409    );
17410    cx.assert_editor_state(&original_state);
17411    let editors = cx.update_workspace(|workspace, _, cx| {
17412        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17413    });
17414    cx.update_editor(|_, _, _| {
17415        assert_eq!(
17416            editors.len(),
17417            1,
17418            "After unsuccessful fallback, no other editor should have been opened"
17419        );
17420    });
17421}
17422
17423#[gpui::test]
17424async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17425    init_test(cx, |_| {});
17426
17427    let language = Arc::new(Language::new(
17428        LanguageConfig::default(),
17429        Some(tree_sitter_rust::LANGUAGE.into()),
17430    ));
17431
17432    let text = r#"
17433        #[cfg(test)]
17434        mod tests() {
17435            #[test]
17436            fn runnable_1() {
17437                let a = 1;
17438            }
17439
17440            #[test]
17441            fn runnable_2() {
17442                let a = 1;
17443                let b = 2;
17444            }
17445        }
17446    "#
17447    .unindent();
17448
17449    let fs = FakeFs::new(cx.executor());
17450    fs.insert_file("/file.rs", Default::default()).await;
17451
17452    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17453    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17454    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17455    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17456    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17457
17458    let editor = cx.new_window_entity(|window, cx| {
17459        Editor::new(
17460            EditorMode::full(),
17461            multi_buffer,
17462            Some(project.clone()),
17463            window,
17464            cx,
17465        )
17466    });
17467
17468    editor.update_in(cx, |editor, window, cx| {
17469        let snapshot = editor.buffer().read(cx).snapshot(cx);
17470        editor.tasks.insert(
17471            (buffer.read(cx).remote_id(), 3),
17472            RunnableTasks {
17473                templates: vec![],
17474                offset: snapshot.anchor_before(43),
17475                column: 0,
17476                extra_variables: HashMap::default(),
17477                context_range: BufferOffset(43)..BufferOffset(85),
17478            },
17479        );
17480        editor.tasks.insert(
17481            (buffer.read(cx).remote_id(), 8),
17482            RunnableTasks {
17483                templates: vec![],
17484                offset: snapshot.anchor_before(86),
17485                column: 0,
17486                extra_variables: HashMap::default(),
17487                context_range: BufferOffset(86)..BufferOffset(191),
17488            },
17489        );
17490
17491        // Test finding task when cursor is inside function body
17492        editor.change_selections(None, window, cx, |s| {
17493            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17494        });
17495        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17496        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17497
17498        // Test finding task when cursor is on function name
17499        editor.change_selections(None, window, cx, |s| {
17500            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17501        });
17502        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17503        assert_eq!(row, 8, "Should find task when cursor is on function name");
17504    });
17505}
17506
17507#[gpui::test]
17508async fn test_folding_buffers(cx: &mut TestAppContext) {
17509    init_test(cx, |_| {});
17510
17511    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17512    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17513    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17514
17515    let fs = FakeFs::new(cx.executor());
17516    fs.insert_tree(
17517        path!("/a"),
17518        json!({
17519            "first.rs": sample_text_1,
17520            "second.rs": sample_text_2,
17521            "third.rs": sample_text_3,
17522        }),
17523    )
17524    .await;
17525    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17526    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17527    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17528    let worktree = project.update(cx, |project, cx| {
17529        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17530        assert_eq!(worktrees.len(), 1);
17531        worktrees.pop().unwrap()
17532    });
17533    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17534
17535    let buffer_1 = project
17536        .update(cx, |project, cx| {
17537            project.open_buffer((worktree_id, "first.rs"), cx)
17538        })
17539        .await
17540        .unwrap();
17541    let buffer_2 = project
17542        .update(cx, |project, cx| {
17543            project.open_buffer((worktree_id, "second.rs"), cx)
17544        })
17545        .await
17546        .unwrap();
17547    let buffer_3 = project
17548        .update(cx, |project, cx| {
17549            project.open_buffer((worktree_id, "third.rs"), cx)
17550        })
17551        .await
17552        .unwrap();
17553
17554    let multi_buffer = cx.new(|cx| {
17555        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17556        multi_buffer.push_excerpts(
17557            buffer_1.clone(),
17558            [
17559                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17560                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17561                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17562            ],
17563            cx,
17564        );
17565        multi_buffer.push_excerpts(
17566            buffer_2.clone(),
17567            [
17568                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17569                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17570                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17571            ],
17572            cx,
17573        );
17574        multi_buffer.push_excerpts(
17575            buffer_3.clone(),
17576            [
17577                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17578                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17579                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17580            ],
17581            cx,
17582        );
17583        multi_buffer
17584    });
17585    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17586        Editor::new(
17587            EditorMode::full(),
17588            multi_buffer.clone(),
17589            Some(project.clone()),
17590            window,
17591            cx,
17592        )
17593    });
17594
17595    assert_eq!(
17596        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17597        "\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",
17598    );
17599
17600    multi_buffer_editor.update(cx, |editor, cx| {
17601        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17602    });
17603    assert_eq!(
17604        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17605        "\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",
17606        "After folding the first buffer, its text should not be displayed"
17607    );
17608
17609    multi_buffer_editor.update(cx, |editor, cx| {
17610        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17611    });
17612    assert_eq!(
17613        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17614        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17615        "After folding the second buffer, its text should not be displayed"
17616    );
17617
17618    multi_buffer_editor.update(cx, |editor, cx| {
17619        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17620    });
17621    assert_eq!(
17622        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17623        "\n\n\n\n\n",
17624        "After folding the third buffer, its text should not be displayed"
17625    );
17626
17627    // Emulate selection inside the fold logic, that should work
17628    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17629        editor
17630            .snapshot(window, cx)
17631            .next_line_boundary(Point::new(0, 4));
17632    });
17633
17634    multi_buffer_editor.update(cx, |editor, cx| {
17635        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17636    });
17637    assert_eq!(
17638        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17639        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17640        "After unfolding the second buffer, its text should be displayed"
17641    );
17642
17643    // Typing inside of buffer 1 causes that buffer to be unfolded.
17644    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17645        assert_eq!(
17646            multi_buffer
17647                .read(cx)
17648                .snapshot(cx)
17649                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17650                .collect::<String>(),
17651            "bbbb"
17652        );
17653        editor.change_selections(None, window, cx, |selections| {
17654            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17655        });
17656        editor.handle_input("B", window, cx);
17657    });
17658
17659    assert_eq!(
17660        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17661        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17662        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17663    );
17664
17665    multi_buffer_editor.update(cx, |editor, cx| {
17666        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17667    });
17668    assert_eq!(
17669        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17670        "\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",
17671        "After unfolding the all buffers, all original text should be displayed"
17672    );
17673}
17674
17675#[gpui::test]
17676async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17677    init_test(cx, |_| {});
17678
17679    let sample_text_1 = "1111\n2222\n3333".to_string();
17680    let sample_text_2 = "4444\n5555\n6666".to_string();
17681    let sample_text_3 = "7777\n8888\n9999".to_string();
17682
17683    let fs = FakeFs::new(cx.executor());
17684    fs.insert_tree(
17685        path!("/a"),
17686        json!({
17687            "first.rs": sample_text_1,
17688            "second.rs": sample_text_2,
17689            "third.rs": sample_text_3,
17690        }),
17691    )
17692    .await;
17693    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17694    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17695    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17696    let worktree = project.update(cx, |project, cx| {
17697        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17698        assert_eq!(worktrees.len(), 1);
17699        worktrees.pop().unwrap()
17700    });
17701    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17702
17703    let buffer_1 = project
17704        .update(cx, |project, cx| {
17705            project.open_buffer((worktree_id, "first.rs"), cx)
17706        })
17707        .await
17708        .unwrap();
17709    let buffer_2 = project
17710        .update(cx, |project, cx| {
17711            project.open_buffer((worktree_id, "second.rs"), cx)
17712        })
17713        .await
17714        .unwrap();
17715    let buffer_3 = project
17716        .update(cx, |project, cx| {
17717            project.open_buffer((worktree_id, "third.rs"), cx)
17718        })
17719        .await
17720        .unwrap();
17721
17722    let multi_buffer = cx.new(|cx| {
17723        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17724        multi_buffer.push_excerpts(
17725            buffer_1.clone(),
17726            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17727            cx,
17728        );
17729        multi_buffer.push_excerpts(
17730            buffer_2.clone(),
17731            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17732            cx,
17733        );
17734        multi_buffer.push_excerpts(
17735            buffer_3.clone(),
17736            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17737            cx,
17738        );
17739        multi_buffer
17740    });
17741
17742    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17743        Editor::new(
17744            EditorMode::full(),
17745            multi_buffer,
17746            Some(project.clone()),
17747            window,
17748            cx,
17749        )
17750    });
17751
17752    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17753    assert_eq!(
17754        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17755        full_text,
17756    );
17757
17758    multi_buffer_editor.update(cx, |editor, cx| {
17759        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17760    });
17761    assert_eq!(
17762        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17763        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17764        "After folding the first buffer, its text should not be displayed"
17765    );
17766
17767    multi_buffer_editor.update(cx, |editor, cx| {
17768        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17769    });
17770
17771    assert_eq!(
17772        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17773        "\n\n\n\n\n\n7777\n8888\n9999",
17774        "After folding the second buffer, its text should not be displayed"
17775    );
17776
17777    multi_buffer_editor.update(cx, |editor, cx| {
17778        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17779    });
17780    assert_eq!(
17781        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17782        "\n\n\n\n\n",
17783        "After folding the third buffer, its text should not be displayed"
17784    );
17785
17786    multi_buffer_editor.update(cx, |editor, cx| {
17787        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17788    });
17789    assert_eq!(
17790        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17791        "\n\n\n\n4444\n5555\n6666\n\n",
17792        "After unfolding the second buffer, its text should be displayed"
17793    );
17794
17795    multi_buffer_editor.update(cx, |editor, cx| {
17796        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17797    });
17798    assert_eq!(
17799        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17800        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17801        "After unfolding the first buffer, its text should be displayed"
17802    );
17803
17804    multi_buffer_editor.update(cx, |editor, cx| {
17805        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17806    });
17807    assert_eq!(
17808        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17809        full_text,
17810        "After unfolding all buffers, all original text should be displayed"
17811    );
17812}
17813
17814#[gpui::test]
17815async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17816    init_test(cx, |_| {});
17817
17818    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17819
17820    let fs = FakeFs::new(cx.executor());
17821    fs.insert_tree(
17822        path!("/a"),
17823        json!({
17824            "main.rs": sample_text,
17825        }),
17826    )
17827    .await;
17828    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17829    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17830    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17831    let worktree = project.update(cx, |project, cx| {
17832        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17833        assert_eq!(worktrees.len(), 1);
17834        worktrees.pop().unwrap()
17835    });
17836    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17837
17838    let buffer_1 = project
17839        .update(cx, |project, cx| {
17840            project.open_buffer((worktree_id, "main.rs"), cx)
17841        })
17842        .await
17843        .unwrap();
17844
17845    let multi_buffer = cx.new(|cx| {
17846        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17847        multi_buffer.push_excerpts(
17848            buffer_1.clone(),
17849            [ExcerptRange::new(
17850                Point::new(0, 0)
17851                    ..Point::new(
17852                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17853                        0,
17854                    ),
17855            )],
17856            cx,
17857        );
17858        multi_buffer
17859    });
17860    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17861        Editor::new(
17862            EditorMode::full(),
17863            multi_buffer,
17864            Some(project.clone()),
17865            window,
17866            cx,
17867        )
17868    });
17869
17870    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17871    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17872        enum TestHighlight {}
17873        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17874        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17875        editor.highlight_text::<TestHighlight>(
17876            vec![highlight_range.clone()],
17877            HighlightStyle::color(Hsla::green()),
17878            cx,
17879        );
17880        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17881    });
17882
17883    let full_text = format!("\n\n{sample_text}");
17884    assert_eq!(
17885        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17886        full_text,
17887    );
17888}
17889
17890#[gpui::test]
17891async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17892    init_test(cx, |_| {});
17893    cx.update(|cx| {
17894        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17895            "keymaps/default-linux.json",
17896            cx,
17897        )
17898        .unwrap();
17899        cx.bind_keys(default_key_bindings);
17900    });
17901
17902    let (editor, cx) = cx.add_window_view(|window, cx| {
17903        let multi_buffer = MultiBuffer::build_multi(
17904            [
17905                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17906                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17907                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17908                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17909            ],
17910            cx,
17911        );
17912        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17913
17914        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17915        // fold all but the second buffer, so that we test navigating between two
17916        // adjacent folded buffers, as well as folded buffers at the start and
17917        // end the multibuffer
17918        editor.fold_buffer(buffer_ids[0], cx);
17919        editor.fold_buffer(buffer_ids[2], cx);
17920        editor.fold_buffer(buffer_ids[3], cx);
17921
17922        editor
17923    });
17924    cx.simulate_resize(size(px(1000.), px(1000.)));
17925
17926    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17927    cx.assert_excerpts_with_selections(indoc! {"
17928        [EXCERPT]
17929        ˇ[FOLDED]
17930        [EXCERPT]
17931        a1
17932        b1
17933        [EXCERPT]
17934        [FOLDED]
17935        [EXCERPT]
17936        [FOLDED]
17937        "
17938    });
17939    cx.simulate_keystroke("down");
17940    cx.assert_excerpts_with_selections(indoc! {"
17941        [EXCERPT]
17942        [FOLDED]
17943        [EXCERPT]
17944        ˇa1
17945        b1
17946        [EXCERPT]
17947        [FOLDED]
17948        [EXCERPT]
17949        [FOLDED]
17950        "
17951    });
17952    cx.simulate_keystroke("down");
17953    cx.assert_excerpts_with_selections(indoc! {"
17954        [EXCERPT]
17955        [FOLDED]
17956        [EXCERPT]
17957        a1
17958        ˇb1
17959        [EXCERPT]
17960        [FOLDED]
17961        [EXCERPT]
17962        [FOLDED]
17963        "
17964    });
17965    cx.simulate_keystroke("down");
17966    cx.assert_excerpts_with_selections(indoc! {"
17967        [EXCERPT]
17968        [FOLDED]
17969        [EXCERPT]
17970        a1
17971        b1
17972        ˇ[EXCERPT]
17973        [FOLDED]
17974        [EXCERPT]
17975        [FOLDED]
17976        "
17977    });
17978    cx.simulate_keystroke("down");
17979    cx.assert_excerpts_with_selections(indoc! {"
17980        [EXCERPT]
17981        [FOLDED]
17982        [EXCERPT]
17983        a1
17984        b1
17985        [EXCERPT]
17986        ˇ[FOLDED]
17987        [EXCERPT]
17988        [FOLDED]
17989        "
17990    });
17991    for _ in 0..5 {
17992        cx.simulate_keystroke("down");
17993        cx.assert_excerpts_with_selections(indoc! {"
17994            [EXCERPT]
17995            [FOLDED]
17996            [EXCERPT]
17997            a1
17998            b1
17999            [EXCERPT]
18000            [FOLDED]
18001            [EXCERPT]
18002            ˇ[FOLDED]
18003            "
18004        });
18005    }
18006
18007    cx.simulate_keystroke("up");
18008    cx.assert_excerpts_with_selections(indoc! {"
18009        [EXCERPT]
18010        [FOLDED]
18011        [EXCERPT]
18012        a1
18013        b1
18014        [EXCERPT]
18015        ˇ[FOLDED]
18016        [EXCERPT]
18017        [FOLDED]
18018        "
18019    });
18020    cx.simulate_keystroke("up");
18021    cx.assert_excerpts_with_selections(indoc! {"
18022        [EXCERPT]
18023        [FOLDED]
18024        [EXCERPT]
18025        a1
18026        b1
18027        ˇ[EXCERPT]
18028        [FOLDED]
18029        [EXCERPT]
18030        [FOLDED]
18031        "
18032    });
18033    cx.simulate_keystroke("up");
18034    cx.assert_excerpts_with_selections(indoc! {"
18035        [EXCERPT]
18036        [FOLDED]
18037        [EXCERPT]
18038        a1
18039        ˇb1
18040        [EXCERPT]
18041        [FOLDED]
18042        [EXCERPT]
18043        [FOLDED]
18044        "
18045    });
18046    cx.simulate_keystroke("up");
18047    cx.assert_excerpts_with_selections(indoc! {"
18048        [EXCERPT]
18049        [FOLDED]
18050        [EXCERPT]
18051        ˇa1
18052        b1
18053        [EXCERPT]
18054        [FOLDED]
18055        [EXCERPT]
18056        [FOLDED]
18057        "
18058    });
18059    for _ in 0..5 {
18060        cx.simulate_keystroke("up");
18061        cx.assert_excerpts_with_selections(indoc! {"
18062            [EXCERPT]
18063            ˇ[FOLDED]
18064            [EXCERPT]
18065            a1
18066            b1
18067            [EXCERPT]
18068            [FOLDED]
18069            [EXCERPT]
18070            [FOLDED]
18071            "
18072        });
18073    }
18074}
18075
18076#[gpui::test]
18077async fn test_inline_completion_text(cx: &mut TestAppContext) {
18078    init_test(cx, |_| {});
18079
18080    // Simple insertion
18081    assert_highlighted_edits(
18082        "Hello, world!",
18083        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18084        true,
18085        cx,
18086        |highlighted_edits, cx| {
18087            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18088            assert_eq!(highlighted_edits.highlights.len(), 1);
18089            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18090            assert_eq!(
18091                highlighted_edits.highlights[0].1.background_color,
18092                Some(cx.theme().status().created_background)
18093            );
18094        },
18095    )
18096    .await;
18097
18098    // Replacement
18099    assert_highlighted_edits(
18100        "This is a test.",
18101        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18102        false,
18103        cx,
18104        |highlighted_edits, cx| {
18105            assert_eq!(highlighted_edits.text, "That is a test.");
18106            assert_eq!(highlighted_edits.highlights.len(), 1);
18107            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18108            assert_eq!(
18109                highlighted_edits.highlights[0].1.background_color,
18110                Some(cx.theme().status().created_background)
18111            );
18112        },
18113    )
18114    .await;
18115
18116    // Multiple edits
18117    assert_highlighted_edits(
18118        "Hello, world!",
18119        vec![
18120            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18121            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18122        ],
18123        false,
18124        cx,
18125        |highlighted_edits, cx| {
18126            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18127            assert_eq!(highlighted_edits.highlights.len(), 2);
18128            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18129            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18130            assert_eq!(
18131                highlighted_edits.highlights[0].1.background_color,
18132                Some(cx.theme().status().created_background)
18133            );
18134            assert_eq!(
18135                highlighted_edits.highlights[1].1.background_color,
18136                Some(cx.theme().status().created_background)
18137            );
18138        },
18139    )
18140    .await;
18141
18142    // Multiple lines with edits
18143    assert_highlighted_edits(
18144        "First line\nSecond line\nThird line\nFourth line",
18145        vec![
18146            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18147            (
18148                Point::new(2, 0)..Point::new(2, 10),
18149                "New third line".to_string(),
18150            ),
18151            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18152        ],
18153        false,
18154        cx,
18155        |highlighted_edits, cx| {
18156            assert_eq!(
18157                highlighted_edits.text,
18158                "Second modified\nNew third line\nFourth updated line"
18159            );
18160            assert_eq!(highlighted_edits.highlights.len(), 3);
18161            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18162            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18163            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18164            for highlight in &highlighted_edits.highlights {
18165                assert_eq!(
18166                    highlight.1.background_color,
18167                    Some(cx.theme().status().created_background)
18168                );
18169            }
18170        },
18171    )
18172    .await;
18173}
18174
18175#[gpui::test]
18176async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18177    init_test(cx, |_| {});
18178
18179    // Deletion
18180    assert_highlighted_edits(
18181        "Hello, world!",
18182        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18183        true,
18184        cx,
18185        |highlighted_edits, cx| {
18186            assert_eq!(highlighted_edits.text, "Hello, world!");
18187            assert_eq!(highlighted_edits.highlights.len(), 1);
18188            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18189            assert_eq!(
18190                highlighted_edits.highlights[0].1.background_color,
18191                Some(cx.theme().status().deleted_background)
18192            );
18193        },
18194    )
18195    .await;
18196
18197    // Insertion
18198    assert_highlighted_edits(
18199        "Hello, world!",
18200        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18201        true,
18202        cx,
18203        |highlighted_edits, cx| {
18204            assert_eq!(highlighted_edits.highlights.len(), 1);
18205            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18206            assert_eq!(
18207                highlighted_edits.highlights[0].1.background_color,
18208                Some(cx.theme().status().created_background)
18209            );
18210        },
18211    )
18212    .await;
18213}
18214
18215async fn assert_highlighted_edits(
18216    text: &str,
18217    edits: Vec<(Range<Point>, String)>,
18218    include_deletions: bool,
18219    cx: &mut TestAppContext,
18220    assertion_fn: impl Fn(HighlightedText, &App),
18221) {
18222    let window = cx.add_window(|window, cx| {
18223        let buffer = MultiBuffer::build_simple(text, cx);
18224        Editor::new(EditorMode::full(), buffer, None, window, cx)
18225    });
18226    let cx = &mut VisualTestContext::from_window(*window, cx);
18227
18228    let (buffer, snapshot) = window
18229        .update(cx, |editor, _window, cx| {
18230            (
18231                editor.buffer().clone(),
18232                editor.buffer().read(cx).snapshot(cx),
18233            )
18234        })
18235        .unwrap();
18236
18237    let edits = edits
18238        .into_iter()
18239        .map(|(range, edit)| {
18240            (
18241                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18242                edit,
18243            )
18244        })
18245        .collect::<Vec<_>>();
18246
18247    let text_anchor_edits = edits
18248        .clone()
18249        .into_iter()
18250        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18251        .collect::<Vec<_>>();
18252
18253    let edit_preview = window
18254        .update(cx, |_, _window, cx| {
18255            buffer
18256                .read(cx)
18257                .as_singleton()
18258                .unwrap()
18259                .read(cx)
18260                .preview_edits(text_anchor_edits.into(), cx)
18261        })
18262        .unwrap()
18263        .await;
18264
18265    cx.update(|_window, cx| {
18266        let highlighted_edits = inline_completion_edit_text(
18267            &snapshot.as_singleton().unwrap().2,
18268            &edits,
18269            &edit_preview,
18270            include_deletions,
18271            cx,
18272        );
18273        assertion_fn(highlighted_edits, cx)
18274    });
18275}
18276
18277#[track_caller]
18278fn assert_breakpoint(
18279    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18280    path: &Arc<Path>,
18281    expected: Vec<(u32, Breakpoint)>,
18282) {
18283    if expected.len() == 0usize {
18284        assert!(!breakpoints.contains_key(path), "{}", path.display());
18285    } else {
18286        let mut breakpoint = breakpoints
18287            .get(path)
18288            .unwrap()
18289            .into_iter()
18290            .map(|breakpoint| {
18291                (
18292                    breakpoint.row,
18293                    Breakpoint {
18294                        message: breakpoint.message.clone(),
18295                        state: breakpoint.state,
18296                        condition: breakpoint.condition.clone(),
18297                        hit_condition: breakpoint.hit_condition.clone(),
18298                    },
18299                )
18300            })
18301            .collect::<Vec<_>>();
18302
18303        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18304
18305        assert_eq!(expected, breakpoint);
18306    }
18307}
18308
18309fn add_log_breakpoint_at_cursor(
18310    editor: &mut Editor,
18311    log_message: &str,
18312    window: &mut Window,
18313    cx: &mut Context<Editor>,
18314) {
18315    let (anchor, bp) = editor
18316        .breakpoints_at_cursors(window, cx)
18317        .first()
18318        .and_then(|(anchor, bp)| {
18319            if let Some(bp) = bp {
18320                Some((*anchor, bp.clone()))
18321            } else {
18322                None
18323            }
18324        })
18325        .unwrap_or_else(|| {
18326            let cursor_position: Point = editor.selections.newest(cx).head();
18327
18328            let breakpoint_position = editor
18329                .snapshot(window, cx)
18330                .display_snapshot
18331                .buffer_snapshot
18332                .anchor_before(Point::new(cursor_position.row, 0));
18333
18334            (breakpoint_position, Breakpoint::new_log(&log_message))
18335        });
18336
18337    editor.edit_breakpoint_at_anchor(
18338        anchor,
18339        bp,
18340        BreakpointEditAction::EditLogMessage(log_message.into()),
18341        cx,
18342    );
18343}
18344
18345#[gpui::test]
18346async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18347    init_test(cx, |_| {});
18348
18349    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18350    let fs = FakeFs::new(cx.executor());
18351    fs.insert_tree(
18352        path!("/a"),
18353        json!({
18354            "main.rs": sample_text,
18355        }),
18356    )
18357    .await;
18358    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18359    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18360    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18361
18362    let fs = FakeFs::new(cx.executor());
18363    fs.insert_tree(
18364        path!("/a"),
18365        json!({
18366            "main.rs": sample_text,
18367        }),
18368    )
18369    .await;
18370    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18371    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18372    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18373    let worktree_id = workspace
18374        .update(cx, |workspace, _window, cx| {
18375            workspace.project().update(cx, |project, cx| {
18376                project.worktrees(cx).next().unwrap().read(cx).id()
18377            })
18378        })
18379        .unwrap();
18380
18381    let buffer = project
18382        .update(cx, |project, cx| {
18383            project.open_buffer((worktree_id, "main.rs"), cx)
18384        })
18385        .await
18386        .unwrap();
18387
18388    let (editor, cx) = cx.add_window_view(|window, cx| {
18389        Editor::new(
18390            EditorMode::full(),
18391            MultiBuffer::build_from_buffer(buffer, cx),
18392            Some(project.clone()),
18393            window,
18394            cx,
18395        )
18396    });
18397
18398    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18399    let abs_path = project.read_with(cx, |project, cx| {
18400        project
18401            .absolute_path(&project_path, cx)
18402            .map(|path_buf| Arc::from(path_buf.to_owned()))
18403            .unwrap()
18404    });
18405
18406    // assert we can add breakpoint on the first line
18407    editor.update_in(cx, |editor, window, cx| {
18408        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18409        editor.move_to_end(&MoveToEnd, window, cx);
18410        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18411    });
18412
18413    let breakpoints = editor.update(cx, |editor, cx| {
18414        editor
18415            .breakpoint_store()
18416            .as_ref()
18417            .unwrap()
18418            .read(cx)
18419            .all_breakpoints(cx)
18420            .clone()
18421    });
18422
18423    assert_eq!(1, breakpoints.len());
18424    assert_breakpoint(
18425        &breakpoints,
18426        &abs_path,
18427        vec![
18428            (0, Breakpoint::new_standard()),
18429            (3, Breakpoint::new_standard()),
18430        ],
18431    );
18432
18433    editor.update_in(cx, |editor, window, cx| {
18434        editor.move_to_beginning(&MoveToBeginning, window, cx);
18435        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18436    });
18437
18438    let breakpoints = editor.update(cx, |editor, cx| {
18439        editor
18440            .breakpoint_store()
18441            .as_ref()
18442            .unwrap()
18443            .read(cx)
18444            .all_breakpoints(cx)
18445            .clone()
18446    });
18447
18448    assert_eq!(1, breakpoints.len());
18449    assert_breakpoint(
18450        &breakpoints,
18451        &abs_path,
18452        vec![(3, Breakpoint::new_standard())],
18453    );
18454
18455    editor.update_in(cx, |editor, window, cx| {
18456        editor.move_to_end(&MoveToEnd, window, cx);
18457        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18458    });
18459
18460    let breakpoints = editor.update(cx, |editor, cx| {
18461        editor
18462            .breakpoint_store()
18463            .as_ref()
18464            .unwrap()
18465            .read(cx)
18466            .all_breakpoints(cx)
18467            .clone()
18468    });
18469
18470    assert_eq!(0, breakpoints.len());
18471    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18472}
18473
18474#[gpui::test]
18475async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18476    init_test(cx, |_| {});
18477
18478    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18479
18480    let fs = FakeFs::new(cx.executor());
18481    fs.insert_tree(
18482        path!("/a"),
18483        json!({
18484            "main.rs": sample_text,
18485        }),
18486    )
18487    .await;
18488    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18489    let (workspace, cx) =
18490        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18491
18492    let worktree_id = workspace.update(cx, |workspace, cx| {
18493        workspace.project().update(cx, |project, cx| {
18494            project.worktrees(cx).next().unwrap().read(cx).id()
18495        })
18496    });
18497
18498    let buffer = project
18499        .update(cx, |project, cx| {
18500            project.open_buffer((worktree_id, "main.rs"), cx)
18501        })
18502        .await
18503        .unwrap();
18504
18505    let (editor, cx) = cx.add_window_view(|window, cx| {
18506        Editor::new(
18507            EditorMode::full(),
18508            MultiBuffer::build_from_buffer(buffer, cx),
18509            Some(project.clone()),
18510            window,
18511            cx,
18512        )
18513    });
18514
18515    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18516    let abs_path = project.read_with(cx, |project, cx| {
18517        project
18518            .absolute_path(&project_path, cx)
18519            .map(|path_buf| Arc::from(path_buf.to_owned()))
18520            .unwrap()
18521    });
18522
18523    editor.update_in(cx, |editor, window, cx| {
18524        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18525    });
18526
18527    let breakpoints = editor.update(cx, |editor, cx| {
18528        editor
18529            .breakpoint_store()
18530            .as_ref()
18531            .unwrap()
18532            .read(cx)
18533            .all_breakpoints(cx)
18534            .clone()
18535    });
18536
18537    assert_breakpoint(
18538        &breakpoints,
18539        &abs_path,
18540        vec![(0, Breakpoint::new_log("hello world"))],
18541    );
18542
18543    // Removing a log message from a log breakpoint should remove it
18544    editor.update_in(cx, |editor, window, cx| {
18545        add_log_breakpoint_at_cursor(editor, "", window, cx);
18546    });
18547
18548    let breakpoints = editor.update(cx, |editor, cx| {
18549        editor
18550            .breakpoint_store()
18551            .as_ref()
18552            .unwrap()
18553            .read(cx)
18554            .all_breakpoints(cx)
18555            .clone()
18556    });
18557
18558    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18559
18560    editor.update_in(cx, |editor, window, cx| {
18561        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18562        editor.move_to_end(&MoveToEnd, window, cx);
18563        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18564        // Not adding a log message to a standard breakpoint shouldn't remove it
18565        add_log_breakpoint_at_cursor(editor, "", window, cx);
18566    });
18567
18568    let breakpoints = editor.update(cx, |editor, cx| {
18569        editor
18570            .breakpoint_store()
18571            .as_ref()
18572            .unwrap()
18573            .read(cx)
18574            .all_breakpoints(cx)
18575            .clone()
18576    });
18577
18578    assert_breakpoint(
18579        &breakpoints,
18580        &abs_path,
18581        vec![
18582            (0, Breakpoint::new_standard()),
18583            (3, Breakpoint::new_standard()),
18584        ],
18585    );
18586
18587    editor.update_in(cx, |editor, window, cx| {
18588        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18589    });
18590
18591    let breakpoints = editor.update(cx, |editor, cx| {
18592        editor
18593            .breakpoint_store()
18594            .as_ref()
18595            .unwrap()
18596            .read(cx)
18597            .all_breakpoints(cx)
18598            .clone()
18599    });
18600
18601    assert_breakpoint(
18602        &breakpoints,
18603        &abs_path,
18604        vec![
18605            (0, Breakpoint::new_standard()),
18606            (3, Breakpoint::new_log("hello world")),
18607        ],
18608    );
18609
18610    editor.update_in(cx, |editor, window, cx| {
18611        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18612    });
18613
18614    let breakpoints = editor.update(cx, |editor, cx| {
18615        editor
18616            .breakpoint_store()
18617            .as_ref()
18618            .unwrap()
18619            .read(cx)
18620            .all_breakpoints(cx)
18621            .clone()
18622    });
18623
18624    assert_breakpoint(
18625        &breakpoints,
18626        &abs_path,
18627        vec![
18628            (0, Breakpoint::new_standard()),
18629            (3, Breakpoint::new_log("hello Earth!!")),
18630        ],
18631    );
18632}
18633
18634/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18635/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18636/// or when breakpoints were placed out of order. This tests for a regression too
18637#[gpui::test]
18638async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18639    init_test(cx, |_| {});
18640
18641    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18642    let fs = FakeFs::new(cx.executor());
18643    fs.insert_tree(
18644        path!("/a"),
18645        json!({
18646            "main.rs": sample_text,
18647        }),
18648    )
18649    .await;
18650    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18651    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18652    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18653
18654    let fs = FakeFs::new(cx.executor());
18655    fs.insert_tree(
18656        path!("/a"),
18657        json!({
18658            "main.rs": sample_text,
18659        }),
18660    )
18661    .await;
18662    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18663    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18664    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18665    let worktree_id = workspace
18666        .update(cx, |workspace, _window, cx| {
18667            workspace.project().update(cx, |project, cx| {
18668                project.worktrees(cx).next().unwrap().read(cx).id()
18669            })
18670        })
18671        .unwrap();
18672
18673    let buffer = project
18674        .update(cx, |project, cx| {
18675            project.open_buffer((worktree_id, "main.rs"), cx)
18676        })
18677        .await
18678        .unwrap();
18679
18680    let (editor, cx) = cx.add_window_view(|window, cx| {
18681        Editor::new(
18682            EditorMode::full(),
18683            MultiBuffer::build_from_buffer(buffer, cx),
18684            Some(project.clone()),
18685            window,
18686            cx,
18687        )
18688    });
18689
18690    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18691    let abs_path = project.read_with(cx, |project, cx| {
18692        project
18693            .absolute_path(&project_path, cx)
18694            .map(|path_buf| Arc::from(path_buf.to_owned()))
18695            .unwrap()
18696    });
18697
18698    // assert we can add breakpoint on the first line
18699    editor.update_in(cx, |editor, window, cx| {
18700        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18701        editor.move_to_end(&MoveToEnd, window, cx);
18702        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18703        editor.move_up(&MoveUp, window, cx);
18704        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18705    });
18706
18707    let breakpoints = editor.update(cx, |editor, cx| {
18708        editor
18709            .breakpoint_store()
18710            .as_ref()
18711            .unwrap()
18712            .read(cx)
18713            .all_breakpoints(cx)
18714            .clone()
18715    });
18716
18717    assert_eq!(1, breakpoints.len());
18718    assert_breakpoint(
18719        &breakpoints,
18720        &abs_path,
18721        vec![
18722            (0, Breakpoint::new_standard()),
18723            (2, Breakpoint::new_standard()),
18724            (3, Breakpoint::new_standard()),
18725        ],
18726    );
18727
18728    editor.update_in(cx, |editor, window, cx| {
18729        editor.move_to_beginning(&MoveToBeginning, window, cx);
18730        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18731        editor.move_to_end(&MoveToEnd, window, cx);
18732        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18733        // Disabling a breakpoint that doesn't exist should do nothing
18734        editor.move_up(&MoveUp, window, cx);
18735        editor.move_up(&MoveUp, window, cx);
18736        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18737    });
18738
18739    let breakpoints = editor.update(cx, |editor, cx| {
18740        editor
18741            .breakpoint_store()
18742            .as_ref()
18743            .unwrap()
18744            .read(cx)
18745            .all_breakpoints(cx)
18746            .clone()
18747    });
18748
18749    let disable_breakpoint = {
18750        let mut bp = Breakpoint::new_standard();
18751        bp.state = BreakpointState::Disabled;
18752        bp
18753    };
18754
18755    assert_eq!(1, breakpoints.len());
18756    assert_breakpoint(
18757        &breakpoints,
18758        &abs_path,
18759        vec![
18760            (0, disable_breakpoint.clone()),
18761            (2, Breakpoint::new_standard()),
18762            (3, disable_breakpoint.clone()),
18763        ],
18764    );
18765
18766    editor.update_in(cx, |editor, window, cx| {
18767        editor.move_to_beginning(&MoveToBeginning, window, cx);
18768        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18769        editor.move_to_end(&MoveToEnd, window, cx);
18770        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18771        editor.move_up(&MoveUp, window, cx);
18772        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18773    });
18774
18775    let breakpoints = editor.update(cx, |editor, cx| {
18776        editor
18777            .breakpoint_store()
18778            .as_ref()
18779            .unwrap()
18780            .read(cx)
18781            .all_breakpoints(cx)
18782            .clone()
18783    });
18784
18785    assert_eq!(1, breakpoints.len());
18786    assert_breakpoint(
18787        &breakpoints,
18788        &abs_path,
18789        vec![
18790            (0, Breakpoint::new_standard()),
18791            (2, disable_breakpoint),
18792            (3, Breakpoint::new_standard()),
18793        ],
18794    );
18795}
18796
18797#[gpui::test]
18798async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18799    init_test(cx, |_| {});
18800    let capabilities = lsp::ServerCapabilities {
18801        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18802            prepare_provider: Some(true),
18803            work_done_progress_options: Default::default(),
18804        })),
18805        ..Default::default()
18806    };
18807    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18808
18809    cx.set_state(indoc! {"
18810        struct Fˇoo {}
18811    "});
18812
18813    cx.update_editor(|editor, _, cx| {
18814        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18815        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18816        editor.highlight_background::<DocumentHighlightRead>(
18817            &[highlight_range],
18818            |c| c.editor_document_highlight_read_background,
18819            cx,
18820        );
18821    });
18822
18823    let mut prepare_rename_handler = cx
18824        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18825            move |_, _, _| async move {
18826                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18827                    start: lsp::Position {
18828                        line: 0,
18829                        character: 7,
18830                    },
18831                    end: lsp::Position {
18832                        line: 0,
18833                        character: 10,
18834                    },
18835                })))
18836            },
18837        );
18838    let prepare_rename_task = cx
18839        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18840        .expect("Prepare rename was not started");
18841    prepare_rename_handler.next().await.unwrap();
18842    prepare_rename_task.await.expect("Prepare rename failed");
18843
18844    let mut rename_handler =
18845        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18846            let edit = lsp::TextEdit {
18847                range: lsp::Range {
18848                    start: lsp::Position {
18849                        line: 0,
18850                        character: 7,
18851                    },
18852                    end: lsp::Position {
18853                        line: 0,
18854                        character: 10,
18855                    },
18856                },
18857                new_text: "FooRenamed".to_string(),
18858            };
18859            Ok(Some(lsp::WorkspaceEdit::new(
18860                // Specify the same edit twice
18861                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18862            )))
18863        });
18864    let rename_task = cx
18865        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18866        .expect("Confirm rename was not started");
18867    rename_handler.next().await.unwrap();
18868    rename_task.await.expect("Confirm rename failed");
18869    cx.run_until_parked();
18870
18871    // Despite two edits, only one is actually applied as those are identical
18872    cx.assert_editor_state(indoc! {"
18873        struct FooRenamedˇ {}
18874    "});
18875}
18876
18877#[gpui::test]
18878async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18879    init_test(cx, |_| {});
18880    // These capabilities indicate that the server does not support prepare rename.
18881    let capabilities = lsp::ServerCapabilities {
18882        rename_provider: Some(lsp::OneOf::Left(true)),
18883        ..Default::default()
18884    };
18885    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18886
18887    cx.set_state(indoc! {"
18888        struct Fˇoo {}
18889    "});
18890
18891    cx.update_editor(|editor, _window, cx| {
18892        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18893        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18894        editor.highlight_background::<DocumentHighlightRead>(
18895            &[highlight_range],
18896            |c| c.editor_document_highlight_read_background,
18897            cx,
18898        );
18899    });
18900
18901    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18902        .expect("Prepare rename was not started")
18903        .await
18904        .expect("Prepare rename failed");
18905
18906    let mut rename_handler =
18907        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18908            let edit = lsp::TextEdit {
18909                range: lsp::Range {
18910                    start: lsp::Position {
18911                        line: 0,
18912                        character: 7,
18913                    },
18914                    end: lsp::Position {
18915                        line: 0,
18916                        character: 10,
18917                    },
18918                },
18919                new_text: "FooRenamed".to_string(),
18920            };
18921            Ok(Some(lsp::WorkspaceEdit::new(
18922                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18923            )))
18924        });
18925    let rename_task = cx
18926        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18927        .expect("Confirm rename was not started");
18928    rename_handler.next().await.unwrap();
18929    rename_task.await.expect("Confirm rename failed");
18930    cx.run_until_parked();
18931
18932    // Correct range is renamed, as `surrounding_word` is used to find it.
18933    cx.assert_editor_state(indoc! {"
18934        struct FooRenamedˇ {}
18935    "});
18936}
18937
18938#[gpui::test]
18939async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18940    init_test(cx, |_| {});
18941    let mut cx = EditorTestContext::new(cx).await;
18942
18943    let language = Arc::new(
18944        Language::new(
18945            LanguageConfig::default(),
18946            Some(tree_sitter_html::LANGUAGE.into()),
18947        )
18948        .with_brackets_query(
18949            r#"
18950            ("<" @open "/>" @close)
18951            ("</" @open ">" @close)
18952            ("<" @open ">" @close)
18953            ("\"" @open "\"" @close)
18954            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18955        "#,
18956        )
18957        .unwrap(),
18958    );
18959    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18960
18961    cx.set_state(indoc! {"
18962        <span>ˇ</span>
18963    "});
18964    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18965    cx.assert_editor_state(indoc! {"
18966        <span>
18967        ˇ
18968        </span>
18969    "});
18970
18971    cx.set_state(indoc! {"
18972        <span><span></span>ˇ</span>
18973    "});
18974    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18975    cx.assert_editor_state(indoc! {"
18976        <span><span></span>
18977        ˇ</span>
18978    "});
18979
18980    cx.set_state(indoc! {"
18981        <span>ˇ
18982        </span>
18983    "});
18984    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18985    cx.assert_editor_state(indoc! {"
18986        <span>
18987        ˇ
18988        </span>
18989    "});
18990}
18991
18992#[gpui::test(iterations = 10)]
18993async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18994    init_test(cx, |_| {});
18995
18996    let fs = FakeFs::new(cx.executor());
18997    fs.insert_tree(
18998        path!("/dir"),
18999        json!({
19000            "a.ts": "a",
19001        }),
19002    )
19003    .await;
19004
19005    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19006    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19007    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19008
19009    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19010    language_registry.add(Arc::new(Language::new(
19011        LanguageConfig {
19012            name: "TypeScript".into(),
19013            matcher: LanguageMatcher {
19014                path_suffixes: vec!["ts".to_string()],
19015                ..Default::default()
19016            },
19017            ..Default::default()
19018        },
19019        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19020    )));
19021    let mut fake_language_servers = language_registry.register_fake_lsp(
19022        "TypeScript",
19023        FakeLspAdapter {
19024            capabilities: lsp::ServerCapabilities {
19025                code_lens_provider: Some(lsp::CodeLensOptions {
19026                    resolve_provider: Some(true),
19027                }),
19028                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19029                    commands: vec!["_the/command".to_string()],
19030                    ..lsp::ExecuteCommandOptions::default()
19031                }),
19032                ..lsp::ServerCapabilities::default()
19033            },
19034            ..FakeLspAdapter::default()
19035        },
19036    );
19037
19038    let (buffer, _handle) = project
19039        .update(cx, |p, cx| {
19040            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19041        })
19042        .await
19043        .unwrap();
19044    cx.executor().run_until_parked();
19045
19046    let fake_server = fake_language_servers.next().await.unwrap();
19047
19048    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19049    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19050    drop(buffer_snapshot);
19051    let actions = cx
19052        .update_window(*workspace, |_, window, cx| {
19053            project.code_actions(&buffer, anchor..anchor, window, cx)
19054        })
19055        .unwrap();
19056
19057    fake_server
19058        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19059            Ok(Some(vec![
19060                lsp::CodeLens {
19061                    range: lsp::Range::default(),
19062                    command: Some(lsp::Command {
19063                        title: "Code lens command".to_owned(),
19064                        command: "_the/command".to_owned(),
19065                        arguments: None,
19066                    }),
19067                    data: None,
19068                },
19069                lsp::CodeLens {
19070                    range: lsp::Range::default(),
19071                    command: Some(lsp::Command {
19072                        title: "Command not in capabilities".to_owned(),
19073                        command: "not in capabilities".to_owned(),
19074                        arguments: None,
19075                    }),
19076                    data: None,
19077                },
19078                lsp::CodeLens {
19079                    range: lsp::Range {
19080                        start: lsp::Position {
19081                            line: 1,
19082                            character: 1,
19083                        },
19084                        end: lsp::Position {
19085                            line: 1,
19086                            character: 1,
19087                        },
19088                    },
19089                    command: Some(lsp::Command {
19090                        title: "Command not in range".to_owned(),
19091                        command: "_the/command".to_owned(),
19092                        arguments: None,
19093                    }),
19094                    data: None,
19095                },
19096            ]))
19097        })
19098        .next()
19099        .await;
19100
19101    let actions = actions.await.unwrap();
19102    assert_eq!(
19103        actions.len(),
19104        1,
19105        "Should have only one valid action for the 0..0 range"
19106    );
19107    let action = actions[0].clone();
19108    let apply = project.update(cx, |project, cx| {
19109        project.apply_code_action(buffer.clone(), action, true, cx)
19110    });
19111
19112    // Resolving the code action does not populate its edits. In absence of
19113    // edits, we must execute the given command.
19114    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19115        |mut lens, _| async move {
19116            let lens_command = lens.command.as_mut().expect("should have a command");
19117            assert_eq!(lens_command.title, "Code lens command");
19118            lens_command.arguments = Some(vec![json!("the-argument")]);
19119            Ok(lens)
19120        },
19121    );
19122
19123    // While executing the command, the language server sends the editor
19124    // a `workspaceEdit` request.
19125    fake_server
19126        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19127            let fake = fake_server.clone();
19128            move |params, _| {
19129                assert_eq!(params.command, "_the/command");
19130                let fake = fake.clone();
19131                async move {
19132                    fake.server
19133                        .request::<lsp::request::ApplyWorkspaceEdit>(
19134                            lsp::ApplyWorkspaceEditParams {
19135                                label: None,
19136                                edit: lsp::WorkspaceEdit {
19137                                    changes: Some(
19138                                        [(
19139                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19140                                            vec![lsp::TextEdit {
19141                                                range: lsp::Range::new(
19142                                                    lsp::Position::new(0, 0),
19143                                                    lsp::Position::new(0, 0),
19144                                                ),
19145                                                new_text: "X".into(),
19146                                            }],
19147                                        )]
19148                                        .into_iter()
19149                                        .collect(),
19150                                    ),
19151                                    ..Default::default()
19152                                },
19153                            },
19154                        )
19155                        .await
19156                        .unwrap();
19157                    Ok(Some(json!(null)))
19158                }
19159            }
19160        })
19161        .next()
19162        .await;
19163
19164    // Applying the code lens command returns a project transaction containing the edits
19165    // sent by the language server in its `workspaceEdit` request.
19166    let transaction = apply.await.unwrap();
19167    assert!(transaction.0.contains_key(&buffer));
19168    buffer.update(cx, |buffer, cx| {
19169        assert_eq!(buffer.text(), "Xa");
19170        buffer.undo(cx);
19171        assert_eq!(buffer.text(), "a");
19172    });
19173}
19174
19175#[gpui::test]
19176async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19177    init_test(cx, |_| {});
19178
19179    let fs = FakeFs::new(cx.executor());
19180    let main_text = r#"fn main() {
19181println!("1");
19182println!("2");
19183println!("3");
19184println!("4");
19185println!("5");
19186}"#;
19187    let lib_text = "mod foo {}";
19188    fs.insert_tree(
19189        path!("/a"),
19190        json!({
19191            "lib.rs": lib_text,
19192            "main.rs": main_text,
19193        }),
19194    )
19195    .await;
19196
19197    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19198    let (workspace, cx) =
19199        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19200    let worktree_id = workspace.update(cx, |workspace, cx| {
19201        workspace.project().update(cx, |project, cx| {
19202            project.worktrees(cx).next().unwrap().read(cx).id()
19203        })
19204    });
19205
19206    let expected_ranges = vec![
19207        Point::new(0, 0)..Point::new(0, 0),
19208        Point::new(1, 0)..Point::new(1, 1),
19209        Point::new(2, 0)..Point::new(2, 2),
19210        Point::new(3, 0)..Point::new(3, 3),
19211    ];
19212
19213    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19214    let editor_1 = workspace
19215        .update_in(cx, |workspace, window, cx| {
19216            workspace.open_path(
19217                (worktree_id, "main.rs"),
19218                Some(pane_1.downgrade()),
19219                true,
19220                window,
19221                cx,
19222            )
19223        })
19224        .unwrap()
19225        .await
19226        .downcast::<Editor>()
19227        .unwrap();
19228    pane_1.update(cx, |pane, cx| {
19229        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19230        open_editor.update(cx, |editor, cx| {
19231            assert_eq!(
19232                editor.display_text(cx),
19233                main_text,
19234                "Original main.rs text on initial open",
19235            );
19236            assert_eq!(
19237                editor
19238                    .selections
19239                    .all::<Point>(cx)
19240                    .into_iter()
19241                    .map(|s| s.range())
19242                    .collect::<Vec<_>>(),
19243                vec![Point::zero()..Point::zero()],
19244                "Default selections on initial open",
19245            );
19246        })
19247    });
19248    editor_1.update_in(cx, |editor, window, cx| {
19249        editor.change_selections(None, window, cx, |s| {
19250            s.select_ranges(expected_ranges.clone());
19251        });
19252    });
19253
19254    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19255        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19256    });
19257    let editor_2 = workspace
19258        .update_in(cx, |workspace, window, cx| {
19259            workspace.open_path(
19260                (worktree_id, "main.rs"),
19261                Some(pane_2.downgrade()),
19262                true,
19263                window,
19264                cx,
19265            )
19266        })
19267        .unwrap()
19268        .await
19269        .downcast::<Editor>()
19270        .unwrap();
19271    pane_2.update(cx, |pane, cx| {
19272        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19273        open_editor.update(cx, |editor, cx| {
19274            assert_eq!(
19275                editor.display_text(cx),
19276                main_text,
19277                "Original main.rs text on initial open in another panel",
19278            );
19279            assert_eq!(
19280                editor
19281                    .selections
19282                    .all::<Point>(cx)
19283                    .into_iter()
19284                    .map(|s| s.range())
19285                    .collect::<Vec<_>>(),
19286                vec![Point::zero()..Point::zero()],
19287                "Default selections on initial open in another panel",
19288            );
19289        })
19290    });
19291
19292    editor_2.update_in(cx, |editor, window, cx| {
19293        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19294    });
19295
19296    let _other_editor_1 = workspace
19297        .update_in(cx, |workspace, window, cx| {
19298            workspace.open_path(
19299                (worktree_id, "lib.rs"),
19300                Some(pane_1.downgrade()),
19301                true,
19302                window,
19303                cx,
19304            )
19305        })
19306        .unwrap()
19307        .await
19308        .downcast::<Editor>()
19309        .unwrap();
19310    pane_1
19311        .update_in(cx, |pane, window, cx| {
19312            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19313                .unwrap()
19314        })
19315        .await
19316        .unwrap();
19317    drop(editor_1);
19318    pane_1.update(cx, |pane, cx| {
19319        pane.active_item()
19320            .unwrap()
19321            .downcast::<Editor>()
19322            .unwrap()
19323            .update(cx, |editor, cx| {
19324                assert_eq!(
19325                    editor.display_text(cx),
19326                    lib_text,
19327                    "Other file should be open and active",
19328                );
19329            });
19330        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19331    });
19332
19333    let _other_editor_2 = workspace
19334        .update_in(cx, |workspace, window, cx| {
19335            workspace.open_path(
19336                (worktree_id, "lib.rs"),
19337                Some(pane_2.downgrade()),
19338                true,
19339                window,
19340                cx,
19341            )
19342        })
19343        .unwrap()
19344        .await
19345        .downcast::<Editor>()
19346        .unwrap();
19347    pane_2
19348        .update_in(cx, |pane, window, cx| {
19349            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19350                .unwrap()
19351        })
19352        .await
19353        .unwrap();
19354    drop(editor_2);
19355    pane_2.update(cx, |pane, cx| {
19356        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19357        open_editor.update(cx, |editor, cx| {
19358            assert_eq!(
19359                editor.display_text(cx),
19360                lib_text,
19361                "Other file should be open and active in another panel too",
19362            );
19363        });
19364        assert_eq!(
19365            pane.items().count(),
19366            1,
19367            "No other editors should be open in another pane",
19368        );
19369    });
19370
19371    let _editor_1_reopened = workspace
19372        .update_in(cx, |workspace, window, cx| {
19373            workspace.open_path(
19374                (worktree_id, "main.rs"),
19375                Some(pane_1.downgrade()),
19376                true,
19377                window,
19378                cx,
19379            )
19380        })
19381        .unwrap()
19382        .await
19383        .downcast::<Editor>()
19384        .unwrap();
19385    let _editor_2_reopened = workspace
19386        .update_in(cx, |workspace, window, cx| {
19387            workspace.open_path(
19388                (worktree_id, "main.rs"),
19389                Some(pane_2.downgrade()),
19390                true,
19391                window,
19392                cx,
19393            )
19394        })
19395        .unwrap()
19396        .await
19397        .downcast::<Editor>()
19398        .unwrap();
19399    pane_1.update(cx, |pane, cx| {
19400        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19401        open_editor.update(cx, |editor, cx| {
19402            assert_eq!(
19403                editor.display_text(cx),
19404                main_text,
19405                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19406            );
19407            assert_eq!(
19408                editor
19409                    .selections
19410                    .all::<Point>(cx)
19411                    .into_iter()
19412                    .map(|s| s.range())
19413                    .collect::<Vec<_>>(),
19414                expected_ranges,
19415                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19416            );
19417        })
19418    });
19419    pane_2.update(cx, |pane, cx| {
19420        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19421        open_editor.update(cx, |editor, cx| {
19422            assert_eq!(
19423                editor.display_text(cx),
19424                r#"fn main() {
19425⋯rintln!("1");
19426⋯intln!("2");
19427⋯ntln!("3");
19428println!("4");
19429println!("5");
19430}"#,
19431                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19432            );
19433            assert_eq!(
19434                editor
19435                    .selections
19436                    .all::<Point>(cx)
19437                    .into_iter()
19438                    .map(|s| s.range())
19439                    .collect::<Vec<_>>(),
19440                vec![Point::zero()..Point::zero()],
19441                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19442            );
19443        })
19444    });
19445}
19446
19447#[gpui::test]
19448async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19449    init_test(cx, |_| {});
19450
19451    let fs = FakeFs::new(cx.executor());
19452    let main_text = r#"fn main() {
19453println!("1");
19454println!("2");
19455println!("3");
19456println!("4");
19457println!("5");
19458}"#;
19459    let lib_text = "mod foo {}";
19460    fs.insert_tree(
19461        path!("/a"),
19462        json!({
19463            "lib.rs": lib_text,
19464            "main.rs": main_text,
19465        }),
19466    )
19467    .await;
19468
19469    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19470    let (workspace, cx) =
19471        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19472    let worktree_id = workspace.update(cx, |workspace, cx| {
19473        workspace.project().update(cx, |project, cx| {
19474            project.worktrees(cx).next().unwrap().read(cx).id()
19475        })
19476    });
19477
19478    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19479    let editor = workspace
19480        .update_in(cx, |workspace, window, cx| {
19481            workspace.open_path(
19482                (worktree_id, "main.rs"),
19483                Some(pane.downgrade()),
19484                true,
19485                window,
19486                cx,
19487            )
19488        })
19489        .unwrap()
19490        .await
19491        .downcast::<Editor>()
19492        .unwrap();
19493    pane.update(cx, |pane, cx| {
19494        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19495        open_editor.update(cx, |editor, cx| {
19496            assert_eq!(
19497                editor.display_text(cx),
19498                main_text,
19499                "Original main.rs text on initial open",
19500            );
19501        })
19502    });
19503    editor.update_in(cx, |editor, window, cx| {
19504        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19505    });
19506
19507    cx.update_global(|store: &mut SettingsStore, cx| {
19508        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19509            s.restore_on_file_reopen = Some(false);
19510        });
19511    });
19512    editor.update_in(cx, |editor, window, cx| {
19513        editor.fold_ranges(
19514            vec![
19515                Point::new(1, 0)..Point::new(1, 1),
19516                Point::new(2, 0)..Point::new(2, 2),
19517                Point::new(3, 0)..Point::new(3, 3),
19518            ],
19519            false,
19520            window,
19521            cx,
19522        );
19523    });
19524    pane.update_in(cx, |pane, window, cx| {
19525        pane.close_all_items(&CloseAllItems::default(), window, cx)
19526            .unwrap()
19527    })
19528    .await
19529    .unwrap();
19530    pane.update(cx, |pane, _| {
19531        assert!(pane.active_item().is_none());
19532    });
19533    cx.update_global(|store: &mut SettingsStore, cx| {
19534        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19535            s.restore_on_file_reopen = Some(true);
19536        });
19537    });
19538
19539    let _editor_reopened = workspace
19540        .update_in(cx, |workspace, window, cx| {
19541            workspace.open_path(
19542                (worktree_id, "main.rs"),
19543                Some(pane.downgrade()),
19544                true,
19545                window,
19546                cx,
19547            )
19548        })
19549        .unwrap()
19550        .await
19551        .downcast::<Editor>()
19552        .unwrap();
19553    pane.update(cx, |pane, cx| {
19554        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19555        open_editor.update(cx, |editor, cx| {
19556            assert_eq!(
19557                editor.display_text(cx),
19558                main_text,
19559                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19560            );
19561        })
19562    });
19563}
19564
19565#[gpui::test]
19566async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19567    struct EmptyModalView {
19568        focus_handle: gpui::FocusHandle,
19569    }
19570    impl EventEmitter<DismissEvent> for EmptyModalView {}
19571    impl Render for EmptyModalView {
19572        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19573            div()
19574        }
19575    }
19576    impl Focusable for EmptyModalView {
19577        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19578            self.focus_handle.clone()
19579        }
19580    }
19581    impl workspace::ModalView for EmptyModalView {}
19582    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19583        EmptyModalView {
19584            focus_handle: cx.focus_handle(),
19585        }
19586    }
19587
19588    init_test(cx, |_| {});
19589
19590    let fs = FakeFs::new(cx.executor());
19591    let project = Project::test(fs, [], cx).await;
19592    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19593    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19594    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19595    let editor = cx.new_window_entity(|window, cx| {
19596        Editor::new(
19597            EditorMode::full(),
19598            buffer,
19599            Some(project.clone()),
19600            window,
19601            cx,
19602        )
19603    });
19604    workspace
19605        .update(cx, |workspace, window, cx| {
19606            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19607        })
19608        .unwrap();
19609    editor.update_in(cx, |editor, window, cx| {
19610        editor.open_context_menu(&OpenContextMenu, window, cx);
19611        assert!(editor.mouse_context_menu.is_some());
19612    });
19613    workspace
19614        .update(cx, |workspace, window, cx| {
19615            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19616        })
19617        .unwrap();
19618    cx.read(|cx| {
19619        assert!(editor.read(cx).mouse_context_menu.is_none());
19620    });
19621}
19622
19623#[gpui::test]
19624async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19625    init_test(cx, |_| {});
19626
19627    let fs = FakeFs::new(cx.executor());
19628    fs.insert_file(path!("/file.html"), Default::default())
19629        .await;
19630
19631    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19632
19633    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19634    let html_language = Arc::new(Language::new(
19635        LanguageConfig {
19636            name: "HTML".into(),
19637            matcher: LanguageMatcher {
19638                path_suffixes: vec!["html".to_string()],
19639                ..LanguageMatcher::default()
19640            },
19641            brackets: BracketPairConfig {
19642                pairs: vec![BracketPair {
19643                    start: "<".into(),
19644                    end: ">".into(),
19645                    close: true,
19646                    ..Default::default()
19647                }],
19648                ..Default::default()
19649            },
19650            ..Default::default()
19651        },
19652        Some(tree_sitter_html::LANGUAGE.into()),
19653    ));
19654    language_registry.add(html_language);
19655    let mut fake_servers = language_registry.register_fake_lsp(
19656        "HTML",
19657        FakeLspAdapter {
19658            capabilities: lsp::ServerCapabilities {
19659                completion_provider: Some(lsp::CompletionOptions {
19660                    resolve_provider: Some(true),
19661                    ..Default::default()
19662                }),
19663                ..Default::default()
19664            },
19665            ..Default::default()
19666        },
19667    );
19668
19669    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19670    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19671
19672    let worktree_id = workspace
19673        .update(cx, |workspace, _window, cx| {
19674            workspace.project().update(cx, |project, cx| {
19675                project.worktrees(cx).next().unwrap().read(cx).id()
19676            })
19677        })
19678        .unwrap();
19679    project
19680        .update(cx, |project, cx| {
19681            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19682        })
19683        .await
19684        .unwrap();
19685    let editor = workspace
19686        .update(cx, |workspace, window, cx| {
19687            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19688        })
19689        .unwrap()
19690        .await
19691        .unwrap()
19692        .downcast::<Editor>()
19693        .unwrap();
19694
19695    let fake_server = fake_servers.next().await.unwrap();
19696    editor.update_in(cx, |editor, window, cx| {
19697        editor.set_text("<ad></ad>", window, cx);
19698        editor.change_selections(None, window, cx, |selections| {
19699            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19700        });
19701        let Some((buffer, _)) = editor
19702            .buffer
19703            .read(cx)
19704            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19705        else {
19706            panic!("Failed to get buffer for selection position");
19707        };
19708        let buffer = buffer.read(cx);
19709        let buffer_id = buffer.remote_id();
19710        let opening_range =
19711            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19712        let closing_range =
19713            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19714        let mut linked_ranges = HashMap::default();
19715        linked_ranges.insert(
19716            buffer_id,
19717            vec![(opening_range.clone(), vec![closing_range.clone()])],
19718        );
19719        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19720    });
19721    let mut completion_handle =
19722        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19723            Ok(Some(lsp::CompletionResponse::Array(vec![
19724                lsp::CompletionItem {
19725                    label: "head".to_string(),
19726                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19727                        lsp::InsertReplaceEdit {
19728                            new_text: "head".to_string(),
19729                            insert: lsp::Range::new(
19730                                lsp::Position::new(0, 1),
19731                                lsp::Position::new(0, 3),
19732                            ),
19733                            replace: lsp::Range::new(
19734                                lsp::Position::new(0, 1),
19735                                lsp::Position::new(0, 3),
19736                            ),
19737                        },
19738                    )),
19739                    ..Default::default()
19740                },
19741            ])))
19742        });
19743    editor.update_in(cx, |editor, window, cx| {
19744        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19745    });
19746    cx.run_until_parked();
19747    completion_handle.next().await.unwrap();
19748    editor.update(cx, |editor, _| {
19749        assert!(
19750            editor.context_menu_visible(),
19751            "Completion menu should be visible"
19752        );
19753    });
19754    editor.update_in(cx, |editor, window, cx| {
19755        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19756    });
19757    cx.executor().run_until_parked();
19758    editor.update(cx, |editor, cx| {
19759        assert_eq!(editor.text(cx), "<head></head>");
19760    });
19761}
19762
19763fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19764    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19765    point..point
19766}
19767
19768fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19769    let (text, ranges) = marked_text_ranges(marked_text, true);
19770    assert_eq!(editor.text(cx), text);
19771    assert_eq!(
19772        editor.selections.ranges(cx),
19773        ranges,
19774        "Assert selections are {}",
19775        marked_text
19776    );
19777}
19778
19779pub fn handle_signature_help_request(
19780    cx: &mut EditorLspTestContext,
19781    mocked_response: lsp::SignatureHelp,
19782) -> impl Future<Output = ()> + use<> {
19783    let mut request =
19784        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19785            let mocked_response = mocked_response.clone();
19786            async move { Ok(Some(mocked_response)) }
19787        });
19788
19789    async move {
19790        request.next().await;
19791    }
19792}
19793
19794/// Handle completion request passing a marked string specifying where the completion
19795/// should be triggered from using '|' character, what range should be replaced, and what completions
19796/// should be returned using '<' and '>' to delimit the range.
19797///
19798/// Also see `handle_completion_request_with_insert_and_replace`.
19799#[track_caller]
19800pub fn handle_completion_request(
19801    cx: &mut EditorLspTestContext,
19802    marked_string: &str,
19803    completions: Vec<&'static str>,
19804    counter: Arc<AtomicUsize>,
19805) -> impl Future<Output = ()> {
19806    let complete_from_marker: TextRangeMarker = '|'.into();
19807    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19808    let (_, mut marked_ranges) = marked_text_ranges_by(
19809        marked_string,
19810        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19811    );
19812
19813    let complete_from_position =
19814        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19815    let replace_range =
19816        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19817
19818    let mut request =
19819        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19820            let completions = completions.clone();
19821            counter.fetch_add(1, atomic::Ordering::Release);
19822            async move {
19823                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19824                assert_eq!(
19825                    params.text_document_position.position,
19826                    complete_from_position
19827                );
19828                Ok(Some(lsp::CompletionResponse::Array(
19829                    completions
19830                        .iter()
19831                        .map(|completion_text| lsp::CompletionItem {
19832                            label: completion_text.to_string(),
19833                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19834                                range: replace_range,
19835                                new_text: completion_text.to_string(),
19836                            })),
19837                            ..Default::default()
19838                        })
19839                        .collect(),
19840                )))
19841            }
19842        });
19843
19844    async move {
19845        request.next().await;
19846    }
19847}
19848
19849/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19850/// given instead, which also contains an `insert` range.
19851///
19852/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19853/// that is, `replace_range.start..cursor_pos`.
19854pub fn handle_completion_request_with_insert_and_replace(
19855    cx: &mut EditorLspTestContext,
19856    marked_string: &str,
19857    completions: Vec<&'static str>,
19858    counter: Arc<AtomicUsize>,
19859) -> impl Future<Output = ()> {
19860    let complete_from_marker: TextRangeMarker = '|'.into();
19861    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19862    let (_, mut marked_ranges) = marked_text_ranges_by(
19863        marked_string,
19864        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19865    );
19866
19867    let complete_from_position =
19868        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19869    let replace_range =
19870        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19871
19872    let mut request =
19873        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19874            let completions = completions.clone();
19875            counter.fetch_add(1, atomic::Ordering::Release);
19876            async move {
19877                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19878                assert_eq!(
19879                    params.text_document_position.position, complete_from_position,
19880                    "marker `|` position doesn't match",
19881                );
19882                Ok(Some(lsp::CompletionResponse::Array(
19883                    completions
19884                        .iter()
19885                        .map(|completion_text| lsp::CompletionItem {
19886                            label: completion_text.to_string(),
19887                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19888                                lsp::InsertReplaceEdit {
19889                                    insert: lsp::Range {
19890                                        start: replace_range.start,
19891                                        end: complete_from_position,
19892                                    },
19893                                    replace: replace_range,
19894                                    new_text: completion_text.to_string(),
19895                                },
19896                            )),
19897                            ..Default::default()
19898                        })
19899                        .collect(),
19900                )))
19901            }
19902        });
19903
19904    async move {
19905        request.next().await;
19906    }
19907}
19908
19909fn handle_resolve_completion_request(
19910    cx: &mut EditorLspTestContext,
19911    edits: Option<Vec<(&'static str, &'static str)>>,
19912) -> impl Future<Output = ()> {
19913    let edits = edits.map(|edits| {
19914        edits
19915            .iter()
19916            .map(|(marked_string, new_text)| {
19917                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19918                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19919                lsp::TextEdit::new(replace_range, new_text.to_string())
19920            })
19921            .collect::<Vec<_>>()
19922    });
19923
19924    let mut request =
19925        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19926            let edits = edits.clone();
19927            async move {
19928                Ok(lsp::CompletionItem {
19929                    additional_text_edits: edits,
19930                    ..Default::default()
19931                })
19932            }
19933        });
19934
19935    async move {
19936        request.next().await;
19937    }
19938}
19939
19940pub(crate) fn update_test_language_settings(
19941    cx: &mut TestAppContext,
19942    f: impl Fn(&mut AllLanguageSettingsContent),
19943) {
19944    cx.update(|cx| {
19945        SettingsStore::update_global(cx, |store, cx| {
19946            store.update_user_settings::<AllLanguageSettings>(cx, f);
19947        });
19948    });
19949}
19950
19951pub(crate) fn update_test_project_settings(
19952    cx: &mut TestAppContext,
19953    f: impl Fn(&mut ProjectSettings),
19954) {
19955    cx.update(|cx| {
19956        SettingsStore::update_global(cx, |store, cx| {
19957            store.update_user_settings::<ProjectSettings>(cx, f);
19958        });
19959    });
19960}
19961
19962pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19963    cx.update(|cx| {
19964        assets::Assets.load_test_fonts(cx);
19965        let store = SettingsStore::test(cx);
19966        cx.set_global(store);
19967        theme::init(theme::LoadThemes::JustBase, cx);
19968        release_channel::init(SemanticVersion::default(), cx);
19969        client::init_settings(cx);
19970        language::init(cx);
19971        Project::init_settings(cx);
19972        workspace::init_settings(cx);
19973        crate::init(cx);
19974    });
19975
19976    update_test_language_settings(cx, f);
19977}
19978
19979#[track_caller]
19980fn assert_hunk_revert(
19981    not_reverted_text_with_selections: &str,
19982    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19983    expected_reverted_text_with_selections: &str,
19984    base_text: &str,
19985    cx: &mut EditorLspTestContext,
19986) {
19987    cx.set_state(not_reverted_text_with_selections);
19988    cx.set_head_text(base_text);
19989    cx.executor().run_until_parked();
19990
19991    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19992        let snapshot = editor.snapshot(window, cx);
19993        let reverted_hunk_statuses = snapshot
19994            .buffer_snapshot
19995            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19996            .map(|hunk| hunk.status().kind)
19997            .collect::<Vec<_>>();
19998
19999        editor.git_restore(&Default::default(), window, cx);
20000        reverted_hunk_statuses
20001    });
20002    cx.executor().run_until_parked();
20003    cx.assert_editor_state(expected_reverted_text_with_selections);
20004    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20005}