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    CloseActiveItem, CloseAllItems, CloseInactiveItems, NavigationEntry, OpenOptions, 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]
 2801async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 2802    init_test(cx, |settings| {
 2803        settings.defaults.tab_size = NonZeroU32::new(4)
 2804    });
 2805
 2806    let language = Arc::new(Language::new(
 2807        LanguageConfig {
 2808            documentation_block: Some(vec!["/**".into(), "*/".into()]),
 2809            documentation_comment_prefix: Some("*".into()),
 2810            ..LanguageConfig::default()
 2811        },
 2812        None,
 2813    ));
 2814    {
 2815        let mut cx = EditorTestContext::new(cx).await;
 2816        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2817        cx.set_state(indoc! {"
 2818        /**ˇ
 2819    "});
 2820
 2821        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2822        cx.assert_editor_state(indoc! {"
 2823        /**
 2824 2825    "});
 2826        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2827        cx.set_state(indoc! {"
 2828        ˇ/**
 2829    "});
 2830        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2831        cx.assert_editor_state(indoc! {"
 2832
 2833        ˇ/**
 2834    "});
 2835        // Ensure that if cursor is between it doesn't add comment prefix.
 2836        cx.set_state(indoc! {"
 2837        /*ˇ*
 2838    "});
 2839        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2840        cx.assert_editor_state(indoc! {"
 2841        /*
 2842        ˇ*
 2843    "});
 2844        // Ensure that if suffix exists on same line after cursor it adds new line.
 2845        cx.set_state(indoc! {"
 2846        /**ˇ*/
 2847    "});
 2848        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2849        cx.assert_editor_state(indoc! {"
 2850        /**
 2851 2852        */
 2853    "});
 2854        // Ensure that it detects suffix after existing prefix.
 2855        cx.set_state(indoc! {"
 2856        /**ˇ/
 2857    "});
 2858        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2859        cx.assert_editor_state(indoc! {"
 2860        /**
 2861        ˇ/
 2862    "});
 2863        // Ensure that if suffix exists on same line before cursor it does not add comment prefix.
 2864        cx.set_state(indoc! {"
 2865        /** */ˇ
 2866    "});
 2867        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2868        cx.assert_editor_state(indoc! {"
 2869        /** */
 2870        ˇ
 2871    "});
 2872        // Ensure that if suffix exists on same line before cursor it does not add comment prefix.
 2873        cx.set_state(indoc! {"
 2874        /**
 2875        *
 2876        */ˇ
 2877    "});
 2878        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2879        cx.assert_editor_state(indoc! {"
 2880        /**
 2881        *
 2882        */
 2883        ˇ
 2884    "});
 2885    }
 2886    // Ensure that comment continuations can be disabled.
 2887    update_test_language_settings(cx, |settings| {
 2888        settings.defaults.extend_comment_on_newline = Some(false);
 2889    });
 2890    let mut cx = EditorTestContext::new(cx).await;
 2891    cx.set_state(indoc! {"
 2892        /**ˇ
 2893    "});
 2894    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2895    cx.assert_editor_state(indoc! {"
 2896        /**
 2897        ˇ
 2898    "});
 2899}
 2900
 2901#[gpui::test]
 2902fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2903    init_test(cx, |_| {});
 2904
 2905    let editor = cx.add_window(|window, cx| {
 2906        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2907        let mut editor = build_editor(buffer.clone(), window, cx);
 2908        editor.change_selections(None, window, cx, |s| {
 2909            s.select_ranges([3..4, 11..12, 19..20])
 2910        });
 2911        editor
 2912    });
 2913
 2914    _ = editor.update(cx, |editor, window, cx| {
 2915        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2916        editor.buffer.update(cx, |buffer, cx| {
 2917            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2918            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2919        });
 2920        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2921
 2922        editor.insert("Z", window, cx);
 2923        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2924
 2925        // The selections are moved after the inserted characters
 2926        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2927    });
 2928}
 2929
 2930#[gpui::test]
 2931async fn test_tab(cx: &mut TestAppContext) {
 2932    init_test(cx, |settings| {
 2933        settings.defaults.tab_size = NonZeroU32::new(3)
 2934    });
 2935
 2936    let mut cx = EditorTestContext::new(cx).await;
 2937    cx.set_state(indoc! {"
 2938        ˇabˇc
 2939        ˇ🏀ˇ🏀ˇefg
 2940 2941    "});
 2942    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2943    cx.assert_editor_state(indoc! {"
 2944           ˇab ˇc
 2945           ˇ🏀  ˇ🏀  ˇefg
 2946        d  ˇ
 2947    "});
 2948
 2949    cx.set_state(indoc! {"
 2950        a
 2951        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2952    "});
 2953    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2954    cx.assert_editor_state(indoc! {"
 2955        a
 2956           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2957    "});
 2958}
 2959
 2960#[gpui::test]
 2961async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2962    init_test(cx, |_| {});
 2963
 2964    let mut cx = EditorTestContext::new(cx).await;
 2965    let language = Arc::new(
 2966        Language::new(
 2967            LanguageConfig::default(),
 2968            Some(tree_sitter_rust::LANGUAGE.into()),
 2969        )
 2970        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2971        .unwrap(),
 2972    );
 2973    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2974
 2975    // test when all cursors are not at suggested indent
 2976    // then simply move to their suggested indent location
 2977    cx.set_state(indoc! {"
 2978        const a: B = (
 2979            c(
 2980        ˇ
 2981        ˇ    )
 2982        );
 2983    "});
 2984    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2985    cx.assert_editor_state(indoc! {"
 2986        const a: B = (
 2987            c(
 2988                ˇ
 2989            ˇ)
 2990        );
 2991    "});
 2992
 2993    // test cursor already at suggested indent not moving when
 2994    // other cursors are yet to reach their suggested indents
 2995    cx.set_state(indoc! {"
 2996        ˇ
 2997        const a: B = (
 2998            c(
 2999                d(
 3000        ˇ
 3001                )
 3002        ˇ
 3003        ˇ    )
 3004        );
 3005    "});
 3006    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3007    cx.assert_editor_state(indoc! {"
 3008        ˇ
 3009        const a: B = (
 3010            c(
 3011                d(
 3012                    ˇ
 3013                )
 3014                ˇ
 3015            ˇ)
 3016        );
 3017    "});
 3018    // test when all cursors are at suggested indent then tab is inserted
 3019    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3020    cx.assert_editor_state(indoc! {"
 3021            ˇ
 3022        const a: B = (
 3023            c(
 3024                d(
 3025                        ˇ
 3026                )
 3027                    ˇ
 3028                ˇ)
 3029        );
 3030    "});
 3031
 3032    // test when current indent is less than suggested indent,
 3033    // we adjust line to match suggested indent and move cursor to it
 3034    //
 3035    // when no other cursor is at word boundary, all of them should move
 3036    cx.set_state(indoc! {"
 3037        const a: B = (
 3038            c(
 3039                d(
 3040        ˇ
 3041        ˇ   )
 3042        ˇ   )
 3043        );
 3044    "});
 3045    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3046    cx.assert_editor_state(indoc! {"
 3047        const a: B = (
 3048            c(
 3049                d(
 3050                    ˇ
 3051                ˇ)
 3052            ˇ)
 3053        );
 3054    "});
 3055
 3056    // test when current indent is less than suggested indent,
 3057    // we adjust line to match suggested indent and move cursor to it
 3058    //
 3059    // when some other cursor is at word boundary, it should not move
 3060    cx.set_state(indoc! {"
 3061        const a: B = (
 3062            c(
 3063                d(
 3064        ˇ
 3065        ˇ   )
 3066           ˇ)
 3067        );
 3068    "});
 3069    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3070    cx.assert_editor_state(indoc! {"
 3071        const a: B = (
 3072            c(
 3073                d(
 3074                    ˇ
 3075                ˇ)
 3076            ˇ)
 3077        );
 3078    "});
 3079
 3080    // test when current indent is more than suggested indent,
 3081    // we just move cursor to current indent instead of suggested indent
 3082    //
 3083    // when no other cursor is at word boundary, all of them should move
 3084    cx.set_state(indoc! {"
 3085        const a: B = (
 3086            c(
 3087                d(
 3088        ˇ
 3089        ˇ                )
 3090        ˇ   )
 3091        );
 3092    "});
 3093    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3094    cx.assert_editor_state(indoc! {"
 3095        const a: B = (
 3096            c(
 3097                d(
 3098                    ˇ
 3099                        ˇ)
 3100            ˇ)
 3101        );
 3102    "});
 3103    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3104    cx.assert_editor_state(indoc! {"
 3105        const a: B = (
 3106            c(
 3107                d(
 3108                        ˇ
 3109                            ˇ)
 3110                ˇ)
 3111        );
 3112    "});
 3113
 3114    // test when current indent is more than suggested indent,
 3115    // we just move cursor to current indent instead of suggested indent
 3116    //
 3117    // when some other cursor is at word boundary, it doesn't move
 3118    cx.set_state(indoc! {"
 3119        const a: B = (
 3120            c(
 3121                d(
 3122        ˇ
 3123        ˇ                )
 3124            ˇ)
 3125        );
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        const a: B = (
 3130            c(
 3131                d(
 3132                    ˇ
 3133                        ˇ)
 3134            ˇ)
 3135        );
 3136    "});
 3137
 3138    // handle auto-indent when there are multiple cursors on the same line
 3139    cx.set_state(indoc! {"
 3140        const a: B = (
 3141            c(
 3142        ˇ    ˇ
 3143        ˇ    )
 3144        );
 3145    "});
 3146    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3147    cx.assert_editor_state(indoc! {"
 3148        const a: B = (
 3149            c(
 3150                ˇ
 3151            ˇ)
 3152        );
 3153    "});
 3154}
 3155
 3156#[gpui::test]
 3157async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 3158    init_test(cx, |settings| {
 3159        settings.defaults.tab_size = NonZeroU32::new(3)
 3160    });
 3161
 3162    let mut cx = EditorTestContext::new(cx).await;
 3163    cx.set_state(indoc! {"
 3164         ˇ
 3165        \t ˇ
 3166        \t  ˇ
 3167        \t   ˇ
 3168         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 3169    "});
 3170
 3171    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3172    cx.assert_editor_state(indoc! {"
 3173           ˇ
 3174        \t   ˇ
 3175        \t   ˇ
 3176        \t      ˇ
 3177         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 3178    "});
 3179}
 3180
 3181#[gpui::test]
 3182async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 3183    init_test(cx, |settings| {
 3184        settings.defaults.tab_size = NonZeroU32::new(4)
 3185    });
 3186
 3187    let language = Arc::new(
 3188        Language::new(
 3189            LanguageConfig::default(),
 3190            Some(tree_sitter_rust::LANGUAGE.into()),
 3191        )
 3192        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 3193        .unwrap(),
 3194    );
 3195
 3196    let mut cx = EditorTestContext::new(cx).await;
 3197    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 3198    cx.set_state(indoc! {"
 3199        fn a() {
 3200            if b {
 3201        \t ˇc
 3202            }
 3203        }
 3204    "});
 3205
 3206    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3207    cx.assert_editor_state(indoc! {"
 3208        fn a() {
 3209            if b {
 3210                ˇc
 3211            }
 3212        }
 3213    "});
 3214}
 3215
 3216#[gpui::test]
 3217async fn test_indent_outdent(cx: &mut TestAppContext) {
 3218    init_test(cx, |settings| {
 3219        settings.defaults.tab_size = NonZeroU32::new(4);
 3220    });
 3221
 3222    let mut cx = EditorTestContext::new(cx).await;
 3223
 3224    cx.set_state(indoc! {"
 3225          «oneˇ» «twoˇ»
 3226        three
 3227         four
 3228    "});
 3229    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3230    cx.assert_editor_state(indoc! {"
 3231            «oneˇ» «twoˇ»
 3232        three
 3233         four
 3234    "});
 3235
 3236    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3237    cx.assert_editor_state(indoc! {"
 3238        «oneˇ» «twoˇ»
 3239        three
 3240         four
 3241    "});
 3242
 3243    // select across line ending
 3244    cx.set_state(indoc! {"
 3245        one two
 3246        t«hree
 3247        ˇ» four
 3248    "});
 3249    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3250    cx.assert_editor_state(indoc! {"
 3251        one two
 3252            t«hree
 3253        ˇ» four
 3254    "});
 3255
 3256    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3257    cx.assert_editor_state(indoc! {"
 3258        one two
 3259        t«hree
 3260        ˇ» four
 3261    "});
 3262
 3263    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3264    cx.set_state(indoc! {"
 3265        one two
 3266        ˇthree
 3267            four
 3268    "});
 3269    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3270    cx.assert_editor_state(indoc! {"
 3271        one two
 3272            ˇthree
 3273            four
 3274    "});
 3275
 3276    cx.set_state(indoc! {"
 3277        one two
 3278        ˇ    three
 3279            four
 3280    "});
 3281    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3282    cx.assert_editor_state(indoc! {"
 3283        one two
 3284        ˇthree
 3285            four
 3286    "});
 3287}
 3288
 3289#[gpui::test]
 3290async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3291    init_test(cx, |settings| {
 3292        settings.defaults.hard_tabs = Some(true);
 3293    });
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296
 3297    // select two ranges on one line
 3298    cx.set_state(indoc! {"
 3299        «oneˇ» «twoˇ»
 3300        three
 3301        four
 3302    "});
 3303    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3304    cx.assert_editor_state(indoc! {"
 3305        \t«oneˇ» «twoˇ»
 3306        three
 3307        four
 3308    "});
 3309    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3310    cx.assert_editor_state(indoc! {"
 3311        \t\t«oneˇ» «twoˇ»
 3312        three
 3313        four
 3314    "});
 3315    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3316    cx.assert_editor_state(indoc! {"
 3317        \t«oneˇ» «twoˇ»
 3318        three
 3319        four
 3320    "});
 3321    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3322    cx.assert_editor_state(indoc! {"
 3323        «oneˇ» «twoˇ»
 3324        three
 3325        four
 3326    "});
 3327
 3328    // select across a line ending
 3329    cx.set_state(indoc! {"
 3330        one two
 3331        t«hree
 3332        ˇ»four
 3333    "});
 3334    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3335    cx.assert_editor_state(indoc! {"
 3336        one two
 3337        \tt«hree
 3338        ˇ»four
 3339    "});
 3340    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3341    cx.assert_editor_state(indoc! {"
 3342        one two
 3343        \t\tt«hree
 3344        ˇ»four
 3345    "});
 3346    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3347    cx.assert_editor_state(indoc! {"
 3348        one two
 3349        \tt«hree
 3350        ˇ»four
 3351    "});
 3352    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3353    cx.assert_editor_state(indoc! {"
 3354        one two
 3355        t«hree
 3356        ˇ»four
 3357    "});
 3358
 3359    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3360    cx.set_state(indoc! {"
 3361        one two
 3362        ˇthree
 3363        four
 3364    "});
 3365    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3366    cx.assert_editor_state(indoc! {"
 3367        one two
 3368        ˇthree
 3369        four
 3370    "});
 3371    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3372    cx.assert_editor_state(indoc! {"
 3373        one two
 3374        \tˇthree
 3375        four
 3376    "});
 3377    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3378    cx.assert_editor_state(indoc! {"
 3379        one two
 3380        ˇthree
 3381        four
 3382    "});
 3383}
 3384
 3385#[gpui::test]
 3386fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3387    init_test(cx, |settings| {
 3388        settings.languages.extend([
 3389            (
 3390                "TOML".into(),
 3391                LanguageSettingsContent {
 3392                    tab_size: NonZeroU32::new(2),
 3393                    ..Default::default()
 3394                },
 3395            ),
 3396            (
 3397                "Rust".into(),
 3398                LanguageSettingsContent {
 3399                    tab_size: NonZeroU32::new(4),
 3400                    ..Default::default()
 3401                },
 3402            ),
 3403        ]);
 3404    });
 3405
 3406    let toml_language = Arc::new(Language::new(
 3407        LanguageConfig {
 3408            name: "TOML".into(),
 3409            ..Default::default()
 3410        },
 3411        None,
 3412    ));
 3413    let rust_language = Arc::new(Language::new(
 3414        LanguageConfig {
 3415            name: "Rust".into(),
 3416            ..Default::default()
 3417        },
 3418        None,
 3419    ));
 3420
 3421    let toml_buffer =
 3422        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3423    let rust_buffer =
 3424        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3425    let multibuffer = cx.new(|cx| {
 3426        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3427        multibuffer.push_excerpts(
 3428            toml_buffer.clone(),
 3429            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3430            cx,
 3431        );
 3432        multibuffer.push_excerpts(
 3433            rust_buffer.clone(),
 3434            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3435            cx,
 3436        );
 3437        multibuffer
 3438    });
 3439
 3440    cx.add_window(|window, cx| {
 3441        let mut editor = build_editor(multibuffer, window, cx);
 3442
 3443        assert_eq!(
 3444            editor.text(cx),
 3445            indoc! {"
 3446                a = 1
 3447                b = 2
 3448
 3449                const c: usize = 3;
 3450            "}
 3451        );
 3452
 3453        select_ranges(
 3454            &mut editor,
 3455            indoc! {"
 3456                «aˇ» = 1
 3457                b = 2
 3458
 3459                «const c:ˇ» usize = 3;
 3460            "},
 3461            window,
 3462            cx,
 3463        );
 3464
 3465        editor.tab(&Tab, window, cx);
 3466        assert_text_with_selections(
 3467            &mut editor,
 3468            indoc! {"
 3469                  «aˇ» = 1
 3470                b = 2
 3471
 3472                    «const c:ˇ» usize = 3;
 3473            "},
 3474            cx,
 3475        );
 3476        editor.backtab(&Backtab, window, cx);
 3477        assert_text_with_selections(
 3478            &mut editor,
 3479            indoc! {"
 3480                «aˇ» = 1
 3481                b = 2
 3482
 3483                «const c:ˇ» usize = 3;
 3484            "},
 3485            cx,
 3486        );
 3487
 3488        editor
 3489    });
 3490}
 3491
 3492#[gpui::test]
 3493async fn test_backspace(cx: &mut TestAppContext) {
 3494    init_test(cx, |_| {});
 3495
 3496    let mut cx = EditorTestContext::new(cx).await;
 3497
 3498    // Basic backspace
 3499    cx.set_state(indoc! {"
 3500        onˇe two three
 3501        fou«rˇ» five six
 3502        seven «ˇeight nine
 3503        »ten
 3504    "});
 3505    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3506    cx.assert_editor_state(indoc! {"
 3507        oˇe two three
 3508        fouˇ five six
 3509        seven ˇten
 3510    "});
 3511
 3512    // Test backspace inside and around indents
 3513    cx.set_state(indoc! {"
 3514        zero
 3515            ˇone
 3516                ˇtwo
 3517            ˇ ˇ ˇ  three
 3518        ˇ  ˇ  four
 3519    "});
 3520    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3521    cx.assert_editor_state(indoc! {"
 3522        zero
 3523        ˇone
 3524            ˇtwo
 3525        ˇ  threeˇ  four
 3526    "});
 3527}
 3528
 3529#[gpui::test]
 3530async fn test_delete(cx: &mut TestAppContext) {
 3531    init_test(cx, |_| {});
 3532
 3533    let mut cx = EditorTestContext::new(cx).await;
 3534    cx.set_state(indoc! {"
 3535        onˇe two three
 3536        fou«rˇ» five six
 3537        seven «ˇeight nine
 3538        »ten
 3539    "});
 3540    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3541    cx.assert_editor_state(indoc! {"
 3542        onˇ two three
 3543        fouˇ five six
 3544        seven ˇten
 3545    "});
 3546}
 3547
 3548#[gpui::test]
 3549fn test_delete_line(cx: &mut TestAppContext) {
 3550    init_test(cx, |_| {});
 3551
 3552    let editor = cx.add_window(|window, cx| {
 3553        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3554        build_editor(buffer, window, cx)
 3555    });
 3556    _ = editor.update(cx, |editor, window, cx| {
 3557        editor.change_selections(None, window, cx, |s| {
 3558            s.select_display_ranges([
 3559                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3561                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3562            ])
 3563        });
 3564        editor.delete_line(&DeleteLine, window, cx);
 3565        assert_eq!(editor.display_text(cx), "ghi");
 3566        assert_eq!(
 3567            editor.selections.display_ranges(cx),
 3568            vec![
 3569                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3570                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3571            ]
 3572        );
 3573    });
 3574
 3575    let editor = cx.add_window(|window, cx| {
 3576        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3577        build_editor(buffer, window, cx)
 3578    });
 3579    _ = editor.update(cx, |editor, window, cx| {
 3580        editor.change_selections(None, window, cx, |s| {
 3581            s.select_display_ranges([
 3582                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3583            ])
 3584        });
 3585        editor.delete_line(&DeleteLine, window, cx);
 3586        assert_eq!(editor.display_text(cx), "ghi\n");
 3587        assert_eq!(
 3588            editor.selections.display_ranges(cx),
 3589            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3590        );
 3591    });
 3592}
 3593
 3594#[gpui::test]
 3595fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3596    init_test(cx, |_| {});
 3597
 3598    cx.add_window(|window, cx| {
 3599        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3600        let mut editor = build_editor(buffer.clone(), window, cx);
 3601        let buffer = buffer.read(cx).as_singleton().unwrap();
 3602
 3603        assert_eq!(
 3604            editor.selections.ranges::<Point>(cx),
 3605            &[Point::new(0, 0)..Point::new(0, 0)]
 3606        );
 3607
 3608        // When on single line, replace newline at end by space
 3609        editor.join_lines(&JoinLines, window, cx);
 3610        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3611        assert_eq!(
 3612            editor.selections.ranges::<Point>(cx),
 3613            &[Point::new(0, 3)..Point::new(0, 3)]
 3614        );
 3615
 3616        // When multiple lines are selected, remove newlines that are spanned by the selection
 3617        editor.change_selections(None, window, cx, |s| {
 3618            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3619        });
 3620        editor.join_lines(&JoinLines, window, cx);
 3621        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3622        assert_eq!(
 3623            editor.selections.ranges::<Point>(cx),
 3624            &[Point::new(0, 11)..Point::new(0, 11)]
 3625        );
 3626
 3627        // Undo should be transactional
 3628        editor.undo(&Undo, window, cx);
 3629        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3630        assert_eq!(
 3631            editor.selections.ranges::<Point>(cx),
 3632            &[Point::new(0, 5)..Point::new(2, 2)]
 3633        );
 3634
 3635        // When joining an empty line don't insert a space
 3636        editor.change_selections(None, window, cx, |s| {
 3637            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3638        });
 3639        editor.join_lines(&JoinLines, window, cx);
 3640        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3641        assert_eq!(
 3642            editor.selections.ranges::<Point>(cx),
 3643            [Point::new(2, 3)..Point::new(2, 3)]
 3644        );
 3645
 3646        // We can remove trailing newlines
 3647        editor.join_lines(&JoinLines, window, cx);
 3648        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3649        assert_eq!(
 3650            editor.selections.ranges::<Point>(cx),
 3651            [Point::new(2, 3)..Point::new(2, 3)]
 3652        );
 3653
 3654        // We don't blow up on the last line
 3655        editor.join_lines(&JoinLines, window, cx);
 3656        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3657        assert_eq!(
 3658            editor.selections.ranges::<Point>(cx),
 3659            [Point::new(2, 3)..Point::new(2, 3)]
 3660        );
 3661
 3662        // reset to test indentation
 3663        editor.buffer.update(cx, |buffer, cx| {
 3664            buffer.edit(
 3665                [
 3666                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3667                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3668                ],
 3669                None,
 3670                cx,
 3671            )
 3672        });
 3673
 3674        // We remove any leading spaces
 3675        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3676        editor.change_selections(None, window, cx, |s| {
 3677            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3678        });
 3679        editor.join_lines(&JoinLines, window, cx);
 3680        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3681
 3682        // We don't insert a space for a line containing only spaces
 3683        editor.join_lines(&JoinLines, window, cx);
 3684        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3685
 3686        // We ignore any leading tabs
 3687        editor.join_lines(&JoinLines, window, cx);
 3688        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3689
 3690        editor
 3691    });
 3692}
 3693
 3694#[gpui::test]
 3695fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3696    init_test(cx, |_| {});
 3697
 3698    cx.add_window(|window, cx| {
 3699        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3700        let mut editor = build_editor(buffer.clone(), window, cx);
 3701        let buffer = buffer.read(cx).as_singleton().unwrap();
 3702
 3703        editor.change_selections(None, window, cx, |s| {
 3704            s.select_ranges([
 3705                Point::new(0, 2)..Point::new(1, 1),
 3706                Point::new(1, 2)..Point::new(1, 2),
 3707                Point::new(3, 1)..Point::new(3, 2),
 3708            ])
 3709        });
 3710
 3711        editor.join_lines(&JoinLines, window, cx);
 3712        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3713
 3714        assert_eq!(
 3715            editor.selections.ranges::<Point>(cx),
 3716            [
 3717                Point::new(0, 7)..Point::new(0, 7),
 3718                Point::new(1, 3)..Point::new(1, 3)
 3719            ]
 3720        );
 3721        editor
 3722    });
 3723}
 3724
 3725#[gpui::test]
 3726async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3727    init_test(cx, |_| {});
 3728
 3729    let mut cx = EditorTestContext::new(cx).await;
 3730
 3731    let diff_base = r#"
 3732        Line 0
 3733        Line 1
 3734        Line 2
 3735        Line 3
 3736        "#
 3737    .unindent();
 3738
 3739    cx.set_state(
 3740        &r#"
 3741        ˇLine 0
 3742        Line 1
 3743        Line 2
 3744        Line 3
 3745        "#
 3746        .unindent(),
 3747    );
 3748
 3749    cx.set_head_text(&diff_base);
 3750    executor.run_until_parked();
 3751
 3752    // Join lines
 3753    cx.update_editor(|editor, window, cx| {
 3754        editor.join_lines(&JoinLines, window, cx);
 3755    });
 3756    executor.run_until_parked();
 3757
 3758    cx.assert_editor_state(
 3759        &r#"
 3760        Line 0ˇ Line 1
 3761        Line 2
 3762        Line 3
 3763        "#
 3764        .unindent(),
 3765    );
 3766    // Join again
 3767    cx.update_editor(|editor, window, cx| {
 3768        editor.join_lines(&JoinLines, window, cx);
 3769    });
 3770    executor.run_until_parked();
 3771
 3772    cx.assert_editor_state(
 3773        &r#"
 3774        Line 0 Line 1ˇ Line 2
 3775        Line 3
 3776        "#
 3777        .unindent(),
 3778    );
 3779}
 3780
 3781#[gpui::test]
 3782async fn test_custom_newlines_cause_no_false_positive_diffs(
 3783    executor: BackgroundExecutor,
 3784    cx: &mut TestAppContext,
 3785) {
 3786    init_test(cx, |_| {});
 3787    let mut cx = EditorTestContext::new(cx).await;
 3788    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3789    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3790    executor.run_until_parked();
 3791
 3792    cx.update_editor(|editor, window, cx| {
 3793        let snapshot = editor.snapshot(window, cx);
 3794        assert_eq!(
 3795            snapshot
 3796                .buffer_snapshot
 3797                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3798                .collect::<Vec<_>>(),
 3799            Vec::new(),
 3800            "Should not have any diffs for files with custom newlines"
 3801        );
 3802    });
 3803}
 3804
 3805#[gpui::test]
 3806async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3807    init_test(cx, |_| {});
 3808
 3809    let mut cx = EditorTestContext::new(cx).await;
 3810
 3811    // Test sort_lines_case_insensitive()
 3812    cx.set_state(indoc! {"
 3813        «z
 3814        y
 3815        x
 3816        Z
 3817        Y
 3818        Xˇ»
 3819    "});
 3820    cx.update_editor(|e, window, cx| {
 3821        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3822    });
 3823    cx.assert_editor_state(indoc! {"
 3824        «x
 3825        X
 3826        y
 3827        Y
 3828        z
 3829        Zˇ»
 3830    "});
 3831
 3832    // Test reverse_lines()
 3833    cx.set_state(indoc! {"
 3834        «5
 3835        4
 3836        3
 3837        2
 3838        1ˇ»
 3839    "});
 3840    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3841    cx.assert_editor_state(indoc! {"
 3842        «1
 3843        2
 3844        3
 3845        4
 3846        5ˇ»
 3847    "});
 3848
 3849    // Skip testing shuffle_line()
 3850
 3851    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3852    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3853
 3854    // Don't manipulate when cursor is on single line, but expand the selection
 3855    cx.set_state(indoc! {"
 3856        ddˇdd
 3857        ccc
 3858        bb
 3859        a
 3860    "});
 3861    cx.update_editor(|e, window, cx| {
 3862        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3863    });
 3864    cx.assert_editor_state(indoc! {"
 3865        «ddddˇ»
 3866        ccc
 3867        bb
 3868        a
 3869    "});
 3870
 3871    // Basic manipulate case
 3872    // Start selection moves to column 0
 3873    // End of selection shrinks to fit shorter line
 3874    cx.set_state(indoc! {"
 3875        dd«d
 3876        ccc
 3877        bb
 3878        aaaaaˇ»
 3879    "});
 3880    cx.update_editor(|e, window, cx| {
 3881        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3882    });
 3883    cx.assert_editor_state(indoc! {"
 3884        «aaaaa
 3885        bb
 3886        ccc
 3887        dddˇ»
 3888    "});
 3889
 3890    // Manipulate case with newlines
 3891    cx.set_state(indoc! {"
 3892        dd«d
 3893        ccc
 3894
 3895        bb
 3896        aaaaa
 3897
 3898        ˇ»
 3899    "});
 3900    cx.update_editor(|e, window, cx| {
 3901        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3902    });
 3903    cx.assert_editor_state(indoc! {"
 3904        «
 3905
 3906        aaaaa
 3907        bb
 3908        ccc
 3909        dddˇ»
 3910
 3911    "});
 3912
 3913    // Adding new line
 3914    cx.set_state(indoc! {"
 3915        aa«a
 3916        bbˇ»b
 3917    "});
 3918    cx.update_editor(|e, window, cx| {
 3919        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3920    });
 3921    cx.assert_editor_state(indoc! {"
 3922        «aaa
 3923        bbb
 3924        added_lineˇ»
 3925    "});
 3926
 3927    // Removing line
 3928    cx.set_state(indoc! {"
 3929        aa«a
 3930        bbbˇ»
 3931    "});
 3932    cx.update_editor(|e, window, cx| {
 3933        e.manipulate_lines(window, cx, |lines| {
 3934            lines.pop();
 3935        })
 3936    });
 3937    cx.assert_editor_state(indoc! {"
 3938        «aaaˇ»
 3939    "});
 3940
 3941    // Removing all lines
 3942    cx.set_state(indoc! {"
 3943        aa«a
 3944        bbbˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| {
 3947        e.manipulate_lines(window, cx, |lines| {
 3948            lines.drain(..);
 3949        })
 3950    });
 3951    cx.assert_editor_state(indoc! {"
 3952        ˇ
 3953    "});
 3954}
 3955
 3956#[gpui::test]
 3957async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3958    init_test(cx, |_| {});
 3959
 3960    let mut cx = EditorTestContext::new(cx).await;
 3961
 3962    // Consider continuous selection as single selection
 3963    cx.set_state(indoc! {"
 3964        Aaa«aa
 3965        cˇ»c«c
 3966        bb
 3967        aaaˇ»aa
 3968    "});
 3969    cx.update_editor(|e, window, cx| {
 3970        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3971    });
 3972    cx.assert_editor_state(indoc! {"
 3973        «Aaaaa
 3974        ccc
 3975        bb
 3976        aaaaaˇ»
 3977    "});
 3978
 3979    cx.set_state(indoc! {"
 3980        Aaa«aa
 3981        cˇ»c«c
 3982        bb
 3983        aaaˇ»aa
 3984    "});
 3985    cx.update_editor(|e, window, cx| {
 3986        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3987    });
 3988    cx.assert_editor_state(indoc! {"
 3989        «Aaaaa
 3990        ccc
 3991        bbˇ»
 3992    "});
 3993
 3994    // Consider non continuous selection as distinct dedup operations
 3995    cx.set_state(indoc! {"
 3996        «aaaaa
 3997        bb
 3998        aaaaa
 3999        aaaaaˇ»
 4000
 4001        aaa«aaˇ»
 4002    "});
 4003    cx.update_editor(|e, window, cx| {
 4004        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4005    });
 4006    cx.assert_editor_state(indoc! {"
 4007        «aaaaa
 4008        bbˇ»
 4009
 4010        «aaaaaˇ»
 4011    "});
 4012}
 4013
 4014#[gpui::test]
 4015async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 4016    init_test(cx, |_| {});
 4017
 4018    let mut cx = EditorTestContext::new(cx).await;
 4019
 4020    cx.set_state(indoc! {"
 4021        «Aaa
 4022        aAa
 4023        Aaaˇ»
 4024    "});
 4025    cx.update_editor(|e, window, cx| {
 4026        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 4027    });
 4028    cx.assert_editor_state(indoc! {"
 4029        «Aaa
 4030        aAaˇ»
 4031    "});
 4032
 4033    cx.set_state(indoc! {"
 4034        «Aaa
 4035        aAa
 4036        aaAˇ»
 4037    "});
 4038    cx.update_editor(|e, window, cx| {
 4039        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 4040    });
 4041    cx.assert_editor_state(indoc! {"
 4042        «Aaaˇ»
 4043    "});
 4044}
 4045
 4046#[gpui::test]
 4047async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 4048    init_test(cx, |_| {});
 4049
 4050    let mut cx = EditorTestContext::new(cx).await;
 4051
 4052    // Manipulate with multiple selections on a single line
 4053    cx.set_state(indoc! {"
 4054        dd«dd
 4055        cˇ»c«c
 4056        bb
 4057        aaaˇ»aa
 4058    "});
 4059    cx.update_editor(|e, window, cx| {
 4060        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4061    });
 4062    cx.assert_editor_state(indoc! {"
 4063        «aaaaa
 4064        bb
 4065        ccc
 4066        ddddˇ»
 4067    "});
 4068
 4069    // Manipulate with multiple disjoin selections
 4070    cx.set_state(indoc! {"
 4071 4072        4
 4073        3
 4074        2
 4075        1ˇ»
 4076
 4077        dd«dd
 4078        ccc
 4079        bb
 4080        aaaˇ»aa
 4081    "});
 4082    cx.update_editor(|e, window, cx| {
 4083        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 4084    });
 4085    cx.assert_editor_state(indoc! {"
 4086        «1
 4087        2
 4088        3
 4089        4
 4090        5ˇ»
 4091
 4092        «aaaaa
 4093        bb
 4094        ccc
 4095        ddddˇ»
 4096    "});
 4097
 4098    // Adding lines on each selection
 4099    cx.set_state(indoc! {"
 4100 4101        1ˇ»
 4102
 4103        bb«bb
 4104        aaaˇ»aa
 4105    "});
 4106    cx.update_editor(|e, window, cx| {
 4107        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 4108    });
 4109    cx.assert_editor_state(indoc! {"
 4110        «2
 4111        1
 4112        added lineˇ»
 4113
 4114        «bbbb
 4115        aaaaa
 4116        added lineˇ»
 4117    "});
 4118
 4119    // Removing lines on each selection
 4120    cx.set_state(indoc! {"
 4121 4122        1ˇ»
 4123
 4124        bb«bb
 4125        aaaˇ»aa
 4126    "});
 4127    cx.update_editor(|e, window, cx| {
 4128        e.manipulate_lines(window, cx, |lines| {
 4129            lines.pop();
 4130        })
 4131    });
 4132    cx.assert_editor_state(indoc! {"
 4133        «2ˇ»
 4134
 4135        «bbbbˇ»
 4136    "});
 4137}
 4138
 4139#[gpui::test]
 4140async fn test_toggle_case(cx: &mut TestAppContext) {
 4141    init_test(cx, |_| {});
 4142
 4143    let mut cx = EditorTestContext::new(cx).await;
 4144
 4145    // If all lower case -> upper case
 4146    cx.set_state(indoc! {"
 4147        «hello worldˇ»
 4148    "});
 4149    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4150    cx.assert_editor_state(indoc! {"
 4151        «HELLO WORLDˇ»
 4152    "});
 4153
 4154    // If all upper case -> lower case
 4155    cx.set_state(indoc! {"
 4156        «HELLO WORLDˇ»
 4157    "});
 4158    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4159    cx.assert_editor_state(indoc! {"
 4160        «hello worldˇ»
 4161    "});
 4162
 4163    // If any upper case characters are identified -> lower case
 4164    // This matches JetBrains IDEs
 4165    cx.set_state(indoc! {"
 4166        «hEllo worldˇ»
 4167    "});
 4168    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 4169    cx.assert_editor_state(indoc! {"
 4170        «hello worldˇ»
 4171    "});
 4172}
 4173
 4174#[gpui::test]
 4175async fn test_manipulate_text(cx: &mut TestAppContext) {
 4176    init_test(cx, |_| {});
 4177
 4178    let mut cx = EditorTestContext::new(cx).await;
 4179
 4180    // Test convert_to_upper_case()
 4181    cx.set_state(indoc! {"
 4182        «hello worldˇ»
 4183    "});
 4184    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4185    cx.assert_editor_state(indoc! {"
 4186        «HELLO WORLDˇ»
 4187    "});
 4188
 4189    // Test convert_to_lower_case()
 4190    cx.set_state(indoc! {"
 4191        «HELLO WORLDˇ»
 4192    "});
 4193    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 4194    cx.assert_editor_state(indoc! {"
 4195        «hello worldˇ»
 4196    "});
 4197
 4198    // Test multiple line, single selection case
 4199    cx.set_state(indoc! {"
 4200        «The quick brown
 4201        fox jumps over
 4202        the lazy dogˇ»
 4203    "});
 4204    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 4205    cx.assert_editor_state(indoc! {"
 4206        «The Quick Brown
 4207        Fox Jumps Over
 4208        The Lazy Dogˇ»
 4209    "});
 4210
 4211    // Test multiple line, single selection case
 4212    cx.set_state(indoc! {"
 4213        «The quick brown
 4214        fox jumps over
 4215        the lazy dogˇ»
 4216    "});
 4217    cx.update_editor(|e, window, cx| {
 4218        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4219    });
 4220    cx.assert_editor_state(indoc! {"
 4221        «TheQuickBrown
 4222        FoxJumpsOver
 4223        TheLazyDogˇ»
 4224    "});
 4225
 4226    // From here on out, test more complex cases of manipulate_text()
 4227
 4228    // Test no selection case - should affect words cursors are in
 4229    // Cursor at beginning, middle, and end of word
 4230    cx.set_state(indoc! {"
 4231        ˇhello big beauˇtiful worldˇ
 4232    "});
 4233    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4234    cx.assert_editor_state(indoc! {"
 4235        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4236    "});
 4237
 4238    // Test multiple selections on a single line and across multiple lines
 4239    cx.set_state(indoc! {"
 4240        «Theˇ» quick «brown
 4241        foxˇ» jumps «overˇ»
 4242        the «lazyˇ» dog
 4243    "});
 4244    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4245    cx.assert_editor_state(indoc! {"
 4246        «THEˇ» quick «BROWN
 4247        FOXˇ» jumps «OVERˇ»
 4248        the «LAZYˇ» dog
 4249    "});
 4250
 4251    // Test case where text length grows
 4252    cx.set_state(indoc! {"
 4253        «tschüߡ»
 4254    "});
 4255    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4256    cx.assert_editor_state(indoc! {"
 4257        «TSCHÜSSˇ»
 4258    "});
 4259
 4260    // Test to make sure we don't crash when text shrinks
 4261    cx.set_state(indoc! {"
 4262        aaa_bbbˇ
 4263    "});
 4264    cx.update_editor(|e, window, cx| {
 4265        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4266    });
 4267    cx.assert_editor_state(indoc! {"
 4268        «aaaBbbˇ»
 4269    "});
 4270
 4271    // Test to make sure we all aware of the fact that each word can grow and shrink
 4272    // Final selections should be aware of this fact
 4273    cx.set_state(indoc! {"
 4274        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4275    "});
 4276    cx.update_editor(|e, window, cx| {
 4277        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4278    });
 4279    cx.assert_editor_state(indoc! {"
 4280        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4281    "});
 4282
 4283    cx.set_state(indoc! {"
 4284        «hElLo, WoRld!ˇ»
 4285    "});
 4286    cx.update_editor(|e, window, cx| {
 4287        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4288    });
 4289    cx.assert_editor_state(indoc! {"
 4290        «HeLlO, wOrLD!ˇ»
 4291    "});
 4292}
 4293
 4294#[gpui::test]
 4295fn test_duplicate_line(cx: &mut TestAppContext) {
 4296    init_test(cx, |_| {});
 4297
 4298    let editor = cx.add_window(|window, cx| {
 4299        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4300        build_editor(buffer, window, cx)
 4301    });
 4302    _ = editor.update(cx, |editor, window, cx| {
 4303        editor.change_selections(None, window, cx, |s| {
 4304            s.select_display_ranges([
 4305                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4306                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4307                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4308                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4309            ])
 4310        });
 4311        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4312        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4313        assert_eq!(
 4314            editor.selections.display_ranges(cx),
 4315            vec![
 4316                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4317                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4318                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4319                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4320            ]
 4321        );
 4322    });
 4323
 4324    let editor = cx.add_window(|window, cx| {
 4325        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4326        build_editor(buffer, window, cx)
 4327    });
 4328    _ = editor.update(cx, |editor, window, cx| {
 4329        editor.change_selections(None, window, cx, |s| {
 4330            s.select_display_ranges([
 4331                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4332                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4333            ])
 4334        });
 4335        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4336        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4337        assert_eq!(
 4338            editor.selections.display_ranges(cx),
 4339            vec![
 4340                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4341                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4342            ]
 4343        );
 4344    });
 4345
 4346    // With `move_upwards` the selections stay in place, except for
 4347    // the lines inserted above them
 4348    let editor = cx.add_window(|window, cx| {
 4349        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4350        build_editor(buffer, window, cx)
 4351    });
 4352    _ = editor.update(cx, |editor, window, cx| {
 4353        editor.change_selections(None, window, cx, |s| {
 4354            s.select_display_ranges([
 4355                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4356                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4357                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4358                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4359            ])
 4360        });
 4361        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4362        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4363        assert_eq!(
 4364            editor.selections.display_ranges(cx),
 4365            vec![
 4366                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4367                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4368                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4369                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4370            ]
 4371        );
 4372    });
 4373
 4374    let editor = cx.add_window(|window, cx| {
 4375        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4376        build_editor(buffer, window, cx)
 4377    });
 4378    _ = editor.update(cx, |editor, window, cx| {
 4379        editor.change_selections(None, window, cx, |s| {
 4380            s.select_display_ranges([
 4381                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4382                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4383            ])
 4384        });
 4385        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4386        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4387        assert_eq!(
 4388            editor.selections.display_ranges(cx),
 4389            vec![
 4390                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4391                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4392            ]
 4393        );
 4394    });
 4395
 4396    let editor = cx.add_window(|window, cx| {
 4397        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4398        build_editor(buffer, window, cx)
 4399    });
 4400    _ = editor.update(cx, |editor, window, cx| {
 4401        editor.change_selections(None, window, cx, |s| {
 4402            s.select_display_ranges([
 4403                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4404                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4405            ])
 4406        });
 4407        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4408        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4409        assert_eq!(
 4410            editor.selections.display_ranges(cx),
 4411            vec![
 4412                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4413                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4414            ]
 4415        );
 4416    });
 4417}
 4418
 4419#[gpui::test]
 4420fn test_move_line_up_down(cx: &mut TestAppContext) {
 4421    init_test(cx, |_| {});
 4422
 4423    let editor = cx.add_window(|window, cx| {
 4424        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4425        build_editor(buffer, window, cx)
 4426    });
 4427    _ = editor.update(cx, |editor, window, cx| {
 4428        editor.fold_creases(
 4429            vec![
 4430                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4431                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4432                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4433            ],
 4434            true,
 4435            window,
 4436            cx,
 4437        );
 4438        editor.change_selections(None, window, cx, |s| {
 4439            s.select_display_ranges([
 4440                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4441                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4442                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4443                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4444            ])
 4445        });
 4446        assert_eq!(
 4447            editor.display_text(cx),
 4448            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4449        );
 4450
 4451        editor.move_line_up(&MoveLineUp, window, cx);
 4452        assert_eq!(
 4453            editor.display_text(cx),
 4454            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4455        );
 4456        assert_eq!(
 4457            editor.selections.display_ranges(cx),
 4458            vec![
 4459                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4460                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4461                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4462                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4463            ]
 4464        );
 4465    });
 4466
 4467    _ = editor.update(cx, |editor, window, cx| {
 4468        editor.move_line_down(&MoveLineDown, window, cx);
 4469        assert_eq!(
 4470            editor.display_text(cx),
 4471            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4472        );
 4473        assert_eq!(
 4474            editor.selections.display_ranges(cx),
 4475            vec![
 4476                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4477                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4478                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4479                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4480            ]
 4481        );
 4482    });
 4483
 4484    _ = editor.update(cx, |editor, window, cx| {
 4485        editor.move_line_down(&MoveLineDown, window, cx);
 4486        assert_eq!(
 4487            editor.display_text(cx),
 4488            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4489        );
 4490        assert_eq!(
 4491            editor.selections.display_ranges(cx),
 4492            vec![
 4493                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4494                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4495                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4496                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4497            ]
 4498        );
 4499    });
 4500
 4501    _ = editor.update(cx, |editor, window, cx| {
 4502        editor.move_line_up(&MoveLineUp, window, cx);
 4503        assert_eq!(
 4504            editor.display_text(cx),
 4505            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4506        );
 4507        assert_eq!(
 4508            editor.selections.display_ranges(cx),
 4509            vec![
 4510                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4511                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4512                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4513                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4514            ]
 4515        );
 4516    });
 4517}
 4518
 4519#[gpui::test]
 4520fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4521    init_test(cx, |_| {});
 4522
 4523    let editor = cx.add_window(|window, cx| {
 4524        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4525        build_editor(buffer, window, cx)
 4526    });
 4527    _ = editor.update(cx, |editor, window, cx| {
 4528        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4529        editor.insert_blocks(
 4530            [BlockProperties {
 4531                style: BlockStyle::Fixed,
 4532                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4533                height: Some(1),
 4534                render: Arc::new(|_| div().into_any()),
 4535                priority: 0,
 4536                render_in_minimap: true,
 4537            }],
 4538            Some(Autoscroll::fit()),
 4539            cx,
 4540        );
 4541        editor.change_selections(None, window, cx, |s| {
 4542            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4543        });
 4544        editor.move_line_down(&MoveLineDown, window, cx);
 4545    });
 4546}
 4547
 4548#[gpui::test]
 4549async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4550    init_test(cx, |_| {});
 4551
 4552    let mut cx = EditorTestContext::new(cx).await;
 4553    cx.set_state(
 4554        &"
 4555            ˇzero
 4556            one
 4557            two
 4558            three
 4559            four
 4560            five
 4561        "
 4562        .unindent(),
 4563    );
 4564
 4565    // Create a four-line block that replaces three lines of text.
 4566    cx.update_editor(|editor, window, cx| {
 4567        let snapshot = editor.snapshot(window, cx);
 4568        let snapshot = &snapshot.buffer_snapshot;
 4569        let placement = BlockPlacement::Replace(
 4570            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4571        );
 4572        editor.insert_blocks(
 4573            [BlockProperties {
 4574                placement,
 4575                height: Some(4),
 4576                style: BlockStyle::Sticky,
 4577                render: Arc::new(|_| gpui::div().into_any_element()),
 4578                priority: 0,
 4579                render_in_minimap: true,
 4580            }],
 4581            None,
 4582            cx,
 4583        );
 4584    });
 4585
 4586    // Move down so that the cursor touches the block.
 4587    cx.update_editor(|editor, window, cx| {
 4588        editor.move_down(&Default::default(), window, cx);
 4589    });
 4590    cx.assert_editor_state(
 4591        &"
 4592            zero
 4593            «one
 4594            two
 4595            threeˇ»
 4596            four
 4597            five
 4598        "
 4599        .unindent(),
 4600    );
 4601
 4602    // Move down past the block.
 4603    cx.update_editor(|editor, window, cx| {
 4604        editor.move_down(&Default::default(), window, cx);
 4605    });
 4606    cx.assert_editor_state(
 4607        &"
 4608            zero
 4609            one
 4610            two
 4611            three
 4612            ˇfour
 4613            five
 4614        "
 4615        .unindent(),
 4616    );
 4617}
 4618
 4619#[gpui::test]
 4620fn test_transpose(cx: &mut TestAppContext) {
 4621    init_test(cx, |_| {});
 4622
 4623    _ = cx.add_window(|window, cx| {
 4624        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4625        editor.set_style(EditorStyle::default(), window, cx);
 4626        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4627        editor.transpose(&Default::default(), window, cx);
 4628        assert_eq!(editor.text(cx), "bac");
 4629        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4630
 4631        editor.transpose(&Default::default(), window, cx);
 4632        assert_eq!(editor.text(cx), "bca");
 4633        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4634
 4635        editor.transpose(&Default::default(), window, cx);
 4636        assert_eq!(editor.text(cx), "bac");
 4637        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4638
 4639        editor
 4640    });
 4641
 4642    _ = cx.add_window(|window, cx| {
 4643        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4644        editor.set_style(EditorStyle::default(), window, cx);
 4645        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4646        editor.transpose(&Default::default(), window, cx);
 4647        assert_eq!(editor.text(cx), "acb\nde");
 4648        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4649
 4650        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4651        editor.transpose(&Default::default(), window, cx);
 4652        assert_eq!(editor.text(cx), "acbd\ne");
 4653        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4654
 4655        editor.transpose(&Default::default(), window, cx);
 4656        assert_eq!(editor.text(cx), "acbde\n");
 4657        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4658
 4659        editor.transpose(&Default::default(), window, cx);
 4660        assert_eq!(editor.text(cx), "acbd\ne");
 4661        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4662
 4663        editor
 4664    });
 4665
 4666    _ = cx.add_window(|window, cx| {
 4667        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4668        editor.set_style(EditorStyle::default(), window, cx);
 4669        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4670        editor.transpose(&Default::default(), window, cx);
 4671        assert_eq!(editor.text(cx), "bacd\ne");
 4672        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4673
 4674        editor.transpose(&Default::default(), window, cx);
 4675        assert_eq!(editor.text(cx), "bcade\n");
 4676        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4677
 4678        editor.transpose(&Default::default(), window, cx);
 4679        assert_eq!(editor.text(cx), "bcda\ne");
 4680        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4681
 4682        editor.transpose(&Default::default(), window, cx);
 4683        assert_eq!(editor.text(cx), "bcade\n");
 4684        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4685
 4686        editor.transpose(&Default::default(), window, cx);
 4687        assert_eq!(editor.text(cx), "bcaed\n");
 4688        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4689
 4690        editor
 4691    });
 4692
 4693    _ = cx.add_window(|window, cx| {
 4694        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4695        editor.set_style(EditorStyle::default(), window, cx);
 4696        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4697        editor.transpose(&Default::default(), window, cx);
 4698        assert_eq!(editor.text(cx), "🏀🍐✋");
 4699        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4700
 4701        editor.transpose(&Default::default(), window, cx);
 4702        assert_eq!(editor.text(cx), "🏀✋🍐");
 4703        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4704
 4705        editor.transpose(&Default::default(), window, cx);
 4706        assert_eq!(editor.text(cx), "🏀🍐✋");
 4707        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4708
 4709        editor
 4710    });
 4711}
 4712
 4713#[gpui::test]
 4714async fn test_rewrap(cx: &mut TestAppContext) {
 4715    init_test(cx, |settings| {
 4716        settings.languages.extend([
 4717            (
 4718                "Markdown".into(),
 4719                LanguageSettingsContent {
 4720                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4721                    ..Default::default()
 4722                },
 4723            ),
 4724            (
 4725                "Plain Text".into(),
 4726                LanguageSettingsContent {
 4727                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4728                    ..Default::default()
 4729                },
 4730            ),
 4731        ])
 4732    });
 4733
 4734    let mut cx = EditorTestContext::new(cx).await;
 4735
 4736    let language_with_c_comments = Arc::new(Language::new(
 4737        LanguageConfig {
 4738            line_comments: vec!["// ".into()],
 4739            ..LanguageConfig::default()
 4740        },
 4741        None,
 4742    ));
 4743    let language_with_pound_comments = Arc::new(Language::new(
 4744        LanguageConfig {
 4745            line_comments: vec!["# ".into()],
 4746            ..LanguageConfig::default()
 4747        },
 4748        None,
 4749    ));
 4750    let markdown_language = Arc::new(Language::new(
 4751        LanguageConfig {
 4752            name: "Markdown".into(),
 4753            ..LanguageConfig::default()
 4754        },
 4755        None,
 4756    ));
 4757    let language_with_doc_comments = Arc::new(Language::new(
 4758        LanguageConfig {
 4759            line_comments: vec!["// ".into(), "/// ".into()],
 4760            ..LanguageConfig::default()
 4761        },
 4762        Some(tree_sitter_rust::LANGUAGE.into()),
 4763    ));
 4764
 4765    let plaintext_language = Arc::new(Language::new(
 4766        LanguageConfig {
 4767            name: "Plain Text".into(),
 4768            ..LanguageConfig::default()
 4769        },
 4770        None,
 4771    ));
 4772
 4773    assert_rewrap(
 4774        indoc! {"
 4775            // ˇ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.
 4776        "},
 4777        indoc! {"
 4778            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4779            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4780            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4781            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4782            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4783            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4784            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4785            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4786            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4787            // porttitor id. Aliquam id accumsan eros.
 4788        "},
 4789        language_with_c_comments.clone(),
 4790        &mut cx,
 4791    );
 4792
 4793    // Test that rewrapping works inside of a selection
 4794    assert_rewrap(
 4795        indoc! {"
 4796            «// 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.ˇ»
 4797        "},
 4798        indoc! {"
 4799            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4800            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4801            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4802            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4803            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4804            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4805            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4806            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4807            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4808            // porttitor id. Aliquam id accumsan eros.ˇ»
 4809        "},
 4810        language_with_c_comments.clone(),
 4811        &mut cx,
 4812    );
 4813
 4814    // Test that cursors that expand to the same region are collapsed.
 4815    assert_rewrap(
 4816        indoc! {"
 4817            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4818            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4819            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4820            // ˇ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.
 4821        "},
 4822        indoc! {"
 4823            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4824            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4825            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4826            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4827            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4828            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4829            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4830            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4831            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4832            // porttitor id. Aliquam id accumsan eros.
 4833        "},
 4834        language_with_c_comments.clone(),
 4835        &mut cx,
 4836    );
 4837
 4838    // Test that non-contiguous selections are treated separately.
 4839    assert_rewrap(
 4840        indoc! {"
 4841            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4842            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4843            //
 4844            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4845            // ˇ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.
 4846        "},
 4847        indoc! {"
 4848            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4849            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4850            // auctor, eu lacinia sapien scelerisque.
 4851            //
 4852            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4853            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4854            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4855            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4856            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4857            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4858            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4859        "},
 4860        language_with_c_comments.clone(),
 4861        &mut cx,
 4862    );
 4863
 4864    // Test that different comment prefixes are supported.
 4865    assert_rewrap(
 4866        indoc! {"
 4867            # ˇ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.
 4868        "},
 4869        indoc! {"
 4870            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4871            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4872            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4873            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4874            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4875            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4876            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4877            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4878            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4879            # accumsan eros.
 4880        "},
 4881        language_with_pound_comments.clone(),
 4882        &mut cx,
 4883    );
 4884
 4885    // Test that rewrapping is ignored outside of comments in most languages.
 4886    assert_rewrap(
 4887        indoc! {"
 4888            /// Adds two numbers.
 4889            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4890            fn add(a: u32, b: u32) -> u32 {
 4891                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ˇ
 4892            }
 4893        "},
 4894        indoc! {"
 4895            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4896            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4897            fn add(a: u32, b: u32) -> u32 {
 4898                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ˇ
 4899            }
 4900        "},
 4901        language_with_doc_comments.clone(),
 4902        &mut cx,
 4903    );
 4904
 4905    // Test that rewrapping works in Markdown and Plain Text languages.
 4906    assert_rewrap(
 4907        indoc! {"
 4908            # Hello
 4909
 4910            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.
 4911        "},
 4912        indoc! {"
 4913            # Hello
 4914
 4915            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4916            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4917            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4918            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4919            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4920            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4921            Integer sit amet scelerisque nisi.
 4922        "},
 4923        markdown_language,
 4924        &mut cx,
 4925    );
 4926
 4927    assert_rewrap(
 4928        indoc! {"
 4929            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.
 4930        "},
 4931        indoc! {"
 4932            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4933            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4934            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4935            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4936            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4937            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4938            Integer sit amet scelerisque nisi.
 4939        "},
 4940        plaintext_language,
 4941        &mut cx,
 4942    );
 4943
 4944    // Test rewrapping unaligned comments in a selection.
 4945    assert_rewrap(
 4946        indoc! {"
 4947            fn foo() {
 4948                if true {
 4949            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4950            // Praesent semper egestas tellus id dignissim.ˇ»
 4951                    do_something();
 4952                } else {
 4953                    //
 4954                }
 4955            }
 4956        "},
 4957        indoc! {"
 4958            fn foo() {
 4959                if true {
 4960            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4961                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4962                    // egestas tellus id dignissim.ˇ»
 4963                    do_something();
 4964                } else {
 4965                    //
 4966                }
 4967            }
 4968        "},
 4969        language_with_doc_comments.clone(),
 4970        &mut cx,
 4971    );
 4972
 4973    assert_rewrap(
 4974        indoc! {"
 4975            fn foo() {
 4976                if true {
 4977            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4978            // Praesent semper egestas tellus id dignissim.»
 4979                    do_something();
 4980                } else {
 4981                    //
 4982                }
 4983
 4984            }
 4985        "},
 4986        indoc! {"
 4987            fn foo() {
 4988                if true {
 4989            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4990                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4991                    // egestas tellus id dignissim.»
 4992                    do_something();
 4993                } else {
 4994                    //
 4995                }
 4996
 4997            }
 4998        "},
 4999        language_with_doc_comments.clone(),
 5000        &mut cx,
 5001    );
 5002
 5003    #[track_caller]
 5004    fn assert_rewrap(
 5005        unwrapped_text: &str,
 5006        wrapped_text: &str,
 5007        language: Arc<Language>,
 5008        cx: &mut EditorTestContext,
 5009    ) {
 5010        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 5011        cx.set_state(unwrapped_text);
 5012        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 5013        cx.assert_editor_state(wrapped_text);
 5014    }
 5015}
 5016
 5017#[gpui::test]
 5018async fn test_hard_wrap(cx: &mut TestAppContext) {
 5019    init_test(cx, |_| {});
 5020    let mut cx = EditorTestContext::new(cx).await;
 5021
 5022    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 5023    cx.update_editor(|editor, _, cx| {
 5024        editor.set_hard_wrap(Some(14), cx);
 5025    });
 5026
 5027    cx.set_state(indoc!(
 5028        "
 5029        one two three ˇ
 5030        "
 5031    ));
 5032    cx.simulate_input("four");
 5033    cx.run_until_parked();
 5034
 5035    cx.assert_editor_state(indoc!(
 5036        "
 5037        one two three
 5038        fourˇ
 5039        "
 5040    ));
 5041
 5042    cx.update_editor(|editor, window, cx| {
 5043        editor.newline(&Default::default(), window, cx);
 5044    });
 5045    cx.run_until_parked();
 5046    cx.assert_editor_state(indoc!(
 5047        "
 5048        one two three
 5049        four
 5050        ˇ
 5051        "
 5052    ));
 5053
 5054    cx.simulate_input("five");
 5055    cx.run_until_parked();
 5056    cx.assert_editor_state(indoc!(
 5057        "
 5058        one two three
 5059        four
 5060        fiveˇ
 5061        "
 5062    ));
 5063
 5064    cx.update_editor(|editor, window, cx| {
 5065        editor.newline(&Default::default(), window, cx);
 5066    });
 5067    cx.run_until_parked();
 5068    cx.simulate_input("# ");
 5069    cx.run_until_parked();
 5070    cx.assert_editor_state(indoc!(
 5071        "
 5072        one two three
 5073        four
 5074        five
 5075        # ˇ
 5076        "
 5077    ));
 5078
 5079    cx.update_editor(|editor, window, cx| {
 5080        editor.newline(&Default::default(), window, cx);
 5081    });
 5082    cx.run_until_parked();
 5083    cx.assert_editor_state(indoc!(
 5084        "
 5085        one two three
 5086        four
 5087        five
 5088        #\x20
 5089 5090        "
 5091    ));
 5092
 5093    cx.simulate_input(" 6");
 5094    cx.run_until_parked();
 5095    cx.assert_editor_state(indoc!(
 5096        "
 5097        one two three
 5098        four
 5099        five
 5100        #
 5101        # 6ˇ
 5102        "
 5103    ));
 5104}
 5105
 5106#[gpui::test]
 5107async fn test_clipboard(cx: &mut TestAppContext) {
 5108    init_test(cx, |_| {});
 5109
 5110    let mut cx = EditorTestContext::new(cx).await;
 5111
 5112    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 5113    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5114    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 5115
 5116    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 5117    cx.set_state("two ˇfour ˇsix ˇ");
 5118    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5119    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 5120
 5121    // Paste again but with only two cursors. Since the number of cursors doesn't
 5122    // match the number of slices in the clipboard, the entire clipboard text
 5123    // is pasted at each cursor.
 5124    cx.set_state("ˇtwo one✅ four three six five ˇ");
 5125    cx.update_editor(|e, window, cx| {
 5126        e.handle_input("( ", window, cx);
 5127        e.paste(&Paste, window, cx);
 5128        e.handle_input(") ", window, cx);
 5129    });
 5130    cx.assert_editor_state(
 5131        &([
 5132            "( one✅ ",
 5133            "three ",
 5134            "five ) ˇtwo one✅ four three six five ( one✅ ",
 5135            "three ",
 5136            "five ) ˇ",
 5137        ]
 5138        .join("\n")),
 5139    );
 5140
 5141    // Cut with three selections, one of which is full-line.
 5142    cx.set_state(indoc! {"
 5143        1«2ˇ»3
 5144        4ˇ567
 5145        «8ˇ»9"});
 5146    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5147    cx.assert_editor_state(indoc! {"
 5148        1ˇ3
 5149        ˇ9"});
 5150
 5151    // Paste with three selections, noticing how the copied selection that was full-line
 5152    // gets inserted before the second cursor.
 5153    cx.set_state(indoc! {"
 5154        1ˇ3
 5155 5156        «oˇ»ne"});
 5157    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5158    cx.assert_editor_state(indoc! {"
 5159        12ˇ3
 5160        4567
 5161 5162        8ˇne"});
 5163
 5164    // Copy with a single cursor only, which writes the whole line into the clipboard.
 5165    cx.set_state(indoc! {"
 5166        The quick brown
 5167        fox juˇmps over
 5168        the lazy dog"});
 5169    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5170    assert_eq!(
 5171        cx.read_from_clipboard()
 5172            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5173        Some("fox jumps over\n".to_string())
 5174    );
 5175
 5176    // Paste with three selections, noticing how the copied full-line selection is inserted
 5177    // before the empty selections but replaces the selection that is non-empty.
 5178    cx.set_state(indoc! {"
 5179        Tˇhe quick brown
 5180        «foˇ»x jumps over
 5181        tˇhe lazy dog"});
 5182    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5183    cx.assert_editor_state(indoc! {"
 5184        fox jumps over
 5185        Tˇhe quick brown
 5186        fox jumps over
 5187        ˇx jumps over
 5188        fox jumps over
 5189        tˇhe lazy dog"});
 5190}
 5191
 5192#[gpui::test]
 5193async fn test_copy_trim(cx: &mut TestAppContext) {
 5194    init_test(cx, |_| {});
 5195
 5196    let mut cx = EditorTestContext::new(cx).await;
 5197    cx.set_state(
 5198        r#"            «for selection in selections.iter() {
 5199            let mut start = selection.start;
 5200            let mut end = selection.end;
 5201            let is_entire_line = selection.is_empty();
 5202            if is_entire_line {
 5203                start = Point::new(start.row, 0);ˇ»
 5204                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5205            }
 5206        "#,
 5207    );
 5208    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5209    assert_eq!(
 5210        cx.read_from_clipboard()
 5211            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5212        Some(
 5213            "for selection in selections.iter() {
 5214            let mut start = selection.start;
 5215            let mut end = selection.end;
 5216            let is_entire_line = selection.is_empty();
 5217            if is_entire_line {
 5218                start = Point::new(start.row, 0);"
 5219                .to_string()
 5220        ),
 5221        "Regular copying preserves all indentation selected",
 5222    );
 5223    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5224    assert_eq!(
 5225        cx.read_from_clipboard()
 5226            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5227        Some(
 5228            "for selection in selections.iter() {
 5229let mut start = selection.start;
 5230let mut end = selection.end;
 5231let is_entire_line = selection.is_empty();
 5232if is_entire_line {
 5233    start = Point::new(start.row, 0);"
 5234                .to_string()
 5235        ),
 5236        "Copying with stripping should strip all leading whitespaces"
 5237    );
 5238
 5239    cx.set_state(
 5240        r#"       «     for selection in selections.iter() {
 5241            let mut start = selection.start;
 5242            let mut end = selection.end;
 5243            let is_entire_line = selection.is_empty();
 5244            if is_entire_line {
 5245                start = Point::new(start.row, 0);ˇ»
 5246                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5247            }
 5248        "#,
 5249    );
 5250    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5251    assert_eq!(
 5252        cx.read_from_clipboard()
 5253            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5254        Some(
 5255            "     for selection in selections.iter() {
 5256            let mut start = selection.start;
 5257            let mut end = selection.end;
 5258            let is_entire_line = selection.is_empty();
 5259            if is_entire_line {
 5260                start = Point::new(start.row, 0);"
 5261                .to_string()
 5262        ),
 5263        "Regular copying preserves all indentation selected",
 5264    );
 5265    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5266    assert_eq!(
 5267        cx.read_from_clipboard()
 5268            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5269        Some(
 5270            "for selection in selections.iter() {
 5271let mut start = selection.start;
 5272let mut end = selection.end;
 5273let is_entire_line = selection.is_empty();
 5274if is_entire_line {
 5275    start = Point::new(start.row, 0);"
 5276                .to_string()
 5277        ),
 5278        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5279    );
 5280
 5281    cx.set_state(
 5282        r#"       «ˇ     for selection in selections.iter() {
 5283            let mut start = selection.start;
 5284            let mut end = selection.end;
 5285            let is_entire_line = selection.is_empty();
 5286            if is_entire_line {
 5287                start = Point::new(start.row, 0);»
 5288                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5289            }
 5290        "#,
 5291    );
 5292    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5293    assert_eq!(
 5294        cx.read_from_clipboard()
 5295            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5296        Some(
 5297            "     for selection in selections.iter() {
 5298            let mut start = selection.start;
 5299            let mut end = selection.end;
 5300            let is_entire_line = selection.is_empty();
 5301            if is_entire_line {
 5302                start = Point::new(start.row, 0);"
 5303                .to_string()
 5304        ),
 5305        "Regular copying for reverse selection works the same",
 5306    );
 5307    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5308    assert_eq!(
 5309        cx.read_from_clipboard()
 5310            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5311        Some(
 5312            "for selection in selections.iter() {
 5313let mut start = selection.start;
 5314let mut end = selection.end;
 5315let is_entire_line = selection.is_empty();
 5316if is_entire_line {
 5317    start = Point::new(start.row, 0);"
 5318                .to_string()
 5319        ),
 5320        "Copying with stripping for reverse selection works the same"
 5321    );
 5322
 5323    cx.set_state(
 5324        r#"            for selection «in selections.iter() {
 5325            let mut start = selection.start;
 5326            let mut end = selection.end;
 5327            let is_entire_line = selection.is_empty();
 5328            if is_entire_line {
 5329                start = Point::new(start.row, 0);ˇ»
 5330                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5331            }
 5332        "#,
 5333    );
 5334    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5335    assert_eq!(
 5336        cx.read_from_clipboard()
 5337            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5338        Some(
 5339            "in selections.iter() {
 5340            let mut start = selection.start;
 5341            let mut end = selection.end;
 5342            let is_entire_line = selection.is_empty();
 5343            if is_entire_line {
 5344                start = Point::new(start.row, 0);"
 5345                .to_string()
 5346        ),
 5347        "When selecting past the indent, the copying works as usual",
 5348    );
 5349    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5350    assert_eq!(
 5351        cx.read_from_clipboard()
 5352            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5353        Some(
 5354            "in selections.iter() {
 5355            let mut start = selection.start;
 5356            let mut end = selection.end;
 5357            let is_entire_line = selection.is_empty();
 5358            if is_entire_line {
 5359                start = Point::new(start.row, 0);"
 5360                .to_string()
 5361        ),
 5362        "When selecting past the indent, nothing is trimmed"
 5363    );
 5364
 5365    cx.set_state(
 5366        r#"            «for selection in selections.iter() {
 5367            let mut start = selection.start;
 5368
 5369            let mut end = selection.end;
 5370            let is_entire_line = selection.is_empty();
 5371            if is_entire_line {
 5372                start = Point::new(start.row, 0);
 5373ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5374            }
 5375        "#,
 5376    );
 5377    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5378    assert_eq!(
 5379        cx.read_from_clipboard()
 5380            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5381        Some(
 5382            "for selection in selections.iter() {
 5383let mut start = selection.start;
 5384
 5385let mut end = selection.end;
 5386let is_entire_line = selection.is_empty();
 5387if is_entire_line {
 5388    start = Point::new(start.row, 0);
 5389"
 5390            .to_string()
 5391        ),
 5392        "Copying with stripping should ignore empty lines"
 5393    );
 5394}
 5395
 5396#[gpui::test]
 5397async fn test_paste_multiline(cx: &mut TestAppContext) {
 5398    init_test(cx, |_| {});
 5399
 5400    let mut cx = EditorTestContext::new(cx).await;
 5401    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5402
 5403    // Cut an indented block, without the leading whitespace.
 5404    cx.set_state(indoc! {"
 5405        const a: B = (
 5406            c(),
 5407            «d(
 5408                e,
 5409                f
 5410            )ˇ»
 5411        );
 5412    "});
 5413    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5414    cx.assert_editor_state(indoc! {"
 5415        const a: B = (
 5416            c(),
 5417            ˇ
 5418        );
 5419    "});
 5420
 5421    // Paste it at the same position.
 5422    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5423    cx.assert_editor_state(indoc! {"
 5424        const a: B = (
 5425            c(),
 5426            d(
 5427                e,
 5428                f
 5429 5430        );
 5431    "});
 5432
 5433    // Paste it at a line with a lower indent level.
 5434    cx.set_state(indoc! {"
 5435        ˇ
 5436        const a: B = (
 5437            c(),
 5438        );
 5439    "});
 5440    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5441    cx.assert_editor_state(indoc! {"
 5442        d(
 5443            e,
 5444            f
 5445 5446        const a: B = (
 5447            c(),
 5448        );
 5449    "});
 5450
 5451    // Cut an indented block, with the leading whitespace.
 5452    cx.set_state(indoc! {"
 5453        const a: B = (
 5454            c(),
 5455        «    d(
 5456                e,
 5457                f
 5458            )
 5459        ˇ»);
 5460    "});
 5461    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5462    cx.assert_editor_state(indoc! {"
 5463        const a: B = (
 5464            c(),
 5465        ˇ);
 5466    "});
 5467
 5468    // Paste it at the same position.
 5469    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5470    cx.assert_editor_state(indoc! {"
 5471        const a: B = (
 5472            c(),
 5473            d(
 5474                e,
 5475                f
 5476            )
 5477        ˇ);
 5478    "});
 5479
 5480    // Paste it at a line with a higher indent level.
 5481    cx.set_state(indoc! {"
 5482        const a: B = (
 5483            c(),
 5484            d(
 5485                e,
 5486 5487            )
 5488        );
 5489    "});
 5490    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5491    cx.assert_editor_state(indoc! {"
 5492        const a: B = (
 5493            c(),
 5494            d(
 5495                e,
 5496                f    d(
 5497                    e,
 5498                    f
 5499                )
 5500        ˇ
 5501            )
 5502        );
 5503    "});
 5504
 5505    // Copy an indented block, starting mid-line
 5506    cx.set_state(indoc! {"
 5507        const a: B = (
 5508            c(),
 5509            somethin«g(
 5510                e,
 5511                f
 5512            )ˇ»
 5513        );
 5514    "});
 5515    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5516
 5517    // Paste it on a line with a lower indent level
 5518    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5519    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5520    cx.assert_editor_state(indoc! {"
 5521        const a: B = (
 5522            c(),
 5523            something(
 5524                e,
 5525                f
 5526            )
 5527        );
 5528        g(
 5529            e,
 5530            f
 5531"});
 5532}
 5533
 5534#[gpui::test]
 5535async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5536    init_test(cx, |_| {});
 5537
 5538    cx.write_to_clipboard(ClipboardItem::new_string(
 5539        "    d(\n        e\n    );\n".into(),
 5540    ));
 5541
 5542    let mut cx = EditorTestContext::new(cx).await;
 5543    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5544
 5545    cx.set_state(indoc! {"
 5546        fn a() {
 5547            b();
 5548            if c() {
 5549                ˇ
 5550            }
 5551        }
 5552    "});
 5553
 5554    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5555    cx.assert_editor_state(indoc! {"
 5556        fn a() {
 5557            b();
 5558            if c() {
 5559                d(
 5560                    e
 5561                );
 5562        ˇ
 5563            }
 5564        }
 5565    "});
 5566
 5567    cx.set_state(indoc! {"
 5568        fn a() {
 5569            b();
 5570            ˇ
 5571        }
 5572    "});
 5573
 5574    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5575    cx.assert_editor_state(indoc! {"
 5576        fn a() {
 5577            b();
 5578            d(
 5579                e
 5580            );
 5581        ˇ
 5582        }
 5583    "});
 5584}
 5585
 5586#[gpui::test]
 5587fn test_select_all(cx: &mut TestAppContext) {
 5588    init_test(cx, |_| {});
 5589
 5590    let editor = cx.add_window(|window, cx| {
 5591        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5592        build_editor(buffer, window, cx)
 5593    });
 5594    _ = editor.update(cx, |editor, window, cx| {
 5595        editor.select_all(&SelectAll, window, cx);
 5596        assert_eq!(
 5597            editor.selections.display_ranges(cx),
 5598            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5599        );
 5600    });
 5601}
 5602
 5603#[gpui::test]
 5604fn test_select_line(cx: &mut TestAppContext) {
 5605    init_test(cx, |_| {});
 5606
 5607    let editor = cx.add_window(|window, cx| {
 5608        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5609        build_editor(buffer, window, cx)
 5610    });
 5611    _ = editor.update(cx, |editor, window, cx| {
 5612        editor.change_selections(None, window, cx, |s| {
 5613            s.select_display_ranges([
 5614                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5615                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5616                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5617                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5618            ])
 5619        });
 5620        editor.select_line(&SelectLine, window, cx);
 5621        assert_eq!(
 5622            editor.selections.display_ranges(cx),
 5623            vec![
 5624                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5625                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5626            ]
 5627        );
 5628    });
 5629
 5630    _ = editor.update(cx, |editor, window, cx| {
 5631        editor.select_line(&SelectLine, window, cx);
 5632        assert_eq!(
 5633            editor.selections.display_ranges(cx),
 5634            vec![
 5635                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5636                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5637            ]
 5638        );
 5639    });
 5640
 5641    _ = editor.update(cx, |editor, window, cx| {
 5642        editor.select_line(&SelectLine, window, cx);
 5643        assert_eq!(
 5644            editor.selections.display_ranges(cx),
 5645            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5646        );
 5647    });
 5648}
 5649
 5650#[gpui::test]
 5651async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5652    init_test(cx, |_| {});
 5653    let mut cx = EditorTestContext::new(cx).await;
 5654
 5655    #[track_caller]
 5656    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5657        cx.set_state(initial_state);
 5658        cx.update_editor(|e, window, cx| {
 5659            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5660        });
 5661        cx.assert_editor_state(expected_state);
 5662    }
 5663
 5664    // Selection starts and ends at the middle of lines, left-to-right
 5665    test(
 5666        &mut cx,
 5667        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5668        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5669    );
 5670    // Same thing, right-to-left
 5671    test(
 5672        &mut cx,
 5673        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5674        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5675    );
 5676
 5677    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5678    test(
 5679        &mut cx,
 5680        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5681        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5682    );
 5683    // Same thing, right-to-left
 5684    test(
 5685        &mut cx,
 5686        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5687        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5688    );
 5689
 5690    // Whole buffer, left-to-right, last line ends with newline
 5691    test(
 5692        &mut cx,
 5693        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5694        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5695    );
 5696    // Same thing, right-to-left
 5697    test(
 5698        &mut cx,
 5699        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5700        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5701    );
 5702
 5703    // Starts at the end of a line, ends at the start of another
 5704    test(
 5705        &mut cx,
 5706        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5707        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5708    );
 5709}
 5710
 5711#[gpui::test]
 5712async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5713    init_test(cx, |_| {});
 5714
 5715    let editor = cx.add_window(|window, cx| {
 5716        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5717        build_editor(buffer, window, cx)
 5718    });
 5719
 5720    // setup
 5721    _ = editor.update(cx, |editor, window, cx| {
 5722        editor.fold_creases(
 5723            vec![
 5724                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5725                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5726                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5727            ],
 5728            true,
 5729            window,
 5730            cx,
 5731        );
 5732        assert_eq!(
 5733            editor.display_text(cx),
 5734            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5735        );
 5736    });
 5737
 5738    _ = editor.update(cx, |editor, window, cx| {
 5739        editor.change_selections(None, window, cx, |s| {
 5740            s.select_display_ranges([
 5741                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5742                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5743                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5744                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5745            ])
 5746        });
 5747        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5748        assert_eq!(
 5749            editor.display_text(cx),
 5750            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5751        );
 5752    });
 5753    EditorTestContext::for_editor(editor, cx)
 5754        .await
 5755        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5756
 5757    _ = editor.update(cx, |editor, window, cx| {
 5758        editor.change_selections(None, window, cx, |s| {
 5759            s.select_display_ranges([
 5760                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5761            ])
 5762        });
 5763        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5764        assert_eq!(
 5765            editor.display_text(cx),
 5766            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5767        );
 5768        assert_eq!(
 5769            editor.selections.display_ranges(cx),
 5770            [
 5771                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5772                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5773                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5774                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5775                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5776                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5777                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5778            ]
 5779        );
 5780    });
 5781    EditorTestContext::for_editor(editor, cx)
 5782        .await
 5783        .assert_editor_state(
 5784            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5785        );
 5786}
 5787
 5788#[gpui::test]
 5789async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5790    init_test(cx, |_| {});
 5791
 5792    let mut cx = EditorTestContext::new(cx).await;
 5793
 5794    cx.set_state(indoc!(
 5795        r#"abc
 5796           defˇghi
 5797
 5798           jk
 5799           nlmo
 5800           "#
 5801    ));
 5802
 5803    cx.update_editor(|editor, window, cx| {
 5804        editor.add_selection_above(&Default::default(), window, cx);
 5805    });
 5806
 5807    cx.assert_editor_state(indoc!(
 5808        r#"abcˇ
 5809           defˇghi
 5810
 5811           jk
 5812           nlmo
 5813           "#
 5814    ));
 5815
 5816    cx.update_editor(|editor, window, cx| {
 5817        editor.add_selection_above(&Default::default(), window, cx);
 5818    });
 5819
 5820    cx.assert_editor_state(indoc!(
 5821        r#"abcˇ
 5822            defˇghi
 5823
 5824            jk
 5825            nlmo
 5826            "#
 5827    ));
 5828
 5829    cx.update_editor(|editor, window, cx| {
 5830        editor.add_selection_below(&Default::default(), window, cx);
 5831    });
 5832
 5833    cx.assert_editor_state(indoc!(
 5834        r#"abc
 5835           defˇghi
 5836
 5837           jk
 5838           nlmo
 5839           "#
 5840    ));
 5841
 5842    cx.update_editor(|editor, window, cx| {
 5843        editor.undo_selection(&Default::default(), window, cx);
 5844    });
 5845
 5846    cx.assert_editor_state(indoc!(
 5847        r#"abcˇ
 5848           defˇghi
 5849
 5850           jk
 5851           nlmo
 5852           "#
 5853    ));
 5854
 5855    cx.update_editor(|editor, window, cx| {
 5856        editor.redo_selection(&Default::default(), window, cx);
 5857    });
 5858
 5859    cx.assert_editor_state(indoc!(
 5860        r#"abc
 5861           defˇghi
 5862
 5863           jk
 5864           nlmo
 5865           "#
 5866    ));
 5867
 5868    cx.update_editor(|editor, window, cx| {
 5869        editor.add_selection_below(&Default::default(), window, cx);
 5870    });
 5871
 5872    cx.assert_editor_state(indoc!(
 5873        r#"abc
 5874           defˇghi
 5875
 5876           jk
 5877           nlmˇo
 5878           "#
 5879    ));
 5880
 5881    cx.update_editor(|editor, window, cx| {
 5882        editor.add_selection_below(&Default::default(), window, cx);
 5883    });
 5884
 5885    cx.assert_editor_state(indoc!(
 5886        r#"abc
 5887           defˇghi
 5888
 5889           jk
 5890           nlmˇo
 5891           "#
 5892    ));
 5893
 5894    // change selections
 5895    cx.set_state(indoc!(
 5896        r#"abc
 5897           def«ˇg»hi
 5898
 5899           jk
 5900           nlmo
 5901           "#
 5902    ));
 5903
 5904    cx.update_editor(|editor, window, cx| {
 5905        editor.add_selection_below(&Default::default(), window, cx);
 5906    });
 5907
 5908    cx.assert_editor_state(indoc!(
 5909        r#"abc
 5910           def«ˇg»hi
 5911
 5912           jk
 5913           nlm«ˇo»
 5914           "#
 5915    ));
 5916
 5917    cx.update_editor(|editor, window, cx| {
 5918        editor.add_selection_below(&Default::default(), window, cx);
 5919    });
 5920
 5921    cx.assert_editor_state(indoc!(
 5922        r#"abc
 5923           def«ˇg»hi
 5924
 5925           jk
 5926           nlm«ˇo»
 5927           "#
 5928    ));
 5929
 5930    cx.update_editor(|editor, window, cx| {
 5931        editor.add_selection_above(&Default::default(), window, cx);
 5932    });
 5933
 5934    cx.assert_editor_state(indoc!(
 5935        r#"abc
 5936           def«ˇg»hi
 5937
 5938           jk
 5939           nlmo
 5940           "#
 5941    ));
 5942
 5943    cx.update_editor(|editor, window, cx| {
 5944        editor.add_selection_above(&Default::default(), window, cx);
 5945    });
 5946
 5947    cx.assert_editor_state(indoc!(
 5948        r#"abc
 5949           def«ˇg»hi
 5950
 5951           jk
 5952           nlmo
 5953           "#
 5954    ));
 5955
 5956    // Change selections again
 5957    cx.set_state(indoc!(
 5958        r#"a«bc
 5959           defgˇ»hi
 5960
 5961           jk
 5962           nlmo
 5963           "#
 5964    ));
 5965
 5966    cx.update_editor(|editor, window, cx| {
 5967        editor.add_selection_below(&Default::default(), window, cx);
 5968    });
 5969
 5970    cx.assert_editor_state(indoc!(
 5971        r#"a«bcˇ»
 5972           d«efgˇ»hi
 5973
 5974           j«kˇ»
 5975           nlmo
 5976           "#
 5977    ));
 5978
 5979    cx.update_editor(|editor, window, cx| {
 5980        editor.add_selection_below(&Default::default(), window, cx);
 5981    });
 5982    cx.assert_editor_state(indoc!(
 5983        r#"a«bcˇ»
 5984           d«efgˇ»hi
 5985
 5986           j«kˇ»
 5987           n«lmoˇ»
 5988           "#
 5989    ));
 5990    cx.update_editor(|editor, window, cx| {
 5991        editor.add_selection_above(&Default::default(), window, cx);
 5992    });
 5993
 5994    cx.assert_editor_state(indoc!(
 5995        r#"a«bcˇ»
 5996           d«efgˇ»hi
 5997
 5998           j«kˇ»
 5999           nlmo
 6000           "#
 6001    ));
 6002
 6003    // Change selections again
 6004    cx.set_state(indoc!(
 6005        r#"abc
 6006           d«ˇefghi
 6007
 6008           jk
 6009           nlm»o
 6010           "#
 6011    ));
 6012
 6013    cx.update_editor(|editor, window, cx| {
 6014        editor.add_selection_above(&Default::default(), window, cx);
 6015    });
 6016
 6017    cx.assert_editor_state(indoc!(
 6018        r#"a«ˇbc»
 6019           d«ˇef»ghi
 6020
 6021           j«ˇk»
 6022           n«ˇlm»o
 6023           "#
 6024    ));
 6025
 6026    cx.update_editor(|editor, window, cx| {
 6027        editor.add_selection_below(&Default::default(), window, cx);
 6028    });
 6029
 6030    cx.assert_editor_state(indoc!(
 6031        r#"abc
 6032           d«ˇef»ghi
 6033
 6034           j«ˇk»
 6035           n«ˇlm»o
 6036           "#
 6037    ));
 6038}
 6039
 6040#[gpui::test]
 6041async fn test_select_next(cx: &mut TestAppContext) {
 6042    init_test(cx, |_| {});
 6043
 6044    let mut cx = EditorTestContext::new(cx).await;
 6045    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6046
 6047    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6048        .unwrap();
 6049    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6050
 6051    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6052        .unwrap();
 6053    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6054
 6055    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6056    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6057
 6058    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6059    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 6060
 6061    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6062        .unwrap();
 6063    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6064
 6065    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6066        .unwrap();
 6067    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6068
 6069    // Test selection direction should be preserved
 6070    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6071
 6072    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6073        .unwrap();
 6074    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 6075}
 6076
 6077#[gpui::test]
 6078async fn test_select_all_matches(cx: &mut TestAppContext) {
 6079    init_test(cx, |_| {});
 6080
 6081    let mut cx = EditorTestContext::new(cx).await;
 6082
 6083    // Test caret-only selections
 6084    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6085    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6086        .unwrap();
 6087    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6088
 6089    // Test left-to-right selections
 6090    cx.set_state("abc\n«abcˇ»\nabc");
 6091    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6092        .unwrap();
 6093    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 6094
 6095    // Test right-to-left selections
 6096    cx.set_state("abc\n«ˇabc»\nabc");
 6097    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6098        .unwrap();
 6099    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 6100
 6101    // Test selecting whitespace with caret selection
 6102    cx.set_state("abc\nˇ   abc\nabc");
 6103    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6104        .unwrap();
 6105    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 6106
 6107    // Test selecting whitespace with left-to-right selection
 6108    cx.set_state("abc\n«ˇ  »abc\nabc");
 6109    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6110        .unwrap();
 6111    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 6112
 6113    // Test no matches with right-to-left selection
 6114    cx.set_state("abc\n«  ˇ»abc\nabc");
 6115    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6116        .unwrap();
 6117    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 6118}
 6119
 6120#[gpui::test]
 6121async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 6122    init_test(cx, |_| {});
 6123
 6124    let mut cx = EditorTestContext::new(cx).await;
 6125
 6126    let large_body_1 = "\nd".repeat(200);
 6127    let large_body_2 = "\ne".repeat(200);
 6128
 6129    cx.set_state(&format!(
 6130        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 6131    ));
 6132    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 6133        let scroll_position = editor.scroll_position(cx);
 6134        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 6135        scroll_position
 6136    });
 6137
 6138    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 6139        .unwrap();
 6140    cx.assert_editor_state(&format!(
 6141        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 6142    ));
 6143    let scroll_position_after_selection =
 6144        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 6145    assert_eq!(
 6146        initial_scroll_position, scroll_position_after_selection,
 6147        "Scroll position should not change after selecting all matches"
 6148    );
 6149}
 6150
 6151#[gpui::test]
 6152async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 6153    init_test(cx, |_| {});
 6154
 6155    let mut cx = EditorLspTestContext::new_rust(
 6156        lsp::ServerCapabilities {
 6157            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 6158            ..Default::default()
 6159        },
 6160        cx,
 6161    )
 6162    .await;
 6163
 6164    cx.set_state(indoc! {"
 6165        line 1
 6166        line 2
 6167        linˇe 3
 6168        line 4
 6169        line 5
 6170    "});
 6171
 6172    // Make an edit
 6173    cx.update_editor(|editor, window, cx| {
 6174        editor.handle_input("X", window, cx);
 6175    });
 6176
 6177    // Move cursor to a different position
 6178    cx.update_editor(|editor, window, cx| {
 6179        editor.change_selections(None, window, cx, |s| {
 6180            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 6181        });
 6182    });
 6183
 6184    cx.assert_editor_state(indoc! {"
 6185        line 1
 6186        line 2
 6187        linXe 3
 6188        line 4
 6189        liˇne 5
 6190    "});
 6191
 6192    cx.lsp
 6193        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 6194            Ok(Some(vec![lsp::TextEdit::new(
 6195                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 6196                "PREFIX ".to_string(),
 6197            )]))
 6198        });
 6199
 6200    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 6201        .unwrap()
 6202        .await
 6203        .unwrap();
 6204
 6205    cx.assert_editor_state(indoc! {"
 6206        PREFIX line 1
 6207        line 2
 6208        linXe 3
 6209        line 4
 6210        liˇne 5
 6211    "});
 6212
 6213    // Undo formatting
 6214    cx.update_editor(|editor, window, cx| {
 6215        editor.undo(&Default::default(), window, cx);
 6216    });
 6217
 6218    // Verify cursor moved back to position after edit
 6219    cx.assert_editor_state(indoc! {"
 6220        line 1
 6221        line 2
 6222        linXˇe 3
 6223        line 4
 6224        line 5
 6225    "});
 6226}
 6227
 6228#[gpui::test]
 6229async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6230    init_test(cx, |_| {});
 6231
 6232    let mut cx = EditorTestContext::new(cx).await;
 6233    cx.set_state(
 6234        r#"let foo = 2;
 6235lˇet foo = 2;
 6236let fooˇ = 2;
 6237let foo = 2;
 6238let foo = ˇ2;"#,
 6239    );
 6240
 6241    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6242        .unwrap();
 6243    cx.assert_editor_state(
 6244        r#"let foo = 2;
 6245«letˇ» foo = 2;
 6246let «fooˇ» = 2;
 6247let foo = 2;
 6248let foo = «2ˇ»;"#,
 6249    );
 6250
 6251    // noop for multiple selections with different contents
 6252    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6253        .unwrap();
 6254    cx.assert_editor_state(
 6255        r#"let foo = 2;
 6256«letˇ» foo = 2;
 6257let «fooˇ» = 2;
 6258let foo = 2;
 6259let foo = «2ˇ»;"#,
 6260    );
 6261
 6262    // Test last selection direction should be preserved
 6263    cx.set_state(
 6264        r#"let foo = 2;
 6265let foo = 2;
 6266let «fooˇ» = 2;
 6267let «ˇfoo» = 2;
 6268let foo = 2;"#,
 6269    );
 6270
 6271    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6272        .unwrap();
 6273    cx.assert_editor_state(
 6274        r#"let foo = 2;
 6275let foo = 2;
 6276let «fooˇ» = 2;
 6277let «ˇfoo» = 2;
 6278let «ˇfoo» = 2;"#,
 6279    );
 6280}
 6281
 6282#[gpui::test]
 6283async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6284    init_test(cx, |_| {});
 6285
 6286    let mut cx =
 6287        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6288
 6289    cx.assert_editor_state(indoc! {"
 6290        ˇbbb
 6291        ccc
 6292
 6293        bbb
 6294        ccc
 6295        "});
 6296    cx.dispatch_action(SelectPrevious::default());
 6297    cx.assert_editor_state(indoc! {"
 6298                «bbbˇ»
 6299                ccc
 6300
 6301                bbb
 6302                ccc
 6303                "});
 6304    cx.dispatch_action(SelectPrevious::default());
 6305    cx.assert_editor_state(indoc! {"
 6306                «bbbˇ»
 6307                ccc
 6308
 6309                «bbbˇ»
 6310                ccc
 6311                "});
 6312}
 6313
 6314#[gpui::test]
 6315async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6316    init_test(cx, |_| {});
 6317
 6318    let mut cx = EditorTestContext::new(cx).await;
 6319    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6320
 6321    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6322        .unwrap();
 6323    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6324
 6325    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6326        .unwrap();
 6327    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6328
 6329    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6330    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6331
 6332    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6333    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6334
 6335    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6336        .unwrap();
 6337    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6338
 6339    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6340        .unwrap();
 6341    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 6342}
 6343
 6344#[gpui::test]
 6345async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6346    init_test(cx, |_| {});
 6347
 6348    let mut cx = EditorTestContext::new(cx).await;
 6349    cx.set_state("");
 6350
 6351    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6352        .unwrap();
 6353    cx.assert_editor_state("«aˇ»");
 6354    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6355        .unwrap();
 6356    cx.assert_editor_state("«aˇ»");
 6357}
 6358
 6359#[gpui::test]
 6360async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6361    init_test(cx, |_| {});
 6362
 6363    let mut cx = EditorTestContext::new(cx).await;
 6364    cx.set_state(
 6365        r#"let foo = 2;
 6366lˇet foo = 2;
 6367let fooˇ = 2;
 6368let foo = 2;
 6369let foo = ˇ2;"#,
 6370    );
 6371
 6372    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6373        .unwrap();
 6374    cx.assert_editor_state(
 6375        r#"let foo = 2;
 6376«letˇ» foo = 2;
 6377let «fooˇ» = 2;
 6378let foo = 2;
 6379let foo = «2ˇ»;"#,
 6380    );
 6381
 6382    // noop for multiple selections with different contents
 6383    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6384        .unwrap();
 6385    cx.assert_editor_state(
 6386        r#"let foo = 2;
 6387«letˇ» foo = 2;
 6388let «fooˇ» = 2;
 6389let foo = 2;
 6390let foo = «2ˇ»;"#,
 6391    );
 6392}
 6393
 6394#[gpui::test]
 6395async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6396    init_test(cx, |_| {});
 6397
 6398    let mut cx = EditorTestContext::new(cx).await;
 6399    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6400
 6401    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6402        .unwrap();
 6403    // selection direction is preserved
 6404    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6405
 6406    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6407        .unwrap();
 6408    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6409
 6410    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6411    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6412
 6413    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6414    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6415
 6416    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6417        .unwrap();
 6418    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6419
 6420    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6421        .unwrap();
 6422    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6423}
 6424
 6425#[gpui::test]
 6426async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6427    init_test(cx, |_| {});
 6428
 6429    let language = Arc::new(Language::new(
 6430        LanguageConfig::default(),
 6431        Some(tree_sitter_rust::LANGUAGE.into()),
 6432    ));
 6433
 6434    let text = r#"
 6435        use mod1::mod2::{mod3, mod4};
 6436
 6437        fn fn_1(param1: bool, param2: &str) {
 6438            let var1 = "text";
 6439        }
 6440    "#
 6441    .unindent();
 6442
 6443    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6444    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6445    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6446
 6447    editor
 6448        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6449        .await;
 6450
 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), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6455                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6456                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6457            ]);
 6458        });
 6459        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6460    });
 6461    editor.update(cx, |editor, cx| {
 6462        assert_text_with_selections(
 6463            editor,
 6464            indoc! {r#"
 6465                use mod1::mod2::{mod3, «mod4ˇ»};
 6466
 6467                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6468                    let var1 = "«ˇtext»";
 6469                }
 6470            "#},
 6471            cx,
 6472        );
 6473    });
 6474
 6475    editor.update_in(cx, |editor, window, cx| {
 6476        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6477    });
 6478    editor.update(cx, |editor, cx| {
 6479        assert_text_with_selections(
 6480            editor,
 6481            indoc! {r#"
 6482                use mod1::mod2::«{mod3, mod4}ˇ»;
 6483
 6484                «ˇfn fn_1(param1: bool, param2: &str) {
 6485                    let var1 = "text";
 6486 6487            "#},
 6488            cx,
 6489        );
 6490    });
 6491
 6492    editor.update_in(cx, |editor, window, cx| {
 6493        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6494    });
 6495    assert_eq!(
 6496        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6497        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6498    );
 6499
 6500    // Trying to expand the selected syntax node one more time has no effect.
 6501    editor.update_in(cx, |editor, window, cx| {
 6502        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6503    });
 6504    assert_eq!(
 6505        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6506        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6507    );
 6508
 6509    editor.update_in(cx, |editor, window, cx| {
 6510        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6511    });
 6512    editor.update(cx, |editor, cx| {
 6513        assert_text_with_selections(
 6514            editor,
 6515            indoc! {r#"
 6516                use mod1::mod2::«{mod3, mod4}ˇ»;
 6517
 6518                «ˇfn fn_1(param1: bool, param2: &str) {
 6519                    let var1 = "text";
 6520 6521            "#},
 6522            cx,
 6523        );
 6524    });
 6525
 6526    editor.update_in(cx, |editor, window, cx| {
 6527        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6528    });
 6529    editor.update(cx, |editor, cx| {
 6530        assert_text_with_selections(
 6531            editor,
 6532            indoc! {r#"
 6533                use mod1::mod2::{mod3, «mod4ˇ»};
 6534
 6535                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6536                    let var1 = "«ˇtext»";
 6537                }
 6538            "#},
 6539            cx,
 6540        );
 6541    });
 6542
 6543    editor.update_in(cx, |editor, window, cx| {
 6544        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6545    });
 6546    editor.update(cx, |editor, cx| {
 6547        assert_text_with_selections(
 6548            editor,
 6549            indoc! {r#"
 6550                use mod1::mod2::{mod3, mo«ˇ»d4};
 6551
 6552                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6553                    let var1 = "te«ˇ»xt";
 6554                }
 6555            "#},
 6556            cx,
 6557        );
 6558    });
 6559
 6560    // Trying to shrink the selected syntax node one more time has no effect.
 6561    editor.update_in(cx, |editor, window, cx| {
 6562        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6563    });
 6564    editor.update_in(cx, |editor, _, cx| {
 6565        assert_text_with_selections(
 6566            editor,
 6567            indoc! {r#"
 6568                use mod1::mod2::{mod3, mo«ˇ»d4};
 6569
 6570                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6571                    let var1 = "te«ˇ»xt";
 6572                }
 6573            "#},
 6574            cx,
 6575        );
 6576    });
 6577
 6578    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6579    // a fold.
 6580    editor.update_in(cx, |editor, window, cx| {
 6581        editor.fold_creases(
 6582            vec![
 6583                Crease::simple(
 6584                    Point::new(0, 21)..Point::new(0, 24),
 6585                    FoldPlaceholder::test(),
 6586                ),
 6587                Crease::simple(
 6588                    Point::new(3, 20)..Point::new(3, 22),
 6589                    FoldPlaceholder::test(),
 6590                ),
 6591            ],
 6592            true,
 6593            window,
 6594            cx,
 6595        );
 6596        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6597    });
 6598    editor.update(cx, |editor, cx| {
 6599        assert_text_with_selections(
 6600            editor,
 6601            indoc! {r#"
 6602                use mod1::mod2::«{mod3, mod4}ˇ»;
 6603
 6604                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6605                    let var1 = "«ˇtext»";
 6606                }
 6607            "#},
 6608            cx,
 6609        );
 6610    });
 6611}
 6612
 6613#[gpui::test]
 6614async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContext) {
 6615    init_test(cx, |_| {});
 6616
 6617    let language = Arc::new(Language::new(
 6618        LanguageConfig::default(),
 6619        Some(tree_sitter_rust::LANGUAGE.into()),
 6620    ));
 6621
 6622    let text = "let a = 2;";
 6623
 6624    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6625    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6626    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6627
 6628    editor
 6629        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6630        .await;
 6631
 6632    // Test case 1: Cursor at end of word
 6633    editor.update_in(cx, |editor, window, cx| {
 6634        editor.change_selections(None, window, cx, |s| {
 6635            s.select_display_ranges([
 6636                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
 6637            ]);
 6638        });
 6639    });
 6640    editor.update(cx, |editor, cx| {
 6641        assert_text_with_selections(editor, "let aˇ = 2;", cx);
 6642    });
 6643    editor.update_in(cx, |editor, window, cx| {
 6644        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6645    });
 6646    editor.update(cx, |editor, cx| {
 6647        assert_text_with_selections(editor, "let «ˇa» = 2;", cx);
 6648    });
 6649    editor.update_in(cx, |editor, window, cx| {
 6650        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6651    });
 6652    editor.update(cx, |editor, cx| {
 6653        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6654    });
 6655
 6656    // Test case 2: Cursor at end of statement
 6657    editor.update_in(cx, |editor, window, cx| {
 6658        editor.change_selections(None, window, cx, |s| {
 6659            s.select_display_ranges([
 6660                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
 6661            ]);
 6662        });
 6663    });
 6664    editor.update(cx, |editor, cx| {
 6665        assert_text_with_selections(editor, "let a = 2;ˇ", cx);
 6666    });
 6667    editor.update_in(cx, |editor, window, cx| {
 6668        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6669    });
 6670    editor.update(cx, |editor, cx| {
 6671        assert_text_with_selections(editor, "«ˇlet a = 2;»", cx);
 6672    });
 6673}
 6674
 6675#[gpui::test]
 6676async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6677    init_test(cx, |_| {});
 6678
 6679    let language = Arc::new(Language::new(
 6680        LanguageConfig::default(),
 6681        Some(tree_sitter_rust::LANGUAGE.into()),
 6682    ));
 6683
 6684    let text = r#"
 6685        use mod1::mod2::{mod3, mod4};
 6686
 6687        fn fn_1(param1: bool, param2: &str) {
 6688            let var1 = "hello world";
 6689        }
 6690    "#
 6691    .unindent();
 6692
 6693    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6694    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6695    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6696
 6697    editor
 6698        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6699        .await;
 6700
 6701    // Test 1: Cursor on a letter of a string word
 6702    editor.update_in(cx, |editor, window, cx| {
 6703        editor.change_selections(None, window, cx, |s| {
 6704            s.select_display_ranges([
 6705                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6706            ]);
 6707        });
 6708    });
 6709    editor.update_in(cx, |editor, window, cx| {
 6710        assert_text_with_selections(
 6711            editor,
 6712            indoc! {r#"
 6713                use mod1::mod2::{mod3, mod4};
 6714
 6715                fn fn_1(param1: bool, param2: &str) {
 6716                    let var1 = "hˇello world";
 6717                }
 6718            "#},
 6719            cx,
 6720        );
 6721        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6722        assert_text_with_selections(
 6723            editor,
 6724            indoc! {r#"
 6725                use mod1::mod2::{mod3, mod4};
 6726
 6727                fn fn_1(param1: bool, param2: &str) {
 6728                    let var1 = "«ˇhello» world";
 6729                }
 6730            "#},
 6731            cx,
 6732        );
 6733    });
 6734
 6735    // Test 2: Partial selection within a word
 6736    editor.update_in(cx, |editor, window, cx| {
 6737        editor.change_selections(None, window, cx, |s| {
 6738            s.select_display_ranges([
 6739                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6740            ]);
 6741        });
 6742    });
 6743    editor.update_in(cx, |editor, window, cx| {
 6744        assert_text_with_selections(
 6745            editor,
 6746            indoc! {r#"
 6747                use mod1::mod2::{mod3, mod4};
 6748
 6749                fn fn_1(param1: bool, param2: &str) {
 6750                    let var1 = "h«elˇ»lo world";
 6751                }
 6752            "#},
 6753            cx,
 6754        );
 6755        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6756        assert_text_with_selections(
 6757            editor,
 6758            indoc! {r#"
 6759                use mod1::mod2::{mod3, mod4};
 6760
 6761                fn fn_1(param1: bool, param2: &str) {
 6762                    let var1 = "«ˇhello» world";
 6763                }
 6764            "#},
 6765            cx,
 6766        );
 6767    });
 6768
 6769    // Test 3: Complete word already selected
 6770    editor.update_in(cx, |editor, window, cx| {
 6771        editor.change_selections(None, window, cx, |s| {
 6772            s.select_display_ranges([
 6773                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6774            ]);
 6775        });
 6776    });
 6777    editor.update_in(cx, |editor, window, cx| {
 6778        assert_text_with_selections(
 6779            editor,
 6780            indoc! {r#"
 6781                use mod1::mod2::{mod3, mod4};
 6782
 6783                fn fn_1(param1: bool, param2: &str) {
 6784                    let var1 = "«helloˇ» world";
 6785                }
 6786            "#},
 6787            cx,
 6788        );
 6789        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6790        assert_text_with_selections(
 6791            editor,
 6792            indoc! {r#"
 6793                use mod1::mod2::{mod3, mod4};
 6794
 6795                fn fn_1(param1: bool, param2: &str) {
 6796                    let var1 = "«hello worldˇ»";
 6797                }
 6798            "#},
 6799            cx,
 6800        );
 6801    });
 6802
 6803    // Test 4: Selection spanning across words
 6804    editor.update_in(cx, |editor, window, cx| {
 6805        editor.change_selections(None, window, cx, |s| {
 6806            s.select_display_ranges([
 6807                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6808            ]);
 6809        });
 6810    });
 6811    editor.update_in(cx, |editor, window, cx| {
 6812        assert_text_with_selections(
 6813            editor,
 6814            indoc! {r#"
 6815                use mod1::mod2::{mod3, mod4};
 6816
 6817                fn fn_1(param1: bool, param2: &str) {
 6818                    let var1 = "hel«lo woˇ»rld";
 6819                }
 6820            "#},
 6821            cx,
 6822        );
 6823        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6824        assert_text_with_selections(
 6825            editor,
 6826            indoc! {r#"
 6827                use mod1::mod2::{mod3, mod4};
 6828
 6829                fn fn_1(param1: bool, param2: &str) {
 6830                    let var1 = "«ˇhello world»";
 6831                }
 6832            "#},
 6833            cx,
 6834        );
 6835    });
 6836
 6837    // Test 5: Expansion beyond string
 6838    editor.update_in(cx, |editor, window, cx| {
 6839        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6840        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6841        assert_text_with_selections(
 6842            editor,
 6843            indoc! {r#"
 6844                use mod1::mod2::{mod3, mod4};
 6845
 6846                fn fn_1(param1: bool, param2: &str) {
 6847                    «ˇlet var1 = "hello world";»
 6848                }
 6849            "#},
 6850            cx,
 6851        );
 6852    });
 6853}
 6854
 6855#[gpui::test]
 6856async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6857    init_test(cx, |_| {});
 6858
 6859    let base_text = r#"
 6860        impl A {
 6861            // this is an uncommitted comment
 6862
 6863            fn b() {
 6864                c();
 6865            }
 6866
 6867            // this is another uncommitted comment
 6868
 6869            fn d() {
 6870                // e
 6871                // f
 6872            }
 6873        }
 6874
 6875        fn g() {
 6876            // h
 6877        }
 6878    "#
 6879    .unindent();
 6880
 6881    let text = r#"
 6882        ˇimpl A {
 6883
 6884            fn b() {
 6885                c();
 6886            }
 6887
 6888            fn d() {
 6889                // e
 6890                // f
 6891            }
 6892        }
 6893
 6894        fn g() {
 6895            // h
 6896        }
 6897    "#
 6898    .unindent();
 6899
 6900    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6901    cx.set_state(&text);
 6902    cx.set_head_text(&base_text);
 6903    cx.update_editor(|editor, window, cx| {
 6904        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6905    });
 6906
 6907    cx.assert_state_with_diff(
 6908        "
 6909        ˇimpl A {
 6910      -     // this is an uncommitted comment
 6911
 6912            fn b() {
 6913                c();
 6914            }
 6915
 6916      -     // this is another uncommitted comment
 6917      -
 6918            fn d() {
 6919                // e
 6920                // f
 6921            }
 6922        }
 6923
 6924        fn g() {
 6925            // h
 6926        }
 6927    "
 6928        .unindent(),
 6929    );
 6930
 6931    let expected_display_text = "
 6932        impl A {
 6933            // this is an uncommitted comment
 6934
 6935            fn b() {
 6936 6937            }
 6938
 6939            // this is another uncommitted comment
 6940
 6941            fn d() {
 6942 6943            }
 6944        }
 6945
 6946        fn g() {
 6947 6948        }
 6949        "
 6950    .unindent();
 6951
 6952    cx.update_editor(|editor, window, cx| {
 6953        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6954        assert_eq!(editor.display_text(cx), expected_display_text);
 6955    });
 6956}
 6957
 6958#[gpui::test]
 6959async fn test_autoindent(cx: &mut TestAppContext) {
 6960    init_test(cx, |_| {});
 6961
 6962    let language = Arc::new(
 6963        Language::new(
 6964            LanguageConfig {
 6965                brackets: BracketPairConfig {
 6966                    pairs: vec![
 6967                        BracketPair {
 6968                            start: "{".to_string(),
 6969                            end: "}".to_string(),
 6970                            close: false,
 6971                            surround: false,
 6972                            newline: true,
 6973                        },
 6974                        BracketPair {
 6975                            start: "(".to_string(),
 6976                            end: ")".to_string(),
 6977                            close: false,
 6978                            surround: false,
 6979                            newline: true,
 6980                        },
 6981                    ],
 6982                    ..Default::default()
 6983                },
 6984                ..Default::default()
 6985            },
 6986            Some(tree_sitter_rust::LANGUAGE.into()),
 6987        )
 6988        .with_indents_query(
 6989            r#"
 6990                (_ "(" ")" @end) @indent
 6991                (_ "{" "}" @end) @indent
 6992            "#,
 6993        )
 6994        .unwrap(),
 6995    );
 6996
 6997    let text = "fn a() {}";
 6998
 6999    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7000    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7001    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7002    editor
 7003        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7004        .await;
 7005
 7006    editor.update_in(cx, |editor, window, cx| {
 7007        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 7008        editor.newline(&Newline, window, cx);
 7009        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 7010        assert_eq!(
 7011            editor.selections.ranges(cx),
 7012            &[
 7013                Point::new(1, 4)..Point::new(1, 4),
 7014                Point::new(3, 4)..Point::new(3, 4),
 7015                Point::new(5, 0)..Point::new(5, 0)
 7016            ]
 7017        );
 7018    });
 7019}
 7020
 7021#[gpui::test]
 7022async fn test_autoindent_selections(cx: &mut TestAppContext) {
 7023    init_test(cx, |_| {});
 7024
 7025    {
 7026        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 7027        cx.set_state(indoc! {"
 7028            impl A {
 7029
 7030                fn b() {}
 7031
 7032            «fn c() {
 7033
 7034            }ˇ»
 7035            }
 7036        "});
 7037
 7038        cx.update_editor(|editor, window, cx| {
 7039            editor.autoindent(&Default::default(), window, cx);
 7040        });
 7041
 7042        cx.assert_editor_state(indoc! {"
 7043            impl A {
 7044
 7045                fn b() {}
 7046
 7047                «fn c() {
 7048
 7049                }ˇ»
 7050            }
 7051        "});
 7052    }
 7053
 7054    {
 7055        let mut cx = EditorTestContext::new_multibuffer(
 7056            cx,
 7057            [indoc! { "
 7058                impl A {
 7059                «
 7060                // a
 7061                fn b(){}
 7062                »
 7063                «
 7064                    }
 7065                    fn c(){}
 7066                »
 7067            "}],
 7068        );
 7069
 7070        let buffer = cx.update_editor(|editor, _, cx| {
 7071            let buffer = editor.buffer().update(cx, |buffer, _| {
 7072                buffer.all_buffers().iter().next().unwrap().clone()
 7073            });
 7074            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 7075            buffer
 7076        });
 7077
 7078        cx.run_until_parked();
 7079        cx.update_editor(|editor, window, cx| {
 7080            editor.select_all(&Default::default(), window, cx);
 7081            editor.autoindent(&Default::default(), window, cx)
 7082        });
 7083        cx.run_until_parked();
 7084
 7085        cx.update(|_, cx| {
 7086            assert_eq!(
 7087                buffer.read(cx).text(),
 7088                indoc! { "
 7089                    impl A {
 7090
 7091                        // a
 7092                        fn b(){}
 7093
 7094
 7095                    }
 7096                    fn c(){}
 7097
 7098                " }
 7099            )
 7100        });
 7101    }
 7102}
 7103
 7104#[gpui::test]
 7105async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 7106    init_test(cx, |_| {});
 7107
 7108    let mut cx = EditorTestContext::new(cx).await;
 7109
 7110    let language = Arc::new(Language::new(
 7111        LanguageConfig {
 7112            brackets: BracketPairConfig {
 7113                pairs: vec![
 7114                    BracketPair {
 7115                        start: "{".to_string(),
 7116                        end: "}".to_string(),
 7117                        close: true,
 7118                        surround: true,
 7119                        newline: true,
 7120                    },
 7121                    BracketPair {
 7122                        start: "(".to_string(),
 7123                        end: ")".to_string(),
 7124                        close: true,
 7125                        surround: true,
 7126                        newline: true,
 7127                    },
 7128                    BracketPair {
 7129                        start: "/*".to_string(),
 7130                        end: " */".to_string(),
 7131                        close: true,
 7132                        surround: true,
 7133                        newline: true,
 7134                    },
 7135                    BracketPair {
 7136                        start: "[".to_string(),
 7137                        end: "]".to_string(),
 7138                        close: false,
 7139                        surround: false,
 7140                        newline: true,
 7141                    },
 7142                    BracketPair {
 7143                        start: "\"".to_string(),
 7144                        end: "\"".to_string(),
 7145                        close: true,
 7146                        surround: true,
 7147                        newline: false,
 7148                    },
 7149                    BracketPair {
 7150                        start: "<".to_string(),
 7151                        end: ">".to_string(),
 7152                        close: false,
 7153                        surround: true,
 7154                        newline: true,
 7155                    },
 7156                ],
 7157                ..Default::default()
 7158            },
 7159            autoclose_before: "})]".to_string(),
 7160            ..Default::default()
 7161        },
 7162        Some(tree_sitter_rust::LANGUAGE.into()),
 7163    ));
 7164
 7165    cx.language_registry().add(language.clone());
 7166    cx.update_buffer(|buffer, cx| {
 7167        buffer.set_language(Some(language), cx);
 7168    });
 7169
 7170    cx.set_state(
 7171        &r#"
 7172            🏀ˇ
 7173            εˇ
 7174            ❤️ˇ
 7175        "#
 7176        .unindent(),
 7177    );
 7178
 7179    // autoclose multiple nested brackets at multiple cursors
 7180    cx.update_editor(|editor, window, cx| {
 7181        editor.handle_input("{", window, cx);
 7182        editor.handle_input("{", window, cx);
 7183        editor.handle_input("{", window, cx);
 7184    });
 7185    cx.assert_editor_state(
 7186        &"
 7187            🏀{{{ˇ}}}
 7188            ε{{{ˇ}}}
 7189            ❤️{{{ˇ}}}
 7190        "
 7191        .unindent(),
 7192    );
 7193
 7194    // insert a different closing bracket
 7195    cx.update_editor(|editor, window, cx| {
 7196        editor.handle_input(")", window, cx);
 7197    });
 7198    cx.assert_editor_state(
 7199        &"
 7200            🏀{{{)ˇ}}}
 7201            ε{{{)ˇ}}}
 7202            ❤️{{{)ˇ}}}
 7203        "
 7204        .unindent(),
 7205    );
 7206
 7207    // skip over the auto-closed brackets when typing a closing bracket
 7208    cx.update_editor(|editor, window, cx| {
 7209        editor.move_right(&MoveRight, window, cx);
 7210        editor.handle_input("}", window, cx);
 7211        editor.handle_input("}", window, cx);
 7212        editor.handle_input("}", window, cx);
 7213    });
 7214    cx.assert_editor_state(
 7215        &"
 7216            🏀{{{)}}}}ˇ
 7217            ε{{{)}}}}ˇ
 7218            ❤️{{{)}}}}ˇ
 7219        "
 7220        .unindent(),
 7221    );
 7222
 7223    // autoclose multi-character pairs
 7224    cx.set_state(
 7225        &"
 7226            ˇ
 7227            ˇ
 7228        "
 7229        .unindent(),
 7230    );
 7231    cx.update_editor(|editor, window, cx| {
 7232        editor.handle_input("/", window, cx);
 7233        editor.handle_input("*", window, cx);
 7234    });
 7235    cx.assert_editor_state(
 7236        &"
 7237            /*ˇ */
 7238            /*ˇ */
 7239        "
 7240        .unindent(),
 7241    );
 7242
 7243    // one cursor autocloses a multi-character pair, one cursor
 7244    // does not autoclose.
 7245    cx.set_state(
 7246        &"
 7247 7248            ˇ
 7249        "
 7250        .unindent(),
 7251    );
 7252    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 7253    cx.assert_editor_state(
 7254        &"
 7255            /*ˇ */
 7256 7257        "
 7258        .unindent(),
 7259    );
 7260
 7261    // Don't autoclose if the next character isn't whitespace and isn't
 7262    // listed in the language's "autoclose_before" section.
 7263    cx.set_state("ˇa b");
 7264    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7265    cx.assert_editor_state("{ˇa b");
 7266
 7267    // Don't autoclose if `close` is false for the bracket pair
 7268    cx.set_state("ˇ");
 7269    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7270    cx.assert_editor_state("");
 7271
 7272    // Surround with brackets if text is selected
 7273    cx.set_state("«aˇ» b");
 7274    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7275    cx.assert_editor_state("{«aˇ»} b");
 7276
 7277    // Autoclose when not immediately after a word character
 7278    cx.set_state("a ˇ");
 7279    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7280    cx.assert_editor_state("a \"ˇ\"");
 7281
 7282    // Autoclose pair where the start and end characters are the same
 7283    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7284    cx.assert_editor_state("a \"\"ˇ");
 7285
 7286    // Don't autoclose when immediately after a word character
 7287    cx.set_state("");
 7288    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7289    cx.assert_editor_state("a\"ˇ");
 7290
 7291    // Do autoclose when after a non-word character
 7292    cx.set_state("");
 7293    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7294    cx.assert_editor_state("{\"ˇ\"");
 7295
 7296    // Non identical pairs autoclose regardless of preceding character
 7297    cx.set_state("");
 7298    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7299    cx.assert_editor_state("a{ˇ}");
 7300
 7301    // Don't autoclose pair if autoclose is disabled
 7302    cx.set_state("ˇ");
 7303    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7304    cx.assert_editor_state("");
 7305
 7306    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7307    cx.set_state("«aˇ» b");
 7308    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7309    cx.assert_editor_state("<«aˇ»> b");
 7310}
 7311
 7312#[gpui::test]
 7313async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7314    init_test(cx, |settings| {
 7315        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7316    });
 7317
 7318    let mut cx = EditorTestContext::new(cx).await;
 7319
 7320    let language = Arc::new(Language::new(
 7321        LanguageConfig {
 7322            brackets: BracketPairConfig {
 7323                pairs: vec![
 7324                    BracketPair {
 7325                        start: "{".to_string(),
 7326                        end: "}".to_string(),
 7327                        close: true,
 7328                        surround: true,
 7329                        newline: true,
 7330                    },
 7331                    BracketPair {
 7332                        start: "(".to_string(),
 7333                        end: ")".to_string(),
 7334                        close: true,
 7335                        surround: true,
 7336                        newline: true,
 7337                    },
 7338                    BracketPair {
 7339                        start: "[".to_string(),
 7340                        end: "]".to_string(),
 7341                        close: false,
 7342                        surround: false,
 7343                        newline: true,
 7344                    },
 7345                ],
 7346                ..Default::default()
 7347            },
 7348            autoclose_before: "})]".to_string(),
 7349            ..Default::default()
 7350        },
 7351        Some(tree_sitter_rust::LANGUAGE.into()),
 7352    ));
 7353
 7354    cx.language_registry().add(language.clone());
 7355    cx.update_buffer(|buffer, cx| {
 7356        buffer.set_language(Some(language), cx);
 7357    });
 7358
 7359    cx.set_state(
 7360        &"
 7361            ˇ
 7362            ˇ
 7363            ˇ
 7364        "
 7365        .unindent(),
 7366    );
 7367
 7368    // ensure only matching closing brackets are skipped over
 7369    cx.update_editor(|editor, window, cx| {
 7370        editor.handle_input("}", window, cx);
 7371        editor.move_left(&MoveLeft, window, cx);
 7372        editor.handle_input(")", window, cx);
 7373        editor.move_left(&MoveLeft, window, cx);
 7374    });
 7375    cx.assert_editor_state(
 7376        &"
 7377            ˇ)}
 7378            ˇ)}
 7379            ˇ)}
 7380        "
 7381        .unindent(),
 7382    );
 7383
 7384    // skip-over closing brackets at multiple cursors
 7385    cx.update_editor(|editor, window, cx| {
 7386        editor.handle_input(")", window, cx);
 7387        editor.handle_input("}", window, cx);
 7388    });
 7389    cx.assert_editor_state(
 7390        &"
 7391            )}ˇ
 7392            )}ˇ
 7393            )}ˇ
 7394        "
 7395        .unindent(),
 7396    );
 7397
 7398    // ignore non-close brackets
 7399    cx.update_editor(|editor, window, cx| {
 7400        editor.handle_input("]", window, cx);
 7401        editor.move_left(&MoveLeft, window, cx);
 7402        editor.handle_input("]", window, cx);
 7403    });
 7404    cx.assert_editor_state(
 7405        &"
 7406            )}]ˇ]
 7407            )}]ˇ]
 7408            )}]ˇ]
 7409        "
 7410        .unindent(),
 7411    );
 7412}
 7413
 7414#[gpui::test]
 7415async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7416    init_test(cx, |_| {});
 7417
 7418    let mut cx = EditorTestContext::new(cx).await;
 7419
 7420    let html_language = Arc::new(
 7421        Language::new(
 7422            LanguageConfig {
 7423                name: "HTML".into(),
 7424                brackets: BracketPairConfig {
 7425                    pairs: vec![
 7426                        BracketPair {
 7427                            start: "<".into(),
 7428                            end: ">".into(),
 7429                            close: true,
 7430                            ..Default::default()
 7431                        },
 7432                        BracketPair {
 7433                            start: "{".into(),
 7434                            end: "}".into(),
 7435                            close: true,
 7436                            ..Default::default()
 7437                        },
 7438                        BracketPair {
 7439                            start: "(".into(),
 7440                            end: ")".into(),
 7441                            close: true,
 7442                            ..Default::default()
 7443                        },
 7444                    ],
 7445                    ..Default::default()
 7446                },
 7447                autoclose_before: "})]>".into(),
 7448                ..Default::default()
 7449            },
 7450            Some(tree_sitter_html::LANGUAGE.into()),
 7451        )
 7452        .with_injection_query(
 7453            r#"
 7454            (script_element
 7455                (raw_text) @injection.content
 7456                (#set! injection.language "javascript"))
 7457            "#,
 7458        )
 7459        .unwrap(),
 7460    );
 7461
 7462    let javascript_language = Arc::new(Language::new(
 7463        LanguageConfig {
 7464            name: "JavaScript".into(),
 7465            brackets: BracketPairConfig {
 7466                pairs: vec![
 7467                    BracketPair {
 7468                        start: "/*".into(),
 7469                        end: " */".into(),
 7470                        close: true,
 7471                        ..Default::default()
 7472                    },
 7473                    BracketPair {
 7474                        start: "{".into(),
 7475                        end: "}".into(),
 7476                        close: true,
 7477                        ..Default::default()
 7478                    },
 7479                    BracketPair {
 7480                        start: "(".into(),
 7481                        end: ")".into(),
 7482                        close: true,
 7483                        ..Default::default()
 7484                    },
 7485                ],
 7486                ..Default::default()
 7487            },
 7488            autoclose_before: "})]>".into(),
 7489            ..Default::default()
 7490        },
 7491        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7492    ));
 7493
 7494    cx.language_registry().add(html_language.clone());
 7495    cx.language_registry().add(javascript_language.clone());
 7496
 7497    cx.update_buffer(|buffer, cx| {
 7498        buffer.set_language(Some(html_language), cx);
 7499    });
 7500
 7501    cx.set_state(
 7502        &r#"
 7503            <body>ˇ
 7504                <script>
 7505                    var x = 1;ˇ
 7506                </script>
 7507            </body>ˇ
 7508        "#
 7509        .unindent(),
 7510    );
 7511
 7512    // Precondition: different languages are active at different locations.
 7513    cx.update_editor(|editor, window, cx| {
 7514        let snapshot = editor.snapshot(window, cx);
 7515        let cursors = editor.selections.ranges::<usize>(cx);
 7516        let languages = cursors
 7517            .iter()
 7518            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7519            .collect::<Vec<_>>();
 7520        assert_eq!(
 7521            languages,
 7522            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7523        );
 7524    });
 7525
 7526    // Angle brackets autoclose in HTML, but not JavaScript.
 7527    cx.update_editor(|editor, window, cx| {
 7528        editor.handle_input("<", window, cx);
 7529        editor.handle_input("a", window, cx);
 7530    });
 7531    cx.assert_editor_state(
 7532        &r#"
 7533            <body><aˇ>
 7534                <script>
 7535                    var x = 1;<aˇ
 7536                </script>
 7537            </body><aˇ>
 7538        "#
 7539        .unindent(),
 7540    );
 7541
 7542    // Curly braces and parens autoclose in both HTML and JavaScript.
 7543    cx.update_editor(|editor, window, cx| {
 7544        editor.handle_input(" b=", window, cx);
 7545        editor.handle_input("{", window, cx);
 7546        editor.handle_input("c", window, cx);
 7547        editor.handle_input("(", window, cx);
 7548    });
 7549    cx.assert_editor_state(
 7550        &r#"
 7551            <body><a b={c(ˇ)}>
 7552                <script>
 7553                    var x = 1;<a b={c(ˇ)}
 7554                </script>
 7555            </body><a b={c(ˇ)}>
 7556        "#
 7557        .unindent(),
 7558    );
 7559
 7560    // Brackets that were already autoclosed are skipped.
 7561    cx.update_editor(|editor, window, cx| {
 7562        editor.handle_input(")", window, cx);
 7563        editor.handle_input("d", window, cx);
 7564        editor.handle_input("}", window, cx);
 7565    });
 7566    cx.assert_editor_state(
 7567        &r#"
 7568            <body><a b={c()d}ˇ>
 7569                <script>
 7570                    var x = 1;<a b={c()d}ˇ
 7571                </script>
 7572            </body><a b={c()d}ˇ>
 7573        "#
 7574        .unindent(),
 7575    );
 7576    cx.update_editor(|editor, window, cx| {
 7577        editor.handle_input(">", window, cx);
 7578    });
 7579    cx.assert_editor_state(
 7580        &r#"
 7581            <body><a b={c()d}>ˇ
 7582                <script>
 7583                    var x = 1;<a b={c()d}>ˇ
 7584                </script>
 7585            </body><a b={c()d}>ˇ
 7586        "#
 7587        .unindent(),
 7588    );
 7589
 7590    // Reset
 7591    cx.set_state(
 7592        &r#"
 7593            <body>ˇ
 7594                <script>
 7595                    var x = 1;ˇ
 7596                </script>
 7597            </body>ˇ
 7598        "#
 7599        .unindent(),
 7600    );
 7601
 7602    cx.update_editor(|editor, window, cx| {
 7603        editor.handle_input("<", window, cx);
 7604    });
 7605    cx.assert_editor_state(
 7606        &r#"
 7607            <body><ˇ>
 7608                <script>
 7609                    var x = 1;<ˇ
 7610                </script>
 7611            </body><ˇ>
 7612        "#
 7613        .unindent(),
 7614    );
 7615
 7616    // When backspacing, the closing angle brackets are removed.
 7617    cx.update_editor(|editor, window, cx| {
 7618        editor.backspace(&Backspace, window, cx);
 7619    });
 7620    cx.assert_editor_state(
 7621        &r#"
 7622            <body>ˇ
 7623                <script>
 7624                    var x = 1;ˇ
 7625                </script>
 7626            </body>ˇ
 7627        "#
 7628        .unindent(),
 7629    );
 7630
 7631    // Block comments autoclose in JavaScript, but not HTML.
 7632    cx.update_editor(|editor, window, cx| {
 7633        editor.handle_input("/", window, cx);
 7634        editor.handle_input("*", window, cx);
 7635    });
 7636    cx.assert_editor_state(
 7637        &r#"
 7638            <body>/*ˇ
 7639                <script>
 7640                    var x = 1;/*ˇ */
 7641                </script>
 7642            </body>/*ˇ
 7643        "#
 7644        .unindent(),
 7645    );
 7646}
 7647
 7648#[gpui::test]
 7649async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7650    init_test(cx, |_| {});
 7651
 7652    let mut cx = EditorTestContext::new(cx).await;
 7653
 7654    let rust_language = Arc::new(
 7655        Language::new(
 7656            LanguageConfig {
 7657                name: "Rust".into(),
 7658                brackets: serde_json::from_value(json!([
 7659                    { "start": "{", "end": "}", "close": true, "newline": true },
 7660                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7661                ]))
 7662                .unwrap(),
 7663                autoclose_before: "})]>".into(),
 7664                ..Default::default()
 7665            },
 7666            Some(tree_sitter_rust::LANGUAGE.into()),
 7667        )
 7668        .with_override_query("(string_literal) @string")
 7669        .unwrap(),
 7670    );
 7671
 7672    cx.language_registry().add(rust_language.clone());
 7673    cx.update_buffer(|buffer, cx| {
 7674        buffer.set_language(Some(rust_language), cx);
 7675    });
 7676
 7677    cx.set_state(
 7678        &r#"
 7679            let x = ˇ
 7680        "#
 7681        .unindent(),
 7682    );
 7683
 7684    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7685    cx.update_editor(|editor, window, cx| {
 7686        editor.handle_input("\"", window, cx);
 7687    });
 7688    cx.assert_editor_state(
 7689        &r#"
 7690            let x = "ˇ"
 7691        "#
 7692        .unindent(),
 7693    );
 7694
 7695    // Inserting another quotation mark. The cursor moves across the existing
 7696    // automatically-inserted quotation mark.
 7697    cx.update_editor(|editor, window, cx| {
 7698        editor.handle_input("\"", window, cx);
 7699    });
 7700    cx.assert_editor_state(
 7701        &r#"
 7702            let x = ""ˇ
 7703        "#
 7704        .unindent(),
 7705    );
 7706
 7707    // Reset
 7708    cx.set_state(
 7709        &r#"
 7710            let x = ˇ
 7711        "#
 7712        .unindent(),
 7713    );
 7714
 7715    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7716    cx.update_editor(|editor, window, cx| {
 7717        editor.handle_input("\"", window, cx);
 7718        editor.handle_input(" ", window, cx);
 7719        editor.move_left(&Default::default(), window, cx);
 7720        editor.handle_input("\\", window, cx);
 7721        editor.handle_input("\"", window, cx);
 7722    });
 7723    cx.assert_editor_state(
 7724        &r#"
 7725            let x = "\"ˇ "
 7726        "#
 7727        .unindent(),
 7728    );
 7729
 7730    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7731    // mark. Nothing is inserted.
 7732    cx.update_editor(|editor, window, cx| {
 7733        editor.move_right(&Default::default(), window, cx);
 7734        editor.handle_input("\"", window, cx);
 7735    });
 7736    cx.assert_editor_state(
 7737        &r#"
 7738            let x = "\" "ˇ
 7739        "#
 7740        .unindent(),
 7741    );
 7742}
 7743
 7744#[gpui::test]
 7745async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7746    init_test(cx, |_| {});
 7747
 7748    let language = Arc::new(Language::new(
 7749        LanguageConfig {
 7750            brackets: BracketPairConfig {
 7751                pairs: vec![
 7752                    BracketPair {
 7753                        start: "{".to_string(),
 7754                        end: "}".to_string(),
 7755                        close: true,
 7756                        surround: true,
 7757                        newline: true,
 7758                    },
 7759                    BracketPair {
 7760                        start: "/* ".to_string(),
 7761                        end: "*/".to_string(),
 7762                        close: true,
 7763                        surround: true,
 7764                        ..Default::default()
 7765                    },
 7766                ],
 7767                ..Default::default()
 7768            },
 7769            ..Default::default()
 7770        },
 7771        Some(tree_sitter_rust::LANGUAGE.into()),
 7772    ));
 7773
 7774    let text = r#"
 7775        a
 7776        b
 7777        c
 7778    "#
 7779    .unindent();
 7780
 7781    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7782    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7783    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7784    editor
 7785        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7786        .await;
 7787
 7788    editor.update_in(cx, |editor, window, cx| {
 7789        editor.change_selections(None, window, cx, |s| {
 7790            s.select_display_ranges([
 7791                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7792                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7793                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7794            ])
 7795        });
 7796
 7797        editor.handle_input("{", window, cx);
 7798        editor.handle_input("{", window, cx);
 7799        editor.handle_input("{", window, cx);
 7800        assert_eq!(
 7801            editor.text(cx),
 7802            "
 7803                {{{a}}}
 7804                {{{b}}}
 7805                {{{c}}}
 7806            "
 7807            .unindent()
 7808        );
 7809        assert_eq!(
 7810            editor.selections.display_ranges(cx),
 7811            [
 7812                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7813                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7814                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7815            ]
 7816        );
 7817
 7818        editor.undo(&Undo, window, cx);
 7819        editor.undo(&Undo, window, cx);
 7820        editor.undo(&Undo, window, cx);
 7821        assert_eq!(
 7822            editor.text(cx),
 7823            "
 7824                a
 7825                b
 7826                c
 7827            "
 7828            .unindent()
 7829        );
 7830        assert_eq!(
 7831            editor.selections.display_ranges(cx),
 7832            [
 7833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7834                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7835                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7836            ]
 7837        );
 7838
 7839        // Ensure inserting the first character of a multi-byte bracket pair
 7840        // doesn't surround the selections with the bracket.
 7841        editor.handle_input("/", window, cx);
 7842        assert_eq!(
 7843            editor.text(cx),
 7844            "
 7845                /
 7846                /
 7847                /
 7848            "
 7849            .unindent()
 7850        );
 7851        assert_eq!(
 7852            editor.selections.display_ranges(cx),
 7853            [
 7854                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7855                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7856                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7857            ]
 7858        );
 7859
 7860        editor.undo(&Undo, window, cx);
 7861        assert_eq!(
 7862            editor.text(cx),
 7863            "
 7864                a
 7865                b
 7866                c
 7867            "
 7868            .unindent()
 7869        );
 7870        assert_eq!(
 7871            editor.selections.display_ranges(cx),
 7872            [
 7873                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7874                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7875                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7876            ]
 7877        );
 7878
 7879        // Ensure inserting the last character of a multi-byte bracket pair
 7880        // doesn't surround the selections with the bracket.
 7881        editor.handle_input("*", window, cx);
 7882        assert_eq!(
 7883            editor.text(cx),
 7884            "
 7885                *
 7886                *
 7887                *
 7888            "
 7889            .unindent()
 7890        );
 7891        assert_eq!(
 7892            editor.selections.display_ranges(cx),
 7893            [
 7894                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7895                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7896                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7897            ]
 7898        );
 7899    });
 7900}
 7901
 7902#[gpui::test]
 7903async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7904    init_test(cx, |_| {});
 7905
 7906    let language = Arc::new(Language::new(
 7907        LanguageConfig {
 7908            brackets: BracketPairConfig {
 7909                pairs: vec![BracketPair {
 7910                    start: "{".to_string(),
 7911                    end: "}".to_string(),
 7912                    close: true,
 7913                    surround: true,
 7914                    newline: true,
 7915                }],
 7916                ..Default::default()
 7917            },
 7918            autoclose_before: "}".to_string(),
 7919            ..Default::default()
 7920        },
 7921        Some(tree_sitter_rust::LANGUAGE.into()),
 7922    ));
 7923
 7924    let text = r#"
 7925        a
 7926        b
 7927        c
 7928    "#
 7929    .unindent();
 7930
 7931    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7932    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7933    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7934    editor
 7935        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7936        .await;
 7937
 7938    editor.update_in(cx, |editor, window, cx| {
 7939        editor.change_selections(None, window, cx, |s| {
 7940            s.select_ranges([
 7941                Point::new(0, 1)..Point::new(0, 1),
 7942                Point::new(1, 1)..Point::new(1, 1),
 7943                Point::new(2, 1)..Point::new(2, 1),
 7944            ])
 7945        });
 7946
 7947        editor.handle_input("{", window, cx);
 7948        editor.handle_input("{", window, cx);
 7949        editor.handle_input("_", window, cx);
 7950        assert_eq!(
 7951            editor.text(cx),
 7952            "
 7953                a{{_}}
 7954                b{{_}}
 7955                c{{_}}
 7956            "
 7957            .unindent()
 7958        );
 7959        assert_eq!(
 7960            editor.selections.ranges::<Point>(cx),
 7961            [
 7962                Point::new(0, 4)..Point::new(0, 4),
 7963                Point::new(1, 4)..Point::new(1, 4),
 7964                Point::new(2, 4)..Point::new(2, 4)
 7965            ]
 7966        );
 7967
 7968        editor.backspace(&Default::default(), window, cx);
 7969        editor.backspace(&Default::default(), window, cx);
 7970        assert_eq!(
 7971            editor.text(cx),
 7972            "
 7973                a{}
 7974                b{}
 7975                c{}
 7976            "
 7977            .unindent()
 7978        );
 7979        assert_eq!(
 7980            editor.selections.ranges::<Point>(cx),
 7981            [
 7982                Point::new(0, 2)..Point::new(0, 2),
 7983                Point::new(1, 2)..Point::new(1, 2),
 7984                Point::new(2, 2)..Point::new(2, 2)
 7985            ]
 7986        );
 7987
 7988        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7989        assert_eq!(
 7990            editor.text(cx),
 7991            "
 7992                a
 7993                b
 7994                c
 7995            "
 7996            .unindent()
 7997        );
 7998        assert_eq!(
 7999            editor.selections.ranges::<Point>(cx),
 8000            [
 8001                Point::new(0, 1)..Point::new(0, 1),
 8002                Point::new(1, 1)..Point::new(1, 1),
 8003                Point::new(2, 1)..Point::new(2, 1)
 8004            ]
 8005        );
 8006    });
 8007}
 8008
 8009#[gpui::test]
 8010async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 8011    init_test(cx, |settings| {
 8012        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 8013    });
 8014
 8015    let mut cx = EditorTestContext::new(cx).await;
 8016
 8017    let language = Arc::new(Language::new(
 8018        LanguageConfig {
 8019            brackets: BracketPairConfig {
 8020                pairs: vec![
 8021                    BracketPair {
 8022                        start: "{".to_string(),
 8023                        end: "}".to_string(),
 8024                        close: true,
 8025                        surround: true,
 8026                        newline: true,
 8027                    },
 8028                    BracketPair {
 8029                        start: "(".to_string(),
 8030                        end: ")".to_string(),
 8031                        close: true,
 8032                        surround: true,
 8033                        newline: true,
 8034                    },
 8035                    BracketPair {
 8036                        start: "[".to_string(),
 8037                        end: "]".to_string(),
 8038                        close: false,
 8039                        surround: true,
 8040                        newline: true,
 8041                    },
 8042                ],
 8043                ..Default::default()
 8044            },
 8045            autoclose_before: "})]".to_string(),
 8046            ..Default::default()
 8047        },
 8048        Some(tree_sitter_rust::LANGUAGE.into()),
 8049    ));
 8050
 8051    cx.language_registry().add(language.clone());
 8052    cx.update_buffer(|buffer, cx| {
 8053        buffer.set_language(Some(language), cx);
 8054    });
 8055
 8056    cx.set_state(
 8057        &"
 8058            {(ˇ)}
 8059            [[ˇ]]
 8060            {(ˇ)}
 8061        "
 8062        .unindent(),
 8063    );
 8064
 8065    cx.update_editor(|editor, window, cx| {
 8066        editor.backspace(&Default::default(), window, cx);
 8067        editor.backspace(&Default::default(), window, cx);
 8068    });
 8069
 8070    cx.assert_editor_state(
 8071        &"
 8072            ˇ
 8073            ˇ]]
 8074            ˇ
 8075        "
 8076        .unindent(),
 8077    );
 8078
 8079    cx.update_editor(|editor, window, cx| {
 8080        editor.handle_input("{", window, cx);
 8081        editor.handle_input("{", window, cx);
 8082        editor.move_right(&MoveRight, window, cx);
 8083        editor.move_right(&MoveRight, window, cx);
 8084        editor.move_left(&MoveLeft, window, cx);
 8085        editor.move_left(&MoveLeft, window, cx);
 8086        editor.backspace(&Default::default(), window, cx);
 8087    });
 8088
 8089    cx.assert_editor_state(
 8090        &"
 8091            {ˇ}
 8092            {ˇ}]]
 8093            {ˇ}
 8094        "
 8095        .unindent(),
 8096    );
 8097
 8098    cx.update_editor(|editor, window, cx| {
 8099        editor.backspace(&Default::default(), window, cx);
 8100    });
 8101
 8102    cx.assert_editor_state(
 8103        &"
 8104            ˇ
 8105            ˇ]]
 8106            ˇ
 8107        "
 8108        .unindent(),
 8109    );
 8110}
 8111
 8112#[gpui::test]
 8113async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 8114    init_test(cx, |_| {});
 8115
 8116    let language = Arc::new(Language::new(
 8117        LanguageConfig::default(),
 8118        Some(tree_sitter_rust::LANGUAGE.into()),
 8119    ));
 8120
 8121    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 8122    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8123    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8124    editor
 8125        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 8126        .await;
 8127
 8128    editor.update_in(cx, |editor, window, cx| {
 8129        editor.set_auto_replace_emoji_shortcode(true);
 8130
 8131        editor.handle_input("Hello ", window, cx);
 8132        editor.handle_input(":wave", window, cx);
 8133        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 8134
 8135        editor.handle_input(":", window, cx);
 8136        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 8137
 8138        editor.handle_input(" :smile", window, cx);
 8139        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 8140
 8141        editor.handle_input(":", window, cx);
 8142        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 8143
 8144        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 8145        editor.handle_input(":wave", window, cx);
 8146        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 8147
 8148        editor.handle_input(":", window, cx);
 8149        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 8150
 8151        editor.handle_input(":1", window, cx);
 8152        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 8153
 8154        editor.handle_input(":", window, cx);
 8155        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 8156
 8157        // Ensure shortcode does not get replaced when it is part of a word
 8158        editor.handle_input(" Test:wave", window, cx);
 8159        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 8160
 8161        editor.handle_input(":", window, cx);
 8162        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 8163
 8164        editor.set_auto_replace_emoji_shortcode(false);
 8165
 8166        // Ensure shortcode does not get replaced when auto replace is off
 8167        editor.handle_input(" :wave", window, cx);
 8168        assert_eq!(
 8169            editor.text(cx),
 8170            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 8171        );
 8172
 8173        editor.handle_input(":", window, cx);
 8174        assert_eq!(
 8175            editor.text(cx),
 8176            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 8177        );
 8178    });
 8179}
 8180
 8181#[gpui::test]
 8182async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 8183    init_test(cx, |_| {});
 8184
 8185    let (text, insertion_ranges) = marked_text_ranges(
 8186        indoc! {"
 8187            ˇ
 8188        "},
 8189        false,
 8190    );
 8191
 8192    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8193    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8194
 8195    _ = editor.update_in(cx, |editor, window, cx| {
 8196        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 8197
 8198        editor
 8199            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8200            .unwrap();
 8201
 8202        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8203            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8204            assert_eq!(editor.text(cx), expected_text);
 8205            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8206        }
 8207
 8208        assert(
 8209            editor,
 8210            cx,
 8211            indoc! {"
 8212            type «» =•
 8213            "},
 8214        );
 8215
 8216        assert!(editor.context_menu_visible(), "There should be a matches");
 8217    });
 8218}
 8219
 8220#[gpui::test]
 8221async fn test_snippets(cx: &mut TestAppContext) {
 8222    init_test(cx, |_| {});
 8223
 8224    let (text, insertion_ranges) = marked_text_ranges(
 8225        indoc! {"
 8226            a.ˇ b
 8227            a.ˇ b
 8228            a.ˇ b
 8229        "},
 8230        false,
 8231    );
 8232
 8233    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 8234    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 8235
 8236    editor.update_in(cx, |editor, window, cx| {
 8237        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 8238
 8239        editor
 8240            .insert_snippet(&insertion_ranges, snippet, window, cx)
 8241            .unwrap();
 8242
 8243        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 8244            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 8245            assert_eq!(editor.text(cx), expected_text);
 8246            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 8247        }
 8248
 8249        assert(
 8250            editor,
 8251            cx,
 8252            indoc! {"
 8253                a.f(«one», two, «three») b
 8254                a.f(«one», two, «three») b
 8255                a.f(«one», two, «three») b
 8256            "},
 8257        );
 8258
 8259        // Can't move earlier than the first tab stop
 8260        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 8261        assert(
 8262            editor,
 8263            cx,
 8264            indoc! {"
 8265                a.f(«one», two, «three») b
 8266                a.f(«one», two, «three») b
 8267                a.f(«one», two, «three») b
 8268            "},
 8269        );
 8270
 8271        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8272        assert(
 8273            editor,
 8274            cx,
 8275            indoc! {"
 8276                a.f(one, «two», three) b
 8277                a.f(one, «two», three) b
 8278                a.f(one, «two», three) b
 8279            "},
 8280        );
 8281
 8282        editor.move_to_prev_snippet_tabstop(window, cx);
 8283        assert(
 8284            editor,
 8285            cx,
 8286            indoc! {"
 8287                a.f(«one», two, «three») b
 8288                a.f(«one», two, «three») b
 8289                a.f(«one», two, «three») b
 8290            "},
 8291        );
 8292
 8293        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8294        assert(
 8295            editor,
 8296            cx,
 8297            indoc! {"
 8298                a.f(one, «two», three) b
 8299                a.f(one, «two», three) b
 8300                a.f(one, «two», three) b
 8301            "},
 8302        );
 8303        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8304        assert(
 8305            editor,
 8306            cx,
 8307            indoc! {"
 8308                a.f(one, two, three)ˇ b
 8309                a.f(one, two, three)ˇ b
 8310                a.f(one, two, three)ˇ b
 8311            "},
 8312        );
 8313
 8314        // As soon as the last tab stop is reached, snippet state is gone
 8315        editor.move_to_prev_snippet_tabstop(window, cx);
 8316        assert(
 8317            editor,
 8318            cx,
 8319            indoc! {"
 8320                a.f(one, two, three)ˇ b
 8321                a.f(one, two, three)ˇ b
 8322                a.f(one, two, three)ˇ b
 8323            "},
 8324        );
 8325    });
 8326}
 8327
 8328#[gpui::test]
 8329async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8330    init_test(cx, |_| {});
 8331
 8332    let fs = FakeFs::new(cx.executor());
 8333    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8334
 8335    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8336
 8337    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8338    language_registry.add(rust_lang());
 8339    let mut fake_servers = language_registry.register_fake_lsp(
 8340        "Rust",
 8341        FakeLspAdapter {
 8342            capabilities: lsp::ServerCapabilities {
 8343                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8344                ..Default::default()
 8345            },
 8346            ..Default::default()
 8347        },
 8348    );
 8349
 8350    let buffer = project
 8351        .update(cx, |project, cx| {
 8352            project.open_local_buffer(path!("/file.rs"), cx)
 8353        })
 8354        .await
 8355        .unwrap();
 8356
 8357    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8358    let (editor, cx) = cx.add_window_view(|window, cx| {
 8359        build_editor_with_project(project.clone(), buffer, window, cx)
 8360    });
 8361    editor.update_in(cx, |editor, window, cx| {
 8362        editor.set_text("one\ntwo\nthree\n", window, cx)
 8363    });
 8364    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8365
 8366    cx.executor().start_waiting();
 8367    let fake_server = fake_servers.next().await.unwrap();
 8368
 8369    {
 8370        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8371            move |params, _| async move {
 8372                assert_eq!(
 8373                    params.text_document.uri,
 8374                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8375                );
 8376                assert_eq!(params.options.tab_size, 4);
 8377                Ok(Some(vec![lsp::TextEdit::new(
 8378                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8379                    ", ".to_string(),
 8380                )]))
 8381            },
 8382        );
 8383        let save = editor
 8384            .update_in(cx, |editor, window, cx| {
 8385                editor.save(true, project.clone(), window, cx)
 8386            })
 8387            .unwrap();
 8388        cx.executor().start_waiting();
 8389        save.await;
 8390
 8391        assert_eq!(
 8392            editor.update(cx, |editor, cx| editor.text(cx)),
 8393            "one, two\nthree\n"
 8394        );
 8395        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8396    }
 8397
 8398    {
 8399        editor.update_in(cx, |editor, window, cx| {
 8400            editor.set_text("one\ntwo\nthree\n", window, cx)
 8401        });
 8402        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8403
 8404        // Ensure we can still save even if formatting hangs.
 8405        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8406            move |params, _| async move {
 8407                assert_eq!(
 8408                    params.text_document.uri,
 8409                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8410                );
 8411                futures::future::pending::<()>().await;
 8412                unreachable!()
 8413            },
 8414        );
 8415        let save = editor
 8416            .update_in(cx, |editor, window, cx| {
 8417                editor.save(true, project.clone(), window, cx)
 8418            })
 8419            .unwrap();
 8420        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8421        cx.executor().start_waiting();
 8422        save.await;
 8423        assert_eq!(
 8424            editor.update(cx, |editor, cx| editor.text(cx)),
 8425            "one\ntwo\nthree\n"
 8426        );
 8427    }
 8428
 8429    // For non-dirty buffer, no formatting request should be sent
 8430    {
 8431        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8432
 8433        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8434            panic!("Should not be invoked on non-dirty buffer");
 8435        });
 8436        let save = editor
 8437            .update_in(cx, |editor, window, cx| {
 8438                editor.save(true, project.clone(), window, cx)
 8439            })
 8440            .unwrap();
 8441        cx.executor().start_waiting();
 8442        save.await;
 8443    }
 8444
 8445    // Set rust language override and assert overridden tabsize is sent to language server
 8446    update_test_language_settings(cx, |settings| {
 8447        settings.languages.insert(
 8448            "Rust".into(),
 8449            LanguageSettingsContent {
 8450                tab_size: NonZeroU32::new(8),
 8451                ..Default::default()
 8452            },
 8453        );
 8454    });
 8455
 8456    {
 8457        editor.update_in(cx, |editor, window, cx| {
 8458            editor.set_text("somehting_new\n", window, cx)
 8459        });
 8460        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8461        let _formatting_request_signal = fake_server
 8462            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8463                assert_eq!(
 8464                    params.text_document.uri,
 8465                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8466                );
 8467                assert_eq!(params.options.tab_size, 8);
 8468                Ok(Some(vec![]))
 8469            });
 8470        let save = editor
 8471            .update_in(cx, |editor, window, cx| {
 8472                editor.save(true, project.clone(), window, cx)
 8473            })
 8474            .unwrap();
 8475        cx.executor().start_waiting();
 8476        save.await;
 8477    }
 8478}
 8479
 8480#[gpui::test]
 8481async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8482    init_test(cx, |_| {});
 8483
 8484    let cols = 4;
 8485    let rows = 10;
 8486    let sample_text_1 = sample_text(rows, cols, 'a');
 8487    assert_eq!(
 8488        sample_text_1,
 8489        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8490    );
 8491    let sample_text_2 = sample_text(rows, cols, 'l');
 8492    assert_eq!(
 8493        sample_text_2,
 8494        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8495    );
 8496    let sample_text_3 = sample_text(rows, cols, 'v');
 8497    assert_eq!(
 8498        sample_text_3,
 8499        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8500    );
 8501
 8502    let fs = FakeFs::new(cx.executor());
 8503    fs.insert_tree(
 8504        path!("/a"),
 8505        json!({
 8506            "main.rs": sample_text_1,
 8507            "other.rs": sample_text_2,
 8508            "lib.rs": sample_text_3,
 8509        }),
 8510    )
 8511    .await;
 8512
 8513    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8514    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8515    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8516
 8517    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8518    language_registry.add(rust_lang());
 8519    let mut fake_servers = language_registry.register_fake_lsp(
 8520        "Rust",
 8521        FakeLspAdapter {
 8522            capabilities: lsp::ServerCapabilities {
 8523                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8524                ..Default::default()
 8525            },
 8526            ..Default::default()
 8527        },
 8528    );
 8529
 8530    let worktree = project.update(cx, |project, cx| {
 8531        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8532        assert_eq!(worktrees.len(), 1);
 8533        worktrees.pop().unwrap()
 8534    });
 8535    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8536
 8537    let buffer_1 = project
 8538        .update(cx, |project, cx| {
 8539            project.open_buffer((worktree_id, "main.rs"), cx)
 8540        })
 8541        .await
 8542        .unwrap();
 8543    let buffer_2 = project
 8544        .update(cx, |project, cx| {
 8545            project.open_buffer((worktree_id, "other.rs"), cx)
 8546        })
 8547        .await
 8548        .unwrap();
 8549    let buffer_3 = project
 8550        .update(cx, |project, cx| {
 8551            project.open_buffer((worktree_id, "lib.rs"), cx)
 8552        })
 8553        .await
 8554        .unwrap();
 8555
 8556    let multi_buffer = cx.new(|cx| {
 8557        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8558        multi_buffer.push_excerpts(
 8559            buffer_1.clone(),
 8560            [
 8561                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8562                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8563                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8564            ],
 8565            cx,
 8566        );
 8567        multi_buffer.push_excerpts(
 8568            buffer_2.clone(),
 8569            [
 8570                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8571                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8572                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8573            ],
 8574            cx,
 8575        );
 8576        multi_buffer.push_excerpts(
 8577            buffer_3.clone(),
 8578            [
 8579                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8580                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8581                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8582            ],
 8583            cx,
 8584        );
 8585        multi_buffer
 8586    });
 8587    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8588        Editor::new(
 8589            EditorMode::full(),
 8590            multi_buffer,
 8591            Some(project.clone()),
 8592            window,
 8593            cx,
 8594        )
 8595    });
 8596
 8597    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8598        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8599            s.select_ranges(Some(1..2))
 8600        });
 8601        editor.insert("|one|two|three|", window, cx);
 8602    });
 8603    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8604    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8605        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8606            s.select_ranges(Some(60..70))
 8607        });
 8608        editor.insert("|four|five|six|", window, cx);
 8609    });
 8610    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8611
 8612    // First two buffers should be edited, but not the third one.
 8613    assert_eq!(
 8614        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8615        "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}",
 8616    );
 8617    buffer_1.update(cx, |buffer, _| {
 8618        assert!(buffer.is_dirty());
 8619        assert_eq!(
 8620            buffer.text(),
 8621            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8622        )
 8623    });
 8624    buffer_2.update(cx, |buffer, _| {
 8625        assert!(buffer.is_dirty());
 8626        assert_eq!(
 8627            buffer.text(),
 8628            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8629        )
 8630    });
 8631    buffer_3.update(cx, |buffer, _| {
 8632        assert!(!buffer.is_dirty());
 8633        assert_eq!(buffer.text(), sample_text_3,)
 8634    });
 8635    cx.executor().run_until_parked();
 8636
 8637    cx.executor().start_waiting();
 8638    let save = multi_buffer_editor
 8639        .update_in(cx, |editor, window, cx| {
 8640            editor.save(true, project.clone(), window, cx)
 8641        })
 8642        .unwrap();
 8643
 8644    let fake_server = fake_servers.next().await.unwrap();
 8645    fake_server
 8646        .server
 8647        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8648            Ok(Some(vec![lsp::TextEdit::new(
 8649                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8650                format!("[{} formatted]", params.text_document.uri),
 8651            )]))
 8652        })
 8653        .detach();
 8654    save.await;
 8655
 8656    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8657    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8658    assert_eq!(
 8659        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8660        uri!(
 8661            "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}"
 8662        ),
 8663    );
 8664    buffer_1.update(cx, |buffer, _| {
 8665        assert!(!buffer.is_dirty());
 8666        assert_eq!(
 8667            buffer.text(),
 8668            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8669        )
 8670    });
 8671    buffer_2.update(cx, |buffer, _| {
 8672        assert!(!buffer.is_dirty());
 8673        assert_eq!(
 8674            buffer.text(),
 8675            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8676        )
 8677    });
 8678    buffer_3.update(cx, |buffer, _| {
 8679        assert!(!buffer.is_dirty());
 8680        assert_eq!(buffer.text(), sample_text_3,)
 8681    });
 8682}
 8683
 8684#[gpui::test]
 8685async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8686    init_test(cx, |_| {});
 8687
 8688    let fs = FakeFs::new(cx.executor());
 8689    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8690
 8691    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8692
 8693    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8694    language_registry.add(rust_lang());
 8695    let mut fake_servers = language_registry.register_fake_lsp(
 8696        "Rust",
 8697        FakeLspAdapter {
 8698            capabilities: lsp::ServerCapabilities {
 8699                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8700                ..Default::default()
 8701            },
 8702            ..Default::default()
 8703        },
 8704    );
 8705
 8706    let buffer = project
 8707        .update(cx, |project, cx| {
 8708            project.open_local_buffer(path!("/file.rs"), cx)
 8709        })
 8710        .await
 8711        .unwrap();
 8712
 8713    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8714    let (editor, cx) = cx.add_window_view(|window, cx| {
 8715        build_editor_with_project(project.clone(), buffer, window, cx)
 8716    });
 8717    editor.update_in(cx, |editor, window, cx| {
 8718        editor.set_text("one\ntwo\nthree\n", window, cx)
 8719    });
 8720    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8721
 8722    cx.executor().start_waiting();
 8723    let fake_server = fake_servers.next().await.unwrap();
 8724
 8725    let save = editor
 8726        .update_in(cx, |editor, window, cx| {
 8727            editor.save(true, project.clone(), window, cx)
 8728        })
 8729        .unwrap();
 8730    fake_server
 8731        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8732            assert_eq!(
 8733                params.text_document.uri,
 8734                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8735            );
 8736            assert_eq!(params.options.tab_size, 4);
 8737            Ok(Some(vec![lsp::TextEdit::new(
 8738                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8739                ", ".to_string(),
 8740            )]))
 8741        })
 8742        .next()
 8743        .await;
 8744    cx.executor().start_waiting();
 8745    save.await;
 8746    assert_eq!(
 8747        editor.update(cx, |editor, cx| editor.text(cx)),
 8748        "one, two\nthree\n"
 8749    );
 8750    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8751
 8752    editor.update_in(cx, |editor, window, cx| {
 8753        editor.set_text("one\ntwo\nthree\n", window, cx)
 8754    });
 8755    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8756
 8757    // Ensure we can still save even if formatting hangs.
 8758    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8759        move |params, _| async move {
 8760            assert_eq!(
 8761                params.text_document.uri,
 8762                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8763            );
 8764            futures::future::pending::<()>().await;
 8765            unreachable!()
 8766        },
 8767    );
 8768    let save = editor
 8769        .update_in(cx, |editor, window, cx| {
 8770            editor.save(true, project.clone(), window, cx)
 8771        })
 8772        .unwrap();
 8773    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8774    cx.executor().start_waiting();
 8775    save.await;
 8776    assert_eq!(
 8777        editor.update(cx, |editor, cx| editor.text(cx)),
 8778        "one\ntwo\nthree\n"
 8779    );
 8780    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8781
 8782    // For non-dirty buffer, no formatting request should be sent
 8783    let save = editor
 8784        .update_in(cx, |editor, window, cx| {
 8785            editor.save(true, project.clone(), window, cx)
 8786        })
 8787        .unwrap();
 8788    let _pending_format_request = fake_server
 8789        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8790            panic!("Should not be invoked on non-dirty buffer");
 8791        })
 8792        .next();
 8793    cx.executor().start_waiting();
 8794    save.await;
 8795
 8796    // Set Rust language override and assert overridden tabsize is sent to language server
 8797    update_test_language_settings(cx, |settings| {
 8798        settings.languages.insert(
 8799            "Rust".into(),
 8800            LanguageSettingsContent {
 8801                tab_size: NonZeroU32::new(8),
 8802                ..Default::default()
 8803            },
 8804        );
 8805    });
 8806
 8807    editor.update_in(cx, |editor, window, cx| {
 8808        editor.set_text("somehting_new\n", window, cx)
 8809    });
 8810    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8811    let save = editor
 8812        .update_in(cx, |editor, window, cx| {
 8813            editor.save(true, project.clone(), window, cx)
 8814        })
 8815        .unwrap();
 8816    fake_server
 8817        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8818            assert_eq!(
 8819                params.text_document.uri,
 8820                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8821            );
 8822            assert_eq!(params.options.tab_size, 8);
 8823            Ok(Some(vec![]))
 8824        })
 8825        .next()
 8826        .await;
 8827    cx.executor().start_waiting();
 8828    save.await;
 8829}
 8830
 8831#[gpui::test]
 8832async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8833    init_test(cx, |settings| {
 8834        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8835            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8836        ))
 8837    });
 8838
 8839    let fs = FakeFs::new(cx.executor());
 8840    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8841
 8842    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8843
 8844    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8845    language_registry.add(Arc::new(Language::new(
 8846        LanguageConfig {
 8847            name: "Rust".into(),
 8848            matcher: LanguageMatcher {
 8849                path_suffixes: vec!["rs".to_string()],
 8850                ..Default::default()
 8851            },
 8852            ..LanguageConfig::default()
 8853        },
 8854        Some(tree_sitter_rust::LANGUAGE.into()),
 8855    )));
 8856    update_test_language_settings(cx, |settings| {
 8857        // Enable Prettier formatting for the same buffer, and ensure
 8858        // LSP is called instead of Prettier.
 8859        settings.defaults.prettier = Some(PrettierSettings {
 8860            allowed: true,
 8861            ..PrettierSettings::default()
 8862        });
 8863    });
 8864    let mut fake_servers = language_registry.register_fake_lsp(
 8865        "Rust",
 8866        FakeLspAdapter {
 8867            capabilities: lsp::ServerCapabilities {
 8868                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8869                ..Default::default()
 8870            },
 8871            ..Default::default()
 8872        },
 8873    );
 8874
 8875    let buffer = project
 8876        .update(cx, |project, cx| {
 8877            project.open_local_buffer(path!("/file.rs"), cx)
 8878        })
 8879        .await
 8880        .unwrap();
 8881
 8882    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8883    let (editor, cx) = cx.add_window_view(|window, cx| {
 8884        build_editor_with_project(project.clone(), buffer, window, cx)
 8885    });
 8886    editor.update_in(cx, |editor, window, cx| {
 8887        editor.set_text("one\ntwo\nthree\n", window, cx)
 8888    });
 8889
 8890    cx.executor().start_waiting();
 8891    let fake_server = fake_servers.next().await.unwrap();
 8892
 8893    let format = editor
 8894        .update_in(cx, |editor, window, cx| {
 8895            editor.perform_format(
 8896                project.clone(),
 8897                FormatTrigger::Manual,
 8898                FormatTarget::Buffers,
 8899                window,
 8900                cx,
 8901            )
 8902        })
 8903        .unwrap();
 8904    fake_server
 8905        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8906            assert_eq!(
 8907                params.text_document.uri,
 8908                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8909            );
 8910            assert_eq!(params.options.tab_size, 4);
 8911            Ok(Some(vec![lsp::TextEdit::new(
 8912                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8913                ", ".to_string(),
 8914            )]))
 8915        })
 8916        .next()
 8917        .await;
 8918    cx.executor().start_waiting();
 8919    format.await;
 8920    assert_eq!(
 8921        editor.update(cx, |editor, cx| editor.text(cx)),
 8922        "one, two\nthree\n"
 8923    );
 8924
 8925    editor.update_in(cx, |editor, window, cx| {
 8926        editor.set_text("one\ntwo\nthree\n", window, cx)
 8927    });
 8928    // Ensure we don't lock if formatting hangs.
 8929    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8930        move |params, _| async move {
 8931            assert_eq!(
 8932                params.text_document.uri,
 8933                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8934            );
 8935            futures::future::pending::<()>().await;
 8936            unreachable!()
 8937        },
 8938    );
 8939    let format = editor
 8940        .update_in(cx, |editor, window, cx| {
 8941            editor.perform_format(
 8942                project,
 8943                FormatTrigger::Manual,
 8944                FormatTarget::Buffers,
 8945                window,
 8946                cx,
 8947            )
 8948        })
 8949        .unwrap();
 8950    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8951    cx.executor().start_waiting();
 8952    format.await;
 8953    assert_eq!(
 8954        editor.update(cx, |editor, cx| editor.text(cx)),
 8955        "one\ntwo\nthree\n"
 8956    );
 8957}
 8958
 8959#[gpui::test]
 8960async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8961    init_test(cx, |settings| {
 8962        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8963        settings.defaults.formatter =
 8964            Some(language_settings::SelectedFormatter::List(FormatterList(
 8965                vec![
 8966                    Formatter::LanguageServer { name: None },
 8967                    Formatter::CodeActions(
 8968                        [
 8969                            ("code-action-1".into(), true),
 8970                            ("code-action-2".into(), true),
 8971                        ]
 8972                        .into_iter()
 8973                        .collect(),
 8974                    ),
 8975                ]
 8976                .into(),
 8977            )))
 8978    });
 8979
 8980    let fs = FakeFs::new(cx.executor());
 8981    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8982        .await;
 8983
 8984    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8985    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8986    language_registry.add(rust_lang());
 8987
 8988    let mut fake_servers = language_registry.register_fake_lsp(
 8989        "Rust",
 8990        FakeLspAdapter {
 8991            capabilities: lsp::ServerCapabilities {
 8992                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8993                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8994                    commands: vec!["the-command-for-code-action-1".into()],
 8995                    ..Default::default()
 8996                }),
 8997                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8998                ..Default::default()
 8999            },
 9000            ..Default::default()
 9001        },
 9002    );
 9003
 9004    let buffer = project
 9005        .update(cx, |project, cx| {
 9006            project.open_local_buffer(path!("/file.rs"), cx)
 9007        })
 9008        .await
 9009        .unwrap();
 9010
 9011    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9012    let (editor, cx) = cx.add_window_view(|window, cx| {
 9013        build_editor_with_project(project.clone(), buffer, window, cx)
 9014    });
 9015
 9016    cx.executor().start_waiting();
 9017
 9018    let fake_server = fake_servers.next().await.unwrap();
 9019    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 9020        move |_params, _| async move {
 9021            Ok(Some(vec![lsp::TextEdit::new(
 9022                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9023                "applied-formatting\n".to_string(),
 9024            )]))
 9025        },
 9026    );
 9027    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9028        move |params, _| async move {
 9029            assert_eq!(
 9030                params.context.only,
 9031                Some(vec!["code-action-1".into(), "code-action-2".into()])
 9032            );
 9033            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 9034            Ok(Some(vec![
 9035                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9036                    kind: Some("code-action-1".into()),
 9037                    edit: Some(lsp::WorkspaceEdit::new(
 9038                        [(
 9039                            uri.clone(),
 9040                            vec![lsp::TextEdit::new(
 9041                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9042                                "applied-code-action-1-edit\n".to_string(),
 9043                            )],
 9044                        )]
 9045                        .into_iter()
 9046                        .collect(),
 9047                    )),
 9048                    command: Some(lsp::Command {
 9049                        command: "the-command-for-code-action-1".into(),
 9050                        ..Default::default()
 9051                    }),
 9052                    ..Default::default()
 9053                }),
 9054                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 9055                    kind: Some("code-action-2".into()),
 9056                    edit: Some(lsp::WorkspaceEdit::new(
 9057                        [(
 9058                            uri.clone(),
 9059                            vec![lsp::TextEdit::new(
 9060                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 9061                                "applied-code-action-2-edit\n".to_string(),
 9062                            )],
 9063                        )]
 9064                        .into_iter()
 9065                        .collect(),
 9066                    )),
 9067                    ..Default::default()
 9068                }),
 9069            ]))
 9070        },
 9071    );
 9072
 9073    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 9074        move |params, _| async move { Ok(params) }
 9075    });
 9076
 9077    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 9078    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 9079        let fake = fake_server.clone();
 9080        let lock = command_lock.clone();
 9081        move |params, _| {
 9082            assert_eq!(params.command, "the-command-for-code-action-1");
 9083            let fake = fake.clone();
 9084            let lock = lock.clone();
 9085            async move {
 9086                lock.lock().await;
 9087                fake.server
 9088                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 9089                        label: None,
 9090                        edit: lsp::WorkspaceEdit {
 9091                            changes: Some(
 9092                                [(
 9093                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 9094                                    vec![lsp::TextEdit {
 9095                                        range: lsp::Range::new(
 9096                                            lsp::Position::new(0, 0),
 9097                                            lsp::Position::new(0, 0),
 9098                                        ),
 9099                                        new_text: "applied-code-action-1-command\n".into(),
 9100                                    }],
 9101                                )]
 9102                                .into_iter()
 9103                                .collect(),
 9104                            ),
 9105                            ..Default::default()
 9106                        },
 9107                    })
 9108                    .await
 9109                    .into_response()
 9110                    .unwrap();
 9111                Ok(Some(json!(null)))
 9112            }
 9113        }
 9114    });
 9115
 9116    cx.executor().start_waiting();
 9117    editor
 9118        .update_in(cx, |editor, window, cx| {
 9119            editor.perform_format(
 9120                project.clone(),
 9121                FormatTrigger::Manual,
 9122                FormatTarget::Buffers,
 9123                window,
 9124                cx,
 9125            )
 9126        })
 9127        .unwrap()
 9128        .await;
 9129    editor.update(cx, |editor, cx| {
 9130        assert_eq!(
 9131            editor.text(cx),
 9132            r#"
 9133                applied-code-action-2-edit
 9134                applied-code-action-1-command
 9135                applied-code-action-1-edit
 9136                applied-formatting
 9137                one
 9138                two
 9139                three
 9140            "#
 9141            .unindent()
 9142        );
 9143    });
 9144
 9145    editor.update_in(cx, |editor, window, cx| {
 9146        editor.undo(&Default::default(), window, cx);
 9147        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9148    });
 9149
 9150    // Perform a manual edit while waiting for an LSP command
 9151    // that's being run as part of a formatting code action.
 9152    let lock_guard = command_lock.lock().await;
 9153    let format = editor
 9154        .update_in(cx, |editor, window, cx| {
 9155            editor.perform_format(
 9156                project.clone(),
 9157                FormatTrigger::Manual,
 9158                FormatTarget::Buffers,
 9159                window,
 9160                cx,
 9161            )
 9162        })
 9163        .unwrap();
 9164    cx.run_until_parked();
 9165    editor.update(cx, |editor, cx| {
 9166        assert_eq!(
 9167            editor.text(cx),
 9168            r#"
 9169                applied-code-action-1-edit
 9170                applied-formatting
 9171                one
 9172                two
 9173                three
 9174            "#
 9175            .unindent()
 9176        );
 9177
 9178        editor.buffer.update(cx, |buffer, cx| {
 9179            let ix = buffer.len(cx);
 9180            buffer.edit([(ix..ix, "edited\n")], None, cx);
 9181        });
 9182    });
 9183
 9184    // Allow the LSP command to proceed. Because the buffer was edited,
 9185    // the second code action will not be run.
 9186    drop(lock_guard);
 9187    format.await;
 9188    editor.update_in(cx, |editor, window, cx| {
 9189        assert_eq!(
 9190            editor.text(cx),
 9191            r#"
 9192                applied-code-action-1-command
 9193                applied-code-action-1-edit
 9194                applied-formatting
 9195                one
 9196                two
 9197                three
 9198                edited
 9199            "#
 9200            .unindent()
 9201        );
 9202
 9203        // The manual edit is undone first, because it is the last thing the user did
 9204        // (even though the command completed afterwards).
 9205        editor.undo(&Default::default(), window, cx);
 9206        assert_eq!(
 9207            editor.text(cx),
 9208            r#"
 9209                applied-code-action-1-command
 9210                applied-code-action-1-edit
 9211                applied-formatting
 9212                one
 9213                two
 9214                three
 9215            "#
 9216            .unindent()
 9217        );
 9218
 9219        // All the formatting (including the command, which completed after the manual edit)
 9220        // is undone together.
 9221        editor.undo(&Default::default(), window, cx);
 9222        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 9223    });
 9224}
 9225
 9226#[gpui::test]
 9227async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 9228    init_test(cx, |settings| {
 9229        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 9230            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 9231        ))
 9232    });
 9233
 9234    let fs = FakeFs::new(cx.executor());
 9235    fs.insert_file(path!("/file.ts"), Default::default()).await;
 9236
 9237    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 9238
 9239    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9240    language_registry.add(Arc::new(Language::new(
 9241        LanguageConfig {
 9242            name: "TypeScript".into(),
 9243            matcher: LanguageMatcher {
 9244                path_suffixes: vec!["ts".to_string()],
 9245                ..Default::default()
 9246            },
 9247            ..LanguageConfig::default()
 9248        },
 9249        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9250    )));
 9251    update_test_language_settings(cx, |settings| {
 9252        settings.defaults.prettier = Some(PrettierSettings {
 9253            allowed: true,
 9254            ..PrettierSettings::default()
 9255        });
 9256    });
 9257    let mut fake_servers = language_registry.register_fake_lsp(
 9258        "TypeScript",
 9259        FakeLspAdapter {
 9260            capabilities: lsp::ServerCapabilities {
 9261                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 9262                ..Default::default()
 9263            },
 9264            ..Default::default()
 9265        },
 9266    );
 9267
 9268    let buffer = project
 9269        .update(cx, |project, cx| {
 9270            project.open_local_buffer(path!("/file.ts"), cx)
 9271        })
 9272        .await
 9273        .unwrap();
 9274
 9275    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9276    let (editor, cx) = cx.add_window_view(|window, cx| {
 9277        build_editor_with_project(project.clone(), buffer, window, cx)
 9278    });
 9279    editor.update_in(cx, |editor, window, cx| {
 9280        editor.set_text(
 9281            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9282            window,
 9283            cx,
 9284        )
 9285    });
 9286
 9287    cx.executor().start_waiting();
 9288    let fake_server = fake_servers.next().await.unwrap();
 9289
 9290    let format = editor
 9291        .update_in(cx, |editor, window, cx| {
 9292            editor.perform_code_action_kind(
 9293                project.clone(),
 9294                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9295                window,
 9296                cx,
 9297            )
 9298        })
 9299        .unwrap();
 9300    fake_server
 9301        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9302            assert_eq!(
 9303                params.text_document.uri,
 9304                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9305            );
 9306            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9307                lsp::CodeAction {
 9308                    title: "Organize Imports".to_string(),
 9309                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9310                    edit: Some(lsp::WorkspaceEdit {
 9311                        changes: Some(
 9312                            [(
 9313                                params.text_document.uri.clone(),
 9314                                vec![lsp::TextEdit::new(
 9315                                    lsp::Range::new(
 9316                                        lsp::Position::new(1, 0),
 9317                                        lsp::Position::new(2, 0),
 9318                                    ),
 9319                                    "".to_string(),
 9320                                )],
 9321                            )]
 9322                            .into_iter()
 9323                            .collect(),
 9324                        ),
 9325                        ..Default::default()
 9326                    }),
 9327                    ..Default::default()
 9328                },
 9329            )]))
 9330        })
 9331        .next()
 9332        .await;
 9333    cx.executor().start_waiting();
 9334    format.await;
 9335    assert_eq!(
 9336        editor.update(cx, |editor, cx| editor.text(cx)),
 9337        "import { a } from 'module';\n\nconst x = a;\n"
 9338    );
 9339
 9340    editor.update_in(cx, |editor, window, cx| {
 9341        editor.set_text(
 9342            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9343            window,
 9344            cx,
 9345        )
 9346    });
 9347    // Ensure we don't lock if code action hangs.
 9348    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9349        move |params, _| async move {
 9350            assert_eq!(
 9351                params.text_document.uri,
 9352                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9353            );
 9354            futures::future::pending::<()>().await;
 9355            unreachable!()
 9356        },
 9357    );
 9358    let format = editor
 9359        .update_in(cx, |editor, window, cx| {
 9360            editor.perform_code_action_kind(
 9361                project,
 9362                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9363                window,
 9364                cx,
 9365            )
 9366        })
 9367        .unwrap();
 9368    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9369    cx.executor().start_waiting();
 9370    format.await;
 9371    assert_eq!(
 9372        editor.update(cx, |editor, cx| editor.text(cx)),
 9373        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9374    );
 9375}
 9376
 9377#[gpui::test]
 9378async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9379    init_test(cx, |_| {});
 9380
 9381    let mut cx = EditorLspTestContext::new_rust(
 9382        lsp::ServerCapabilities {
 9383            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9384            ..Default::default()
 9385        },
 9386        cx,
 9387    )
 9388    .await;
 9389
 9390    cx.set_state(indoc! {"
 9391        one.twoˇ
 9392    "});
 9393
 9394    // The format request takes a long time. When it completes, it inserts
 9395    // a newline and an indent before the `.`
 9396    cx.lsp
 9397        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9398            let executor = cx.background_executor().clone();
 9399            async move {
 9400                executor.timer(Duration::from_millis(100)).await;
 9401                Ok(Some(vec![lsp::TextEdit {
 9402                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9403                    new_text: "\n    ".into(),
 9404                }]))
 9405            }
 9406        });
 9407
 9408    // Submit a format request.
 9409    let format_1 = cx
 9410        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9411        .unwrap();
 9412    cx.executor().run_until_parked();
 9413
 9414    // Submit a second format request.
 9415    let format_2 = cx
 9416        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9417        .unwrap();
 9418    cx.executor().run_until_parked();
 9419
 9420    // Wait for both format requests to complete
 9421    cx.executor().advance_clock(Duration::from_millis(200));
 9422    cx.executor().start_waiting();
 9423    format_1.await.unwrap();
 9424    cx.executor().start_waiting();
 9425    format_2.await.unwrap();
 9426
 9427    // The formatting edits only happens once.
 9428    cx.assert_editor_state(indoc! {"
 9429        one
 9430            .twoˇ
 9431    "});
 9432}
 9433
 9434#[gpui::test]
 9435async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9436    init_test(cx, |settings| {
 9437        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9438    });
 9439
 9440    let mut cx = EditorLspTestContext::new_rust(
 9441        lsp::ServerCapabilities {
 9442            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9443            ..Default::default()
 9444        },
 9445        cx,
 9446    )
 9447    .await;
 9448
 9449    // Set up a buffer white some trailing whitespace and no trailing newline.
 9450    cx.set_state(
 9451        &[
 9452            "one ",   //
 9453            "twoˇ",   //
 9454            "three ", //
 9455            "four",   //
 9456        ]
 9457        .join("\n"),
 9458    );
 9459
 9460    // Submit a format request.
 9461    let format = cx
 9462        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9463        .unwrap();
 9464
 9465    // Record which buffer changes have been sent to the language server
 9466    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9467    cx.lsp
 9468        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9469            let buffer_changes = buffer_changes.clone();
 9470            move |params, _| {
 9471                buffer_changes.lock().extend(
 9472                    params
 9473                        .content_changes
 9474                        .into_iter()
 9475                        .map(|e| (e.range.unwrap(), e.text)),
 9476                );
 9477            }
 9478        });
 9479
 9480    // Handle formatting requests to the language server.
 9481    cx.lsp
 9482        .set_request_handler::<lsp::request::Formatting, _, _>({
 9483            let buffer_changes = buffer_changes.clone();
 9484            move |_, _| {
 9485                // When formatting is requested, trailing whitespace has already been stripped,
 9486                // and the trailing newline has already been added.
 9487                assert_eq!(
 9488                    &buffer_changes.lock()[1..],
 9489                    &[
 9490                        (
 9491                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9492                            "".into()
 9493                        ),
 9494                        (
 9495                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9496                            "".into()
 9497                        ),
 9498                        (
 9499                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9500                            "\n".into()
 9501                        ),
 9502                    ]
 9503                );
 9504
 9505                // Insert blank lines between each line of the buffer.
 9506                async move {
 9507                    Ok(Some(vec![
 9508                        lsp::TextEdit {
 9509                            range: lsp::Range::new(
 9510                                lsp::Position::new(1, 0),
 9511                                lsp::Position::new(1, 0),
 9512                            ),
 9513                            new_text: "\n".into(),
 9514                        },
 9515                        lsp::TextEdit {
 9516                            range: lsp::Range::new(
 9517                                lsp::Position::new(2, 0),
 9518                                lsp::Position::new(2, 0),
 9519                            ),
 9520                            new_text: "\n".into(),
 9521                        },
 9522                    ]))
 9523                }
 9524            }
 9525        });
 9526
 9527    // After formatting the buffer, the trailing whitespace is stripped,
 9528    // a newline is appended, and the edits provided by the language server
 9529    // have been applied.
 9530    format.await.unwrap();
 9531    cx.assert_editor_state(
 9532        &[
 9533            "one",   //
 9534            "",      //
 9535            "twoˇ",  //
 9536            "",      //
 9537            "three", //
 9538            "four",  //
 9539            "",      //
 9540        ]
 9541        .join("\n"),
 9542    );
 9543
 9544    // Undoing the formatting undoes the trailing whitespace removal, the
 9545    // trailing newline, and the LSP edits.
 9546    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9547    cx.assert_editor_state(
 9548        &[
 9549            "one ",   //
 9550            "twoˇ",   //
 9551            "three ", //
 9552            "four",   //
 9553        ]
 9554        .join("\n"),
 9555    );
 9556}
 9557
 9558#[gpui::test]
 9559async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9560    cx: &mut TestAppContext,
 9561) {
 9562    init_test(cx, |_| {});
 9563
 9564    cx.update(|cx| {
 9565        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9566            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9567                settings.auto_signature_help = Some(true);
 9568            });
 9569        });
 9570    });
 9571
 9572    let mut cx = EditorLspTestContext::new_rust(
 9573        lsp::ServerCapabilities {
 9574            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9575                ..Default::default()
 9576            }),
 9577            ..Default::default()
 9578        },
 9579        cx,
 9580    )
 9581    .await;
 9582
 9583    let language = Language::new(
 9584        LanguageConfig {
 9585            name: "Rust".into(),
 9586            brackets: BracketPairConfig {
 9587                pairs: vec![
 9588                    BracketPair {
 9589                        start: "{".to_string(),
 9590                        end: "}".to_string(),
 9591                        close: true,
 9592                        surround: true,
 9593                        newline: true,
 9594                    },
 9595                    BracketPair {
 9596                        start: "(".to_string(),
 9597                        end: ")".to_string(),
 9598                        close: true,
 9599                        surround: true,
 9600                        newline: true,
 9601                    },
 9602                    BracketPair {
 9603                        start: "/*".to_string(),
 9604                        end: " */".to_string(),
 9605                        close: true,
 9606                        surround: true,
 9607                        newline: true,
 9608                    },
 9609                    BracketPair {
 9610                        start: "[".to_string(),
 9611                        end: "]".to_string(),
 9612                        close: false,
 9613                        surround: false,
 9614                        newline: true,
 9615                    },
 9616                    BracketPair {
 9617                        start: "\"".to_string(),
 9618                        end: "\"".to_string(),
 9619                        close: true,
 9620                        surround: true,
 9621                        newline: false,
 9622                    },
 9623                    BracketPair {
 9624                        start: "<".to_string(),
 9625                        end: ">".to_string(),
 9626                        close: false,
 9627                        surround: true,
 9628                        newline: true,
 9629                    },
 9630                ],
 9631                ..Default::default()
 9632            },
 9633            autoclose_before: "})]".to_string(),
 9634            ..Default::default()
 9635        },
 9636        Some(tree_sitter_rust::LANGUAGE.into()),
 9637    );
 9638    let language = Arc::new(language);
 9639
 9640    cx.language_registry().add(language.clone());
 9641    cx.update_buffer(|buffer, cx| {
 9642        buffer.set_language(Some(language), cx);
 9643    });
 9644
 9645    cx.set_state(
 9646        &r#"
 9647            fn main() {
 9648                sampleˇ
 9649            }
 9650        "#
 9651        .unindent(),
 9652    );
 9653
 9654    cx.update_editor(|editor, window, cx| {
 9655        editor.handle_input("(", window, cx);
 9656    });
 9657    cx.assert_editor_state(
 9658        &"
 9659            fn main() {
 9660                sample(ˇ)
 9661            }
 9662        "
 9663        .unindent(),
 9664    );
 9665
 9666    let mocked_response = lsp::SignatureHelp {
 9667        signatures: vec![lsp::SignatureInformation {
 9668            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9669            documentation: None,
 9670            parameters: Some(vec![
 9671                lsp::ParameterInformation {
 9672                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9673                    documentation: None,
 9674                },
 9675                lsp::ParameterInformation {
 9676                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9677                    documentation: None,
 9678                },
 9679            ]),
 9680            active_parameter: None,
 9681        }],
 9682        active_signature: Some(0),
 9683        active_parameter: Some(0),
 9684    };
 9685    handle_signature_help_request(&mut cx, mocked_response).await;
 9686
 9687    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9688        .await;
 9689
 9690    cx.editor(|editor, _, _| {
 9691        let signature_help_state = editor.signature_help_state.popover().cloned();
 9692        assert_eq!(
 9693            signature_help_state.unwrap().label,
 9694            "param1: u8, param2: u8"
 9695        );
 9696    });
 9697}
 9698
 9699#[gpui::test]
 9700async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9701    init_test(cx, |_| {});
 9702
 9703    cx.update(|cx| {
 9704        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9705            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9706                settings.auto_signature_help = Some(false);
 9707                settings.show_signature_help_after_edits = Some(false);
 9708            });
 9709        });
 9710    });
 9711
 9712    let mut cx = EditorLspTestContext::new_rust(
 9713        lsp::ServerCapabilities {
 9714            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9715                ..Default::default()
 9716            }),
 9717            ..Default::default()
 9718        },
 9719        cx,
 9720    )
 9721    .await;
 9722
 9723    let language = Language::new(
 9724        LanguageConfig {
 9725            name: "Rust".into(),
 9726            brackets: BracketPairConfig {
 9727                pairs: vec![
 9728                    BracketPair {
 9729                        start: "{".to_string(),
 9730                        end: "}".to_string(),
 9731                        close: true,
 9732                        surround: true,
 9733                        newline: true,
 9734                    },
 9735                    BracketPair {
 9736                        start: "(".to_string(),
 9737                        end: ")".to_string(),
 9738                        close: true,
 9739                        surround: true,
 9740                        newline: true,
 9741                    },
 9742                    BracketPair {
 9743                        start: "/*".to_string(),
 9744                        end: " */".to_string(),
 9745                        close: true,
 9746                        surround: true,
 9747                        newline: true,
 9748                    },
 9749                    BracketPair {
 9750                        start: "[".to_string(),
 9751                        end: "]".to_string(),
 9752                        close: false,
 9753                        surround: false,
 9754                        newline: true,
 9755                    },
 9756                    BracketPair {
 9757                        start: "\"".to_string(),
 9758                        end: "\"".to_string(),
 9759                        close: true,
 9760                        surround: true,
 9761                        newline: false,
 9762                    },
 9763                    BracketPair {
 9764                        start: "<".to_string(),
 9765                        end: ">".to_string(),
 9766                        close: false,
 9767                        surround: true,
 9768                        newline: true,
 9769                    },
 9770                ],
 9771                ..Default::default()
 9772            },
 9773            autoclose_before: "})]".to_string(),
 9774            ..Default::default()
 9775        },
 9776        Some(tree_sitter_rust::LANGUAGE.into()),
 9777    );
 9778    let language = Arc::new(language);
 9779
 9780    cx.language_registry().add(language.clone());
 9781    cx.update_buffer(|buffer, cx| {
 9782        buffer.set_language(Some(language), cx);
 9783    });
 9784
 9785    // Ensure that signature_help is not called when no signature help is enabled.
 9786    cx.set_state(
 9787        &r#"
 9788            fn main() {
 9789                sampleˇ
 9790            }
 9791        "#
 9792        .unindent(),
 9793    );
 9794    cx.update_editor(|editor, window, cx| {
 9795        editor.handle_input("(", window, cx);
 9796    });
 9797    cx.assert_editor_state(
 9798        &"
 9799            fn main() {
 9800                sample(ˇ)
 9801            }
 9802        "
 9803        .unindent(),
 9804    );
 9805    cx.editor(|editor, _, _| {
 9806        assert!(editor.signature_help_state.task().is_none());
 9807    });
 9808
 9809    let mocked_response = lsp::SignatureHelp {
 9810        signatures: vec![lsp::SignatureInformation {
 9811            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9812            documentation: None,
 9813            parameters: Some(vec![
 9814                lsp::ParameterInformation {
 9815                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9816                    documentation: None,
 9817                },
 9818                lsp::ParameterInformation {
 9819                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9820                    documentation: None,
 9821                },
 9822            ]),
 9823            active_parameter: None,
 9824        }],
 9825        active_signature: Some(0),
 9826        active_parameter: Some(0),
 9827    };
 9828
 9829    // Ensure that signature_help is called when enabled afte edits
 9830    cx.update(|_, cx| {
 9831        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9832            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9833                settings.auto_signature_help = Some(false);
 9834                settings.show_signature_help_after_edits = Some(true);
 9835            });
 9836        });
 9837    });
 9838    cx.set_state(
 9839        &r#"
 9840            fn main() {
 9841                sampleˇ
 9842            }
 9843        "#
 9844        .unindent(),
 9845    );
 9846    cx.update_editor(|editor, window, cx| {
 9847        editor.handle_input("(", window, cx);
 9848    });
 9849    cx.assert_editor_state(
 9850        &"
 9851            fn main() {
 9852                sample(ˇ)
 9853            }
 9854        "
 9855        .unindent(),
 9856    );
 9857    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9858    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9859        .await;
 9860    cx.update_editor(|editor, _, _| {
 9861        let signature_help_state = editor.signature_help_state.popover().cloned();
 9862        assert!(signature_help_state.is_some());
 9863        assert_eq!(
 9864            signature_help_state.unwrap().label,
 9865            "param1: u8, param2: u8"
 9866        );
 9867        editor.signature_help_state = SignatureHelpState::default();
 9868    });
 9869
 9870    // Ensure that signature_help is called when auto signature help override is enabled
 9871    cx.update(|_, cx| {
 9872        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9873            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9874                settings.auto_signature_help = Some(true);
 9875                settings.show_signature_help_after_edits = Some(false);
 9876            });
 9877        });
 9878    });
 9879    cx.set_state(
 9880        &r#"
 9881            fn main() {
 9882                sampleˇ
 9883            }
 9884        "#
 9885        .unindent(),
 9886    );
 9887    cx.update_editor(|editor, window, cx| {
 9888        editor.handle_input("(", window, cx);
 9889    });
 9890    cx.assert_editor_state(
 9891        &"
 9892            fn main() {
 9893                sample(ˇ)
 9894            }
 9895        "
 9896        .unindent(),
 9897    );
 9898    handle_signature_help_request(&mut cx, mocked_response).await;
 9899    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9900        .await;
 9901    cx.editor(|editor, _, _| {
 9902        let signature_help_state = editor.signature_help_state.popover().cloned();
 9903        assert!(signature_help_state.is_some());
 9904        assert_eq!(
 9905            signature_help_state.unwrap().label,
 9906            "param1: u8, param2: u8"
 9907        );
 9908    });
 9909}
 9910
 9911#[gpui::test]
 9912async fn test_signature_help(cx: &mut TestAppContext) {
 9913    init_test(cx, |_| {});
 9914    cx.update(|cx| {
 9915        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9916            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9917                settings.auto_signature_help = Some(true);
 9918            });
 9919        });
 9920    });
 9921
 9922    let mut cx = EditorLspTestContext::new_rust(
 9923        lsp::ServerCapabilities {
 9924            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9925                ..Default::default()
 9926            }),
 9927            ..Default::default()
 9928        },
 9929        cx,
 9930    )
 9931    .await;
 9932
 9933    // A test that directly calls `show_signature_help`
 9934    cx.update_editor(|editor, window, cx| {
 9935        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9936    });
 9937
 9938    let mocked_response = lsp::SignatureHelp {
 9939        signatures: vec![lsp::SignatureInformation {
 9940            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9941            documentation: None,
 9942            parameters: Some(vec![
 9943                lsp::ParameterInformation {
 9944                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9945                    documentation: None,
 9946                },
 9947                lsp::ParameterInformation {
 9948                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9949                    documentation: None,
 9950                },
 9951            ]),
 9952            active_parameter: None,
 9953        }],
 9954        active_signature: Some(0),
 9955        active_parameter: Some(0),
 9956    };
 9957    handle_signature_help_request(&mut cx, mocked_response).await;
 9958
 9959    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9960        .await;
 9961
 9962    cx.editor(|editor, _, _| {
 9963        let signature_help_state = editor.signature_help_state.popover().cloned();
 9964        assert!(signature_help_state.is_some());
 9965        assert_eq!(
 9966            signature_help_state.unwrap().label,
 9967            "param1: u8, param2: u8"
 9968        );
 9969    });
 9970
 9971    // When exiting outside from inside the brackets, `signature_help` is closed.
 9972    cx.set_state(indoc! {"
 9973        fn main() {
 9974            sample(ˇ);
 9975        }
 9976
 9977        fn sample(param1: u8, param2: u8) {}
 9978    "});
 9979
 9980    cx.update_editor(|editor, window, cx| {
 9981        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9982    });
 9983
 9984    let mocked_response = lsp::SignatureHelp {
 9985        signatures: Vec::new(),
 9986        active_signature: None,
 9987        active_parameter: None,
 9988    };
 9989    handle_signature_help_request(&mut cx, mocked_response).await;
 9990
 9991    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9992        .await;
 9993
 9994    cx.editor(|editor, _, _| {
 9995        assert!(!editor.signature_help_state.is_shown());
 9996    });
 9997
 9998    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9999    cx.set_state(indoc! {"
10000        fn main() {
10001            sample(ˇ);
10002        }
10003
10004        fn sample(param1: u8, param2: u8) {}
10005    "});
10006
10007    let mocked_response = lsp::SignatureHelp {
10008        signatures: vec![lsp::SignatureInformation {
10009            label: "fn sample(param1: u8, param2: u8)".to_string(),
10010            documentation: None,
10011            parameters: Some(vec![
10012                lsp::ParameterInformation {
10013                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10014                    documentation: None,
10015                },
10016                lsp::ParameterInformation {
10017                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10018                    documentation: None,
10019                },
10020            ]),
10021            active_parameter: None,
10022        }],
10023        active_signature: Some(0),
10024        active_parameter: Some(0),
10025    };
10026    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10027    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10028        .await;
10029    cx.editor(|editor, _, _| {
10030        assert!(editor.signature_help_state.is_shown());
10031    });
10032
10033    // Restore the popover with more parameter input
10034    cx.set_state(indoc! {"
10035        fn main() {
10036            sample(param1, param2ˇ);
10037        }
10038
10039        fn sample(param1: u8, param2: u8) {}
10040    "});
10041
10042    let mocked_response = lsp::SignatureHelp {
10043        signatures: vec![lsp::SignatureInformation {
10044            label: "fn sample(param1: u8, param2: u8)".to_string(),
10045            documentation: None,
10046            parameters: Some(vec![
10047                lsp::ParameterInformation {
10048                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10049                    documentation: None,
10050                },
10051                lsp::ParameterInformation {
10052                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10053                    documentation: None,
10054                },
10055            ]),
10056            active_parameter: None,
10057        }],
10058        active_signature: Some(0),
10059        active_parameter: Some(1),
10060    };
10061    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10062    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10063        .await;
10064
10065    // When selecting a range, the popover is gone.
10066    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
10067    cx.update_editor(|editor, window, cx| {
10068        editor.change_selections(None, window, cx, |s| {
10069            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10070        })
10071    });
10072    cx.assert_editor_state(indoc! {"
10073        fn main() {
10074            sample(param1, «ˇparam2»);
10075        }
10076
10077        fn sample(param1: u8, param2: u8) {}
10078    "});
10079    cx.editor(|editor, _, _| {
10080        assert!(!editor.signature_help_state.is_shown());
10081    });
10082
10083    // When unselecting again, the popover is back if within the brackets.
10084    cx.update_editor(|editor, window, cx| {
10085        editor.change_selections(None, window, cx, |s| {
10086            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10087        })
10088    });
10089    cx.assert_editor_state(indoc! {"
10090        fn main() {
10091            sample(param1, ˇparam2);
10092        }
10093
10094        fn sample(param1: u8, param2: u8) {}
10095    "});
10096    handle_signature_help_request(&mut cx, mocked_response).await;
10097    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10098        .await;
10099    cx.editor(|editor, _, _| {
10100        assert!(editor.signature_help_state.is_shown());
10101    });
10102
10103    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
10104    cx.update_editor(|editor, window, cx| {
10105        editor.change_selections(None, window, cx, |s| {
10106            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
10107            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10108        })
10109    });
10110    cx.assert_editor_state(indoc! {"
10111        fn main() {
10112            sample(param1, ˇparam2);
10113        }
10114
10115        fn sample(param1: u8, param2: u8) {}
10116    "});
10117
10118    let mocked_response = lsp::SignatureHelp {
10119        signatures: vec![lsp::SignatureInformation {
10120            label: "fn sample(param1: u8, param2: u8)".to_string(),
10121            documentation: None,
10122            parameters: Some(vec![
10123                lsp::ParameterInformation {
10124                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
10125                    documentation: None,
10126                },
10127                lsp::ParameterInformation {
10128                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
10129                    documentation: None,
10130                },
10131            ]),
10132            active_parameter: None,
10133        }],
10134        active_signature: Some(0),
10135        active_parameter: Some(1),
10136    };
10137    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
10138    cx.condition(|editor, _| editor.signature_help_state.is_shown())
10139        .await;
10140    cx.update_editor(|editor, _, cx| {
10141        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
10142    });
10143    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
10144        .await;
10145    cx.update_editor(|editor, window, cx| {
10146        editor.change_selections(None, window, cx, |s| {
10147            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
10148        })
10149    });
10150    cx.assert_editor_state(indoc! {"
10151        fn main() {
10152            sample(param1, «ˇparam2»);
10153        }
10154
10155        fn sample(param1: u8, param2: u8) {}
10156    "});
10157    cx.update_editor(|editor, window, cx| {
10158        editor.change_selections(None, window, cx, |s| {
10159            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
10160        })
10161    });
10162    cx.assert_editor_state(indoc! {"
10163        fn main() {
10164            sample(param1, ˇparam2);
10165        }
10166
10167        fn sample(param1: u8, param2: u8) {}
10168    "});
10169    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
10170        .await;
10171}
10172
10173#[gpui::test]
10174async fn test_completion_mode(cx: &mut TestAppContext) {
10175    init_test(cx, |_| {});
10176    let mut cx = EditorLspTestContext::new_rust(
10177        lsp::ServerCapabilities {
10178            completion_provider: Some(lsp::CompletionOptions {
10179                resolve_provider: Some(true),
10180                ..Default::default()
10181            }),
10182            ..Default::default()
10183        },
10184        cx,
10185    )
10186    .await;
10187
10188    struct Run {
10189        run_description: &'static str,
10190        initial_state: String,
10191        buffer_marked_text: String,
10192        completion_text: &'static str,
10193        expected_with_insert_mode: String,
10194        expected_with_replace_mode: String,
10195        expected_with_replace_subsequence_mode: String,
10196        expected_with_replace_suffix_mode: String,
10197    }
10198
10199    let runs = [
10200        Run {
10201            run_description: "Start of word matches completion text",
10202            initial_state: "before ediˇ after".into(),
10203            buffer_marked_text: "before <edi|> after".into(),
10204            completion_text: "editor",
10205            expected_with_insert_mode: "before editorˇ after".into(),
10206            expected_with_replace_mode: "before editorˇ after".into(),
10207            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10208            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10209        },
10210        Run {
10211            run_description: "Accept same text at the middle of the word",
10212            initial_state: "before ediˇtor after".into(),
10213            buffer_marked_text: "before <edi|tor> after".into(),
10214            completion_text: "editor",
10215            expected_with_insert_mode: "before editorˇtor after".into(),
10216            expected_with_replace_mode: "before editorˇ after".into(),
10217            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10218            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10219        },
10220        Run {
10221            run_description: "End of word matches completion text -- cursor at end",
10222            initial_state: "before torˇ after".into(),
10223            buffer_marked_text: "before <tor|> after".into(),
10224            completion_text: "editor",
10225            expected_with_insert_mode: "before editorˇ after".into(),
10226            expected_with_replace_mode: "before editorˇ after".into(),
10227            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10228            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10229        },
10230        Run {
10231            run_description: "End of word matches completion text -- cursor at start",
10232            initial_state: "before ˇtor after".into(),
10233            buffer_marked_text: "before <|tor> after".into(),
10234            completion_text: "editor",
10235            expected_with_insert_mode: "before editorˇtor after".into(),
10236            expected_with_replace_mode: "before editorˇ after".into(),
10237            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
10238            expected_with_replace_suffix_mode: "before editorˇ after".into(),
10239        },
10240        Run {
10241            run_description: "Prepend text containing whitespace",
10242            initial_state: "pˇfield: bool".into(),
10243            buffer_marked_text: "<p|field>: bool".into(),
10244            completion_text: "pub ",
10245            expected_with_insert_mode: "pub ˇfield: bool".into(),
10246            expected_with_replace_mode: "pub ˇ: bool".into(),
10247            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
10248            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
10249        },
10250        Run {
10251            run_description: "Add element to start of list",
10252            initial_state: "[element_ˇelement_2]".into(),
10253            buffer_marked_text: "[<element_|element_2>]".into(),
10254            completion_text: "element_1",
10255            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
10256            expected_with_replace_mode: "[element_1ˇ]".into(),
10257            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
10258            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
10259        },
10260        Run {
10261            run_description: "Add element to start of list -- first and second elements are equal",
10262            initial_state: "[elˇelement]".into(),
10263            buffer_marked_text: "[<el|element>]".into(),
10264            completion_text: "element",
10265            expected_with_insert_mode: "[elementˇelement]".into(),
10266            expected_with_replace_mode: "[elementˇ]".into(),
10267            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10268            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10269        },
10270        Run {
10271            run_description: "Ends with matching suffix",
10272            initial_state: "SubˇError".into(),
10273            buffer_marked_text: "<Sub|Error>".into(),
10274            completion_text: "SubscriptionError",
10275            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10276            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10277            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10278            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10279        },
10280        Run {
10281            run_description: "Suffix is a subsequence -- contiguous",
10282            initial_state: "SubˇErr".into(),
10283            buffer_marked_text: "<Sub|Err>".into(),
10284            completion_text: "SubscriptionError",
10285            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10286            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10287            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10288            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10289        },
10290        Run {
10291            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10292            initial_state: "Suˇscrirr".into(),
10293            buffer_marked_text: "<Su|scrirr>".into(),
10294            completion_text: "SubscriptionError",
10295            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10296            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10297            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10298            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10299        },
10300        Run {
10301            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10302            initial_state: "foo(indˇix)".into(),
10303            buffer_marked_text: "foo(<ind|ix>)".into(),
10304            completion_text: "node_index",
10305            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10306            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10307            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10308            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10309        },
10310    ];
10311
10312    for run in runs {
10313        let run_variations = [
10314            (LspInsertMode::Insert, run.expected_with_insert_mode),
10315            (LspInsertMode::Replace, run.expected_with_replace_mode),
10316            (
10317                LspInsertMode::ReplaceSubsequence,
10318                run.expected_with_replace_subsequence_mode,
10319            ),
10320            (
10321                LspInsertMode::ReplaceSuffix,
10322                run.expected_with_replace_suffix_mode,
10323            ),
10324        ];
10325
10326        for (lsp_insert_mode, expected_text) in run_variations {
10327            eprintln!(
10328                "run = {:?}, mode = {lsp_insert_mode:.?}",
10329                run.run_description,
10330            );
10331
10332            update_test_language_settings(&mut cx, |settings| {
10333                settings.defaults.completions = Some(CompletionSettings {
10334                    lsp_insert_mode,
10335                    words: WordsCompletionMode::Disabled,
10336                    lsp: true,
10337                    lsp_fetch_timeout_ms: 0,
10338                });
10339            });
10340
10341            cx.set_state(&run.initial_state);
10342            cx.update_editor(|editor, window, cx| {
10343                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10344            });
10345
10346            let counter = Arc::new(AtomicUsize::new(0));
10347            handle_completion_request_with_insert_and_replace(
10348                &mut cx,
10349                &run.buffer_marked_text,
10350                vec![run.completion_text],
10351                counter.clone(),
10352            )
10353            .await;
10354            cx.condition(|editor, _| editor.context_menu_visible())
10355                .await;
10356            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10357
10358            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10359                editor
10360                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10361                    .unwrap()
10362            });
10363            cx.assert_editor_state(&expected_text);
10364            handle_resolve_completion_request(&mut cx, None).await;
10365            apply_additional_edits.await.unwrap();
10366        }
10367    }
10368}
10369
10370#[gpui::test]
10371async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10372    init_test(cx, |_| {});
10373    let mut cx = EditorLspTestContext::new_rust(
10374        lsp::ServerCapabilities {
10375            completion_provider: Some(lsp::CompletionOptions {
10376                resolve_provider: Some(true),
10377                ..Default::default()
10378            }),
10379            ..Default::default()
10380        },
10381        cx,
10382    )
10383    .await;
10384
10385    let initial_state = "SubˇError";
10386    let buffer_marked_text = "<Sub|Error>";
10387    let completion_text = "SubscriptionError";
10388    let expected_with_insert_mode = "SubscriptionErrorˇError";
10389    let expected_with_replace_mode = "SubscriptionErrorˇ";
10390
10391    update_test_language_settings(&mut cx, |settings| {
10392        settings.defaults.completions = Some(CompletionSettings {
10393            words: WordsCompletionMode::Disabled,
10394            // set the opposite here to ensure that the action is overriding the default behavior
10395            lsp_insert_mode: LspInsertMode::Insert,
10396            lsp: true,
10397            lsp_fetch_timeout_ms: 0,
10398        });
10399    });
10400
10401    cx.set_state(initial_state);
10402    cx.update_editor(|editor, window, cx| {
10403        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10404    });
10405
10406    let counter = Arc::new(AtomicUsize::new(0));
10407    handle_completion_request_with_insert_and_replace(
10408        &mut cx,
10409        &buffer_marked_text,
10410        vec![completion_text],
10411        counter.clone(),
10412    )
10413    .await;
10414    cx.condition(|editor, _| editor.context_menu_visible())
10415        .await;
10416    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10417
10418    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10419        editor
10420            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10421            .unwrap()
10422    });
10423    cx.assert_editor_state(&expected_with_replace_mode);
10424    handle_resolve_completion_request(&mut cx, None).await;
10425    apply_additional_edits.await.unwrap();
10426
10427    update_test_language_settings(&mut cx, |settings| {
10428        settings.defaults.completions = Some(CompletionSettings {
10429            words: WordsCompletionMode::Disabled,
10430            // set the opposite here to ensure that the action is overriding the default behavior
10431            lsp_insert_mode: LspInsertMode::Replace,
10432            lsp: true,
10433            lsp_fetch_timeout_ms: 0,
10434        });
10435    });
10436
10437    cx.set_state(initial_state);
10438    cx.update_editor(|editor, window, cx| {
10439        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10440    });
10441    handle_completion_request_with_insert_and_replace(
10442        &mut cx,
10443        &buffer_marked_text,
10444        vec![completion_text],
10445        counter.clone(),
10446    )
10447    .await;
10448    cx.condition(|editor, _| editor.context_menu_visible())
10449        .await;
10450    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10451
10452    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10453        editor
10454            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10455            .unwrap()
10456    });
10457    cx.assert_editor_state(&expected_with_insert_mode);
10458    handle_resolve_completion_request(&mut cx, None).await;
10459    apply_additional_edits.await.unwrap();
10460}
10461
10462#[gpui::test]
10463async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10464    init_test(cx, |_| {});
10465    let mut cx = EditorLspTestContext::new_rust(
10466        lsp::ServerCapabilities {
10467            completion_provider: Some(lsp::CompletionOptions {
10468                resolve_provider: Some(true),
10469                ..Default::default()
10470            }),
10471            ..Default::default()
10472        },
10473        cx,
10474    )
10475    .await;
10476
10477    // scenario: surrounding text matches completion text
10478    let completion_text = "to_offset";
10479    let initial_state = indoc! {"
10480        1. buf.to_offˇsuffix
10481        2. buf.to_offˇsuf
10482        3. buf.to_offˇfix
10483        4. buf.to_offˇ
10484        5. into_offˇensive
10485        6. ˇsuffix
10486        7. let ˇ //
10487        8. aaˇzz
10488        9. buf.to_off«zzzzzˇ»suffix
10489        10. buf.«ˇzzzzz»suffix
10490        11. to_off«ˇzzzzz»
10491
10492        buf.to_offˇsuffix  // newest cursor
10493    "};
10494    let completion_marked_buffer = indoc! {"
10495        1. buf.to_offsuffix
10496        2. buf.to_offsuf
10497        3. buf.to_offfix
10498        4. buf.to_off
10499        5. into_offensive
10500        6. suffix
10501        7. let  //
10502        8. aazz
10503        9. buf.to_offzzzzzsuffix
10504        10. buf.zzzzzsuffix
10505        11. to_offzzzzz
10506
10507        buf.<to_off|suffix>  // newest cursor
10508    "};
10509    let expected = indoc! {"
10510        1. buf.to_offsetˇ
10511        2. buf.to_offsetˇsuf
10512        3. buf.to_offsetˇfix
10513        4. buf.to_offsetˇ
10514        5. into_offsetˇensive
10515        6. to_offsetˇsuffix
10516        7. let to_offsetˇ //
10517        8. aato_offsetˇzz
10518        9. buf.to_offsetˇ
10519        10. buf.to_offsetˇsuffix
10520        11. to_offsetˇ
10521
10522        buf.to_offsetˇ  // newest cursor
10523    "};
10524    cx.set_state(initial_state);
10525    cx.update_editor(|editor, window, cx| {
10526        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10527    });
10528    handle_completion_request_with_insert_and_replace(
10529        &mut cx,
10530        completion_marked_buffer,
10531        vec![completion_text],
10532        Arc::new(AtomicUsize::new(0)),
10533    )
10534    .await;
10535    cx.condition(|editor, _| editor.context_menu_visible())
10536        .await;
10537    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10538        editor
10539            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10540            .unwrap()
10541    });
10542    cx.assert_editor_state(expected);
10543    handle_resolve_completion_request(&mut cx, None).await;
10544    apply_additional_edits.await.unwrap();
10545
10546    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10547    let completion_text = "foo_and_bar";
10548    let initial_state = indoc! {"
10549        1. ooanbˇ
10550        2. zooanbˇ
10551        3. ooanbˇz
10552        4. zooanbˇz
10553        5. ooanˇ
10554        6. oanbˇ
10555
10556        ooanbˇ
10557    "};
10558    let completion_marked_buffer = indoc! {"
10559        1. ooanb
10560        2. zooanb
10561        3. ooanbz
10562        4. zooanbz
10563        5. ooan
10564        6. oanb
10565
10566        <ooanb|>
10567    "};
10568    let expected = indoc! {"
10569        1. foo_and_barˇ
10570        2. zfoo_and_barˇ
10571        3. foo_and_barˇz
10572        4. zfoo_and_barˇz
10573        5. ooanfoo_and_barˇ
10574        6. oanbfoo_and_barˇ
10575
10576        foo_and_barˇ
10577    "};
10578    cx.set_state(initial_state);
10579    cx.update_editor(|editor, window, cx| {
10580        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10581    });
10582    handle_completion_request_with_insert_and_replace(
10583        &mut cx,
10584        completion_marked_buffer,
10585        vec![completion_text],
10586        Arc::new(AtomicUsize::new(0)),
10587    )
10588    .await;
10589    cx.condition(|editor, _| editor.context_menu_visible())
10590        .await;
10591    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10592        editor
10593            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10594            .unwrap()
10595    });
10596    cx.assert_editor_state(expected);
10597    handle_resolve_completion_request(&mut cx, None).await;
10598    apply_additional_edits.await.unwrap();
10599
10600    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10601    // (expects the same as if it was inserted at the end)
10602    let completion_text = "foo_and_bar";
10603    let initial_state = indoc! {"
10604        1. ooˇanb
10605        2. zooˇanb
10606        3. ooˇanbz
10607        4. zooˇanbz
10608
10609        ooˇanb
10610    "};
10611    let completion_marked_buffer = indoc! {"
10612        1. ooanb
10613        2. zooanb
10614        3. ooanbz
10615        4. zooanbz
10616
10617        <oo|anb>
10618    "};
10619    let expected = indoc! {"
10620        1. foo_and_barˇ
10621        2. zfoo_and_barˇ
10622        3. foo_and_barˇz
10623        4. zfoo_and_barˇz
10624
10625        foo_and_barˇ
10626    "};
10627    cx.set_state(initial_state);
10628    cx.update_editor(|editor, window, cx| {
10629        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10630    });
10631    handle_completion_request_with_insert_and_replace(
10632        &mut cx,
10633        completion_marked_buffer,
10634        vec![completion_text],
10635        Arc::new(AtomicUsize::new(0)),
10636    )
10637    .await;
10638    cx.condition(|editor, _| editor.context_menu_visible())
10639        .await;
10640    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10641        editor
10642            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10643            .unwrap()
10644    });
10645    cx.assert_editor_state(expected);
10646    handle_resolve_completion_request(&mut cx, None).await;
10647    apply_additional_edits.await.unwrap();
10648}
10649
10650// This used to crash
10651#[gpui::test]
10652async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10653    init_test(cx, |_| {});
10654
10655    let buffer_text = indoc! {"
10656        fn main() {
10657            10.satu;
10658
10659            //
10660            // separate cursors so they open in different excerpts (manually reproducible)
10661            //
10662
10663            10.satu20;
10664        }
10665    "};
10666    let multibuffer_text_with_selections = indoc! {"
10667        fn main() {
10668            10.satuˇ;
10669
10670            //
10671
10672            //
10673
10674            10.satuˇ20;
10675        }
10676    "};
10677    let expected_multibuffer = indoc! {"
10678        fn main() {
10679            10.saturating_sub()ˇ;
10680
10681            //
10682
10683            //
10684
10685            10.saturating_sub()ˇ;
10686        }
10687    "};
10688
10689    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10690    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10691
10692    let fs = FakeFs::new(cx.executor());
10693    fs.insert_tree(
10694        path!("/a"),
10695        json!({
10696            "main.rs": buffer_text,
10697        }),
10698    )
10699    .await;
10700
10701    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10702    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10703    language_registry.add(rust_lang());
10704    let mut fake_servers = language_registry.register_fake_lsp(
10705        "Rust",
10706        FakeLspAdapter {
10707            capabilities: lsp::ServerCapabilities {
10708                completion_provider: Some(lsp::CompletionOptions {
10709                    resolve_provider: None,
10710                    ..lsp::CompletionOptions::default()
10711                }),
10712                ..lsp::ServerCapabilities::default()
10713            },
10714            ..FakeLspAdapter::default()
10715        },
10716    );
10717    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10718    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10719    let buffer = project
10720        .update(cx, |project, cx| {
10721            project.open_local_buffer(path!("/a/main.rs"), cx)
10722        })
10723        .await
10724        .unwrap();
10725
10726    let multi_buffer = cx.new(|cx| {
10727        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10728        multi_buffer.push_excerpts(
10729            buffer.clone(),
10730            [ExcerptRange::new(0..first_excerpt_end)],
10731            cx,
10732        );
10733        multi_buffer.push_excerpts(
10734            buffer.clone(),
10735            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10736            cx,
10737        );
10738        multi_buffer
10739    });
10740
10741    let editor = workspace
10742        .update(cx, |_, window, cx| {
10743            cx.new(|cx| {
10744                Editor::new(
10745                    EditorMode::Full {
10746                        scale_ui_elements_with_buffer_font_size: false,
10747                        show_active_line_background: false,
10748                        sized_by_content: false,
10749                    },
10750                    multi_buffer.clone(),
10751                    Some(project.clone()),
10752                    window,
10753                    cx,
10754                )
10755            })
10756        })
10757        .unwrap();
10758
10759    let pane = workspace
10760        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10761        .unwrap();
10762    pane.update_in(cx, |pane, window, cx| {
10763        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10764    });
10765
10766    let fake_server = fake_servers.next().await.unwrap();
10767
10768    editor.update_in(cx, |editor, window, cx| {
10769        editor.change_selections(None, window, cx, |s| {
10770            s.select_ranges([
10771                Point::new(1, 11)..Point::new(1, 11),
10772                Point::new(7, 11)..Point::new(7, 11),
10773            ])
10774        });
10775
10776        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10777    });
10778
10779    editor.update_in(cx, |editor, window, cx| {
10780        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10781    });
10782
10783    fake_server
10784        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10785            let completion_item = lsp::CompletionItem {
10786                label: "saturating_sub()".into(),
10787                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10788                    lsp::InsertReplaceEdit {
10789                        new_text: "saturating_sub()".to_owned(),
10790                        insert: lsp::Range::new(
10791                            lsp::Position::new(7, 7),
10792                            lsp::Position::new(7, 11),
10793                        ),
10794                        replace: lsp::Range::new(
10795                            lsp::Position::new(7, 7),
10796                            lsp::Position::new(7, 13),
10797                        ),
10798                    },
10799                )),
10800                ..lsp::CompletionItem::default()
10801            };
10802
10803            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10804        })
10805        .next()
10806        .await
10807        .unwrap();
10808
10809    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10810        .await;
10811
10812    editor
10813        .update_in(cx, |editor, window, cx| {
10814            editor
10815                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10816                .unwrap()
10817        })
10818        .await
10819        .unwrap();
10820
10821    editor.update(cx, |editor, cx| {
10822        assert_text_with_selections(editor, expected_multibuffer, cx);
10823    })
10824}
10825
10826#[gpui::test]
10827async fn test_completion(cx: &mut TestAppContext) {
10828    init_test(cx, |_| {});
10829
10830    let mut cx = EditorLspTestContext::new_rust(
10831        lsp::ServerCapabilities {
10832            completion_provider: Some(lsp::CompletionOptions {
10833                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10834                resolve_provider: Some(true),
10835                ..Default::default()
10836            }),
10837            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10838            ..Default::default()
10839        },
10840        cx,
10841    )
10842    .await;
10843    let counter = Arc::new(AtomicUsize::new(0));
10844
10845    cx.set_state(indoc! {"
10846        oneˇ
10847        two
10848        three
10849    "});
10850    cx.simulate_keystroke(".");
10851    handle_completion_request(
10852        &mut cx,
10853        indoc! {"
10854            one.|<>
10855            two
10856            three
10857        "},
10858        vec!["first_completion", "second_completion"],
10859        counter.clone(),
10860    )
10861    .await;
10862    cx.condition(|editor, _| editor.context_menu_visible())
10863        .await;
10864    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10865
10866    let _handler = handle_signature_help_request(
10867        &mut cx,
10868        lsp::SignatureHelp {
10869            signatures: vec![lsp::SignatureInformation {
10870                label: "test signature".to_string(),
10871                documentation: None,
10872                parameters: Some(vec![lsp::ParameterInformation {
10873                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10874                    documentation: None,
10875                }]),
10876                active_parameter: None,
10877            }],
10878            active_signature: None,
10879            active_parameter: None,
10880        },
10881    );
10882    cx.update_editor(|editor, window, cx| {
10883        assert!(
10884            !editor.signature_help_state.is_shown(),
10885            "No signature help was called for"
10886        );
10887        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10888    });
10889    cx.run_until_parked();
10890    cx.update_editor(|editor, _, _| {
10891        assert!(
10892            !editor.signature_help_state.is_shown(),
10893            "No signature help should be shown when completions menu is open"
10894        );
10895    });
10896
10897    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10898        editor.context_menu_next(&Default::default(), window, cx);
10899        editor
10900            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10901            .unwrap()
10902    });
10903    cx.assert_editor_state(indoc! {"
10904        one.second_completionˇ
10905        two
10906        three
10907    "});
10908
10909    handle_resolve_completion_request(
10910        &mut cx,
10911        Some(vec![
10912            (
10913                //This overlaps with the primary completion edit which is
10914                //misbehavior from the LSP spec, test that we filter it out
10915                indoc! {"
10916                    one.second_ˇcompletion
10917                    two
10918                    threeˇ
10919                "},
10920                "overlapping additional edit",
10921            ),
10922            (
10923                indoc! {"
10924                    one.second_completion
10925                    two
10926                    threeˇ
10927                "},
10928                "\nadditional edit",
10929            ),
10930        ]),
10931    )
10932    .await;
10933    apply_additional_edits.await.unwrap();
10934    cx.assert_editor_state(indoc! {"
10935        one.second_completionˇ
10936        two
10937        three
10938        additional edit
10939    "});
10940
10941    cx.set_state(indoc! {"
10942        one.second_completion
10943        twoˇ
10944        threeˇ
10945        additional edit
10946    "});
10947    cx.simulate_keystroke(" ");
10948    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10949    cx.simulate_keystroke("s");
10950    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10951
10952    cx.assert_editor_state(indoc! {"
10953        one.second_completion
10954        two sˇ
10955        three sˇ
10956        additional edit
10957    "});
10958    handle_completion_request(
10959        &mut cx,
10960        indoc! {"
10961            one.second_completion
10962            two s
10963            three <s|>
10964            additional edit
10965        "},
10966        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10967        counter.clone(),
10968    )
10969    .await;
10970    cx.condition(|editor, _| editor.context_menu_visible())
10971        .await;
10972    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10973
10974    cx.simulate_keystroke("i");
10975
10976    handle_completion_request(
10977        &mut cx,
10978        indoc! {"
10979            one.second_completion
10980            two si
10981            three <si|>
10982            additional edit
10983        "},
10984        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10985        counter.clone(),
10986    )
10987    .await;
10988    cx.condition(|editor, _| editor.context_menu_visible())
10989        .await;
10990    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10991
10992    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10993        editor
10994            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10995            .unwrap()
10996    });
10997    cx.assert_editor_state(indoc! {"
10998        one.second_completion
10999        two sixth_completionˇ
11000        three sixth_completionˇ
11001        additional edit
11002    "});
11003
11004    apply_additional_edits.await.unwrap();
11005
11006    update_test_language_settings(&mut cx, |settings| {
11007        settings.defaults.show_completions_on_input = Some(false);
11008    });
11009    cx.set_state("editorˇ");
11010    cx.simulate_keystroke(".");
11011    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11012    cx.simulate_keystrokes("c l o");
11013    cx.assert_editor_state("editor.cloˇ");
11014    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
11015    cx.update_editor(|editor, window, cx| {
11016        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
11017    });
11018    handle_completion_request(
11019        &mut cx,
11020        "editor.<clo|>",
11021        vec!["close", "clobber"],
11022        counter.clone(),
11023    )
11024    .await;
11025    cx.condition(|editor, _| editor.context_menu_visible())
11026        .await;
11027    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
11028
11029    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11030        editor
11031            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11032            .unwrap()
11033    });
11034    cx.assert_editor_state("editor.closeˇ");
11035    handle_resolve_completion_request(&mut cx, None).await;
11036    apply_additional_edits.await.unwrap();
11037}
11038
11039#[gpui::test]
11040async fn test_word_completion(cx: &mut TestAppContext) {
11041    let lsp_fetch_timeout_ms = 10;
11042    init_test(cx, |language_settings| {
11043        language_settings.defaults.completions = Some(CompletionSettings {
11044            words: WordsCompletionMode::Fallback,
11045            lsp: true,
11046            lsp_fetch_timeout_ms: 10,
11047            lsp_insert_mode: LspInsertMode::Insert,
11048        });
11049    });
11050
11051    let mut cx = EditorLspTestContext::new_rust(
11052        lsp::ServerCapabilities {
11053            completion_provider: Some(lsp::CompletionOptions {
11054                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11055                ..lsp::CompletionOptions::default()
11056            }),
11057            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11058            ..lsp::ServerCapabilities::default()
11059        },
11060        cx,
11061    )
11062    .await;
11063
11064    let throttle_completions = Arc::new(AtomicBool::new(false));
11065
11066    let lsp_throttle_completions = throttle_completions.clone();
11067    let _completion_requests_handler =
11068        cx.lsp
11069            .server
11070            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
11071                let lsp_throttle_completions = lsp_throttle_completions.clone();
11072                let cx = cx.clone();
11073                async move {
11074                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
11075                        cx.background_executor()
11076                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
11077                            .await;
11078                    }
11079                    Ok(Some(lsp::CompletionResponse::Array(vec![
11080                        lsp::CompletionItem {
11081                            label: "first".into(),
11082                            ..lsp::CompletionItem::default()
11083                        },
11084                        lsp::CompletionItem {
11085                            label: "last".into(),
11086                            ..lsp::CompletionItem::default()
11087                        },
11088                    ])))
11089                }
11090            });
11091
11092    cx.set_state(indoc! {"
11093        oneˇ
11094        two
11095        three
11096    "});
11097    cx.simulate_keystroke(".");
11098    cx.executor().run_until_parked();
11099    cx.condition(|editor, _| editor.context_menu_visible())
11100        .await;
11101    cx.update_editor(|editor, window, cx| {
11102        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11103        {
11104            assert_eq!(
11105                completion_menu_entries(&menu),
11106                &["first", "last"],
11107                "When LSP server is fast to reply, no fallback word completions are used"
11108            );
11109        } else {
11110            panic!("expected completion menu to be open");
11111        }
11112        editor.cancel(&Cancel, window, cx);
11113    });
11114    cx.executor().run_until_parked();
11115    cx.condition(|editor, _| !editor.context_menu_visible())
11116        .await;
11117
11118    throttle_completions.store(true, atomic::Ordering::Release);
11119    cx.simulate_keystroke(".");
11120    cx.executor()
11121        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
11122    cx.executor().run_until_parked();
11123    cx.condition(|editor, _| editor.context_menu_visible())
11124        .await;
11125    cx.update_editor(|editor, _, _| {
11126        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11127        {
11128            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
11129                "When LSP server is slow, document words can be shown instead, if configured accordingly");
11130        } else {
11131            panic!("expected completion menu to be open");
11132        }
11133    });
11134}
11135
11136#[gpui::test]
11137async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
11138    init_test(cx, |language_settings| {
11139        language_settings.defaults.completions = Some(CompletionSettings {
11140            words: WordsCompletionMode::Enabled,
11141            lsp: true,
11142            lsp_fetch_timeout_ms: 0,
11143            lsp_insert_mode: LspInsertMode::Insert,
11144        });
11145    });
11146
11147    let mut cx = EditorLspTestContext::new_rust(
11148        lsp::ServerCapabilities {
11149            completion_provider: Some(lsp::CompletionOptions {
11150                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11151                ..lsp::CompletionOptions::default()
11152            }),
11153            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11154            ..lsp::ServerCapabilities::default()
11155        },
11156        cx,
11157    )
11158    .await;
11159
11160    let _completion_requests_handler =
11161        cx.lsp
11162            .server
11163            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11164                Ok(Some(lsp::CompletionResponse::Array(vec![
11165                    lsp::CompletionItem {
11166                        label: "first".into(),
11167                        ..lsp::CompletionItem::default()
11168                    },
11169                    lsp::CompletionItem {
11170                        label: "last".into(),
11171                        ..lsp::CompletionItem::default()
11172                    },
11173                ])))
11174            });
11175
11176    cx.set_state(indoc! {"ˇ
11177        first
11178        last
11179        second
11180    "});
11181    cx.simulate_keystroke(".");
11182    cx.executor().run_until_parked();
11183    cx.condition(|editor, _| editor.context_menu_visible())
11184        .await;
11185    cx.update_editor(|editor, _, _| {
11186        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11187        {
11188            assert_eq!(
11189                completion_menu_entries(&menu),
11190                &["first", "last", "second"],
11191                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
11192            );
11193        } else {
11194            panic!("expected completion menu to be open");
11195        }
11196    });
11197}
11198
11199#[gpui::test]
11200async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
11201    init_test(cx, |language_settings| {
11202        language_settings.defaults.completions = Some(CompletionSettings {
11203            words: WordsCompletionMode::Disabled,
11204            lsp: true,
11205            lsp_fetch_timeout_ms: 0,
11206            lsp_insert_mode: LspInsertMode::Insert,
11207        });
11208    });
11209
11210    let mut cx = EditorLspTestContext::new_rust(
11211        lsp::ServerCapabilities {
11212            completion_provider: Some(lsp::CompletionOptions {
11213                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11214                ..lsp::CompletionOptions::default()
11215            }),
11216            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11217            ..lsp::ServerCapabilities::default()
11218        },
11219        cx,
11220    )
11221    .await;
11222
11223    let _completion_requests_handler =
11224        cx.lsp
11225            .server
11226            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
11227                panic!("LSP completions should not be queried when dealing with word completions")
11228            });
11229
11230    cx.set_state(indoc! {"ˇ
11231        first
11232        last
11233        second
11234    "});
11235    cx.update_editor(|editor, window, cx| {
11236        editor.show_word_completions(&ShowWordCompletions, window, cx);
11237    });
11238    cx.executor().run_until_parked();
11239    cx.condition(|editor, _| editor.context_menu_visible())
11240        .await;
11241    cx.update_editor(|editor, _, _| {
11242        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11243        {
11244            assert_eq!(
11245                completion_menu_entries(&menu),
11246                &["first", "last", "second"],
11247                "`ShowWordCompletions` action should show word completions"
11248            );
11249        } else {
11250            panic!("expected completion menu to be open");
11251        }
11252    });
11253
11254    cx.simulate_keystroke("l");
11255    cx.executor().run_until_parked();
11256    cx.condition(|editor, _| editor.context_menu_visible())
11257        .await;
11258    cx.update_editor(|editor, _, _| {
11259        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11260        {
11261            assert_eq!(
11262                completion_menu_entries(&menu),
11263                &["last"],
11264                "After showing word completions, further editing should filter them and not query the LSP"
11265            );
11266        } else {
11267            panic!("expected completion menu to be open");
11268        }
11269    });
11270}
11271
11272#[gpui::test]
11273async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11274    init_test(cx, |language_settings| {
11275        language_settings.defaults.completions = Some(CompletionSettings {
11276            words: WordsCompletionMode::Fallback,
11277            lsp: false,
11278            lsp_fetch_timeout_ms: 0,
11279            lsp_insert_mode: LspInsertMode::Insert,
11280        });
11281    });
11282
11283    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11284
11285    cx.set_state(indoc! {"ˇ
11286        0_usize
11287        let
11288        33
11289        4.5f32
11290    "});
11291    cx.update_editor(|editor, window, cx| {
11292        editor.show_completions(&ShowCompletions::default(), window, cx);
11293    });
11294    cx.executor().run_until_parked();
11295    cx.condition(|editor, _| editor.context_menu_visible())
11296        .await;
11297    cx.update_editor(|editor, window, cx| {
11298        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11299        {
11300            assert_eq!(
11301                completion_menu_entries(&menu),
11302                &["let"],
11303                "With no digits in the completion query, no digits should be in the word completions"
11304            );
11305        } else {
11306            panic!("expected completion menu to be open");
11307        }
11308        editor.cancel(&Cancel, window, cx);
11309    });
11310
11311    cx.set_state(indoc! {"11312        0_usize
11313        let
11314        3
11315        33.35f32
11316    "});
11317    cx.update_editor(|editor, window, cx| {
11318        editor.show_completions(&ShowCompletions::default(), window, cx);
11319    });
11320    cx.executor().run_until_parked();
11321    cx.condition(|editor, _| editor.context_menu_visible())
11322        .await;
11323    cx.update_editor(|editor, _, _| {
11324        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11325        {
11326            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11327                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11328        } else {
11329            panic!("expected completion menu to be open");
11330        }
11331    });
11332}
11333
11334fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11335    let position = || lsp::Position {
11336        line: params.text_document_position.position.line,
11337        character: params.text_document_position.position.character,
11338    };
11339    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11340        range: lsp::Range {
11341            start: position(),
11342            end: position(),
11343        },
11344        new_text: text.to_string(),
11345    }))
11346}
11347
11348#[gpui::test]
11349async fn test_multiline_completion(cx: &mut TestAppContext) {
11350    init_test(cx, |_| {});
11351
11352    let fs = FakeFs::new(cx.executor());
11353    fs.insert_tree(
11354        path!("/a"),
11355        json!({
11356            "main.ts": "a",
11357        }),
11358    )
11359    .await;
11360
11361    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11362    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11363    let typescript_language = Arc::new(Language::new(
11364        LanguageConfig {
11365            name: "TypeScript".into(),
11366            matcher: LanguageMatcher {
11367                path_suffixes: vec!["ts".to_string()],
11368                ..LanguageMatcher::default()
11369            },
11370            line_comments: vec!["// ".into()],
11371            ..LanguageConfig::default()
11372        },
11373        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11374    ));
11375    language_registry.add(typescript_language.clone());
11376    let mut fake_servers = language_registry.register_fake_lsp(
11377        "TypeScript",
11378        FakeLspAdapter {
11379            capabilities: lsp::ServerCapabilities {
11380                completion_provider: Some(lsp::CompletionOptions {
11381                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11382                    ..lsp::CompletionOptions::default()
11383                }),
11384                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11385                ..lsp::ServerCapabilities::default()
11386            },
11387            // Emulate vtsls label generation
11388            label_for_completion: Some(Box::new(|item, _| {
11389                let text = if let Some(description) = item
11390                    .label_details
11391                    .as_ref()
11392                    .and_then(|label_details| label_details.description.as_ref())
11393                {
11394                    format!("{} {}", item.label, description)
11395                } else if let Some(detail) = &item.detail {
11396                    format!("{} {}", item.label, detail)
11397                } else {
11398                    item.label.clone()
11399                };
11400                let len = text.len();
11401                Some(language::CodeLabel {
11402                    text,
11403                    runs: Vec::new(),
11404                    filter_range: 0..len,
11405                })
11406            })),
11407            ..FakeLspAdapter::default()
11408        },
11409    );
11410    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11411    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11412    let worktree_id = workspace
11413        .update(cx, |workspace, _window, cx| {
11414            workspace.project().update(cx, |project, cx| {
11415                project.worktrees(cx).next().unwrap().read(cx).id()
11416            })
11417        })
11418        .unwrap();
11419    let _buffer = project
11420        .update(cx, |project, cx| {
11421            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11422        })
11423        .await
11424        .unwrap();
11425    let editor = workspace
11426        .update(cx, |workspace, window, cx| {
11427            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11428        })
11429        .unwrap()
11430        .await
11431        .unwrap()
11432        .downcast::<Editor>()
11433        .unwrap();
11434    let fake_server = fake_servers.next().await.unwrap();
11435
11436    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11437    let multiline_label_2 = "a\nb\nc\n";
11438    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11439    let multiline_description = "d\ne\nf\n";
11440    let multiline_detail_2 = "g\nh\ni\n";
11441
11442    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11443        move |params, _| async move {
11444            Ok(Some(lsp::CompletionResponse::Array(vec![
11445                lsp::CompletionItem {
11446                    label: multiline_label.to_string(),
11447                    text_edit: gen_text_edit(&params, "new_text_1"),
11448                    ..lsp::CompletionItem::default()
11449                },
11450                lsp::CompletionItem {
11451                    label: "single line label 1".to_string(),
11452                    detail: Some(multiline_detail.to_string()),
11453                    text_edit: gen_text_edit(&params, "new_text_2"),
11454                    ..lsp::CompletionItem::default()
11455                },
11456                lsp::CompletionItem {
11457                    label: "single line label 2".to_string(),
11458                    label_details: Some(lsp::CompletionItemLabelDetails {
11459                        description: Some(multiline_description.to_string()),
11460                        detail: None,
11461                    }),
11462                    text_edit: gen_text_edit(&params, "new_text_2"),
11463                    ..lsp::CompletionItem::default()
11464                },
11465                lsp::CompletionItem {
11466                    label: multiline_label_2.to_string(),
11467                    detail: Some(multiline_detail_2.to_string()),
11468                    text_edit: gen_text_edit(&params, "new_text_3"),
11469                    ..lsp::CompletionItem::default()
11470                },
11471                lsp::CompletionItem {
11472                    label: "Label with many     spaces and \t but without newlines".to_string(),
11473                    detail: Some(
11474                        "Details with many     spaces and \t but without newlines".to_string(),
11475                    ),
11476                    text_edit: gen_text_edit(&params, "new_text_4"),
11477                    ..lsp::CompletionItem::default()
11478                },
11479            ])))
11480        },
11481    );
11482
11483    editor.update_in(cx, |editor, window, cx| {
11484        cx.focus_self(window);
11485        editor.move_to_end(&MoveToEnd, window, cx);
11486        editor.handle_input(".", window, cx);
11487    });
11488    cx.run_until_parked();
11489    completion_handle.next().await.unwrap();
11490
11491    editor.update(cx, |editor, _| {
11492        assert!(editor.context_menu_visible());
11493        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11494        {
11495            let completion_labels = menu
11496                .completions
11497                .borrow()
11498                .iter()
11499                .map(|c| c.label.text.clone())
11500                .collect::<Vec<_>>();
11501            assert_eq!(
11502                completion_labels,
11503                &[
11504                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11505                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11506                    "single line label 2 d e f ",
11507                    "a b c g h i ",
11508                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11509                ],
11510                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11511            );
11512
11513            for completion in menu
11514                .completions
11515                .borrow()
11516                .iter() {
11517                    assert_eq!(
11518                        completion.label.filter_range,
11519                        0..completion.label.text.len(),
11520                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11521                    );
11522                }
11523        } else {
11524            panic!("expected completion menu to be open");
11525        }
11526    });
11527}
11528
11529#[gpui::test]
11530async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11531    init_test(cx, |_| {});
11532    let mut cx = EditorLspTestContext::new_rust(
11533        lsp::ServerCapabilities {
11534            completion_provider: Some(lsp::CompletionOptions {
11535                trigger_characters: Some(vec![".".to_string()]),
11536                ..Default::default()
11537            }),
11538            ..Default::default()
11539        },
11540        cx,
11541    )
11542    .await;
11543    cx.lsp
11544        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11545            Ok(Some(lsp::CompletionResponse::Array(vec![
11546                lsp::CompletionItem {
11547                    label: "first".into(),
11548                    ..Default::default()
11549                },
11550                lsp::CompletionItem {
11551                    label: "last".into(),
11552                    ..Default::default()
11553                },
11554            ])))
11555        });
11556    cx.set_state("variableˇ");
11557    cx.simulate_keystroke(".");
11558    cx.executor().run_until_parked();
11559
11560    cx.update_editor(|editor, _, _| {
11561        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11562        {
11563            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11564        } else {
11565            panic!("expected completion menu to be open");
11566        }
11567    });
11568
11569    cx.update_editor(|editor, window, cx| {
11570        editor.move_page_down(&MovePageDown::default(), window, cx);
11571        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11572        {
11573            assert!(
11574                menu.selected_item == 1,
11575                "expected PageDown to select the last item from the context menu"
11576            );
11577        } else {
11578            panic!("expected completion menu to stay open after PageDown");
11579        }
11580    });
11581
11582    cx.update_editor(|editor, window, cx| {
11583        editor.move_page_up(&MovePageUp::default(), window, cx);
11584        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11585        {
11586            assert!(
11587                menu.selected_item == 0,
11588                "expected PageUp to select the first item from the context menu"
11589            );
11590        } else {
11591            panic!("expected completion menu to stay open after PageUp");
11592        }
11593    });
11594}
11595
11596#[gpui::test]
11597async fn test_as_is_completions(cx: &mut TestAppContext) {
11598    init_test(cx, |_| {});
11599    let mut cx = EditorLspTestContext::new_rust(
11600        lsp::ServerCapabilities {
11601            completion_provider: Some(lsp::CompletionOptions {
11602                ..Default::default()
11603            }),
11604            ..Default::default()
11605        },
11606        cx,
11607    )
11608    .await;
11609    cx.lsp
11610        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11611            Ok(Some(lsp::CompletionResponse::Array(vec![
11612                lsp::CompletionItem {
11613                    label: "unsafe".into(),
11614                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11615                        range: lsp::Range {
11616                            start: lsp::Position {
11617                                line: 1,
11618                                character: 2,
11619                            },
11620                            end: lsp::Position {
11621                                line: 1,
11622                                character: 3,
11623                            },
11624                        },
11625                        new_text: "unsafe".to_string(),
11626                    })),
11627                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11628                    ..Default::default()
11629                },
11630            ])))
11631        });
11632    cx.set_state("fn a() {}\n");
11633    cx.executor().run_until_parked();
11634    cx.update_editor(|editor, window, cx| {
11635        editor.show_completions(
11636            &ShowCompletions {
11637                trigger: Some("\n".into()),
11638            },
11639            window,
11640            cx,
11641        );
11642    });
11643    cx.executor().run_until_parked();
11644
11645    cx.update_editor(|editor, window, cx| {
11646        editor.confirm_completion(&Default::default(), window, cx)
11647    });
11648    cx.executor().run_until_parked();
11649    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11650}
11651
11652#[gpui::test]
11653async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11654    init_test(cx, |_| {});
11655
11656    let mut cx = EditorLspTestContext::new_rust(
11657        lsp::ServerCapabilities {
11658            completion_provider: Some(lsp::CompletionOptions {
11659                trigger_characters: Some(vec![".".to_string()]),
11660                resolve_provider: Some(true),
11661                ..Default::default()
11662            }),
11663            ..Default::default()
11664        },
11665        cx,
11666    )
11667    .await;
11668
11669    cx.set_state("fn main() { let a = 2ˇ; }");
11670    cx.simulate_keystroke(".");
11671    let completion_item = lsp::CompletionItem {
11672        label: "Some".into(),
11673        kind: Some(lsp::CompletionItemKind::SNIPPET),
11674        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11675        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11676            kind: lsp::MarkupKind::Markdown,
11677            value: "```rust\nSome(2)\n```".to_string(),
11678        })),
11679        deprecated: Some(false),
11680        sort_text: Some("Some".to_string()),
11681        filter_text: Some("Some".to_string()),
11682        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11683        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11684            range: lsp::Range {
11685                start: lsp::Position {
11686                    line: 0,
11687                    character: 22,
11688                },
11689                end: lsp::Position {
11690                    line: 0,
11691                    character: 22,
11692                },
11693            },
11694            new_text: "Some(2)".to_string(),
11695        })),
11696        additional_text_edits: Some(vec![lsp::TextEdit {
11697            range: lsp::Range {
11698                start: lsp::Position {
11699                    line: 0,
11700                    character: 20,
11701                },
11702                end: lsp::Position {
11703                    line: 0,
11704                    character: 22,
11705                },
11706            },
11707            new_text: "".to_string(),
11708        }]),
11709        ..Default::default()
11710    };
11711
11712    let closure_completion_item = completion_item.clone();
11713    let counter = Arc::new(AtomicUsize::new(0));
11714    let counter_clone = counter.clone();
11715    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11716        let task_completion_item = closure_completion_item.clone();
11717        counter_clone.fetch_add(1, atomic::Ordering::Release);
11718        async move {
11719            Ok(Some(lsp::CompletionResponse::Array(vec![
11720                task_completion_item,
11721            ])))
11722        }
11723    });
11724
11725    cx.condition(|editor, _| editor.context_menu_visible())
11726        .await;
11727    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11728    assert!(request.next().await.is_some());
11729    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11730
11731    cx.simulate_keystrokes("S o m");
11732    cx.condition(|editor, _| editor.context_menu_visible())
11733        .await;
11734    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11735    assert!(request.next().await.is_some());
11736    assert!(request.next().await.is_some());
11737    assert!(request.next().await.is_some());
11738    request.close();
11739    assert!(request.next().await.is_none());
11740    assert_eq!(
11741        counter.load(atomic::Ordering::Acquire),
11742        4,
11743        "With the completions menu open, only one LSP request should happen per input"
11744    );
11745}
11746
11747#[gpui::test]
11748async fn test_toggle_comment(cx: &mut TestAppContext) {
11749    init_test(cx, |_| {});
11750    let mut cx = EditorTestContext::new(cx).await;
11751    let language = Arc::new(Language::new(
11752        LanguageConfig {
11753            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11754            ..Default::default()
11755        },
11756        Some(tree_sitter_rust::LANGUAGE.into()),
11757    ));
11758    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11759
11760    // If multiple selections intersect a line, the line is only toggled once.
11761    cx.set_state(indoc! {"
11762        fn a() {
11763            «//b();
11764            ˇ»// «c();
11765            //ˇ»  d();
11766        }
11767    "});
11768
11769    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11770
11771    cx.assert_editor_state(indoc! {"
11772        fn a() {
11773            «b();
11774            c();
11775            ˇ» d();
11776        }
11777    "});
11778
11779    // The comment prefix is inserted at the same column for every line in a
11780    // selection.
11781    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11782
11783    cx.assert_editor_state(indoc! {"
11784        fn a() {
11785            // «b();
11786            // c();
11787            ˇ»//  d();
11788        }
11789    "});
11790
11791    // If a selection ends at the beginning of a line, that line is not toggled.
11792    cx.set_selections_state(indoc! {"
11793        fn a() {
11794            // b();
11795            «// c();
11796        ˇ»    //  d();
11797        }
11798    "});
11799
11800    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11801
11802    cx.assert_editor_state(indoc! {"
11803        fn a() {
11804            // b();
11805            «c();
11806        ˇ»    //  d();
11807        }
11808    "});
11809
11810    // If a selection span a single line and is empty, the line is toggled.
11811    cx.set_state(indoc! {"
11812        fn a() {
11813            a();
11814            b();
11815        ˇ
11816        }
11817    "});
11818
11819    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11820
11821    cx.assert_editor_state(indoc! {"
11822        fn a() {
11823            a();
11824            b();
11825        //•ˇ
11826        }
11827    "});
11828
11829    // If a selection span multiple lines, empty lines are not toggled.
11830    cx.set_state(indoc! {"
11831        fn a() {
11832            «a();
11833
11834            c();ˇ»
11835        }
11836    "});
11837
11838    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11839
11840    cx.assert_editor_state(indoc! {"
11841        fn a() {
11842            // «a();
11843
11844            // c();ˇ»
11845        }
11846    "});
11847
11848    // If a selection includes multiple comment prefixes, all lines are uncommented.
11849    cx.set_state(indoc! {"
11850        fn a() {
11851            «// a();
11852            /// b();
11853            //! c();ˇ»
11854        }
11855    "});
11856
11857    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11858
11859    cx.assert_editor_state(indoc! {"
11860        fn a() {
11861            «a();
11862            b();
11863            c();ˇ»
11864        }
11865    "});
11866}
11867
11868#[gpui::test]
11869async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11870    init_test(cx, |_| {});
11871    let mut cx = EditorTestContext::new(cx).await;
11872    let language = Arc::new(Language::new(
11873        LanguageConfig {
11874            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11875            ..Default::default()
11876        },
11877        Some(tree_sitter_rust::LANGUAGE.into()),
11878    ));
11879    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11880
11881    let toggle_comments = &ToggleComments {
11882        advance_downwards: false,
11883        ignore_indent: true,
11884    };
11885
11886    // If multiple selections intersect a line, the line is only toggled once.
11887    cx.set_state(indoc! {"
11888        fn a() {
11889        //    «b();
11890        //    c();
11891        //    ˇ» d();
11892        }
11893    "});
11894
11895    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11896
11897    cx.assert_editor_state(indoc! {"
11898        fn a() {
11899            «b();
11900            c();
11901            ˇ» d();
11902        }
11903    "});
11904
11905    // The comment prefix is inserted at the beginning of each line
11906    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11907
11908    cx.assert_editor_state(indoc! {"
11909        fn a() {
11910        //    «b();
11911        //    c();
11912        //    ˇ» d();
11913        }
11914    "});
11915
11916    // If a selection ends at the beginning of a line, that line is not toggled.
11917    cx.set_selections_state(indoc! {"
11918        fn a() {
11919        //    b();
11920        //    «c();
11921        ˇ»//     d();
11922        }
11923    "});
11924
11925    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11926
11927    cx.assert_editor_state(indoc! {"
11928        fn a() {
11929        //    b();
11930            «c();
11931        ˇ»//     d();
11932        }
11933    "});
11934
11935    // If a selection span a single line and is empty, the line is toggled.
11936    cx.set_state(indoc! {"
11937        fn a() {
11938            a();
11939            b();
11940        ˇ
11941        }
11942    "});
11943
11944    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11945
11946    cx.assert_editor_state(indoc! {"
11947        fn a() {
11948            a();
11949            b();
11950        //ˇ
11951        }
11952    "});
11953
11954    // If a selection span multiple lines, empty lines are not toggled.
11955    cx.set_state(indoc! {"
11956        fn a() {
11957            «a();
11958
11959            c();ˇ»
11960        }
11961    "});
11962
11963    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11964
11965    cx.assert_editor_state(indoc! {"
11966        fn a() {
11967        //    «a();
11968
11969        //    c();ˇ»
11970        }
11971    "});
11972
11973    // If a selection includes multiple comment prefixes, all lines are uncommented.
11974    cx.set_state(indoc! {"
11975        fn a() {
11976        //    «a();
11977        ///    b();
11978        //!    c();ˇ»
11979        }
11980    "});
11981
11982    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11983
11984    cx.assert_editor_state(indoc! {"
11985        fn a() {
11986            «a();
11987            b();
11988            c();ˇ»
11989        }
11990    "});
11991}
11992
11993#[gpui::test]
11994async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11995    init_test(cx, |_| {});
11996
11997    let language = Arc::new(Language::new(
11998        LanguageConfig {
11999            line_comments: vec!["// ".into()],
12000            ..Default::default()
12001        },
12002        Some(tree_sitter_rust::LANGUAGE.into()),
12003    ));
12004
12005    let mut cx = EditorTestContext::new(cx).await;
12006
12007    cx.language_registry().add(language.clone());
12008    cx.update_buffer(|buffer, cx| {
12009        buffer.set_language(Some(language), cx);
12010    });
12011
12012    let toggle_comments = &ToggleComments {
12013        advance_downwards: true,
12014        ignore_indent: false,
12015    };
12016
12017    // Single cursor on one line -> advance
12018    // Cursor moves horizontally 3 characters as well on non-blank line
12019    cx.set_state(indoc!(
12020        "fn a() {
12021             ˇdog();
12022             cat();
12023        }"
12024    ));
12025    cx.update_editor(|editor, window, cx| {
12026        editor.toggle_comments(toggle_comments, window, cx);
12027    });
12028    cx.assert_editor_state(indoc!(
12029        "fn a() {
12030             // dog();
12031             catˇ();
12032        }"
12033    ));
12034
12035    // Single selection on one line -> don't advance
12036    cx.set_state(indoc!(
12037        "fn a() {
12038             «dog()ˇ»;
12039             cat();
12040        }"
12041    ));
12042    cx.update_editor(|editor, window, cx| {
12043        editor.toggle_comments(toggle_comments, window, cx);
12044    });
12045    cx.assert_editor_state(indoc!(
12046        "fn a() {
12047             // «dog()ˇ»;
12048             cat();
12049        }"
12050    ));
12051
12052    // Multiple cursors on one line -> advance
12053    cx.set_state(indoc!(
12054        "fn a() {
12055             ˇdˇog();
12056             cat();
12057        }"
12058    ));
12059    cx.update_editor(|editor, window, cx| {
12060        editor.toggle_comments(toggle_comments, window, cx);
12061    });
12062    cx.assert_editor_state(indoc!(
12063        "fn a() {
12064             // dog();
12065             catˇ(ˇ);
12066        }"
12067    ));
12068
12069    // Multiple cursors on one line, with selection -> don't advance
12070    cx.set_state(indoc!(
12071        "fn a() {
12072             ˇdˇog«()ˇ»;
12073             cat();
12074        }"
12075    ));
12076    cx.update_editor(|editor, window, cx| {
12077        editor.toggle_comments(toggle_comments, window, cx);
12078    });
12079    cx.assert_editor_state(indoc!(
12080        "fn a() {
12081             // ˇdˇog«()ˇ»;
12082             cat();
12083        }"
12084    ));
12085
12086    // Single cursor on one line -> advance
12087    // Cursor moves to column 0 on blank line
12088    cx.set_state(indoc!(
12089        "fn a() {
12090             ˇdog();
12091
12092             cat();
12093        }"
12094    ));
12095    cx.update_editor(|editor, window, cx| {
12096        editor.toggle_comments(toggle_comments, window, cx);
12097    });
12098    cx.assert_editor_state(indoc!(
12099        "fn a() {
12100             // dog();
12101        ˇ
12102             cat();
12103        }"
12104    ));
12105
12106    // Single cursor on one line -> advance
12107    // Cursor starts and ends at column 0
12108    cx.set_state(indoc!(
12109        "fn a() {
12110         ˇ    dog();
12111             cat();
12112        }"
12113    ));
12114    cx.update_editor(|editor, window, cx| {
12115        editor.toggle_comments(toggle_comments, window, cx);
12116    });
12117    cx.assert_editor_state(indoc!(
12118        "fn a() {
12119             // dog();
12120         ˇ    cat();
12121        }"
12122    ));
12123}
12124
12125#[gpui::test]
12126async fn test_toggle_block_comment(cx: &mut TestAppContext) {
12127    init_test(cx, |_| {});
12128
12129    let mut cx = EditorTestContext::new(cx).await;
12130
12131    let html_language = Arc::new(
12132        Language::new(
12133            LanguageConfig {
12134                name: "HTML".into(),
12135                block_comment: Some(("<!-- ".into(), " -->".into())),
12136                ..Default::default()
12137            },
12138            Some(tree_sitter_html::LANGUAGE.into()),
12139        )
12140        .with_injection_query(
12141            r#"
12142            (script_element
12143                (raw_text) @injection.content
12144                (#set! injection.language "javascript"))
12145            "#,
12146        )
12147        .unwrap(),
12148    );
12149
12150    let javascript_language = Arc::new(Language::new(
12151        LanguageConfig {
12152            name: "JavaScript".into(),
12153            line_comments: vec!["// ".into()],
12154            ..Default::default()
12155        },
12156        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12157    ));
12158
12159    cx.language_registry().add(html_language.clone());
12160    cx.language_registry().add(javascript_language.clone());
12161    cx.update_buffer(|buffer, cx| {
12162        buffer.set_language(Some(html_language), cx);
12163    });
12164
12165    // Toggle comments for empty selections
12166    cx.set_state(
12167        &r#"
12168            <p>A</p>ˇ
12169            <p>B</p>ˇ
12170            <p>C</p>ˇ
12171        "#
12172        .unindent(),
12173    );
12174    cx.update_editor(|editor, window, cx| {
12175        editor.toggle_comments(&ToggleComments::default(), window, cx)
12176    });
12177    cx.assert_editor_state(
12178        &r#"
12179            <!-- <p>A</p>ˇ -->
12180            <!-- <p>B</p>ˇ -->
12181            <!-- <p>C</p>ˇ -->
12182        "#
12183        .unindent(),
12184    );
12185    cx.update_editor(|editor, window, cx| {
12186        editor.toggle_comments(&ToggleComments::default(), window, cx)
12187    });
12188    cx.assert_editor_state(
12189        &r#"
12190            <p>A</p>ˇ
12191            <p>B</p>ˇ
12192            <p>C</p>ˇ
12193        "#
12194        .unindent(),
12195    );
12196
12197    // Toggle comments for mixture of empty and non-empty selections, where
12198    // multiple selections occupy a given line.
12199    cx.set_state(
12200        &r#"
12201            <p>A«</p>
12202            <p>ˇ»B</p>ˇ
12203            <p>C«</p>
12204            <p>ˇ»D</p>ˇ
12205        "#
12206        .unindent(),
12207    );
12208
12209    cx.update_editor(|editor, window, cx| {
12210        editor.toggle_comments(&ToggleComments::default(), window, cx)
12211    });
12212    cx.assert_editor_state(
12213        &r#"
12214            <!-- <p>A«</p>
12215            <p>ˇ»B</p>ˇ -->
12216            <!-- <p>C«</p>
12217            <p>ˇ»D</p>ˇ -->
12218        "#
12219        .unindent(),
12220    );
12221    cx.update_editor(|editor, window, cx| {
12222        editor.toggle_comments(&ToggleComments::default(), window, cx)
12223    });
12224    cx.assert_editor_state(
12225        &r#"
12226            <p>A«</p>
12227            <p>ˇ»B</p>ˇ
12228            <p>C«</p>
12229            <p>ˇ»D</p>ˇ
12230        "#
12231        .unindent(),
12232    );
12233
12234    // Toggle comments when different languages are active for different
12235    // selections.
12236    cx.set_state(
12237        &r#"
12238            ˇ<script>
12239                ˇvar x = new Y();
12240            ˇ</script>
12241        "#
12242        .unindent(),
12243    );
12244    cx.executor().run_until_parked();
12245    cx.update_editor(|editor, window, cx| {
12246        editor.toggle_comments(&ToggleComments::default(), window, cx)
12247    });
12248    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
12249    // Uncommenting and commenting from this position brings in even more wrong artifacts.
12250    cx.assert_editor_state(
12251        &r#"
12252            <!-- ˇ<script> -->
12253                // ˇvar x = new Y();
12254            <!-- ˇ</script> -->
12255        "#
12256        .unindent(),
12257    );
12258}
12259
12260#[gpui::test]
12261fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12262    init_test(cx, |_| {});
12263
12264    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12265    let multibuffer = cx.new(|cx| {
12266        let mut multibuffer = MultiBuffer::new(ReadWrite);
12267        multibuffer.push_excerpts(
12268            buffer.clone(),
12269            [
12270                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12271                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12272            ],
12273            cx,
12274        );
12275        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12276        multibuffer
12277    });
12278
12279    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12280    editor.update_in(cx, |editor, window, cx| {
12281        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12282        editor.change_selections(None, window, cx, |s| {
12283            s.select_ranges([
12284                Point::new(0, 0)..Point::new(0, 0),
12285                Point::new(1, 0)..Point::new(1, 0),
12286            ])
12287        });
12288
12289        editor.handle_input("X", window, cx);
12290        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12291        assert_eq!(
12292            editor.selections.ranges(cx),
12293            [
12294                Point::new(0, 1)..Point::new(0, 1),
12295                Point::new(1, 1)..Point::new(1, 1),
12296            ]
12297        );
12298
12299        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12300        editor.change_selections(None, window, cx, |s| {
12301            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12302        });
12303        editor.backspace(&Default::default(), window, cx);
12304        assert_eq!(editor.text(cx), "Xa\nbbb");
12305        assert_eq!(
12306            editor.selections.ranges(cx),
12307            [Point::new(1, 0)..Point::new(1, 0)]
12308        );
12309
12310        editor.change_selections(None, window, cx, |s| {
12311            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12312        });
12313        editor.backspace(&Default::default(), window, cx);
12314        assert_eq!(editor.text(cx), "X\nbb");
12315        assert_eq!(
12316            editor.selections.ranges(cx),
12317            [Point::new(0, 1)..Point::new(0, 1)]
12318        );
12319    });
12320}
12321
12322#[gpui::test]
12323fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12324    init_test(cx, |_| {});
12325
12326    let markers = vec![('[', ']').into(), ('(', ')').into()];
12327    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12328        indoc! {"
12329            [aaaa
12330            (bbbb]
12331            cccc)",
12332        },
12333        markers.clone(),
12334    );
12335    let excerpt_ranges = markers.into_iter().map(|marker| {
12336        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12337        ExcerptRange::new(context.clone())
12338    });
12339    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12340    let multibuffer = cx.new(|cx| {
12341        let mut multibuffer = MultiBuffer::new(ReadWrite);
12342        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12343        multibuffer
12344    });
12345
12346    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12347    editor.update_in(cx, |editor, window, cx| {
12348        let (expected_text, selection_ranges) = marked_text_ranges(
12349            indoc! {"
12350                aaaa
12351                bˇbbb
12352                bˇbbˇb
12353                cccc"
12354            },
12355            true,
12356        );
12357        assert_eq!(editor.text(cx), expected_text);
12358        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12359
12360        editor.handle_input("X", window, cx);
12361
12362        let (expected_text, expected_selections) = marked_text_ranges(
12363            indoc! {"
12364                aaaa
12365                bXˇbbXb
12366                bXˇbbXˇb
12367                cccc"
12368            },
12369            false,
12370        );
12371        assert_eq!(editor.text(cx), expected_text);
12372        assert_eq!(editor.selections.ranges(cx), expected_selections);
12373
12374        editor.newline(&Newline, window, cx);
12375        let (expected_text, expected_selections) = marked_text_ranges(
12376            indoc! {"
12377                aaaa
12378                bX
12379                ˇbbX
12380                b
12381                bX
12382                ˇbbX
12383                ˇb
12384                cccc"
12385            },
12386            false,
12387        );
12388        assert_eq!(editor.text(cx), expected_text);
12389        assert_eq!(editor.selections.ranges(cx), expected_selections);
12390    });
12391}
12392
12393#[gpui::test]
12394fn test_refresh_selections(cx: &mut TestAppContext) {
12395    init_test(cx, |_| {});
12396
12397    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12398    let mut excerpt1_id = None;
12399    let multibuffer = cx.new(|cx| {
12400        let mut multibuffer = MultiBuffer::new(ReadWrite);
12401        excerpt1_id = multibuffer
12402            .push_excerpts(
12403                buffer.clone(),
12404                [
12405                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12406                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12407                ],
12408                cx,
12409            )
12410            .into_iter()
12411            .next();
12412        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12413        multibuffer
12414    });
12415
12416    let editor = cx.add_window(|window, cx| {
12417        let mut editor = build_editor(multibuffer.clone(), window, cx);
12418        let snapshot = editor.snapshot(window, cx);
12419        editor.change_selections(None, window, cx, |s| {
12420            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12421        });
12422        editor.begin_selection(
12423            Point::new(2, 1).to_display_point(&snapshot),
12424            true,
12425            1,
12426            window,
12427            cx,
12428        );
12429        assert_eq!(
12430            editor.selections.ranges(cx),
12431            [
12432                Point::new(1, 3)..Point::new(1, 3),
12433                Point::new(2, 1)..Point::new(2, 1),
12434            ]
12435        );
12436        editor
12437    });
12438
12439    // Refreshing selections is a no-op when excerpts haven't changed.
12440    _ = editor.update(cx, |editor, window, cx| {
12441        editor.change_selections(None, window, cx, |s| s.refresh());
12442        assert_eq!(
12443            editor.selections.ranges(cx),
12444            [
12445                Point::new(1, 3)..Point::new(1, 3),
12446                Point::new(2, 1)..Point::new(2, 1),
12447            ]
12448        );
12449    });
12450
12451    multibuffer.update(cx, |multibuffer, cx| {
12452        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12453    });
12454    _ = editor.update(cx, |editor, window, cx| {
12455        // Removing an excerpt causes the first selection to become degenerate.
12456        assert_eq!(
12457            editor.selections.ranges(cx),
12458            [
12459                Point::new(0, 0)..Point::new(0, 0),
12460                Point::new(0, 1)..Point::new(0, 1)
12461            ]
12462        );
12463
12464        // Refreshing selections will relocate the first selection to the original buffer
12465        // location.
12466        editor.change_selections(None, window, cx, |s| s.refresh());
12467        assert_eq!(
12468            editor.selections.ranges(cx),
12469            [
12470                Point::new(0, 1)..Point::new(0, 1),
12471                Point::new(0, 3)..Point::new(0, 3)
12472            ]
12473        );
12474        assert!(editor.selections.pending_anchor().is_some());
12475    });
12476}
12477
12478#[gpui::test]
12479fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12480    init_test(cx, |_| {});
12481
12482    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12483    let mut excerpt1_id = None;
12484    let multibuffer = cx.new(|cx| {
12485        let mut multibuffer = MultiBuffer::new(ReadWrite);
12486        excerpt1_id = multibuffer
12487            .push_excerpts(
12488                buffer.clone(),
12489                [
12490                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12491                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12492                ],
12493                cx,
12494            )
12495            .into_iter()
12496            .next();
12497        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12498        multibuffer
12499    });
12500
12501    let editor = cx.add_window(|window, cx| {
12502        let mut editor = build_editor(multibuffer.clone(), window, cx);
12503        let snapshot = editor.snapshot(window, cx);
12504        editor.begin_selection(
12505            Point::new(1, 3).to_display_point(&snapshot),
12506            false,
12507            1,
12508            window,
12509            cx,
12510        );
12511        assert_eq!(
12512            editor.selections.ranges(cx),
12513            [Point::new(1, 3)..Point::new(1, 3)]
12514        );
12515        editor
12516    });
12517
12518    multibuffer.update(cx, |multibuffer, cx| {
12519        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12520    });
12521    _ = editor.update(cx, |editor, window, cx| {
12522        assert_eq!(
12523            editor.selections.ranges(cx),
12524            [Point::new(0, 0)..Point::new(0, 0)]
12525        );
12526
12527        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12528        editor.change_selections(None, window, cx, |s| s.refresh());
12529        assert_eq!(
12530            editor.selections.ranges(cx),
12531            [Point::new(0, 3)..Point::new(0, 3)]
12532        );
12533        assert!(editor.selections.pending_anchor().is_some());
12534    });
12535}
12536
12537#[gpui::test]
12538async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12539    init_test(cx, |_| {});
12540
12541    let language = Arc::new(
12542        Language::new(
12543            LanguageConfig {
12544                brackets: BracketPairConfig {
12545                    pairs: vec![
12546                        BracketPair {
12547                            start: "{".to_string(),
12548                            end: "}".to_string(),
12549                            close: true,
12550                            surround: true,
12551                            newline: true,
12552                        },
12553                        BracketPair {
12554                            start: "/* ".to_string(),
12555                            end: " */".to_string(),
12556                            close: true,
12557                            surround: true,
12558                            newline: true,
12559                        },
12560                    ],
12561                    ..Default::default()
12562                },
12563                ..Default::default()
12564            },
12565            Some(tree_sitter_rust::LANGUAGE.into()),
12566        )
12567        .with_indents_query("")
12568        .unwrap(),
12569    );
12570
12571    let text = concat!(
12572        "{   }\n",     //
12573        "  x\n",       //
12574        "  /*   */\n", //
12575        "x\n",         //
12576        "{{} }\n",     //
12577    );
12578
12579    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12580    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12581    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12582    editor
12583        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12584        .await;
12585
12586    editor.update_in(cx, |editor, window, cx| {
12587        editor.change_selections(None, window, cx, |s| {
12588            s.select_display_ranges([
12589                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12590                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12591                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12592            ])
12593        });
12594        editor.newline(&Newline, window, cx);
12595
12596        assert_eq!(
12597            editor.buffer().read(cx).read(cx).text(),
12598            concat!(
12599                "{ \n",    // Suppress rustfmt
12600                "\n",      //
12601                "}\n",     //
12602                "  x\n",   //
12603                "  /* \n", //
12604                "  \n",    //
12605                "  */\n",  //
12606                "x\n",     //
12607                "{{} \n",  //
12608                "}\n",     //
12609            )
12610        );
12611    });
12612}
12613
12614#[gpui::test]
12615fn test_highlighted_ranges(cx: &mut TestAppContext) {
12616    init_test(cx, |_| {});
12617
12618    let editor = cx.add_window(|window, cx| {
12619        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12620        build_editor(buffer.clone(), window, cx)
12621    });
12622
12623    _ = editor.update(cx, |editor, window, cx| {
12624        struct Type1;
12625        struct Type2;
12626
12627        let buffer = editor.buffer.read(cx).snapshot(cx);
12628
12629        let anchor_range =
12630            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12631
12632        editor.highlight_background::<Type1>(
12633            &[
12634                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12635                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12636                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12637                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12638            ],
12639            |_| Hsla::red(),
12640            cx,
12641        );
12642        editor.highlight_background::<Type2>(
12643            &[
12644                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12645                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12646                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12647                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12648            ],
12649            |_| Hsla::green(),
12650            cx,
12651        );
12652
12653        let snapshot = editor.snapshot(window, cx);
12654        let mut highlighted_ranges = editor.background_highlights_in_range(
12655            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12656            &snapshot,
12657            cx.theme().colors(),
12658        );
12659        // Enforce a consistent ordering based on color without relying on the ordering of the
12660        // highlight's `TypeId` which is non-executor.
12661        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12662        assert_eq!(
12663            highlighted_ranges,
12664            &[
12665                (
12666                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12667                    Hsla::red(),
12668                ),
12669                (
12670                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12671                    Hsla::red(),
12672                ),
12673                (
12674                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12675                    Hsla::green(),
12676                ),
12677                (
12678                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12679                    Hsla::green(),
12680                ),
12681            ]
12682        );
12683        assert_eq!(
12684            editor.background_highlights_in_range(
12685                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12686                &snapshot,
12687                cx.theme().colors(),
12688            ),
12689            &[(
12690                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12691                Hsla::red(),
12692            )]
12693        );
12694    });
12695}
12696
12697#[gpui::test]
12698async fn test_following(cx: &mut TestAppContext) {
12699    init_test(cx, |_| {});
12700
12701    let fs = FakeFs::new(cx.executor());
12702    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12703
12704    let buffer = project.update(cx, |project, cx| {
12705        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12706        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12707    });
12708    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12709    let follower = cx.update(|cx| {
12710        cx.open_window(
12711            WindowOptions {
12712                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12713                    gpui::Point::new(px(0.), px(0.)),
12714                    gpui::Point::new(px(10.), px(80.)),
12715                ))),
12716                ..Default::default()
12717            },
12718            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12719        )
12720        .unwrap()
12721    });
12722
12723    let is_still_following = Rc::new(RefCell::new(true));
12724    let follower_edit_event_count = Rc::new(RefCell::new(0));
12725    let pending_update = Rc::new(RefCell::new(None));
12726    let leader_entity = leader.root(cx).unwrap();
12727    let follower_entity = follower.root(cx).unwrap();
12728    _ = follower.update(cx, {
12729        let update = pending_update.clone();
12730        let is_still_following = is_still_following.clone();
12731        let follower_edit_event_count = follower_edit_event_count.clone();
12732        |_, window, cx| {
12733            cx.subscribe_in(
12734                &leader_entity,
12735                window,
12736                move |_, leader, event, window, cx| {
12737                    leader.read(cx).add_event_to_update_proto(
12738                        event,
12739                        &mut update.borrow_mut(),
12740                        window,
12741                        cx,
12742                    );
12743                },
12744            )
12745            .detach();
12746
12747            cx.subscribe_in(
12748                &follower_entity,
12749                window,
12750                move |_, _, event: &EditorEvent, _window, _cx| {
12751                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12752                        *is_still_following.borrow_mut() = false;
12753                    }
12754
12755                    if let EditorEvent::BufferEdited = event {
12756                        *follower_edit_event_count.borrow_mut() += 1;
12757                    }
12758                },
12759            )
12760            .detach();
12761        }
12762    });
12763
12764    // Update the selections only
12765    _ = leader.update(cx, |leader, window, cx| {
12766        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12767    });
12768    follower
12769        .update(cx, |follower, window, cx| {
12770            follower.apply_update_proto(
12771                &project,
12772                pending_update.borrow_mut().take().unwrap(),
12773                window,
12774                cx,
12775            )
12776        })
12777        .unwrap()
12778        .await
12779        .unwrap();
12780    _ = follower.update(cx, |follower, _, cx| {
12781        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12782    });
12783    assert!(*is_still_following.borrow());
12784    assert_eq!(*follower_edit_event_count.borrow(), 0);
12785
12786    // Update the scroll position only
12787    _ = leader.update(cx, |leader, window, cx| {
12788        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12789    });
12790    follower
12791        .update(cx, |follower, window, cx| {
12792            follower.apply_update_proto(
12793                &project,
12794                pending_update.borrow_mut().take().unwrap(),
12795                window,
12796                cx,
12797            )
12798        })
12799        .unwrap()
12800        .await
12801        .unwrap();
12802    assert_eq!(
12803        follower
12804            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12805            .unwrap(),
12806        gpui::Point::new(1.5, 3.5)
12807    );
12808    assert!(*is_still_following.borrow());
12809    assert_eq!(*follower_edit_event_count.borrow(), 0);
12810
12811    // Update the selections and scroll position. The follower's scroll position is updated
12812    // via autoscroll, not via the leader's exact scroll position.
12813    _ = leader.update(cx, |leader, window, cx| {
12814        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12815        leader.request_autoscroll(Autoscroll::newest(), cx);
12816        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12817    });
12818    follower
12819        .update(cx, |follower, window, cx| {
12820            follower.apply_update_proto(
12821                &project,
12822                pending_update.borrow_mut().take().unwrap(),
12823                window,
12824                cx,
12825            )
12826        })
12827        .unwrap()
12828        .await
12829        .unwrap();
12830    _ = follower.update(cx, |follower, _, cx| {
12831        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12832        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12833    });
12834    assert!(*is_still_following.borrow());
12835
12836    // Creating a pending selection that precedes another selection
12837    _ = leader.update(cx, |leader, window, cx| {
12838        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12839        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12840    });
12841    follower
12842        .update(cx, |follower, window, cx| {
12843            follower.apply_update_proto(
12844                &project,
12845                pending_update.borrow_mut().take().unwrap(),
12846                window,
12847                cx,
12848            )
12849        })
12850        .unwrap()
12851        .await
12852        .unwrap();
12853    _ = follower.update(cx, |follower, _, cx| {
12854        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12855    });
12856    assert!(*is_still_following.borrow());
12857
12858    // Extend the pending selection so that it surrounds another selection
12859    _ = leader.update(cx, |leader, window, cx| {
12860        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12861    });
12862    follower
12863        .update(cx, |follower, window, cx| {
12864            follower.apply_update_proto(
12865                &project,
12866                pending_update.borrow_mut().take().unwrap(),
12867                window,
12868                cx,
12869            )
12870        })
12871        .unwrap()
12872        .await
12873        .unwrap();
12874    _ = follower.update(cx, |follower, _, cx| {
12875        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12876    });
12877
12878    // Scrolling locally breaks the follow
12879    _ = follower.update(cx, |follower, window, cx| {
12880        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12881        follower.set_scroll_anchor(
12882            ScrollAnchor {
12883                anchor: top_anchor,
12884                offset: gpui::Point::new(0.0, 0.5),
12885            },
12886            window,
12887            cx,
12888        );
12889    });
12890    assert!(!(*is_still_following.borrow()));
12891}
12892
12893#[gpui::test]
12894async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12895    init_test(cx, |_| {});
12896
12897    let fs = FakeFs::new(cx.executor());
12898    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12899    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12900    let pane = workspace
12901        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12902        .unwrap();
12903
12904    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12905
12906    let leader = pane.update_in(cx, |_, window, cx| {
12907        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12908        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12909    });
12910
12911    // Start following the editor when it has no excerpts.
12912    let mut state_message =
12913        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12914    let workspace_entity = workspace.root(cx).unwrap();
12915    let follower_1 = cx
12916        .update_window(*workspace.deref(), |_, window, cx| {
12917            Editor::from_state_proto(
12918                workspace_entity,
12919                ViewId {
12920                    creator: CollaboratorId::PeerId(PeerId::default()),
12921                    id: 0,
12922                },
12923                &mut state_message,
12924                window,
12925                cx,
12926            )
12927        })
12928        .unwrap()
12929        .unwrap()
12930        .await
12931        .unwrap();
12932
12933    let update_message = Rc::new(RefCell::new(None));
12934    follower_1.update_in(cx, {
12935        let update = update_message.clone();
12936        |_, window, cx| {
12937            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12938                leader.read(cx).add_event_to_update_proto(
12939                    event,
12940                    &mut update.borrow_mut(),
12941                    window,
12942                    cx,
12943                );
12944            })
12945            .detach();
12946        }
12947    });
12948
12949    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12950        (
12951            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12952            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12953        )
12954    });
12955
12956    // Insert some excerpts.
12957    leader.update(cx, |leader, cx| {
12958        leader.buffer.update(cx, |multibuffer, cx| {
12959            multibuffer.set_excerpts_for_path(
12960                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12961                buffer_1.clone(),
12962                vec![
12963                    Point::row_range(0..3),
12964                    Point::row_range(1..6),
12965                    Point::row_range(12..15),
12966                ],
12967                0,
12968                cx,
12969            );
12970            multibuffer.set_excerpts_for_path(
12971                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12972                buffer_2.clone(),
12973                vec![Point::row_range(0..6), Point::row_range(8..12)],
12974                0,
12975                cx,
12976            );
12977        });
12978    });
12979
12980    // Apply the update of adding the excerpts.
12981    follower_1
12982        .update_in(cx, |follower, window, cx| {
12983            follower.apply_update_proto(
12984                &project,
12985                update_message.borrow().clone().unwrap(),
12986                window,
12987                cx,
12988            )
12989        })
12990        .await
12991        .unwrap();
12992    assert_eq!(
12993        follower_1.update(cx, |editor, cx| editor.text(cx)),
12994        leader.update(cx, |editor, cx| editor.text(cx))
12995    );
12996    update_message.borrow_mut().take();
12997
12998    // Start following separately after it already has excerpts.
12999    let mut state_message =
13000        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
13001    let workspace_entity = workspace.root(cx).unwrap();
13002    let follower_2 = cx
13003        .update_window(*workspace.deref(), |_, window, cx| {
13004            Editor::from_state_proto(
13005                workspace_entity,
13006                ViewId {
13007                    creator: CollaboratorId::PeerId(PeerId::default()),
13008                    id: 0,
13009                },
13010                &mut state_message,
13011                window,
13012                cx,
13013            )
13014        })
13015        .unwrap()
13016        .unwrap()
13017        .await
13018        .unwrap();
13019    assert_eq!(
13020        follower_2.update(cx, |editor, cx| editor.text(cx)),
13021        leader.update(cx, |editor, cx| editor.text(cx))
13022    );
13023
13024    // Remove some excerpts.
13025    leader.update(cx, |leader, cx| {
13026        leader.buffer.update(cx, |multibuffer, cx| {
13027            let excerpt_ids = multibuffer.excerpt_ids();
13028            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
13029            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
13030        });
13031    });
13032
13033    // Apply the update of removing the excerpts.
13034    follower_1
13035        .update_in(cx, |follower, window, cx| {
13036            follower.apply_update_proto(
13037                &project,
13038                update_message.borrow().clone().unwrap(),
13039                window,
13040                cx,
13041            )
13042        })
13043        .await
13044        .unwrap();
13045    follower_2
13046        .update_in(cx, |follower, window, cx| {
13047            follower.apply_update_proto(
13048                &project,
13049                update_message.borrow().clone().unwrap(),
13050                window,
13051                cx,
13052            )
13053        })
13054        .await
13055        .unwrap();
13056    update_message.borrow_mut().take();
13057    assert_eq!(
13058        follower_1.update(cx, |editor, cx| editor.text(cx)),
13059        leader.update(cx, |editor, cx| editor.text(cx))
13060    );
13061}
13062
13063#[gpui::test]
13064async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13065    init_test(cx, |_| {});
13066
13067    let mut cx = EditorTestContext::new(cx).await;
13068    let lsp_store =
13069        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
13070
13071    cx.set_state(indoc! {"
13072        ˇfn func(abc def: i32) -> u32 {
13073        }
13074    "});
13075
13076    cx.update(|_, cx| {
13077        lsp_store.update(cx, |lsp_store, cx| {
13078            lsp_store
13079                .update_diagnostics(
13080                    LanguageServerId(0),
13081                    lsp::PublishDiagnosticsParams {
13082                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
13083                        version: None,
13084                        diagnostics: vec![
13085                            lsp::Diagnostic {
13086                                range: lsp::Range::new(
13087                                    lsp::Position::new(0, 11),
13088                                    lsp::Position::new(0, 12),
13089                                ),
13090                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13091                                ..Default::default()
13092                            },
13093                            lsp::Diagnostic {
13094                                range: lsp::Range::new(
13095                                    lsp::Position::new(0, 12),
13096                                    lsp::Position::new(0, 15),
13097                                ),
13098                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13099                                ..Default::default()
13100                            },
13101                            lsp::Diagnostic {
13102                                range: lsp::Range::new(
13103                                    lsp::Position::new(0, 25),
13104                                    lsp::Position::new(0, 28),
13105                                ),
13106                                severity: Some(lsp::DiagnosticSeverity::ERROR),
13107                                ..Default::default()
13108                            },
13109                        ],
13110                    },
13111                    &[],
13112                    cx,
13113                )
13114                .unwrap()
13115        });
13116    });
13117
13118    executor.run_until_parked();
13119
13120    cx.update_editor(|editor, window, cx| {
13121        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13122    });
13123
13124    cx.assert_editor_state(indoc! {"
13125        fn func(abc def: i32) -> ˇu32 {
13126        }
13127    "});
13128
13129    cx.update_editor(|editor, window, cx| {
13130        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13131    });
13132
13133    cx.assert_editor_state(indoc! {"
13134        fn func(abc ˇdef: i32) -> u32 {
13135        }
13136    "});
13137
13138    cx.update_editor(|editor, window, cx| {
13139        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13140    });
13141
13142    cx.assert_editor_state(indoc! {"
13143        fn func(abcˇ def: i32) -> u32 {
13144        }
13145    "});
13146
13147    cx.update_editor(|editor, window, cx| {
13148        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
13149    });
13150
13151    cx.assert_editor_state(indoc! {"
13152        fn func(abc def: i32) -> ˇu32 {
13153        }
13154    "});
13155}
13156
13157#[gpui::test]
13158async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13159    init_test(cx, |_| {});
13160
13161    let mut cx = EditorTestContext::new(cx).await;
13162
13163    let diff_base = r#"
13164        use some::mod;
13165
13166        const A: u32 = 42;
13167
13168        fn main() {
13169            println!("hello");
13170
13171            println!("world");
13172        }
13173        "#
13174    .unindent();
13175
13176    // Edits are modified, removed, modified, added
13177    cx.set_state(
13178        &r#"
13179        use some::modified;
13180
13181        ˇ
13182        fn main() {
13183            println!("hello there");
13184
13185            println!("around the");
13186            println!("world");
13187        }
13188        "#
13189        .unindent(),
13190    );
13191
13192    cx.set_head_text(&diff_base);
13193    executor.run_until_parked();
13194
13195    cx.update_editor(|editor, window, cx| {
13196        //Wrap around the bottom of the buffer
13197        for _ in 0..3 {
13198            editor.go_to_next_hunk(&GoToHunk, window, cx);
13199        }
13200    });
13201
13202    cx.assert_editor_state(
13203        &r#"
13204        ˇuse some::modified;
13205
13206
13207        fn main() {
13208            println!("hello there");
13209
13210            println!("around the");
13211            println!("world");
13212        }
13213        "#
13214        .unindent(),
13215    );
13216
13217    cx.update_editor(|editor, window, cx| {
13218        //Wrap around the top of the buffer
13219        for _ in 0..2 {
13220            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13221        }
13222    });
13223
13224    cx.assert_editor_state(
13225        &r#"
13226        use some::modified;
13227
13228
13229        fn main() {
13230        ˇ    println!("hello there");
13231
13232            println!("around the");
13233            println!("world");
13234        }
13235        "#
13236        .unindent(),
13237    );
13238
13239    cx.update_editor(|editor, window, cx| {
13240        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13241    });
13242
13243    cx.assert_editor_state(
13244        &r#"
13245        use some::modified;
13246
13247        ˇ
13248        fn main() {
13249            println!("hello there");
13250
13251            println!("around the");
13252            println!("world");
13253        }
13254        "#
13255        .unindent(),
13256    );
13257
13258    cx.update_editor(|editor, window, cx| {
13259        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13260    });
13261
13262    cx.assert_editor_state(
13263        &r#"
13264        ˇuse some::modified;
13265
13266
13267        fn main() {
13268            println!("hello there");
13269
13270            println!("around the");
13271            println!("world");
13272        }
13273        "#
13274        .unindent(),
13275    );
13276
13277    cx.update_editor(|editor, window, cx| {
13278        for _ in 0..2 {
13279            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13280        }
13281    });
13282
13283    cx.assert_editor_state(
13284        &r#"
13285        use some::modified;
13286
13287
13288        fn main() {
13289        ˇ    println!("hello there");
13290
13291            println!("around the");
13292            println!("world");
13293        }
13294        "#
13295        .unindent(),
13296    );
13297
13298    cx.update_editor(|editor, window, cx| {
13299        editor.fold(&Fold, window, cx);
13300    });
13301
13302    cx.update_editor(|editor, window, cx| {
13303        editor.go_to_next_hunk(&GoToHunk, window, cx);
13304    });
13305
13306    cx.assert_editor_state(
13307        &r#"
13308        ˇuse some::modified;
13309
13310
13311        fn main() {
13312            println!("hello there");
13313
13314            println!("around the");
13315            println!("world");
13316        }
13317        "#
13318        .unindent(),
13319    );
13320}
13321
13322#[test]
13323fn test_split_words() {
13324    fn split(text: &str) -> Vec<&str> {
13325        split_words(text).collect()
13326    }
13327
13328    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13329    assert_eq!(split("hello_world"), &["hello_", "world"]);
13330    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13331    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13332    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13333    assert_eq!(split("helloworld"), &["helloworld"]);
13334
13335    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13336}
13337
13338#[gpui::test]
13339async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13340    init_test(cx, |_| {});
13341
13342    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13343    let mut assert = |before, after| {
13344        let _state_context = cx.set_state(before);
13345        cx.run_until_parked();
13346        cx.update_editor(|editor, window, cx| {
13347            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13348        });
13349        cx.run_until_parked();
13350        cx.assert_editor_state(after);
13351    };
13352
13353    // Outside bracket jumps to outside of matching bracket
13354    assert("console.logˇ(var);", "console.log(var)ˇ;");
13355    assert("console.log(var)ˇ;", "console.logˇ(var);");
13356
13357    // Inside bracket jumps to inside of matching bracket
13358    assert("console.log(ˇvar);", "console.log(varˇ);");
13359    assert("console.log(varˇ);", "console.log(ˇvar);");
13360
13361    // When outside a bracket and inside, favor jumping to the inside bracket
13362    assert(
13363        "console.log('foo', [1, 2, 3]ˇ);",
13364        "console.log(ˇ'foo', [1, 2, 3]);",
13365    );
13366    assert(
13367        "console.log(ˇ'foo', [1, 2, 3]);",
13368        "console.log('foo', [1, 2, 3]ˇ);",
13369    );
13370
13371    // Bias forward if two options are equally likely
13372    assert(
13373        "let result = curried_fun()ˇ();",
13374        "let result = curried_fun()()ˇ;",
13375    );
13376
13377    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13378    assert(
13379        indoc! {"
13380            function test() {
13381                console.log('test')ˇ
13382            }"},
13383        indoc! {"
13384            function test() {
13385                console.logˇ('test')
13386            }"},
13387    );
13388}
13389
13390#[gpui::test]
13391async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13392    init_test(cx, |_| {});
13393
13394    let fs = FakeFs::new(cx.executor());
13395    fs.insert_tree(
13396        path!("/a"),
13397        json!({
13398            "main.rs": "fn main() { let a = 5; }",
13399            "other.rs": "// Test file",
13400        }),
13401    )
13402    .await;
13403    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13404
13405    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13406    language_registry.add(Arc::new(Language::new(
13407        LanguageConfig {
13408            name: "Rust".into(),
13409            matcher: LanguageMatcher {
13410                path_suffixes: vec!["rs".to_string()],
13411                ..Default::default()
13412            },
13413            brackets: BracketPairConfig {
13414                pairs: vec![BracketPair {
13415                    start: "{".to_string(),
13416                    end: "}".to_string(),
13417                    close: true,
13418                    surround: true,
13419                    newline: true,
13420                }],
13421                disabled_scopes_by_bracket_ix: Vec::new(),
13422            },
13423            ..Default::default()
13424        },
13425        Some(tree_sitter_rust::LANGUAGE.into()),
13426    )));
13427    let mut fake_servers = language_registry.register_fake_lsp(
13428        "Rust",
13429        FakeLspAdapter {
13430            capabilities: lsp::ServerCapabilities {
13431                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13432                    first_trigger_character: "{".to_string(),
13433                    more_trigger_character: None,
13434                }),
13435                ..Default::default()
13436            },
13437            ..Default::default()
13438        },
13439    );
13440
13441    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13442
13443    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13444
13445    let worktree_id = workspace
13446        .update(cx, |workspace, _, cx| {
13447            workspace.project().update(cx, |project, cx| {
13448                project.worktrees(cx).next().unwrap().read(cx).id()
13449            })
13450        })
13451        .unwrap();
13452
13453    let buffer = project
13454        .update(cx, |project, cx| {
13455            project.open_local_buffer(path!("/a/main.rs"), cx)
13456        })
13457        .await
13458        .unwrap();
13459    let editor_handle = workspace
13460        .update(cx, |workspace, window, cx| {
13461            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13462        })
13463        .unwrap()
13464        .await
13465        .unwrap()
13466        .downcast::<Editor>()
13467        .unwrap();
13468
13469    cx.executor().start_waiting();
13470    let fake_server = fake_servers.next().await.unwrap();
13471
13472    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13473        |params, _| async move {
13474            assert_eq!(
13475                params.text_document_position.text_document.uri,
13476                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13477            );
13478            assert_eq!(
13479                params.text_document_position.position,
13480                lsp::Position::new(0, 21),
13481            );
13482
13483            Ok(Some(vec![lsp::TextEdit {
13484                new_text: "]".to_string(),
13485                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13486            }]))
13487        },
13488    );
13489
13490    editor_handle.update_in(cx, |editor, window, cx| {
13491        window.focus(&editor.focus_handle(cx));
13492        editor.change_selections(None, window, cx, |s| {
13493            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13494        });
13495        editor.handle_input("{", window, cx);
13496    });
13497
13498    cx.executor().run_until_parked();
13499
13500    buffer.update(cx, |buffer, _| {
13501        assert_eq!(
13502            buffer.text(),
13503            "fn main() { let a = {5}; }",
13504            "No extra braces from on type formatting should appear in the buffer"
13505        )
13506    });
13507}
13508
13509#[gpui::test]
13510async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13511    init_test(cx, |_| {});
13512
13513    let fs = FakeFs::new(cx.executor());
13514    fs.insert_tree(
13515        path!("/a"),
13516        json!({
13517            "main.rs": "fn main() { let a = 5; }",
13518            "other.rs": "// Test file",
13519        }),
13520    )
13521    .await;
13522
13523    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13524
13525    let server_restarts = Arc::new(AtomicUsize::new(0));
13526    let closure_restarts = Arc::clone(&server_restarts);
13527    let language_server_name = "test language server";
13528    let language_name: LanguageName = "Rust".into();
13529
13530    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13531    language_registry.add(Arc::new(Language::new(
13532        LanguageConfig {
13533            name: language_name.clone(),
13534            matcher: LanguageMatcher {
13535                path_suffixes: vec!["rs".to_string()],
13536                ..Default::default()
13537            },
13538            ..Default::default()
13539        },
13540        Some(tree_sitter_rust::LANGUAGE.into()),
13541    )));
13542    let mut fake_servers = language_registry.register_fake_lsp(
13543        "Rust",
13544        FakeLspAdapter {
13545            name: language_server_name,
13546            initialization_options: Some(json!({
13547                "testOptionValue": true
13548            })),
13549            initializer: Some(Box::new(move |fake_server| {
13550                let task_restarts = Arc::clone(&closure_restarts);
13551                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13552                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13553                    futures::future::ready(Ok(()))
13554                });
13555            })),
13556            ..Default::default()
13557        },
13558    );
13559
13560    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13561    let _buffer = project
13562        .update(cx, |project, cx| {
13563            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13564        })
13565        .await
13566        .unwrap();
13567    let _fake_server = fake_servers.next().await.unwrap();
13568    update_test_language_settings(cx, |language_settings| {
13569        language_settings.languages.insert(
13570            language_name.clone(),
13571            LanguageSettingsContent {
13572                tab_size: NonZeroU32::new(8),
13573                ..Default::default()
13574            },
13575        );
13576    });
13577    cx.executor().run_until_parked();
13578    assert_eq!(
13579        server_restarts.load(atomic::Ordering::Acquire),
13580        0,
13581        "Should not restart LSP server on an unrelated change"
13582    );
13583
13584    update_test_project_settings(cx, |project_settings| {
13585        project_settings.lsp.insert(
13586            "Some other server name".into(),
13587            LspSettings {
13588                binary: None,
13589                settings: None,
13590                initialization_options: Some(json!({
13591                    "some other init value": false
13592                })),
13593                enable_lsp_tasks: false,
13594            },
13595        );
13596    });
13597    cx.executor().run_until_parked();
13598    assert_eq!(
13599        server_restarts.load(atomic::Ordering::Acquire),
13600        0,
13601        "Should not restart LSP server on an unrelated LSP settings change"
13602    );
13603
13604    update_test_project_settings(cx, |project_settings| {
13605        project_settings.lsp.insert(
13606            language_server_name.into(),
13607            LspSettings {
13608                binary: None,
13609                settings: None,
13610                initialization_options: Some(json!({
13611                    "anotherInitValue": false
13612                })),
13613                enable_lsp_tasks: false,
13614            },
13615        );
13616    });
13617    cx.executor().run_until_parked();
13618    assert_eq!(
13619        server_restarts.load(atomic::Ordering::Acquire),
13620        1,
13621        "Should restart LSP server on a related LSP settings change"
13622    );
13623
13624    update_test_project_settings(cx, |project_settings| {
13625        project_settings.lsp.insert(
13626            language_server_name.into(),
13627            LspSettings {
13628                binary: None,
13629                settings: None,
13630                initialization_options: Some(json!({
13631                    "anotherInitValue": false
13632                })),
13633                enable_lsp_tasks: false,
13634            },
13635        );
13636    });
13637    cx.executor().run_until_parked();
13638    assert_eq!(
13639        server_restarts.load(atomic::Ordering::Acquire),
13640        1,
13641        "Should not restart LSP server on a related LSP settings change that is the same"
13642    );
13643
13644    update_test_project_settings(cx, |project_settings| {
13645        project_settings.lsp.insert(
13646            language_server_name.into(),
13647            LspSettings {
13648                binary: None,
13649                settings: None,
13650                initialization_options: None,
13651                enable_lsp_tasks: false,
13652            },
13653        );
13654    });
13655    cx.executor().run_until_parked();
13656    assert_eq!(
13657        server_restarts.load(atomic::Ordering::Acquire),
13658        2,
13659        "Should restart LSP server on another related LSP settings change"
13660    );
13661}
13662
13663#[gpui::test]
13664async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13665    init_test(cx, |_| {});
13666
13667    let mut cx = EditorLspTestContext::new_rust(
13668        lsp::ServerCapabilities {
13669            completion_provider: Some(lsp::CompletionOptions {
13670                trigger_characters: Some(vec![".".to_string()]),
13671                resolve_provider: Some(true),
13672                ..Default::default()
13673            }),
13674            ..Default::default()
13675        },
13676        cx,
13677    )
13678    .await;
13679
13680    cx.set_state("fn main() { let a = 2ˇ; }");
13681    cx.simulate_keystroke(".");
13682    let completion_item = lsp::CompletionItem {
13683        label: "some".into(),
13684        kind: Some(lsp::CompletionItemKind::SNIPPET),
13685        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13686        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13687            kind: lsp::MarkupKind::Markdown,
13688            value: "```rust\nSome(2)\n```".to_string(),
13689        })),
13690        deprecated: Some(false),
13691        sort_text: Some("fffffff2".to_string()),
13692        filter_text: Some("some".to_string()),
13693        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13694        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13695            range: lsp::Range {
13696                start: lsp::Position {
13697                    line: 0,
13698                    character: 22,
13699                },
13700                end: lsp::Position {
13701                    line: 0,
13702                    character: 22,
13703                },
13704            },
13705            new_text: "Some(2)".to_string(),
13706        })),
13707        additional_text_edits: Some(vec![lsp::TextEdit {
13708            range: lsp::Range {
13709                start: lsp::Position {
13710                    line: 0,
13711                    character: 20,
13712                },
13713                end: lsp::Position {
13714                    line: 0,
13715                    character: 22,
13716                },
13717            },
13718            new_text: "".to_string(),
13719        }]),
13720        ..Default::default()
13721    };
13722
13723    let closure_completion_item = completion_item.clone();
13724    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13725        let task_completion_item = closure_completion_item.clone();
13726        async move {
13727            Ok(Some(lsp::CompletionResponse::Array(vec![
13728                task_completion_item,
13729            ])))
13730        }
13731    });
13732
13733    request.next().await;
13734
13735    cx.condition(|editor, _| editor.context_menu_visible())
13736        .await;
13737    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13738        editor
13739            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13740            .unwrap()
13741    });
13742    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13743
13744    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13745        let task_completion_item = completion_item.clone();
13746        async move { Ok(task_completion_item) }
13747    })
13748    .next()
13749    .await
13750    .unwrap();
13751    apply_additional_edits.await.unwrap();
13752    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13753}
13754
13755#[gpui::test]
13756async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13757    init_test(cx, |_| {});
13758
13759    let mut cx = EditorLspTestContext::new_rust(
13760        lsp::ServerCapabilities {
13761            completion_provider: Some(lsp::CompletionOptions {
13762                trigger_characters: Some(vec![".".to_string()]),
13763                resolve_provider: Some(true),
13764                ..Default::default()
13765            }),
13766            ..Default::default()
13767        },
13768        cx,
13769    )
13770    .await;
13771
13772    cx.set_state("fn main() { let a = 2ˇ; }");
13773    cx.simulate_keystroke(".");
13774
13775    let item1 = lsp::CompletionItem {
13776        label: "method id()".to_string(),
13777        filter_text: Some("id".to_string()),
13778        detail: None,
13779        documentation: None,
13780        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13781            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13782            new_text: ".id".to_string(),
13783        })),
13784        ..lsp::CompletionItem::default()
13785    };
13786
13787    let item2 = lsp::CompletionItem {
13788        label: "other".to_string(),
13789        filter_text: Some("other".to_string()),
13790        detail: None,
13791        documentation: None,
13792        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13793            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13794            new_text: ".other".to_string(),
13795        })),
13796        ..lsp::CompletionItem::default()
13797    };
13798
13799    let item1 = item1.clone();
13800    cx.set_request_handler::<lsp::request::Completion, _, _>({
13801        let item1 = item1.clone();
13802        move |_, _, _| {
13803            let item1 = item1.clone();
13804            let item2 = item2.clone();
13805            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13806        }
13807    })
13808    .next()
13809    .await;
13810
13811    cx.condition(|editor, _| editor.context_menu_visible())
13812        .await;
13813    cx.update_editor(|editor, _, _| {
13814        let context_menu = editor.context_menu.borrow_mut();
13815        let context_menu = context_menu
13816            .as_ref()
13817            .expect("Should have the context menu deployed");
13818        match context_menu {
13819            CodeContextMenu::Completions(completions_menu) => {
13820                let completions = completions_menu.completions.borrow_mut();
13821                assert_eq!(
13822                    completions
13823                        .iter()
13824                        .map(|completion| &completion.label.text)
13825                        .collect::<Vec<_>>(),
13826                    vec!["method id()", "other"]
13827                )
13828            }
13829            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13830        }
13831    });
13832
13833    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13834        let item1 = item1.clone();
13835        move |_, item_to_resolve, _| {
13836            let item1 = item1.clone();
13837            async move {
13838                if item1 == item_to_resolve {
13839                    Ok(lsp::CompletionItem {
13840                        label: "method id()".to_string(),
13841                        filter_text: Some("id".to_string()),
13842                        detail: Some("Now resolved!".to_string()),
13843                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13844                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13845                            range: lsp::Range::new(
13846                                lsp::Position::new(0, 22),
13847                                lsp::Position::new(0, 22),
13848                            ),
13849                            new_text: ".id".to_string(),
13850                        })),
13851                        ..lsp::CompletionItem::default()
13852                    })
13853                } else {
13854                    Ok(item_to_resolve)
13855                }
13856            }
13857        }
13858    })
13859    .next()
13860    .await
13861    .unwrap();
13862    cx.run_until_parked();
13863
13864    cx.update_editor(|editor, window, cx| {
13865        editor.context_menu_next(&Default::default(), window, cx);
13866    });
13867
13868    cx.update_editor(|editor, _, _| {
13869        let context_menu = editor.context_menu.borrow_mut();
13870        let context_menu = context_menu
13871            .as_ref()
13872            .expect("Should have the context menu deployed");
13873        match context_menu {
13874            CodeContextMenu::Completions(completions_menu) => {
13875                let completions = completions_menu.completions.borrow_mut();
13876                assert_eq!(
13877                    completions
13878                        .iter()
13879                        .map(|completion| &completion.label.text)
13880                        .collect::<Vec<_>>(),
13881                    vec!["method id() Now resolved!", "other"],
13882                    "Should update first completion label, but not second as the filter text did not match."
13883                );
13884            }
13885            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13886        }
13887    });
13888}
13889
13890#[gpui::test]
13891async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13892    init_test(cx, |_| {});
13893
13894    let mut cx = EditorLspTestContext::new_rust(
13895        lsp::ServerCapabilities {
13896            completion_provider: Some(lsp::CompletionOptions {
13897                trigger_characters: Some(vec![".".to_string()]),
13898                resolve_provider: Some(true),
13899                ..Default::default()
13900            }),
13901            ..Default::default()
13902        },
13903        cx,
13904    )
13905    .await;
13906
13907    cx.set_state("fn main() { let a = 2ˇ; }");
13908    cx.simulate_keystroke(".");
13909
13910    let unresolved_item_1 = lsp::CompletionItem {
13911        label: "id".to_string(),
13912        filter_text: Some("id".to_string()),
13913        detail: None,
13914        documentation: None,
13915        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13916            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13917            new_text: ".id".to_string(),
13918        })),
13919        ..lsp::CompletionItem::default()
13920    };
13921    let resolved_item_1 = lsp::CompletionItem {
13922        additional_text_edits: Some(vec![lsp::TextEdit {
13923            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13924            new_text: "!!".to_string(),
13925        }]),
13926        ..unresolved_item_1.clone()
13927    };
13928    let unresolved_item_2 = lsp::CompletionItem {
13929        label: "other".to_string(),
13930        filter_text: Some("other".to_string()),
13931        detail: None,
13932        documentation: None,
13933        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13934            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13935            new_text: ".other".to_string(),
13936        })),
13937        ..lsp::CompletionItem::default()
13938    };
13939    let resolved_item_2 = lsp::CompletionItem {
13940        additional_text_edits: Some(vec![lsp::TextEdit {
13941            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13942            new_text: "??".to_string(),
13943        }]),
13944        ..unresolved_item_2.clone()
13945    };
13946
13947    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13948    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13949    cx.lsp
13950        .server
13951        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13952            let unresolved_item_1 = unresolved_item_1.clone();
13953            let resolved_item_1 = resolved_item_1.clone();
13954            let unresolved_item_2 = unresolved_item_2.clone();
13955            let resolved_item_2 = resolved_item_2.clone();
13956            let resolve_requests_1 = resolve_requests_1.clone();
13957            let resolve_requests_2 = resolve_requests_2.clone();
13958            move |unresolved_request, _| {
13959                let unresolved_item_1 = unresolved_item_1.clone();
13960                let resolved_item_1 = resolved_item_1.clone();
13961                let unresolved_item_2 = unresolved_item_2.clone();
13962                let resolved_item_2 = resolved_item_2.clone();
13963                let resolve_requests_1 = resolve_requests_1.clone();
13964                let resolve_requests_2 = resolve_requests_2.clone();
13965                async move {
13966                    if unresolved_request == unresolved_item_1 {
13967                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13968                        Ok(resolved_item_1.clone())
13969                    } else if unresolved_request == unresolved_item_2 {
13970                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13971                        Ok(resolved_item_2.clone())
13972                    } else {
13973                        panic!("Unexpected completion item {unresolved_request:?}")
13974                    }
13975                }
13976            }
13977        })
13978        .detach();
13979
13980    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13981        let unresolved_item_1 = unresolved_item_1.clone();
13982        let unresolved_item_2 = unresolved_item_2.clone();
13983        async move {
13984            Ok(Some(lsp::CompletionResponse::Array(vec![
13985                unresolved_item_1,
13986                unresolved_item_2,
13987            ])))
13988        }
13989    })
13990    .next()
13991    .await;
13992
13993    cx.condition(|editor, _| editor.context_menu_visible())
13994        .await;
13995    cx.update_editor(|editor, _, _| {
13996        let context_menu = editor.context_menu.borrow_mut();
13997        let context_menu = context_menu
13998            .as_ref()
13999            .expect("Should have the context menu deployed");
14000        match context_menu {
14001            CodeContextMenu::Completions(completions_menu) => {
14002                let completions = completions_menu.completions.borrow_mut();
14003                assert_eq!(
14004                    completions
14005                        .iter()
14006                        .map(|completion| &completion.label.text)
14007                        .collect::<Vec<_>>(),
14008                    vec!["id", "other"]
14009                )
14010            }
14011            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
14012        }
14013    });
14014    cx.run_until_parked();
14015
14016    cx.update_editor(|editor, window, cx| {
14017        editor.context_menu_next(&ContextMenuNext, window, cx);
14018    });
14019    cx.run_until_parked();
14020    cx.update_editor(|editor, window, cx| {
14021        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14022    });
14023    cx.run_until_parked();
14024    cx.update_editor(|editor, window, cx| {
14025        editor.context_menu_next(&ContextMenuNext, window, cx);
14026    });
14027    cx.run_until_parked();
14028    cx.update_editor(|editor, window, cx| {
14029        editor
14030            .compose_completion(&ComposeCompletion::default(), window, cx)
14031            .expect("No task returned")
14032    })
14033    .await
14034    .expect("Completion failed");
14035    cx.run_until_parked();
14036
14037    cx.update_editor(|editor, _, cx| {
14038        assert_eq!(
14039            resolve_requests_1.load(atomic::Ordering::Acquire),
14040            1,
14041            "Should always resolve once despite multiple selections"
14042        );
14043        assert_eq!(
14044            resolve_requests_2.load(atomic::Ordering::Acquire),
14045            1,
14046            "Should always resolve once after multiple selections and applying the completion"
14047        );
14048        assert_eq!(
14049            editor.text(cx),
14050            "fn main() { let a = ??.other; }",
14051            "Should use resolved data when applying the completion"
14052        );
14053    });
14054}
14055
14056#[gpui::test]
14057async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
14058    init_test(cx, |_| {});
14059
14060    let item_0 = lsp::CompletionItem {
14061        label: "abs".into(),
14062        insert_text: Some("abs".into()),
14063        data: Some(json!({ "very": "special"})),
14064        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
14065        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
14066            lsp::InsertReplaceEdit {
14067                new_text: "abs".to_string(),
14068                insert: lsp::Range::default(),
14069                replace: lsp::Range::default(),
14070            },
14071        )),
14072        ..lsp::CompletionItem::default()
14073    };
14074    let items = iter::once(item_0.clone())
14075        .chain((11..51).map(|i| lsp::CompletionItem {
14076            label: format!("item_{}", i),
14077            insert_text: Some(format!("item_{}", i)),
14078            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
14079            ..lsp::CompletionItem::default()
14080        }))
14081        .collect::<Vec<_>>();
14082
14083    let default_commit_characters = vec!["?".to_string()];
14084    let default_data = json!({ "default": "data"});
14085    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
14086    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
14087    let default_edit_range = lsp::Range {
14088        start: lsp::Position {
14089            line: 0,
14090            character: 5,
14091        },
14092        end: lsp::Position {
14093            line: 0,
14094            character: 5,
14095        },
14096    };
14097
14098    let mut cx = EditorLspTestContext::new_rust(
14099        lsp::ServerCapabilities {
14100            completion_provider: Some(lsp::CompletionOptions {
14101                trigger_characters: Some(vec![".".to_string()]),
14102                resolve_provider: Some(true),
14103                ..Default::default()
14104            }),
14105            ..Default::default()
14106        },
14107        cx,
14108    )
14109    .await;
14110
14111    cx.set_state("fn main() { let a = 2ˇ; }");
14112    cx.simulate_keystroke(".");
14113
14114    let completion_data = default_data.clone();
14115    let completion_characters = default_commit_characters.clone();
14116    let completion_items = items.clone();
14117    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
14118        let default_data = completion_data.clone();
14119        let default_commit_characters = completion_characters.clone();
14120        let items = completion_items.clone();
14121        async move {
14122            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
14123                items,
14124                item_defaults: Some(lsp::CompletionListItemDefaults {
14125                    data: Some(default_data.clone()),
14126                    commit_characters: Some(default_commit_characters.clone()),
14127                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
14128                        default_edit_range,
14129                    )),
14130                    insert_text_format: Some(default_insert_text_format),
14131                    insert_text_mode: Some(default_insert_text_mode),
14132                }),
14133                ..lsp::CompletionList::default()
14134            })))
14135        }
14136    })
14137    .next()
14138    .await;
14139
14140    let resolved_items = Arc::new(Mutex::new(Vec::new()));
14141    cx.lsp
14142        .server
14143        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
14144            let closure_resolved_items = resolved_items.clone();
14145            move |item_to_resolve, _| {
14146                let closure_resolved_items = closure_resolved_items.clone();
14147                async move {
14148                    closure_resolved_items.lock().push(item_to_resolve.clone());
14149                    Ok(item_to_resolve)
14150                }
14151            }
14152        })
14153        .detach();
14154
14155    cx.condition(|editor, _| editor.context_menu_visible())
14156        .await;
14157    cx.run_until_parked();
14158    cx.update_editor(|editor, _, _| {
14159        let menu = editor.context_menu.borrow_mut();
14160        match menu.as_ref().expect("should have the completions menu") {
14161            CodeContextMenu::Completions(completions_menu) => {
14162                assert_eq!(
14163                    completions_menu
14164                        .entries
14165                        .borrow()
14166                        .iter()
14167                        .map(|mat| mat.string.clone())
14168                        .collect::<Vec<String>>(),
14169                    items
14170                        .iter()
14171                        .map(|completion| completion.label.clone())
14172                        .collect::<Vec<String>>()
14173                );
14174            }
14175            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
14176        }
14177    });
14178    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
14179    // with 4 from the end.
14180    assert_eq!(
14181        *resolved_items.lock(),
14182        [&items[0..16], &items[items.len() - 4..items.len()]]
14183            .concat()
14184            .iter()
14185            .cloned()
14186            .map(|mut item| {
14187                if item.data.is_none() {
14188                    item.data = Some(default_data.clone());
14189                }
14190                item
14191            })
14192            .collect::<Vec<lsp::CompletionItem>>(),
14193        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
14194    );
14195    resolved_items.lock().clear();
14196
14197    cx.update_editor(|editor, window, cx| {
14198        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
14199    });
14200    cx.run_until_parked();
14201    // Completions that have already been resolved are skipped.
14202    assert_eq!(
14203        *resolved_items.lock(),
14204        items[items.len() - 16..items.len() - 4]
14205            .iter()
14206            .cloned()
14207            .map(|mut item| {
14208                if item.data.is_none() {
14209                    item.data = Some(default_data.clone());
14210                }
14211                item
14212            })
14213            .collect::<Vec<lsp::CompletionItem>>()
14214    );
14215    resolved_items.lock().clear();
14216}
14217
14218#[gpui::test]
14219async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14220    init_test(cx, |_| {});
14221
14222    let mut cx = EditorLspTestContext::new(
14223        Language::new(
14224            LanguageConfig {
14225                matcher: LanguageMatcher {
14226                    path_suffixes: vec!["jsx".into()],
14227                    ..Default::default()
14228                },
14229                overrides: [(
14230                    "element".into(),
14231                    LanguageConfigOverride {
14232                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14233                        ..Default::default()
14234                    },
14235                )]
14236                .into_iter()
14237                .collect(),
14238                ..Default::default()
14239            },
14240            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14241        )
14242        .with_override_query("(jsx_self_closing_element) @element")
14243        .unwrap(),
14244        lsp::ServerCapabilities {
14245            completion_provider: Some(lsp::CompletionOptions {
14246                trigger_characters: Some(vec![":".to_string()]),
14247                ..Default::default()
14248            }),
14249            ..Default::default()
14250        },
14251        cx,
14252    )
14253    .await;
14254
14255    cx.lsp
14256        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14257            Ok(Some(lsp::CompletionResponse::Array(vec![
14258                lsp::CompletionItem {
14259                    label: "bg-blue".into(),
14260                    ..Default::default()
14261                },
14262                lsp::CompletionItem {
14263                    label: "bg-red".into(),
14264                    ..Default::default()
14265                },
14266                lsp::CompletionItem {
14267                    label: "bg-yellow".into(),
14268                    ..Default::default()
14269                },
14270            ])))
14271        });
14272
14273    cx.set_state(r#"<p class="bgˇ" />"#);
14274
14275    // Trigger completion when typing a dash, because the dash is an extra
14276    // word character in the 'element' scope, which contains the cursor.
14277    cx.simulate_keystroke("-");
14278    cx.executor().run_until_parked();
14279    cx.update_editor(|editor, _, _| {
14280        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14281        {
14282            assert_eq!(
14283                completion_menu_entries(&menu),
14284                &["bg-red", "bg-blue", "bg-yellow"]
14285            );
14286        } else {
14287            panic!("expected completion menu to be open");
14288        }
14289    });
14290
14291    cx.simulate_keystroke("l");
14292    cx.executor().run_until_parked();
14293    cx.update_editor(|editor, _, _| {
14294        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14295        {
14296            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14297        } else {
14298            panic!("expected completion menu to be open");
14299        }
14300    });
14301
14302    // When filtering completions, consider the character after the '-' to
14303    // be the start of a subword.
14304    cx.set_state(r#"<p class="yelˇ" />"#);
14305    cx.simulate_keystroke("l");
14306    cx.executor().run_until_parked();
14307    cx.update_editor(|editor, _, _| {
14308        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14309        {
14310            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14311        } else {
14312            panic!("expected completion menu to be open");
14313        }
14314    });
14315}
14316
14317fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14318    let entries = menu.entries.borrow();
14319    entries.iter().map(|mat| mat.string.clone()).collect()
14320}
14321
14322#[gpui::test]
14323async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14324    init_test(cx, |settings| {
14325        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14326            FormatterList(vec![Formatter::Prettier].into()),
14327        ))
14328    });
14329
14330    let fs = FakeFs::new(cx.executor());
14331    fs.insert_file(path!("/file.ts"), Default::default()).await;
14332
14333    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14334    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14335
14336    language_registry.add(Arc::new(Language::new(
14337        LanguageConfig {
14338            name: "TypeScript".into(),
14339            matcher: LanguageMatcher {
14340                path_suffixes: vec!["ts".to_string()],
14341                ..Default::default()
14342            },
14343            ..Default::default()
14344        },
14345        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14346    )));
14347    update_test_language_settings(cx, |settings| {
14348        settings.defaults.prettier = Some(PrettierSettings {
14349            allowed: true,
14350            ..PrettierSettings::default()
14351        });
14352    });
14353
14354    let test_plugin = "test_plugin";
14355    let _ = language_registry.register_fake_lsp(
14356        "TypeScript",
14357        FakeLspAdapter {
14358            prettier_plugins: vec![test_plugin],
14359            ..Default::default()
14360        },
14361    );
14362
14363    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14364    let buffer = project
14365        .update(cx, |project, cx| {
14366            project.open_local_buffer(path!("/file.ts"), cx)
14367        })
14368        .await
14369        .unwrap();
14370
14371    let buffer_text = "one\ntwo\nthree\n";
14372    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14373    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14374    editor.update_in(cx, |editor, window, cx| {
14375        editor.set_text(buffer_text, window, cx)
14376    });
14377
14378    editor
14379        .update_in(cx, |editor, window, cx| {
14380            editor.perform_format(
14381                project.clone(),
14382                FormatTrigger::Manual,
14383                FormatTarget::Buffers,
14384                window,
14385                cx,
14386            )
14387        })
14388        .unwrap()
14389        .await;
14390    assert_eq!(
14391        editor.update(cx, |editor, cx| editor.text(cx)),
14392        buffer_text.to_string() + prettier_format_suffix,
14393        "Test prettier formatting was not applied to the original buffer text",
14394    );
14395
14396    update_test_language_settings(cx, |settings| {
14397        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14398    });
14399    let format = editor.update_in(cx, |editor, window, cx| {
14400        editor.perform_format(
14401            project.clone(),
14402            FormatTrigger::Manual,
14403            FormatTarget::Buffers,
14404            window,
14405            cx,
14406        )
14407    });
14408    format.await.unwrap();
14409    assert_eq!(
14410        editor.update(cx, |editor, cx| editor.text(cx)),
14411        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14412        "Autoformatting (via test prettier) was not applied to the original buffer text",
14413    );
14414}
14415
14416#[gpui::test]
14417async fn test_addition_reverts(cx: &mut TestAppContext) {
14418    init_test(cx, |_| {});
14419    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14420    let base_text = indoc! {r#"
14421        struct Row;
14422        struct Row1;
14423        struct Row2;
14424
14425        struct Row4;
14426        struct Row5;
14427        struct Row6;
14428
14429        struct Row8;
14430        struct Row9;
14431        struct Row10;"#};
14432
14433    // When addition hunks are not adjacent to carets, no hunk revert is performed
14434    assert_hunk_revert(
14435        indoc! {r#"struct Row;
14436                   struct Row1;
14437                   struct Row1.1;
14438                   struct Row1.2;
14439                   struct Row2;ˇ
14440
14441                   struct Row4;
14442                   struct Row5;
14443                   struct Row6;
14444
14445                   struct Row8;
14446                   ˇstruct Row9;
14447                   struct Row9.1;
14448                   struct Row9.2;
14449                   struct Row9.3;
14450                   struct Row10;"#},
14451        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14452        indoc! {r#"struct Row;
14453                   struct Row1;
14454                   struct Row1.1;
14455                   struct Row1.2;
14456                   struct Row2;ˇ
14457
14458                   struct Row4;
14459                   struct Row5;
14460                   struct Row6;
14461
14462                   struct Row8;
14463                   ˇstruct Row9;
14464                   struct Row9.1;
14465                   struct Row9.2;
14466                   struct Row9.3;
14467                   struct Row10;"#},
14468        base_text,
14469        &mut cx,
14470    );
14471    // Same for selections
14472    assert_hunk_revert(
14473        indoc! {r#"struct Row;
14474                   struct Row1;
14475                   struct Row2;
14476                   struct Row2.1;
14477                   struct Row2.2;
14478                   «ˇ
14479                   struct Row4;
14480                   struct» Row5;
14481                   «struct Row6;
14482                   ˇ»
14483                   struct Row9.1;
14484                   struct Row9.2;
14485                   struct Row9.3;
14486                   struct Row8;
14487                   struct Row9;
14488                   struct Row10;"#},
14489        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14490        indoc! {r#"struct Row;
14491                   struct Row1;
14492                   struct Row2;
14493                   struct Row2.1;
14494                   struct Row2.2;
14495                   «ˇ
14496                   struct Row4;
14497                   struct» Row5;
14498                   «struct Row6;
14499                   ˇ»
14500                   struct Row9.1;
14501                   struct Row9.2;
14502                   struct Row9.3;
14503                   struct Row8;
14504                   struct Row9;
14505                   struct Row10;"#},
14506        base_text,
14507        &mut cx,
14508    );
14509
14510    // When carets and selections intersect the addition hunks, those are reverted.
14511    // Adjacent carets got merged.
14512    assert_hunk_revert(
14513        indoc! {r#"struct Row;
14514                   ˇ// something on the top
14515                   struct Row1;
14516                   struct Row2;
14517                   struct Roˇw3.1;
14518                   struct Row2.2;
14519                   struct Row2.3;ˇ
14520
14521                   struct Row4;
14522                   struct ˇRow5.1;
14523                   struct Row5.2;
14524                   struct «Rowˇ»5.3;
14525                   struct Row5;
14526                   struct Row6;
14527                   ˇ
14528                   struct Row9.1;
14529                   struct «Rowˇ»9.2;
14530                   struct «ˇRow»9.3;
14531                   struct Row8;
14532                   struct Row9;
14533                   «ˇ// something on bottom»
14534                   struct Row10;"#},
14535        vec![
14536            DiffHunkStatusKind::Added,
14537            DiffHunkStatusKind::Added,
14538            DiffHunkStatusKind::Added,
14539            DiffHunkStatusKind::Added,
14540            DiffHunkStatusKind::Added,
14541        ],
14542        indoc! {r#"struct Row;
14543                   ˇstruct Row1;
14544                   struct Row2;
14545                   ˇ
14546                   struct Row4;
14547                   ˇstruct Row5;
14548                   struct Row6;
14549                   ˇ
14550                   ˇstruct Row8;
14551                   struct Row9;
14552                   ˇstruct Row10;"#},
14553        base_text,
14554        &mut cx,
14555    );
14556}
14557
14558#[gpui::test]
14559async fn test_modification_reverts(cx: &mut TestAppContext) {
14560    init_test(cx, |_| {});
14561    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14562    let base_text = indoc! {r#"
14563        struct Row;
14564        struct Row1;
14565        struct Row2;
14566
14567        struct Row4;
14568        struct Row5;
14569        struct Row6;
14570
14571        struct Row8;
14572        struct Row9;
14573        struct Row10;"#};
14574
14575    // Modification hunks behave the same as the addition ones.
14576    assert_hunk_revert(
14577        indoc! {r#"struct Row;
14578                   struct Row1;
14579                   struct Row33;
14580                   ˇ
14581                   struct Row4;
14582                   struct Row5;
14583                   struct Row6;
14584                   ˇ
14585                   struct Row99;
14586                   struct Row9;
14587                   struct Row10;"#},
14588        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14589        indoc! {r#"struct Row;
14590                   struct Row1;
14591                   struct Row33;
14592                   ˇ
14593                   struct Row4;
14594                   struct Row5;
14595                   struct Row6;
14596                   ˇ
14597                   struct Row99;
14598                   struct Row9;
14599                   struct Row10;"#},
14600        base_text,
14601        &mut cx,
14602    );
14603    assert_hunk_revert(
14604        indoc! {r#"struct Row;
14605                   struct Row1;
14606                   struct Row33;
14607                   «ˇ
14608                   struct Row4;
14609                   struct» Row5;
14610                   «struct Row6;
14611                   ˇ»
14612                   struct Row99;
14613                   struct Row9;
14614                   struct Row10;"#},
14615        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14616        indoc! {r#"struct Row;
14617                   struct Row1;
14618                   struct Row33;
14619                   «ˇ
14620                   struct Row4;
14621                   struct» Row5;
14622                   «struct Row6;
14623                   ˇ»
14624                   struct Row99;
14625                   struct Row9;
14626                   struct Row10;"#},
14627        base_text,
14628        &mut cx,
14629    );
14630
14631    assert_hunk_revert(
14632        indoc! {r#"ˇstruct Row1.1;
14633                   struct Row1;
14634                   «ˇstr»uct Row22;
14635
14636                   struct ˇRow44;
14637                   struct Row5;
14638                   struct «Rˇ»ow66;ˇ
14639
14640                   «struˇ»ct Row88;
14641                   struct Row9;
14642                   struct Row1011;ˇ"#},
14643        vec![
14644            DiffHunkStatusKind::Modified,
14645            DiffHunkStatusKind::Modified,
14646            DiffHunkStatusKind::Modified,
14647            DiffHunkStatusKind::Modified,
14648            DiffHunkStatusKind::Modified,
14649            DiffHunkStatusKind::Modified,
14650        ],
14651        indoc! {r#"struct Row;
14652                   ˇstruct Row1;
14653                   struct Row2;
14654                   ˇ
14655                   struct Row4;
14656                   ˇstruct Row5;
14657                   struct Row6;
14658                   ˇ
14659                   struct Row8;
14660                   ˇstruct Row9;
14661                   struct Row10;ˇ"#},
14662        base_text,
14663        &mut cx,
14664    );
14665}
14666
14667#[gpui::test]
14668async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14669    init_test(cx, |_| {});
14670    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14671    let base_text = indoc! {r#"
14672        one
14673
14674        two
14675        three
14676        "#};
14677
14678    cx.set_head_text(base_text);
14679    cx.set_state("\nˇ\n");
14680    cx.executor().run_until_parked();
14681    cx.update_editor(|editor, _window, cx| {
14682        editor.expand_selected_diff_hunks(cx);
14683    });
14684    cx.executor().run_until_parked();
14685    cx.update_editor(|editor, window, cx| {
14686        editor.backspace(&Default::default(), window, cx);
14687    });
14688    cx.run_until_parked();
14689    cx.assert_state_with_diff(
14690        indoc! {r#"
14691
14692        - two
14693        - threeˇ
14694        +
14695        "#}
14696        .to_string(),
14697    );
14698}
14699
14700#[gpui::test]
14701async fn test_deletion_reverts(cx: &mut TestAppContext) {
14702    init_test(cx, |_| {});
14703    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14704    let base_text = indoc! {r#"struct Row;
14705struct Row1;
14706struct Row2;
14707
14708struct Row4;
14709struct Row5;
14710struct Row6;
14711
14712struct Row8;
14713struct Row9;
14714struct Row10;"#};
14715
14716    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14717    assert_hunk_revert(
14718        indoc! {r#"struct Row;
14719                   struct Row2;
14720
14721                   ˇstruct Row4;
14722                   struct Row5;
14723                   struct Row6;
14724                   ˇ
14725                   struct Row8;
14726                   struct Row10;"#},
14727        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14728        indoc! {r#"struct Row;
14729                   struct Row2;
14730
14731                   ˇstruct Row4;
14732                   struct Row5;
14733                   struct Row6;
14734                   ˇ
14735                   struct Row8;
14736                   struct Row10;"#},
14737        base_text,
14738        &mut cx,
14739    );
14740    assert_hunk_revert(
14741        indoc! {r#"struct Row;
14742                   struct Row2;
14743
14744                   «ˇstruct Row4;
14745                   struct» Row5;
14746                   «struct Row6;
14747                   ˇ»
14748                   struct Row8;
14749                   struct Row10;"#},
14750        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14751        indoc! {r#"struct Row;
14752                   struct Row2;
14753
14754                   «ˇstruct Row4;
14755                   struct» Row5;
14756                   «struct Row6;
14757                   ˇ»
14758                   struct Row8;
14759                   struct Row10;"#},
14760        base_text,
14761        &mut cx,
14762    );
14763
14764    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14765    assert_hunk_revert(
14766        indoc! {r#"struct Row;
14767                   ˇstruct Row2;
14768
14769                   struct Row4;
14770                   struct Row5;
14771                   struct Row6;
14772
14773                   struct Row8;ˇ
14774                   struct Row10;"#},
14775        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14776        indoc! {r#"struct Row;
14777                   struct Row1;
14778                   ˇstruct Row2;
14779
14780                   struct Row4;
14781                   struct Row5;
14782                   struct Row6;
14783
14784                   struct Row8;ˇ
14785                   struct Row9;
14786                   struct Row10;"#},
14787        base_text,
14788        &mut cx,
14789    );
14790    assert_hunk_revert(
14791        indoc! {r#"struct Row;
14792                   struct Row2«ˇ;
14793                   struct Row4;
14794                   struct» Row5;
14795                   «struct Row6;
14796
14797                   struct Row8;ˇ»
14798                   struct Row10;"#},
14799        vec![
14800            DiffHunkStatusKind::Deleted,
14801            DiffHunkStatusKind::Deleted,
14802            DiffHunkStatusKind::Deleted,
14803        ],
14804        indoc! {r#"struct Row;
14805                   struct Row1;
14806                   struct Row2«ˇ;
14807
14808                   struct Row4;
14809                   struct» Row5;
14810                   «struct Row6;
14811
14812                   struct Row8;ˇ»
14813                   struct Row9;
14814                   struct Row10;"#},
14815        base_text,
14816        &mut cx,
14817    );
14818}
14819
14820#[gpui::test]
14821async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14822    init_test(cx, |_| {});
14823
14824    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14825    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14826    let base_text_3 =
14827        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14828
14829    let text_1 = edit_first_char_of_every_line(base_text_1);
14830    let text_2 = edit_first_char_of_every_line(base_text_2);
14831    let text_3 = edit_first_char_of_every_line(base_text_3);
14832
14833    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14834    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14835    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14836
14837    let multibuffer = cx.new(|cx| {
14838        let mut multibuffer = MultiBuffer::new(ReadWrite);
14839        multibuffer.push_excerpts(
14840            buffer_1.clone(),
14841            [
14842                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14843                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14844                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14845            ],
14846            cx,
14847        );
14848        multibuffer.push_excerpts(
14849            buffer_2.clone(),
14850            [
14851                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14852                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14853                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14854            ],
14855            cx,
14856        );
14857        multibuffer.push_excerpts(
14858            buffer_3.clone(),
14859            [
14860                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14861                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14862                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14863            ],
14864            cx,
14865        );
14866        multibuffer
14867    });
14868
14869    let fs = FakeFs::new(cx.executor());
14870    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14871    let (editor, cx) = cx
14872        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14873    editor.update_in(cx, |editor, _window, cx| {
14874        for (buffer, diff_base) in [
14875            (buffer_1.clone(), base_text_1),
14876            (buffer_2.clone(), base_text_2),
14877            (buffer_3.clone(), base_text_3),
14878        ] {
14879            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14880            editor
14881                .buffer
14882                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14883        }
14884    });
14885    cx.executor().run_until_parked();
14886
14887    editor.update_in(cx, |editor, window, cx| {
14888        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}");
14889        editor.select_all(&SelectAll, window, cx);
14890        editor.git_restore(&Default::default(), window, cx);
14891    });
14892    cx.executor().run_until_parked();
14893
14894    // When all ranges are selected, all buffer hunks are reverted.
14895    editor.update(cx, |editor, cx| {
14896        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");
14897    });
14898    buffer_1.update(cx, |buffer, _| {
14899        assert_eq!(buffer.text(), base_text_1);
14900    });
14901    buffer_2.update(cx, |buffer, _| {
14902        assert_eq!(buffer.text(), base_text_2);
14903    });
14904    buffer_3.update(cx, |buffer, _| {
14905        assert_eq!(buffer.text(), base_text_3);
14906    });
14907
14908    editor.update_in(cx, |editor, window, cx| {
14909        editor.undo(&Default::default(), window, cx);
14910    });
14911
14912    editor.update_in(cx, |editor, window, cx| {
14913        editor.change_selections(None, window, cx, |s| {
14914            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14915        });
14916        editor.git_restore(&Default::default(), window, cx);
14917    });
14918
14919    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14920    // but not affect buffer_2 and its related excerpts.
14921    editor.update(cx, |editor, cx| {
14922        assert_eq!(
14923            editor.text(cx),
14924            "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}"
14925        );
14926    });
14927    buffer_1.update(cx, |buffer, _| {
14928        assert_eq!(buffer.text(), base_text_1);
14929    });
14930    buffer_2.update(cx, |buffer, _| {
14931        assert_eq!(
14932            buffer.text(),
14933            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14934        );
14935    });
14936    buffer_3.update(cx, |buffer, _| {
14937        assert_eq!(
14938            buffer.text(),
14939            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14940        );
14941    });
14942
14943    fn edit_first_char_of_every_line(text: &str) -> String {
14944        text.split('\n')
14945            .map(|line| format!("X{}", &line[1..]))
14946            .collect::<Vec<_>>()
14947            .join("\n")
14948    }
14949}
14950
14951#[gpui::test]
14952async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14953    init_test(cx, |_| {});
14954
14955    let cols = 4;
14956    let rows = 10;
14957    let sample_text_1 = sample_text(rows, cols, 'a');
14958    assert_eq!(
14959        sample_text_1,
14960        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14961    );
14962    let sample_text_2 = sample_text(rows, cols, 'l');
14963    assert_eq!(
14964        sample_text_2,
14965        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14966    );
14967    let sample_text_3 = sample_text(rows, cols, 'v');
14968    assert_eq!(
14969        sample_text_3,
14970        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14971    );
14972
14973    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14974    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14975    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14976
14977    let multi_buffer = cx.new(|cx| {
14978        let mut multibuffer = MultiBuffer::new(ReadWrite);
14979        multibuffer.push_excerpts(
14980            buffer_1.clone(),
14981            [
14982                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14983                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14984                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14985            ],
14986            cx,
14987        );
14988        multibuffer.push_excerpts(
14989            buffer_2.clone(),
14990            [
14991                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14992                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14993                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14994            ],
14995            cx,
14996        );
14997        multibuffer.push_excerpts(
14998            buffer_3.clone(),
14999            [
15000                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15001                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15002                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
15003            ],
15004            cx,
15005        );
15006        multibuffer
15007    });
15008
15009    let fs = FakeFs::new(cx.executor());
15010    fs.insert_tree(
15011        "/a",
15012        json!({
15013            "main.rs": sample_text_1,
15014            "other.rs": sample_text_2,
15015            "lib.rs": sample_text_3,
15016        }),
15017    )
15018    .await;
15019    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15020    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15021    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15022    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15023        Editor::new(
15024            EditorMode::full(),
15025            multi_buffer,
15026            Some(project.clone()),
15027            window,
15028            cx,
15029        )
15030    });
15031    let multibuffer_item_id = workspace
15032        .update(cx, |workspace, window, cx| {
15033            assert!(
15034                workspace.active_item(cx).is_none(),
15035                "active item should be None before the first item is added"
15036            );
15037            workspace.add_item_to_active_pane(
15038                Box::new(multi_buffer_editor.clone()),
15039                None,
15040                true,
15041                window,
15042                cx,
15043            );
15044            let active_item = workspace
15045                .active_item(cx)
15046                .expect("should have an active item after adding the multi buffer");
15047            assert!(
15048                !active_item.is_singleton(cx),
15049                "A multi buffer was expected to active after adding"
15050            );
15051            active_item.item_id()
15052        })
15053        .unwrap();
15054    cx.executor().run_until_parked();
15055
15056    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15057        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15058            s.select_ranges(Some(1..2))
15059        });
15060        editor.open_excerpts(&OpenExcerpts, window, cx);
15061    });
15062    cx.executor().run_until_parked();
15063    let first_item_id = workspace
15064        .update(cx, |workspace, window, cx| {
15065            let active_item = workspace
15066                .active_item(cx)
15067                .expect("should have an active item after navigating into the 1st buffer");
15068            let first_item_id = active_item.item_id();
15069            assert_ne!(
15070                first_item_id, multibuffer_item_id,
15071                "Should navigate into the 1st buffer and activate it"
15072            );
15073            assert!(
15074                active_item.is_singleton(cx),
15075                "New active item should be a singleton buffer"
15076            );
15077            assert_eq!(
15078                active_item
15079                    .act_as::<Editor>(cx)
15080                    .expect("should have navigated into an editor for the 1st buffer")
15081                    .read(cx)
15082                    .text(cx),
15083                sample_text_1
15084            );
15085
15086            workspace
15087                .go_back(workspace.active_pane().downgrade(), window, cx)
15088                .detach_and_log_err(cx);
15089
15090            first_item_id
15091        })
15092        .unwrap();
15093    cx.executor().run_until_parked();
15094    workspace
15095        .update(cx, |workspace, _, cx| {
15096            let active_item = workspace
15097                .active_item(cx)
15098                .expect("should have an active item after navigating back");
15099            assert_eq!(
15100                active_item.item_id(),
15101                multibuffer_item_id,
15102                "Should navigate back to the multi buffer"
15103            );
15104            assert!(!active_item.is_singleton(cx));
15105        })
15106        .unwrap();
15107
15108    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15109        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15110            s.select_ranges(Some(39..40))
15111        });
15112        editor.open_excerpts(&OpenExcerpts, window, cx);
15113    });
15114    cx.executor().run_until_parked();
15115    let second_item_id = workspace
15116        .update(cx, |workspace, window, cx| {
15117            let active_item = workspace
15118                .active_item(cx)
15119                .expect("should have an active item after navigating into the 2nd buffer");
15120            let second_item_id = active_item.item_id();
15121            assert_ne!(
15122                second_item_id, multibuffer_item_id,
15123                "Should navigate away from the multibuffer"
15124            );
15125            assert_ne!(
15126                second_item_id, first_item_id,
15127                "Should navigate into the 2nd buffer and activate it"
15128            );
15129            assert!(
15130                active_item.is_singleton(cx),
15131                "New active item should be a singleton buffer"
15132            );
15133            assert_eq!(
15134                active_item
15135                    .act_as::<Editor>(cx)
15136                    .expect("should have navigated into an editor")
15137                    .read(cx)
15138                    .text(cx),
15139                sample_text_2
15140            );
15141
15142            workspace
15143                .go_back(workspace.active_pane().downgrade(), window, cx)
15144                .detach_and_log_err(cx);
15145
15146            second_item_id
15147        })
15148        .unwrap();
15149    cx.executor().run_until_parked();
15150    workspace
15151        .update(cx, |workspace, _, cx| {
15152            let active_item = workspace
15153                .active_item(cx)
15154                .expect("should have an active item after navigating back from the 2nd buffer");
15155            assert_eq!(
15156                active_item.item_id(),
15157                multibuffer_item_id,
15158                "Should navigate back from the 2nd buffer to the multi buffer"
15159            );
15160            assert!(!active_item.is_singleton(cx));
15161        })
15162        .unwrap();
15163
15164    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15165        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
15166            s.select_ranges(Some(70..70))
15167        });
15168        editor.open_excerpts(&OpenExcerpts, window, cx);
15169    });
15170    cx.executor().run_until_parked();
15171    workspace
15172        .update(cx, |workspace, window, cx| {
15173            let active_item = workspace
15174                .active_item(cx)
15175                .expect("should have an active item after navigating into the 3rd buffer");
15176            let third_item_id = active_item.item_id();
15177            assert_ne!(
15178                third_item_id, multibuffer_item_id,
15179                "Should navigate into the 3rd buffer and activate it"
15180            );
15181            assert_ne!(third_item_id, first_item_id);
15182            assert_ne!(third_item_id, second_item_id);
15183            assert!(
15184                active_item.is_singleton(cx),
15185                "New active item should be a singleton buffer"
15186            );
15187            assert_eq!(
15188                active_item
15189                    .act_as::<Editor>(cx)
15190                    .expect("should have navigated into an editor")
15191                    .read(cx)
15192                    .text(cx),
15193                sample_text_3
15194            );
15195
15196            workspace
15197                .go_back(workspace.active_pane().downgrade(), window, cx)
15198                .detach_and_log_err(cx);
15199        })
15200        .unwrap();
15201    cx.executor().run_until_parked();
15202    workspace
15203        .update(cx, |workspace, _, cx| {
15204            let active_item = workspace
15205                .active_item(cx)
15206                .expect("should have an active item after navigating back from the 3rd buffer");
15207            assert_eq!(
15208                active_item.item_id(),
15209                multibuffer_item_id,
15210                "Should navigate back from the 3rd buffer to the multi buffer"
15211            );
15212            assert!(!active_item.is_singleton(cx));
15213        })
15214        .unwrap();
15215}
15216
15217#[gpui::test]
15218async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15219    init_test(cx, |_| {});
15220
15221    let mut cx = EditorTestContext::new(cx).await;
15222
15223    let diff_base = r#"
15224        use some::mod;
15225
15226        const A: u32 = 42;
15227
15228        fn main() {
15229            println!("hello");
15230
15231            println!("world");
15232        }
15233        "#
15234    .unindent();
15235
15236    cx.set_state(
15237        &r#"
15238        use some::modified;
15239
15240        ˇ
15241        fn main() {
15242            println!("hello there");
15243
15244            println!("around the");
15245            println!("world");
15246        }
15247        "#
15248        .unindent(),
15249    );
15250
15251    cx.set_head_text(&diff_base);
15252    executor.run_until_parked();
15253
15254    cx.update_editor(|editor, window, cx| {
15255        editor.go_to_next_hunk(&GoToHunk, window, cx);
15256        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15257    });
15258    executor.run_until_parked();
15259    cx.assert_state_with_diff(
15260        r#"
15261          use some::modified;
15262
15263
15264          fn main() {
15265        -     println!("hello");
15266        + ˇ    println!("hello there");
15267
15268              println!("around the");
15269              println!("world");
15270          }
15271        "#
15272        .unindent(),
15273    );
15274
15275    cx.update_editor(|editor, window, cx| {
15276        for _ in 0..2 {
15277            editor.go_to_next_hunk(&GoToHunk, window, cx);
15278            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15279        }
15280    });
15281    executor.run_until_parked();
15282    cx.assert_state_with_diff(
15283        r#"
15284        - use some::mod;
15285        + ˇuse some::modified;
15286
15287
15288          fn main() {
15289        -     println!("hello");
15290        +     println!("hello there");
15291
15292        +     println!("around the");
15293              println!("world");
15294          }
15295        "#
15296        .unindent(),
15297    );
15298
15299    cx.update_editor(|editor, window, cx| {
15300        editor.go_to_next_hunk(&GoToHunk, window, cx);
15301        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15302    });
15303    executor.run_until_parked();
15304    cx.assert_state_with_diff(
15305        r#"
15306        - use some::mod;
15307        + use some::modified;
15308
15309        - const A: u32 = 42;
15310          ˇ
15311          fn main() {
15312        -     println!("hello");
15313        +     println!("hello there");
15314
15315        +     println!("around the");
15316              println!("world");
15317          }
15318        "#
15319        .unindent(),
15320    );
15321
15322    cx.update_editor(|editor, window, cx| {
15323        editor.cancel(&Cancel, window, cx);
15324    });
15325
15326    cx.assert_state_with_diff(
15327        r#"
15328          use some::modified;
15329
15330          ˇ
15331          fn main() {
15332              println!("hello there");
15333
15334              println!("around the");
15335              println!("world");
15336          }
15337        "#
15338        .unindent(),
15339    );
15340}
15341
15342#[gpui::test]
15343async fn test_diff_base_change_with_expanded_diff_hunks(
15344    executor: BackgroundExecutor,
15345    cx: &mut TestAppContext,
15346) {
15347    init_test(cx, |_| {});
15348
15349    let mut cx = EditorTestContext::new(cx).await;
15350
15351    let diff_base = r#"
15352        use some::mod1;
15353        use some::mod2;
15354
15355        const A: u32 = 42;
15356        const B: u32 = 42;
15357        const C: u32 = 42;
15358
15359        fn main() {
15360            println!("hello");
15361
15362            println!("world");
15363        }
15364        "#
15365    .unindent();
15366
15367    cx.set_state(
15368        &r#"
15369        use some::mod2;
15370
15371        const A: u32 = 42;
15372        const C: u32 = 42;
15373
15374        fn main(ˇ) {
15375            //println!("hello");
15376
15377            println!("world");
15378            //
15379            //
15380        }
15381        "#
15382        .unindent(),
15383    );
15384
15385    cx.set_head_text(&diff_base);
15386    executor.run_until_parked();
15387
15388    cx.update_editor(|editor, window, cx| {
15389        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15390    });
15391    executor.run_until_parked();
15392    cx.assert_state_with_diff(
15393        r#"
15394        - use some::mod1;
15395          use some::mod2;
15396
15397          const A: u32 = 42;
15398        - const B: u32 = 42;
15399          const C: u32 = 42;
15400
15401          fn main(ˇ) {
15402        -     println!("hello");
15403        +     //println!("hello");
15404
15405              println!("world");
15406        +     //
15407        +     //
15408          }
15409        "#
15410        .unindent(),
15411    );
15412
15413    cx.set_head_text("new diff base!");
15414    executor.run_until_parked();
15415    cx.assert_state_with_diff(
15416        r#"
15417        - new diff base!
15418        + use some::mod2;
15419        +
15420        + const A: u32 = 42;
15421        + const C: u32 = 42;
15422        +
15423        + fn main(ˇ) {
15424        +     //println!("hello");
15425        +
15426        +     println!("world");
15427        +     //
15428        +     //
15429        + }
15430        "#
15431        .unindent(),
15432    );
15433}
15434
15435#[gpui::test]
15436async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15437    init_test(cx, |_| {});
15438
15439    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15440    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15441    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15442    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15443    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15444    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15445
15446    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15447    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15448    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15449
15450    let multi_buffer = cx.new(|cx| {
15451        let mut multibuffer = MultiBuffer::new(ReadWrite);
15452        multibuffer.push_excerpts(
15453            buffer_1.clone(),
15454            [
15455                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15456                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15457                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15458            ],
15459            cx,
15460        );
15461        multibuffer.push_excerpts(
15462            buffer_2.clone(),
15463            [
15464                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15465                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15466                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15467            ],
15468            cx,
15469        );
15470        multibuffer.push_excerpts(
15471            buffer_3.clone(),
15472            [
15473                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15474                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15475                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15476            ],
15477            cx,
15478        );
15479        multibuffer
15480    });
15481
15482    let editor =
15483        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15484    editor
15485        .update(cx, |editor, _window, cx| {
15486            for (buffer, diff_base) in [
15487                (buffer_1.clone(), file_1_old),
15488                (buffer_2.clone(), file_2_old),
15489                (buffer_3.clone(), file_3_old),
15490            ] {
15491                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15492                editor
15493                    .buffer
15494                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15495            }
15496        })
15497        .unwrap();
15498
15499    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15500    cx.run_until_parked();
15501
15502    cx.assert_editor_state(
15503        &"
15504            ˇaaa
15505            ccc
15506            ddd
15507
15508            ggg
15509            hhh
15510
15511
15512            lll
15513            mmm
15514            NNN
15515
15516            qqq
15517            rrr
15518
15519            uuu
15520            111
15521            222
15522            333
15523
15524            666
15525            777
15526
15527            000
15528            !!!"
15529        .unindent(),
15530    );
15531
15532    cx.update_editor(|editor, window, cx| {
15533        editor.select_all(&SelectAll, window, cx);
15534        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15535    });
15536    cx.executor().run_until_parked();
15537
15538    cx.assert_state_with_diff(
15539        "
15540            «aaa
15541          - bbb
15542            ccc
15543            ddd
15544
15545            ggg
15546            hhh
15547
15548
15549            lll
15550            mmm
15551          - nnn
15552          + NNN
15553
15554            qqq
15555            rrr
15556
15557            uuu
15558            111
15559            222
15560            333
15561
15562          + 666
15563            777
15564
15565            000
15566            !!!ˇ»"
15567            .unindent(),
15568    );
15569}
15570
15571#[gpui::test]
15572async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15573    init_test(cx, |_| {});
15574
15575    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15576    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15577
15578    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15579    let multi_buffer = cx.new(|cx| {
15580        let mut multibuffer = MultiBuffer::new(ReadWrite);
15581        multibuffer.push_excerpts(
15582            buffer.clone(),
15583            [
15584                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15585                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15586                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15587            ],
15588            cx,
15589        );
15590        multibuffer
15591    });
15592
15593    let editor =
15594        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15595    editor
15596        .update(cx, |editor, _window, cx| {
15597            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15598            editor
15599                .buffer
15600                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15601        })
15602        .unwrap();
15603
15604    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15605    cx.run_until_parked();
15606
15607    cx.update_editor(|editor, window, cx| {
15608        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15609    });
15610    cx.executor().run_until_parked();
15611
15612    // When the start of a hunk coincides with the start of its excerpt,
15613    // the hunk is expanded. When the start of a a hunk is earlier than
15614    // the start of its excerpt, the hunk is not expanded.
15615    cx.assert_state_with_diff(
15616        "
15617            ˇaaa
15618          - bbb
15619          + BBB
15620
15621          - ddd
15622          - eee
15623          + DDD
15624          + EEE
15625            fff
15626
15627            iii
15628        "
15629        .unindent(),
15630    );
15631}
15632
15633#[gpui::test]
15634async fn test_edits_around_expanded_insertion_hunks(
15635    executor: BackgroundExecutor,
15636    cx: &mut TestAppContext,
15637) {
15638    init_test(cx, |_| {});
15639
15640    let mut cx = EditorTestContext::new(cx).await;
15641
15642    let diff_base = r#"
15643        use some::mod1;
15644        use some::mod2;
15645
15646        const A: u32 = 42;
15647
15648        fn main() {
15649            println!("hello");
15650
15651            println!("world");
15652        }
15653        "#
15654    .unindent();
15655    executor.run_until_parked();
15656    cx.set_state(
15657        &r#"
15658        use some::mod1;
15659        use some::mod2;
15660
15661        const A: u32 = 42;
15662        const B: u32 = 42;
15663        const C: u32 = 42;
15664        ˇ
15665
15666        fn main() {
15667            println!("hello");
15668
15669            println!("world");
15670        }
15671        "#
15672        .unindent(),
15673    );
15674
15675    cx.set_head_text(&diff_base);
15676    executor.run_until_parked();
15677
15678    cx.update_editor(|editor, window, cx| {
15679        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15680    });
15681    executor.run_until_parked();
15682
15683    cx.assert_state_with_diff(
15684        r#"
15685        use some::mod1;
15686        use some::mod2;
15687
15688        const A: u32 = 42;
15689      + const B: u32 = 42;
15690      + const C: u32 = 42;
15691      + ˇ
15692
15693        fn main() {
15694            println!("hello");
15695
15696            println!("world");
15697        }
15698      "#
15699        .unindent(),
15700    );
15701
15702    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15703    executor.run_until_parked();
15704
15705    cx.assert_state_with_diff(
15706        r#"
15707        use some::mod1;
15708        use some::mod2;
15709
15710        const A: u32 = 42;
15711      + const B: u32 = 42;
15712      + const C: u32 = 42;
15713      + const D: u32 = 42;
15714      + ˇ
15715
15716        fn main() {
15717            println!("hello");
15718
15719            println!("world");
15720        }
15721      "#
15722        .unindent(),
15723    );
15724
15725    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15726    executor.run_until_parked();
15727
15728    cx.assert_state_with_diff(
15729        r#"
15730        use some::mod1;
15731        use some::mod2;
15732
15733        const A: u32 = 42;
15734      + const B: u32 = 42;
15735      + const C: u32 = 42;
15736      + const D: u32 = 42;
15737      + const E: u32 = 42;
15738      + ˇ
15739
15740        fn main() {
15741            println!("hello");
15742
15743            println!("world");
15744        }
15745      "#
15746        .unindent(),
15747    );
15748
15749    cx.update_editor(|editor, window, cx| {
15750        editor.delete_line(&DeleteLine, window, cx);
15751    });
15752    executor.run_until_parked();
15753
15754    cx.assert_state_with_diff(
15755        r#"
15756        use some::mod1;
15757        use some::mod2;
15758
15759        const A: u32 = 42;
15760      + const B: u32 = 42;
15761      + const C: u32 = 42;
15762      + const D: u32 = 42;
15763      + const E: u32 = 42;
15764        ˇ
15765        fn main() {
15766            println!("hello");
15767
15768            println!("world");
15769        }
15770      "#
15771        .unindent(),
15772    );
15773
15774    cx.update_editor(|editor, window, cx| {
15775        editor.move_up(&MoveUp, window, cx);
15776        editor.delete_line(&DeleteLine, window, cx);
15777        editor.move_up(&MoveUp, window, cx);
15778        editor.delete_line(&DeleteLine, window, cx);
15779        editor.move_up(&MoveUp, window, cx);
15780        editor.delete_line(&DeleteLine, window, cx);
15781    });
15782    executor.run_until_parked();
15783    cx.assert_state_with_diff(
15784        r#"
15785        use some::mod1;
15786        use some::mod2;
15787
15788        const A: u32 = 42;
15789      + const B: u32 = 42;
15790        ˇ
15791        fn main() {
15792            println!("hello");
15793
15794            println!("world");
15795        }
15796      "#
15797        .unindent(),
15798    );
15799
15800    cx.update_editor(|editor, window, cx| {
15801        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15802        editor.delete_line(&DeleteLine, window, cx);
15803    });
15804    executor.run_until_parked();
15805    cx.assert_state_with_diff(
15806        r#"
15807        ˇ
15808        fn main() {
15809            println!("hello");
15810
15811            println!("world");
15812        }
15813      "#
15814        .unindent(),
15815    );
15816}
15817
15818#[gpui::test]
15819async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15820    init_test(cx, |_| {});
15821
15822    let mut cx = EditorTestContext::new(cx).await;
15823    cx.set_head_text(indoc! { "
15824        one
15825        two
15826        three
15827        four
15828        five
15829        "
15830    });
15831    cx.set_state(indoc! { "
15832        one
15833        ˇthree
15834        five
15835    "});
15836    cx.run_until_parked();
15837    cx.update_editor(|editor, window, cx| {
15838        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15839    });
15840    cx.assert_state_with_diff(
15841        indoc! { "
15842        one
15843      - two
15844        ˇthree
15845      - four
15846        five
15847    "}
15848        .to_string(),
15849    );
15850    cx.update_editor(|editor, window, cx| {
15851        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15852    });
15853
15854    cx.assert_state_with_diff(
15855        indoc! { "
15856        one
15857        ˇthree
15858        five
15859    "}
15860        .to_string(),
15861    );
15862
15863    cx.set_state(indoc! { "
15864        one
15865        ˇTWO
15866        three
15867        four
15868        five
15869    "});
15870    cx.run_until_parked();
15871    cx.update_editor(|editor, window, cx| {
15872        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15873    });
15874
15875    cx.assert_state_with_diff(
15876        indoc! { "
15877            one
15878          - two
15879          + ˇTWO
15880            three
15881            four
15882            five
15883        "}
15884        .to_string(),
15885    );
15886    cx.update_editor(|editor, window, cx| {
15887        editor.move_up(&Default::default(), window, cx);
15888        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15889    });
15890    cx.assert_state_with_diff(
15891        indoc! { "
15892            one
15893            ˇTWO
15894            three
15895            four
15896            five
15897        "}
15898        .to_string(),
15899    );
15900}
15901
15902#[gpui::test]
15903async fn test_edits_around_expanded_deletion_hunks(
15904    executor: BackgroundExecutor,
15905    cx: &mut TestAppContext,
15906) {
15907    init_test(cx, |_| {});
15908
15909    let mut cx = EditorTestContext::new(cx).await;
15910
15911    let diff_base = r#"
15912        use some::mod1;
15913        use some::mod2;
15914
15915        const A: u32 = 42;
15916        const B: u32 = 42;
15917        const C: u32 = 42;
15918
15919
15920        fn main() {
15921            println!("hello");
15922
15923            println!("world");
15924        }
15925    "#
15926    .unindent();
15927    executor.run_until_parked();
15928    cx.set_state(
15929        &r#"
15930        use some::mod1;
15931        use some::mod2;
15932
15933        ˇconst B: u32 = 42;
15934        const C: u32 = 42;
15935
15936
15937        fn main() {
15938            println!("hello");
15939
15940            println!("world");
15941        }
15942        "#
15943        .unindent(),
15944    );
15945
15946    cx.set_head_text(&diff_base);
15947    executor.run_until_parked();
15948
15949    cx.update_editor(|editor, window, cx| {
15950        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15951    });
15952    executor.run_until_parked();
15953
15954    cx.assert_state_with_diff(
15955        r#"
15956        use some::mod1;
15957        use some::mod2;
15958
15959      - const A: u32 = 42;
15960        ˇconst B: u32 = 42;
15961        const C: u32 = 42;
15962
15963
15964        fn main() {
15965            println!("hello");
15966
15967            println!("world");
15968        }
15969      "#
15970        .unindent(),
15971    );
15972
15973    cx.update_editor(|editor, window, cx| {
15974        editor.delete_line(&DeleteLine, window, cx);
15975    });
15976    executor.run_until_parked();
15977    cx.assert_state_with_diff(
15978        r#"
15979        use some::mod1;
15980        use some::mod2;
15981
15982      - const A: u32 = 42;
15983      - const B: u32 = 42;
15984        ˇconst C: u32 = 42;
15985
15986
15987        fn main() {
15988            println!("hello");
15989
15990            println!("world");
15991        }
15992      "#
15993        .unindent(),
15994    );
15995
15996    cx.update_editor(|editor, window, cx| {
15997        editor.delete_line(&DeleteLine, window, cx);
15998    });
15999    executor.run_until_parked();
16000    cx.assert_state_with_diff(
16001        r#"
16002        use some::mod1;
16003        use some::mod2;
16004
16005      - const A: u32 = 42;
16006      - const B: u32 = 42;
16007      - const C: u32 = 42;
16008        ˇ
16009
16010        fn main() {
16011            println!("hello");
16012
16013            println!("world");
16014        }
16015      "#
16016        .unindent(),
16017    );
16018
16019    cx.update_editor(|editor, window, cx| {
16020        editor.handle_input("replacement", window, cx);
16021    });
16022    executor.run_until_parked();
16023    cx.assert_state_with_diff(
16024        r#"
16025        use some::mod1;
16026        use some::mod2;
16027
16028      - const A: u32 = 42;
16029      - const B: u32 = 42;
16030      - const C: u32 = 42;
16031      -
16032      + replacementˇ
16033
16034        fn main() {
16035            println!("hello");
16036
16037            println!("world");
16038        }
16039      "#
16040        .unindent(),
16041    );
16042}
16043
16044#[gpui::test]
16045async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16046    init_test(cx, |_| {});
16047
16048    let mut cx = EditorTestContext::new(cx).await;
16049
16050    let base_text = r#"
16051        one
16052        two
16053        three
16054        four
16055        five
16056    "#
16057    .unindent();
16058    executor.run_until_parked();
16059    cx.set_state(
16060        &r#"
16061        one
16062        two
16063        fˇour
16064        five
16065        "#
16066        .unindent(),
16067    );
16068
16069    cx.set_head_text(&base_text);
16070    executor.run_until_parked();
16071
16072    cx.update_editor(|editor, window, cx| {
16073        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16074    });
16075    executor.run_until_parked();
16076
16077    cx.assert_state_with_diff(
16078        r#"
16079          one
16080          two
16081        - three
16082          fˇour
16083          five
16084        "#
16085        .unindent(),
16086    );
16087
16088    cx.update_editor(|editor, window, cx| {
16089        editor.backspace(&Backspace, window, cx);
16090        editor.backspace(&Backspace, window, cx);
16091    });
16092    executor.run_until_parked();
16093    cx.assert_state_with_diff(
16094        r#"
16095          one
16096          two
16097        - threeˇ
16098        - four
16099        + our
16100          five
16101        "#
16102        .unindent(),
16103    );
16104}
16105
16106#[gpui::test]
16107async fn test_edit_after_expanded_modification_hunk(
16108    executor: BackgroundExecutor,
16109    cx: &mut TestAppContext,
16110) {
16111    init_test(cx, |_| {});
16112
16113    let mut cx = EditorTestContext::new(cx).await;
16114
16115    let diff_base = r#"
16116        use some::mod1;
16117        use some::mod2;
16118
16119        const A: u32 = 42;
16120        const B: u32 = 42;
16121        const C: u32 = 42;
16122        const D: u32 = 42;
16123
16124
16125        fn main() {
16126            println!("hello");
16127
16128            println!("world");
16129        }"#
16130    .unindent();
16131
16132    cx.set_state(
16133        &r#"
16134        use some::mod1;
16135        use some::mod2;
16136
16137        const A: u32 = 42;
16138        const B: u32 = 42;
16139        const C: u32 = 43ˇ
16140        const D: u32 = 42;
16141
16142
16143        fn main() {
16144            println!("hello");
16145
16146            println!("world");
16147        }"#
16148        .unindent(),
16149    );
16150
16151    cx.set_head_text(&diff_base);
16152    executor.run_until_parked();
16153    cx.update_editor(|editor, window, cx| {
16154        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16155    });
16156    executor.run_until_parked();
16157
16158    cx.assert_state_with_diff(
16159        r#"
16160        use some::mod1;
16161        use some::mod2;
16162
16163        const A: u32 = 42;
16164        const B: u32 = 42;
16165      - const C: u32 = 42;
16166      + const C: u32 = 43ˇ
16167        const D: u32 = 42;
16168
16169
16170        fn main() {
16171            println!("hello");
16172
16173            println!("world");
16174        }"#
16175        .unindent(),
16176    );
16177
16178    cx.update_editor(|editor, window, cx| {
16179        editor.handle_input("\nnew_line\n", window, cx);
16180    });
16181    executor.run_until_parked();
16182
16183    cx.assert_state_with_diff(
16184        r#"
16185        use some::mod1;
16186        use some::mod2;
16187
16188        const A: u32 = 42;
16189        const B: u32 = 42;
16190      - const C: u32 = 42;
16191      + const C: u32 = 43
16192      + new_line
16193      + ˇ
16194        const D: u32 = 42;
16195
16196
16197        fn main() {
16198            println!("hello");
16199
16200            println!("world");
16201        }"#
16202        .unindent(),
16203    );
16204}
16205
16206#[gpui::test]
16207async fn test_stage_and_unstage_added_file_hunk(
16208    executor: BackgroundExecutor,
16209    cx: &mut TestAppContext,
16210) {
16211    init_test(cx, |_| {});
16212
16213    let mut cx = EditorTestContext::new(cx).await;
16214    cx.update_editor(|editor, _, cx| {
16215        editor.set_expand_all_diff_hunks(cx);
16216    });
16217
16218    let working_copy = r#"
16219            ˇfn main() {
16220                println!("hello, world!");
16221            }
16222        "#
16223    .unindent();
16224
16225    cx.set_state(&working_copy);
16226    executor.run_until_parked();
16227
16228    cx.assert_state_with_diff(
16229        r#"
16230            + ˇfn main() {
16231            +     println!("hello, world!");
16232            + }
16233        "#
16234        .unindent(),
16235    );
16236    cx.assert_index_text(None);
16237
16238    cx.update_editor(|editor, window, cx| {
16239        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16240    });
16241    executor.run_until_parked();
16242    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16243    cx.assert_state_with_diff(
16244        r#"
16245            + ˇfn main() {
16246            +     println!("hello, world!");
16247            + }
16248        "#
16249        .unindent(),
16250    );
16251
16252    cx.update_editor(|editor, window, cx| {
16253        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16254    });
16255    executor.run_until_parked();
16256    cx.assert_index_text(None);
16257}
16258
16259async fn setup_indent_guides_editor(
16260    text: &str,
16261    cx: &mut TestAppContext,
16262) -> (BufferId, EditorTestContext) {
16263    init_test(cx, |_| {});
16264
16265    let mut cx = EditorTestContext::new(cx).await;
16266
16267    let buffer_id = cx.update_editor(|editor, window, cx| {
16268        editor.set_text(text, window, cx);
16269        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16270
16271        buffer_ids[0]
16272    });
16273
16274    (buffer_id, cx)
16275}
16276
16277fn assert_indent_guides(
16278    range: Range<u32>,
16279    expected: Vec<IndentGuide>,
16280    active_indices: Option<Vec<usize>>,
16281    cx: &mut EditorTestContext,
16282) {
16283    let indent_guides = cx.update_editor(|editor, window, cx| {
16284        let snapshot = editor.snapshot(window, cx).display_snapshot;
16285        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16286            editor,
16287            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16288            true,
16289            &snapshot,
16290            cx,
16291        );
16292
16293        indent_guides.sort_by(|a, b| {
16294            a.depth.cmp(&b.depth).then(
16295                a.start_row
16296                    .cmp(&b.start_row)
16297                    .then(a.end_row.cmp(&b.end_row)),
16298            )
16299        });
16300        indent_guides
16301    });
16302
16303    if let Some(expected) = active_indices {
16304        let active_indices = cx.update_editor(|editor, window, cx| {
16305            let snapshot = editor.snapshot(window, cx).display_snapshot;
16306            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16307        });
16308
16309        assert_eq!(
16310            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16311            expected,
16312            "Active indent guide indices do not match"
16313        );
16314    }
16315
16316    assert_eq!(indent_guides, expected, "Indent guides do not match");
16317}
16318
16319fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16320    IndentGuide {
16321        buffer_id,
16322        start_row: MultiBufferRow(start_row),
16323        end_row: MultiBufferRow(end_row),
16324        depth,
16325        tab_size: 4,
16326        settings: IndentGuideSettings {
16327            enabled: true,
16328            line_width: 1,
16329            active_line_width: 1,
16330            ..Default::default()
16331        },
16332    }
16333}
16334
16335#[gpui::test]
16336async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16337    let (buffer_id, mut cx) = setup_indent_guides_editor(
16338        &"
16339    fn main() {
16340        let a = 1;
16341    }"
16342        .unindent(),
16343        cx,
16344    )
16345    .await;
16346
16347    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16348}
16349
16350#[gpui::test]
16351async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16352    let (buffer_id, mut cx) = setup_indent_guides_editor(
16353        &"
16354    fn main() {
16355        let a = 1;
16356        let b = 2;
16357    }"
16358        .unindent(),
16359        cx,
16360    )
16361    .await;
16362
16363    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16364}
16365
16366#[gpui::test]
16367async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16368    let (buffer_id, mut cx) = setup_indent_guides_editor(
16369        &"
16370    fn main() {
16371        let a = 1;
16372        if a == 3 {
16373            let b = 2;
16374        } else {
16375            let c = 3;
16376        }
16377    }"
16378        .unindent(),
16379        cx,
16380    )
16381    .await;
16382
16383    assert_indent_guides(
16384        0..8,
16385        vec![
16386            indent_guide(buffer_id, 1, 6, 0),
16387            indent_guide(buffer_id, 3, 3, 1),
16388            indent_guide(buffer_id, 5, 5, 1),
16389        ],
16390        None,
16391        &mut cx,
16392    );
16393}
16394
16395#[gpui::test]
16396async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16397    let (buffer_id, mut cx) = setup_indent_guides_editor(
16398        &"
16399    fn main() {
16400        let a = 1;
16401            let b = 2;
16402        let c = 3;
16403    }"
16404        .unindent(),
16405        cx,
16406    )
16407    .await;
16408
16409    assert_indent_guides(
16410        0..5,
16411        vec![
16412            indent_guide(buffer_id, 1, 3, 0),
16413            indent_guide(buffer_id, 2, 2, 1),
16414        ],
16415        None,
16416        &mut cx,
16417    );
16418}
16419
16420#[gpui::test]
16421async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16422    let (buffer_id, mut cx) = setup_indent_guides_editor(
16423        &"
16424        fn main() {
16425            let a = 1;
16426
16427            let c = 3;
16428        }"
16429        .unindent(),
16430        cx,
16431    )
16432    .await;
16433
16434    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16435}
16436
16437#[gpui::test]
16438async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16439    let (buffer_id, mut cx) = setup_indent_guides_editor(
16440        &"
16441        fn main() {
16442            let a = 1;
16443
16444            let c = 3;
16445
16446            if a == 3 {
16447                let b = 2;
16448            } else {
16449                let c = 3;
16450            }
16451        }"
16452        .unindent(),
16453        cx,
16454    )
16455    .await;
16456
16457    assert_indent_guides(
16458        0..11,
16459        vec![
16460            indent_guide(buffer_id, 1, 9, 0),
16461            indent_guide(buffer_id, 6, 6, 1),
16462            indent_guide(buffer_id, 8, 8, 1),
16463        ],
16464        None,
16465        &mut cx,
16466    );
16467}
16468
16469#[gpui::test]
16470async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16471    let (buffer_id, mut cx) = setup_indent_guides_editor(
16472        &"
16473        fn main() {
16474            let a = 1;
16475
16476            let c = 3;
16477
16478            if a == 3 {
16479                let b = 2;
16480            } else {
16481                let c = 3;
16482            }
16483        }"
16484        .unindent(),
16485        cx,
16486    )
16487    .await;
16488
16489    assert_indent_guides(
16490        1..11,
16491        vec![
16492            indent_guide(buffer_id, 1, 9, 0),
16493            indent_guide(buffer_id, 6, 6, 1),
16494            indent_guide(buffer_id, 8, 8, 1),
16495        ],
16496        None,
16497        &mut cx,
16498    );
16499}
16500
16501#[gpui::test]
16502async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16503    let (buffer_id, mut cx) = setup_indent_guides_editor(
16504        &"
16505        fn main() {
16506            let a = 1;
16507
16508            let c = 3;
16509
16510            if a == 3 {
16511                let b = 2;
16512            } else {
16513                let c = 3;
16514            }
16515        }"
16516        .unindent(),
16517        cx,
16518    )
16519    .await;
16520
16521    assert_indent_guides(
16522        1..10,
16523        vec![
16524            indent_guide(buffer_id, 1, 9, 0),
16525            indent_guide(buffer_id, 6, 6, 1),
16526            indent_guide(buffer_id, 8, 8, 1),
16527        ],
16528        None,
16529        &mut cx,
16530    );
16531}
16532
16533#[gpui::test]
16534async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16535    let (buffer_id, mut cx) = setup_indent_guides_editor(
16536        &"
16537        block1
16538            block2
16539                block3
16540                    block4
16541            block2
16542        block1
16543        block1"
16544            .unindent(),
16545        cx,
16546    )
16547    .await;
16548
16549    assert_indent_guides(
16550        1..10,
16551        vec![
16552            indent_guide(buffer_id, 1, 4, 0),
16553            indent_guide(buffer_id, 2, 3, 1),
16554            indent_guide(buffer_id, 3, 3, 2),
16555        ],
16556        None,
16557        &mut cx,
16558    );
16559}
16560
16561#[gpui::test]
16562async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16563    let (buffer_id, mut cx) = setup_indent_guides_editor(
16564        &"
16565        block1
16566            block2
16567                block3
16568
16569        block1
16570        block1"
16571            .unindent(),
16572        cx,
16573    )
16574    .await;
16575
16576    assert_indent_guides(
16577        0..6,
16578        vec![
16579            indent_guide(buffer_id, 1, 2, 0),
16580            indent_guide(buffer_id, 2, 2, 1),
16581        ],
16582        None,
16583        &mut cx,
16584    );
16585}
16586
16587#[gpui::test]
16588async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16589    let (buffer_id, mut cx) = setup_indent_guides_editor(
16590        &"
16591        block1
16592
16593
16594
16595            block2
16596        "
16597        .unindent(),
16598        cx,
16599    )
16600    .await;
16601
16602    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16603}
16604
16605#[gpui::test]
16606async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16607    let (buffer_id, mut cx) = setup_indent_guides_editor(
16608        &"
16609        def a:
16610        \tb = 3
16611        \tif True:
16612        \t\tc = 4
16613        \t\td = 5
16614        \tprint(b)
16615        "
16616        .unindent(),
16617        cx,
16618    )
16619    .await;
16620
16621    assert_indent_guides(
16622        0..6,
16623        vec![
16624            indent_guide(buffer_id, 1, 5, 0),
16625            indent_guide(buffer_id, 3, 4, 1),
16626        ],
16627        None,
16628        &mut cx,
16629    );
16630}
16631
16632#[gpui::test]
16633async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16634    let (buffer_id, mut cx) = setup_indent_guides_editor(
16635        &"
16636    fn main() {
16637        let a = 1;
16638    }"
16639        .unindent(),
16640        cx,
16641    )
16642    .await;
16643
16644    cx.update_editor(|editor, window, cx| {
16645        editor.change_selections(None, window, cx, |s| {
16646            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16647        });
16648    });
16649
16650    assert_indent_guides(
16651        0..3,
16652        vec![indent_guide(buffer_id, 1, 1, 0)],
16653        Some(vec![0]),
16654        &mut cx,
16655    );
16656}
16657
16658#[gpui::test]
16659async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16660    let (buffer_id, mut cx) = setup_indent_guides_editor(
16661        &"
16662    fn main() {
16663        if 1 == 2 {
16664            let a = 1;
16665        }
16666    }"
16667        .unindent(),
16668        cx,
16669    )
16670    .await;
16671
16672    cx.update_editor(|editor, window, cx| {
16673        editor.change_selections(None, window, cx, |s| {
16674            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16675        });
16676    });
16677
16678    assert_indent_guides(
16679        0..4,
16680        vec![
16681            indent_guide(buffer_id, 1, 3, 0),
16682            indent_guide(buffer_id, 2, 2, 1),
16683        ],
16684        Some(vec![1]),
16685        &mut cx,
16686    );
16687
16688    cx.update_editor(|editor, window, cx| {
16689        editor.change_selections(None, window, cx, |s| {
16690            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16691        });
16692    });
16693
16694    assert_indent_guides(
16695        0..4,
16696        vec![
16697            indent_guide(buffer_id, 1, 3, 0),
16698            indent_guide(buffer_id, 2, 2, 1),
16699        ],
16700        Some(vec![1]),
16701        &mut cx,
16702    );
16703
16704    cx.update_editor(|editor, window, cx| {
16705        editor.change_selections(None, window, cx, |s| {
16706            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16707        });
16708    });
16709
16710    assert_indent_guides(
16711        0..4,
16712        vec![
16713            indent_guide(buffer_id, 1, 3, 0),
16714            indent_guide(buffer_id, 2, 2, 1),
16715        ],
16716        Some(vec![0]),
16717        &mut cx,
16718    );
16719}
16720
16721#[gpui::test]
16722async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16723    let (buffer_id, mut cx) = setup_indent_guides_editor(
16724        &"
16725    fn main() {
16726        let a = 1;
16727
16728        let b = 2;
16729    }"
16730        .unindent(),
16731        cx,
16732    )
16733    .await;
16734
16735    cx.update_editor(|editor, window, cx| {
16736        editor.change_selections(None, window, cx, |s| {
16737            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16738        });
16739    });
16740
16741    assert_indent_guides(
16742        0..5,
16743        vec![indent_guide(buffer_id, 1, 3, 0)],
16744        Some(vec![0]),
16745        &mut cx,
16746    );
16747}
16748
16749#[gpui::test]
16750async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16751    let (buffer_id, mut cx) = setup_indent_guides_editor(
16752        &"
16753    def m:
16754        a = 1
16755        pass"
16756            .unindent(),
16757        cx,
16758    )
16759    .await;
16760
16761    cx.update_editor(|editor, window, cx| {
16762        editor.change_selections(None, window, cx, |s| {
16763            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16764        });
16765    });
16766
16767    assert_indent_guides(
16768        0..3,
16769        vec![indent_guide(buffer_id, 1, 2, 0)],
16770        Some(vec![0]),
16771        &mut cx,
16772    );
16773}
16774
16775#[gpui::test]
16776async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16777    init_test(cx, |_| {});
16778    let mut cx = EditorTestContext::new(cx).await;
16779    let text = indoc! {
16780        "
16781        impl A {
16782            fn b() {
16783                0;
16784                3;
16785                5;
16786                6;
16787                7;
16788            }
16789        }
16790        "
16791    };
16792    let base_text = indoc! {
16793        "
16794        impl A {
16795            fn b() {
16796                0;
16797                1;
16798                2;
16799                3;
16800                4;
16801            }
16802            fn c() {
16803                5;
16804                6;
16805                7;
16806            }
16807        }
16808        "
16809    };
16810
16811    cx.update_editor(|editor, window, cx| {
16812        editor.set_text(text, window, cx);
16813
16814        editor.buffer().update(cx, |multibuffer, cx| {
16815            let buffer = multibuffer.as_singleton().unwrap();
16816            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16817
16818            multibuffer.set_all_diff_hunks_expanded(cx);
16819            multibuffer.add_diff(diff, cx);
16820
16821            buffer.read(cx).remote_id()
16822        })
16823    });
16824    cx.run_until_parked();
16825
16826    cx.assert_state_with_diff(
16827        indoc! { "
16828          impl A {
16829              fn b() {
16830                  0;
16831        -         1;
16832        -         2;
16833                  3;
16834        -         4;
16835        -     }
16836        -     fn c() {
16837                  5;
16838                  6;
16839                  7;
16840              }
16841          }
16842          ˇ"
16843        }
16844        .to_string(),
16845    );
16846
16847    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16848        editor
16849            .snapshot(window, cx)
16850            .buffer_snapshot
16851            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16852            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16853            .collect::<Vec<_>>()
16854    });
16855    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16856    assert_eq!(
16857        actual_guides,
16858        vec![
16859            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16860            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16861            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16862        ]
16863    );
16864}
16865
16866#[gpui::test]
16867async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16868    init_test(cx, |_| {});
16869    let mut cx = EditorTestContext::new(cx).await;
16870
16871    let diff_base = r#"
16872        a
16873        b
16874        c
16875        "#
16876    .unindent();
16877
16878    cx.set_state(
16879        &r#"
16880        ˇA
16881        b
16882        C
16883        "#
16884        .unindent(),
16885    );
16886    cx.set_head_text(&diff_base);
16887    cx.update_editor(|editor, window, cx| {
16888        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16889    });
16890    executor.run_until_parked();
16891
16892    let both_hunks_expanded = r#"
16893        - a
16894        + ˇA
16895          b
16896        - c
16897        + C
16898        "#
16899    .unindent();
16900
16901    cx.assert_state_with_diff(both_hunks_expanded.clone());
16902
16903    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16904        let snapshot = editor.snapshot(window, cx);
16905        let hunks = editor
16906            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16907            .collect::<Vec<_>>();
16908        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16909        let buffer_id = hunks[0].buffer_id;
16910        hunks
16911            .into_iter()
16912            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16913            .collect::<Vec<_>>()
16914    });
16915    assert_eq!(hunk_ranges.len(), 2);
16916
16917    cx.update_editor(|editor, _, cx| {
16918        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16919    });
16920    executor.run_until_parked();
16921
16922    let second_hunk_expanded = r#"
16923          ˇA
16924          b
16925        - c
16926        + C
16927        "#
16928    .unindent();
16929
16930    cx.assert_state_with_diff(second_hunk_expanded);
16931
16932    cx.update_editor(|editor, _, cx| {
16933        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16934    });
16935    executor.run_until_parked();
16936
16937    cx.assert_state_with_diff(both_hunks_expanded.clone());
16938
16939    cx.update_editor(|editor, _, cx| {
16940        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16941    });
16942    executor.run_until_parked();
16943
16944    let first_hunk_expanded = r#"
16945        - a
16946        + ˇA
16947          b
16948          C
16949        "#
16950    .unindent();
16951
16952    cx.assert_state_with_diff(first_hunk_expanded);
16953
16954    cx.update_editor(|editor, _, cx| {
16955        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16956    });
16957    executor.run_until_parked();
16958
16959    cx.assert_state_with_diff(both_hunks_expanded);
16960
16961    cx.set_state(
16962        &r#"
16963        ˇA
16964        b
16965        "#
16966        .unindent(),
16967    );
16968    cx.run_until_parked();
16969
16970    // TODO this cursor position seems bad
16971    cx.assert_state_with_diff(
16972        r#"
16973        - ˇa
16974        + A
16975          b
16976        "#
16977        .unindent(),
16978    );
16979
16980    cx.update_editor(|editor, window, cx| {
16981        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16982    });
16983
16984    cx.assert_state_with_diff(
16985        r#"
16986            - ˇa
16987            + A
16988              b
16989            - c
16990            "#
16991        .unindent(),
16992    );
16993
16994    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16995        let snapshot = editor.snapshot(window, cx);
16996        let hunks = editor
16997            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16998            .collect::<Vec<_>>();
16999        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17000        let buffer_id = hunks[0].buffer_id;
17001        hunks
17002            .into_iter()
17003            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17004            .collect::<Vec<_>>()
17005    });
17006    assert_eq!(hunk_ranges.len(), 2);
17007
17008    cx.update_editor(|editor, _, cx| {
17009        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
17010    });
17011    executor.run_until_parked();
17012
17013    cx.assert_state_with_diff(
17014        r#"
17015        - ˇa
17016        + A
17017          b
17018        "#
17019        .unindent(),
17020    );
17021}
17022
17023#[gpui::test]
17024async fn test_toggle_deletion_hunk_at_start_of_file(
17025    executor: BackgroundExecutor,
17026    cx: &mut TestAppContext,
17027) {
17028    init_test(cx, |_| {});
17029    let mut cx = EditorTestContext::new(cx).await;
17030
17031    let diff_base = r#"
17032        a
17033        b
17034        c
17035        "#
17036    .unindent();
17037
17038    cx.set_state(
17039        &r#"
17040        ˇb
17041        c
17042        "#
17043        .unindent(),
17044    );
17045    cx.set_head_text(&diff_base);
17046    cx.update_editor(|editor, window, cx| {
17047        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
17048    });
17049    executor.run_until_parked();
17050
17051    let hunk_expanded = r#"
17052        - a
17053          ˇb
17054          c
17055        "#
17056    .unindent();
17057
17058    cx.assert_state_with_diff(hunk_expanded.clone());
17059
17060    let hunk_ranges = cx.update_editor(|editor, window, cx| {
17061        let snapshot = editor.snapshot(window, cx);
17062        let hunks = editor
17063            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17064            .collect::<Vec<_>>();
17065        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
17066        let buffer_id = hunks[0].buffer_id;
17067        hunks
17068            .into_iter()
17069            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
17070            .collect::<Vec<_>>()
17071    });
17072    assert_eq!(hunk_ranges.len(), 1);
17073
17074    cx.update_editor(|editor, _, cx| {
17075        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17076    });
17077    executor.run_until_parked();
17078
17079    let hunk_collapsed = r#"
17080          ˇb
17081          c
17082        "#
17083    .unindent();
17084
17085    cx.assert_state_with_diff(hunk_collapsed);
17086
17087    cx.update_editor(|editor, _, cx| {
17088        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
17089    });
17090    executor.run_until_parked();
17091
17092    cx.assert_state_with_diff(hunk_expanded.clone());
17093}
17094
17095#[gpui::test]
17096async fn test_display_diff_hunks(cx: &mut TestAppContext) {
17097    init_test(cx, |_| {});
17098
17099    let fs = FakeFs::new(cx.executor());
17100    fs.insert_tree(
17101        path!("/test"),
17102        json!({
17103            ".git": {},
17104            "file-1": "ONE\n",
17105            "file-2": "TWO\n",
17106            "file-3": "THREE\n",
17107        }),
17108    )
17109    .await;
17110
17111    fs.set_head_for_repo(
17112        path!("/test/.git").as_ref(),
17113        &[
17114            ("file-1".into(), "one\n".into()),
17115            ("file-2".into(), "two\n".into()),
17116            ("file-3".into(), "three\n".into()),
17117        ],
17118    );
17119
17120    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
17121    let mut buffers = vec![];
17122    for i in 1..=3 {
17123        let buffer = project
17124            .update(cx, |project, cx| {
17125                let path = format!(path!("/test/file-{}"), i);
17126                project.open_local_buffer(path, cx)
17127            })
17128            .await
17129            .unwrap();
17130        buffers.push(buffer);
17131    }
17132
17133    let multibuffer = cx.new(|cx| {
17134        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
17135        multibuffer.set_all_diff_hunks_expanded(cx);
17136        for buffer in &buffers {
17137            let snapshot = buffer.read(cx).snapshot();
17138            multibuffer.set_excerpts_for_path(
17139                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
17140                buffer.clone(),
17141                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
17142                DEFAULT_MULTIBUFFER_CONTEXT,
17143                cx,
17144            );
17145        }
17146        multibuffer
17147    });
17148
17149    let editor = cx.add_window(|window, cx| {
17150        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
17151    });
17152    cx.run_until_parked();
17153
17154    let snapshot = editor
17155        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17156        .unwrap();
17157    let hunks = snapshot
17158        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
17159        .map(|hunk| match hunk {
17160            DisplayDiffHunk::Unfolded {
17161                display_row_range, ..
17162            } => display_row_range,
17163            DisplayDiffHunk::Folded { .. } => unreachable!(),
17164        })
17165        .collect::<Vec<_>>();
17166    assert_eq!(
17167        hunks,
17168        [
17169            DisplayRow(2)..DisplayRow(4),
17170            DisplayRow(7)..DisplayRow(9),
17171            DisplayRow(12)..DisplayRow(14),
17172        ]
17173    );
17174}
17175
17176#[gpui::test]
17177async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
17178    init_test(cx, |_| {});
17179
17180    let mut cx = EditorTestContext::new(cx).await;
17181    cx.set_head_text(indoc! { "
17182        one
17183        two
17184        three
17185        four
17186        five
17187        "
17188    });
17189    cx.set_index_text(indoc! { "
17190        one
17191        two
17192        three
17193        four
17194        five
17195        "
17196    });
17197    cx.set_state(indoc! {"
17198        one
17199        TWO
17200        ˇTHREE
17201        FOUR
17202        five
17203    "});
17204    cx.run_until_parked();
17205    cx.update_editor(|editor, window, cx| {
17206        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17207    });
17208    cx.run_until_parked();
17209    cx.assert_index_text(Some(indoc! {"
17210        one
17211        TWO
17212        THREE
17213        FOUR
17214        five
17215    "}));
17216    cx.set_state(indoc! { "
17217        one
17218        TWO
17219        ˇTHREE-HUNDRED
17220        FOUR
17221        five
17222    "});
17223    cx.run_until_parked();
17224    cx.update_editor(|editor, window, cx| {
17225        let snapshot = editor.snapshot(window, cx);
17226        let hunks = editor
17227            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17228            .collect::<Vec<_>>();
17229        assert_eq!(hunks.len(), 1);
17230        assert_eq!(
17231            hunks[0].status(),
17232            DiffHunkStatus {
17233                kind: DiffHunkStatusKind::Modified,
17234                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17235            }
17236        );
17237
17238        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17239    });
17240    cx.run_until_parked();
17241    cx.assert_index_text(Some(indoc! {"
17242        one
17243        TWO
17244        THREE-HUNDRED
17245        FOUR
17246        five
17247    "}));
17248}
17249
17250#[gpui::test]
17251fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17252    init_test(cx, |_| {});
17253
17254    let editor = cx.add_window(|window, cx| {
17255        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17256        build_editor(buffer, window, cx)
17257    });
17258
17259    let render_args = Arc::new(Mutex::new(None));
17260    let snapshot = editor
17261        .update(cx, |editor, window, cx| {
17262            let snapshot = editor.buffer().read(cx).snapshot(cx);
17263            let range =
17264                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17265
17266            struct RenderArgs {
17267                row: MultiBufferRow,
17268                folded: bool,
17269                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17270            }
17271
17272            let crease = Crease::inline(
17273                range,
17274                FoldPlaceholder::test(),
17275                {
17276                    let toggle_callback = render_args.clone();
17277                    move |row, folded, callback, _window, _cx| {
17278                        *toggle_callback.lock() = Some(RenderArgs {
17279                            row,
17280                            folded,
17281                            callback,
17282                        });
17283                        div()
17284                    }
17285                },
17286                |_row, _folded, _window, _cx| div(),
17287            );
17288
17289            editor.insert_creases(Some(crease), cx);
17290            let snapshot = editor.snapshot(window, cx);
17291            let _div = snapshot.render_crease_toggle(
17292                MultiBufferRow(1),
17293                false,
17294                cx.entity().clone(),
17295                window,
17296                cx,
17297            );
17298            snapshot
17299        })
17300        .unwrap();
17301
17302    let render_args = render_args.lock().take().unwrap();
17303    assert_eq!(render_args.row, MultiBufferRow(1));
17304    assert!(!render_args.folded);
17305    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17306
17307    cx.update_window(*editor, |_, window, cx| {
17308        (render_args.callback)(true, window, cx)
17309    })
17310    .unwrap();
17311    let snapshot = editor
17312        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17313        .unwrap();
17314    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17315
17316    cx.update_window(*editor, |_, window, cx| {
17317        (render_args.callback)(false, window, cx)
17318    })
17319    .unwrap();
17320    let snapshot = editor
17321        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17322        .unwrap();
17323    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17324}
17325
17326#[gpui::test]
17327async fn test_input_text(cx: &mut TestAppContext) {
17328    init_test(cx, |_| {});
17329    let mut cx = EditorTestContext::new(cx).await;
17330
17331    cx.set_state(
17332        &r#"ˇone
17333        two
17334
17335        three
17336        fourˇ
17337        five
17338
17339        siˇx"#
17340            .unindent(),
17341    );
17342
17343    cx.dispatch_action(HandleInput(String::new()));
17344    cx.assert_editor_state(
17345        &r#"ˇone
17346        two
17347
17348        three
17349        fourˇ
17350        five
17351
17352        siˇx"#
17353            .unindent(),
17354    );
17355
17356    cx.dispatch_action(HandleInput("AAAA".to_string()));
17357    cx.assert_editor_state(
17358        &r#"AAAAˇone
17359        two
17360
17361        three
17362        fourAAAAˇ
17363        five
17364
17365        siAAAAˇx"#
17366            .unindent(),
17367    );
17368}
17369
17370#[gpui::test]
17371async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17372    init_test(cx, |_| {});
17373
17374    let mut cx = EditorTestContext::new(cx).await;
17375    cx.set_state(
17376        r#"let foo = 1;
17377let foo = 2;
17378let foo = 3;
17379let fooˇ = 4;
17380let foo = 5;
17381let foo = 6;
17382let foo = 7;
17383let foo = 8;
17384let foo = 9;
17385let foo = 10;
17386let foo = 11;
17387let foo = 12;
17388let foo = 13;
17389let foo = 14;
17390let foo = 15;"#,
17391    );
17392
17393    cx.update_editor(|e, window, cx| {
17394        assert_eq!(
17395            e.next_scroll_position,
17396            NextScrollCursorCenterTopBottom::Center,
17397            "Default next scroll direction is center",
17398        );
17399
17400        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17401        assert_eq!(
17402            e.next_scroll_position,
17403            NextScrollCursorCenterTopBottom::Top,
17404            "After center, next scroll direction should be top",
17405        );
17406
17407        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17408        assert_eq!(
17409            e.next_scroll_position,
17410            NextScrollCursorCenterTopBottom::Bottom,
17411            "After top, next scroll direction should be bottom",
17412        );
17413
17414        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17415        assert_eq!(
17416            e.next_scroll_position,
17417            NextScrollCursorCenterTopBottom::Center,
17418            "After bottom, scrolling should start over",
17419        );
17420
17421        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17422        assert_eq!(
17423            e.next_scroll_position,
17424            NextScrollCursorCenterTopBottom::Top,
17425            "Scrolling continues if retriggered fast enough"
17426        );
17427    });
17428
17429    cx.executor()
17430        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17431    cx.executor().run_until_parked();
17432    cx.update_editor(|e, _, _| {
17433        assert_eq!(
17434            e.next_scroll_position,
17435            NextScrollCursorCenterTopBottom::Center,
17436            "If scrolling is not triggered fast enough, it should reset"
17437        );
17438    });
17439}
17440
17441#[gpui::test]
17442async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17443    init_test(cx, |_| {});
17444    let mut cx = EditorLspTestContext::new_rust(
17445        lsp::ServerCapabilities {
17446            definition_provider: Some(lsp::OneOf::Left(true)),
17447            references_provider: Some(lsp::OneOf::Left(true)),
17448            ..lsp::ServerCapabilities::default()
17449        },
17450        cx,
17451    )
17452    .await;
17453
17454    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17455        let go_to_definition = cx
17456            .lsp
17457            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17458                move |params, _| async move {
17459                    if empty_go_to_definition {
17460                        Ok(None)
17461                    } else {
17462                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17463                            uri: params.text_document_position_params.text_document.uri,
17464                            range: lsp::Range::new(
17465                                lsp::Position::new(4, 3),
17466                                lsp::Position::new(4, 6),
17467                            ),
17468                        })))
17469                    }
17470                },
17471            );
17472        let references = cx
17473            .lsp
17474            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17475                Ok(Some(vec![lsp::Location {
17476                    uri: params.text_document_position.text_document.uri,
17477                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17478                }]))
17479            });
17480        (go_to_definition, references)
17481    };
17482
17483    cx.set_state(
17484        &r#"fn one() {
17485            let mut a = ˇtwo();
17486        }
17487
17488        fn two() {}"#
17489            .unindent(),
17490    );
17491    set_up_lsp_handlers(false, &mut cx);
17492    let navigated = cx
17493        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17494        .await
17495        .expect("Failed to navigate to definition");
17496    assert_eq!(
17497        navigated,
17498        Navigated::Yes,
17499        "Should have navigated to definition from the GetDefinition response"
17500    );
17501    cx.assert_editor_state(
17502        &r#"fn one() {
17503            let mut a = two();
17504        }
17505
17506        fn «twoˇ»() {}"#
17507            .unindent(),
17508    );
17509
17510    let editors = cx.update_workspace(|workspace, _, cx| {
17511        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17512    });
17513    cx.update_editor(|_, _, test_editor_cx| {
17514        assert_eq!(
17515            editors.len(),
17516            1,
17517            "Initially, only one, test, editor should be open in the workspace"
17518        );
17519        assert_eq!(
17520            test_editor_cx.entity(),
17521            editors.last().expect("Asserted len is 1").clone()
17522        );
17523    });
17524
17525    set_up_lsp_handlers(true, &mut cx);
17526    let navigated = cx
17527        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17528        .await
17529        .expect("Failed to navigate to lookup references");
17530    assert_eq!(
17531        navigated,
17532        Navigated::Yes,
17533        "Should have navigated to references as a fallback after empty GoToDefinition response"
17534    );
17535    // We should not change the selections in the existing file,
17536    // if opening another milti buffer with the references
17537    cx.assert_editor_state(
17538        &r#"fn one() {
17539            let mut a = two();
17540        }
17541
17542        fn «twoˇ»() {}"#
17543            .unindent(),
17544    );
17545    let editors = cx.update_workspace(|workspace, _, cx| {
17546        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17547    });
17548    cx.update_editor(|_, _, test_editor_cx| {
17549        assert_eq!(
17550            editors.len(),
17551            2,
17552            "After falling back to references search, we open a new editor with the results"
17553        );
17554        let references_fallback_text = editors
17555            .into_iter()
17556            .find(|new_editor| *new_editor != test_editor_cx.entity())
17557            .expect("Should have one non-test editor now")
17558            .read(test_editor_cx)
17559            .text(test_editor_cx);
17560        assert_eq!(
17561            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17562            "Should use the range from the references response and not the GoToDefinition one"
17563        );
17564    });
17565}
17566
17567#[gpui::test]
17568async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17569    init_test(cx, |_| {});
17570    cx.update(|cx| {
17571        let mut editor_settings = EditorSettings::get_global(cx).clone();
17572        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17573        EditorSettings::override_global(editor_settings, cx);
17574    });
17575    let mut cx = EditorLspTestContext::new_rust(
17576        lsp::ServerCapabilities {
17577            definition_provider: Some(lsp::OneOf::Left(true)),
17578            references_provider: Some(lsp::OneOf::Left(true)),
17579            ..lsp::ServerCapabilities::default()
17580        },
17581        cx,
17582    )
17583    .await;
17584    let original_state = r#"fn one() {
17585        let mut a = ˇtwo();
17586    }
17587
17588    fn two() {}"#
17589        .unindent();
17590    cx.set_state(&original_state);
17591
17592    let mut go_to_definition = cx
17593        .lsp
17594        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17595            move |_, _| async move { Ok(None) },
17596        );
17597    let _references = cx
17598        .lsp
17599        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17600            panic!("Should not call for references with no go to definition fallback")
17601        });
17602
17603    let navigated = cx
17604        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17605        .await
17606        .expect("Failed to navigate to lookup references");
17607    go_to_definition
17608        .next()
17609        .await
17610        .expect("Should have called the go_to_definition handler");
17611
17612    assert_eq!(
17613        navigated,
17614        Navigated::No,
17615        "Should have navigated to references as a fallback after empty GoToDefinition response"
17616    );
17617    cx.assert_editor_state(&original_state);
17618    let editors = cx.update_workspace(|workspace, _, cx| {
17619        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17620    });
17621    cx.update_editor(|_, _, _| {
17622        assert_eq!(
17623            editors.len(),
17624            1,
17625            "After unsuccessful fallback, no other editor should have been opened"
17626        );
17627    });
17628}
17629
17630#[gpui::test]
17631async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17632    init_test(cx, |_| {});
17633
17634    let language = Arc::new(Language::new(
17635        LanguageConfig::default(),
17636        Some(tree_sitter_rust::LANGUAGE.into()),
17637    ));
17638
17639    let text = r#"
17640        #[cfg(test)]
17641        mod tests() {
17642            #[test]
17643            fn runnable_1() {
17644                let a = 1;
17645            }
17646
17647            #[test]
17648            fn runnable_2() {
17649                let a = 1;
17650                let b = 2;
17651            }
17652        }
17653    "#
17654    .unindent();
17655
17656    let fs = FakeFs::new(cx.executor());
17657    fs.insert_file("/file.rs", Default::default()).await;
17658
17659    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17660    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17661    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17662    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17663    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17664
17665    let editor = cx.new_window_entity(|window, cx| {
17666        Editor::new(
17667            EditorMode::full(),
17668            multi_buffer,
17669            Some(project.clone()),
17670            window,
17671            cx,
17672        )
17673    });
17674
17675    editor.update_in(cx, |editor, window, cx| {
17676        let snapshot = editor.buffer().read(cx).snapshot(cx);
17677        editor.tasks.insert(
17678            (buffer.read(cx).remote_id(), 3),
17679            RunnableTasks {
17680                templates: vec![],
17681                offset: snapshot.anchor_before(43),
17682                column: 0,
17683                extra_variables: HashMap::default(),
17684                context_range: BufferOffset(43)..BufferOffset(85),
17685            },
17686        );
17687        editor.tasks.insert(
17688            (buffer.read(cx).remote_id(), 8),
17689            RunnableTasks {
17690                templates: vec![],
17691                offset: snapshot.anchor_before(86),
17692                column: 0,
17693                extra_variables: HashMap::default(),
17694                context_range: BufferOffset(86)..BufferOffset(191),
17695            },
17696        );
17697
17698        // Test finding task when cursor is inside function body
17699        editor.change_selections(None, window, cx, |s| {
17700            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17701        });
17702        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17703        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17704
17705        // Test finding task when cursor is on function name
17706        editor.change_selections(None, window, cx, |s| {
17707            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17708        });
17709        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17710        assert_eq!(row, 8, "Should find task when cursor is on function name");
17711    });
17712}
17713
17714#[gpui::test]
17715async fn test_folding_buffers(cx: &mut TestAppContext) {
17716    init_test(cx, |_| {});
17717
17718    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17719    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17720    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17721
17722    let fs = FakeFs::new(cx.executor());
17723    fs.insert_tree(
17724        path!("/a"),
17725        json!({
17726            "first.rs": sample_text_1,
17727            "second.rs": sample_text_2,
17728            "third.rs": sample_text_3,
17729        }),
17730    )
17731    .await;
17732    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17733    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17734    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17735    let worktree = project.update(cx, |project, cx| {
17736        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17737        assert_eq!(worktrees.len(), 1);
17738        worktrees.pop().unwrap()
17739    });
17740    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17741
17742    let buffer_1 = project
17743        .update(cx, |project, cx| {
17744            project.open_buffer((worktree_id, "first.rs"), cx)
17745        })
17746        .await
17747        .unwrap();
17748    let buffer_2 = project
17749        .update(cx, |project, cx| {
17750            project.open_buffer((worktree_id, "second.rs"), cx)
17751        })
17752        .await
17753        .unwrap();
17754    let buffer_3 = project
17755        .update(cx, |project, cx| {
17756            project.open_buffer((worktree_id, "third.rs"), cx)
17757        })
17758        .await
17759        .unwrap();
17760
17761    let multi_buffer = cx.new(|cx| {
17762        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17763        multi_buffer.push_excerpts(
17764            buffer_1.clone(),
17765            [
17766                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17767                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17768                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17769            ],
17770            cx,
17771        );
17772        multi_buffer.push_excerpts(
17773            buffer_2.clone(),
17774            [
17775                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17776                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17777                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17778            ],
17779            cx,
17780        );
17781        multi_buffer.push_excerpts(
17782            buffer_3.clone(),
17783            [
17784                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17785                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17786                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17787            ],
17788            cx,
17789        );
17790        multi_buffer
17791    });
17792    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17793        Editor::new(
17794            EditorMode::full(),
17795            multi_buffer.clone(),
17796            Some(project.clone()),
17797            window,
17798            cx,
17799        )
17800    });
17801
17802    assert_eq!(
17803        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17804        "\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",
17805    );
17806
17807    multi_buffer_editor.update(cx, |editor, cx| {
17808        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17809    });
17810    assert_eq!(
17811        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17812        "\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",
17813        "After folding the first buffer, its text should not be displayed"
17814    );
17815
17816    multi_buffer_editor.update(cx, |editor, cx| {
17817        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17818    });
17819    assert_eq!(
17820        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17821        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17822        "After folding the second buffer, its text should not be displayed"
17823    );
17824
17825    multi_buffer_editor.update(cx, |editor, cx| {
17826        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17827    });
17828    assert_eq!(
17829        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17830        "\n\n\n\n\n",
17831        "After folding the third buffer, its text should not be displayed"
17832    );
17833
17834    // Emulate selection inside the fold logic, that should work
17835    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17836        editor
17837            .snapshot(window, cx)
17838            .next_line_boundary(Point::new(0, 4));
17839    });
17840
17841    multi_buffer_editor.update(cx, |editor, cx| {
17842        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17843    });
17844    assert_eq!(
17845        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17846        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17847        "After unfolding the second buffer, its text should be displayed"
17848    );
17849
17850    // Typing inside of buffer 1 causes that buffer to be unfolded.
17851    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17852        assert_eq!(
17853            multi_buffer
17854                .read(cx)
17855                .snapshot(cx)
17856                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17857                .collect::<String>(),
17858            "bbbb"
17859        );
17860        editor.change_selections(None, window, cx, |selections| {
17861            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17862        });
17863        editor.handle_input("B", window, cx);
17864    });
17865
17866    assert_eq!(
17867        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17868        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17869        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17870    );
17871
17872    multi_buffer_editor.update(cx, |editor, cx| {
17873        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17874    });
17875    assert_eq!(
17876        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17877        "\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",
17878        "After unfolding the all buffers, all original text should be displayed"
17879    );
17880}
17881
17882#[gpui::test]
17883async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17884    init_test(cx, |_| {});
17885
17886    let sample_text_1 = "1111\n2222\n3333".to_string();
17887    let sample_text_2 = "4444\n5555\n6666".to_string();
17888    let sample_text_3 = "7777\n8888\n9999".to_string();
17889
17890    let fs = FakeFs::new(cx.executor());
17891    fs.insert_tree(
17892        path!("/a"),
17893        json!({
17894            "first.rs": sample_text_1,
17895            "second.rs": sample_text_2,
17896            "third.rs": sample_text_3,
17897        }),
17898    )
17899    .await;
17900    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17901    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17902    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17903    let worktree = project.update(cx, |project, cx| {
17904        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17905        assert_eq!(worktrees.len(), 1);
17906        worktrees.pop().unwrap()
17907    });
17908    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17909
17910    let buffer_1 = project
17911        .update(cx, |project, cx| {
17912            project.open_buffer((worktree_id, "first.rs"), cx)
17913        })
17914        .await
17915        .unwrap();
17916    let buffer_2 = project
17917        .update(cx, |project, cx| {
17918            project.open_buffer((worktree_id, "second.rs"), cx)
17919        })
17920        .await
17921        .unwrap();
17922    let buffer_3 = project
17923        .update(cx, |project, cx| {
17924            project.open_buffer((worktree_id, "third.rs"), cx)
17925        })
17926        .await
17927        .unwrap();
17928
17929    let multi_buffer = cx.new(|cx| {
17930        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17931        multi_buffer.push_excerpts(
17932            buffer_1.clone(),
17933            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17934            cx,
17935        );
17936        multi_buffer.push_excerpts(
17937            buffer_2.clone(),
17938            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17939            cx,
17940        );
17941        multi_buffer.push_excerpts(
17942            buffer_3.clone(),
17943            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17944            cx,
17945        );
17946        multi_buffer
17947    });
17948
17949    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17950        Editor::new(
17951            EditorMode::full(),
17952            multi_buffer,
17953            Some(project.clone()),
17954            window,
17955            cx,
17956        )
17957    });
17958
17959    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17960    assert_eq!(
17961        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17962        full_text,
17963    );
17964
17965    multi_buffer_editor.update(cx, |editor, cx| {
17966        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17967    });
17968    assert_eq!(
17969        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17970        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17971        "After folding the first buffer, its text should not be displayed"
17972    );
17973
17974    multi_buffer_editor.update(cx, |editor, cx| {
17975        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17976    });
17977
17978    assert_eq!(
17979        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17980        "\n\n\n\n\n\n7777\n8888\n9999",
17981        "After folding the second buffer, its text should not be displayed"
17982    );
17983
17984    multi_buffer_editor.update(cx, |editor, cx| {
17985        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17986    });
17987    assert_eq!(
17988        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17989        "\n\n\n\n\n",
17990        "After folding the third buffer, its text should not be displayed"
17991    );
17992
17993    multi_buffer_editor.update(cx, |editor, cx| {
17994        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17995    });
17996    assert_eq!(
17997        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17998        "\n\n\n\n4444\n5555\n6666\n\n",
17999        "After unfolding the second buffer, its text should be displayed"
18000    );
18001
18002    multi_buffer_editor.update(cx, |editor, cx| {
18003        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
18004    });
18005    assert_eq!(
18006        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18007        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
18008        "After unfolding the first buffer, its text should be displayed"
18009    );
18010
18011    multi_buffer_editor.update(cx, |editor, cx| {
18012        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
18013    });
18014    assert_eq!(
18015        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18016        full_text,
18017        "After unfolding all buffers, all original text should be displayed"
18018    );
18019}
18020
18021#[gpui::test]
18022async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
18023    init_test(cx, |_| {});
18024
18025    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
18026
18027    let fs = FakeFs::new(cx.executor());
18028    fs.insert_tree(
18029        path!("/a"),
18030        json!({
18031            "main.rs": sample_text,
18032        }),
18033    )
18034    .await;
18035    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18036    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18037    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18038    let worktree = project.update(cx, |project, cx| {
18039        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
18040        assert_eq!(worktrees.len(), 1);
18041        worktrees.pop().unwrap()
18042    });
18043    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
18044
18045    let buffer_1 = project
18046        .update(cx, |project, cx| {
18047            project.open_buffer((worktree_id, "main.rs"), cx)
18048        })
18049        .await
18050        .unwrap();
18051
18052    let multi_buffer = cx.new(|cx| {
18053        let mut multi_buffer = MultiBuffer::new(ReadWrite);
18054        multi_buffer.push_excerpts(
18055            buffer_1.clone(),
18056            [ExcerptRange::new(
18057                Point::new(0, 0)
18058                    ..Point::new(
18059                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
18060                        0,
18061                    ),
18062            )],
18063            cx,
18064        );
18065        multi_buffer
18066    });
18067    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
18068        Editor::new(
18069            EditorMode::full(),
18070            multi_buffer,
18071            Some(project.clone()),
18072            window,
18073            cx,
18074        )
18075    });
18076
18077    let selection_range = Point::new(1, 0)..Point::new(2, 0);
18078    multi_buffer_editor.update_in(cx, |editor, window, cx| {
18079        enum TestHighlight {}
18080        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
18081        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
18082        editor.highlight_text::<TestHighlight>(
18083            vec![highlight_range.clone()],
18084            HighlightStyle::color(Hsla::green()),
18085            cx,
18086        );
18087        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
18088    });
18089
18090    let full_text = format!("\n\n{sample_text}");
18091    assert_eq!(
18092        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
18093        full_text,
18094    );
18095}
18096
18097#[gpui::test]
18098async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
18099    init_test(cx, |_| {});
18100    cx.update(|cx| {
18101        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
18102            "keymaps/default-linux.json",
18103            cx,
18104        )
18105        .unwrap();
18106        cx.bind_keys(default_key_bindings);
18107    });
18108
18109    let (editor, cx) = cx.add_window_view(|window, cx| {
18110        let multi_buffer = MultiBuffer::build_multi(
18111            [
18112                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
18113                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
18114                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
18115                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
18116            ],
18117            cx,
18118        );
18119        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
18120
18121        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
18122        // fold all but the second buffer, so that we test navigating between two
18123        // adjacent folded buffers, as well as folded buffers at the start and
18124        // end the multibuffer
18125        editor.fold_buffer(buffer_ids[0], cx);
18126        editor.fold_buffer(buffer_ids[2], cx);
18127        editor.fold_buffer(buffer_ids[3], cx);
18128
18129        editor
18130    });
18131    cx.simulate_resize(size(px(1000.), px(1000.)));
18132
18133    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
18134    cx.assert_excerpts_with_selections(indoc! {"
18135        [EXCERPT]
18136        ˇ[FOLDED]
18137        [EXCERPT]
18138        a1
18139        b1
18140        [EXCERPT]
18141        [FOLDED]
18142        [EXCERPT]
18143        [FOLDED]
18144        "
18145    });
18146    cx.simulate_keystroke("down");
18147    cx.assert_excerpts_with_selections(indoc! {"
18148        [EXCERPT]
18149        [FOLDED]
18150        [EXCERPT]
18151        ˇa1
18152        b1
18153        [EXCERPT]
18154        [FOLDED]
18155        [EXCERPT]
18156        [FOLDED]
18157        "
18158    });
18159    cx.simulate_keystroke("down");
18160    cx.assert_excerpts_with_selections(indoc! {"
18161        [EXCERPT]
18162        [FOLDED]
18163        [EXCERPT]
18164        a1
18165        ˇb1
18166        [EXCERPT]
18167        [FOLDED]
18168        [EXCERPT]
18169        [FOLDED]
18170        "
18171    });
18172    cx.simulate_keystroke("down");
18173    cx.assert_excerpts_with_selections(indoc! {"
18174        [EXCERPT]
18175        [FOLDED]
18176        [EXCERPT]
18177        a1
18178        b1
18179        ˇ[EXCERPT]
18180        [FOLDED]
18181        [EXCERPT]
18182        [FOLDED]
18183        "
18184    });
18185    cx.simulate_keystroke("down");
18186    cx.assert_excerpts_with_selections(indoc! {"
18187        [EXCERPT]
18188        [FOLDED]
18189        [EXCERPT]
18190        a1
18191        b1
18192        [EXCERPT]
18193        ˇ[FOLDED]
18194        [EXCERPT]
18195        [FOLDED]
18196        "
18197    });
18198    for _ in 0..5 {
18199        cx.simulate_keystroke("down");
18200        cx.assert_excerpts_with_selections(indoc! {"
18201            [EXCERPT]
18202            [FOLDED]
18203            [EXCERPT]
18204            a1
18205            b1
18206            [EXCERPT]
18207            [FOLDED]
18208            [EXCERPT]
18209            ˇ[FOLDED]
18210            "
18211        });
18212    }
18213
18214    cx.simulate_keystroke("up");
18215    cx.assert_excerpts_with_selections(indoc! {"
18216        [EXCERPT]
18217        [FOLDED]
18218        [EXCERPT]
18219        a1
18220        b1
18221        [EXCERPT]
18222        ˇ[FOLDED]
18223        [EXCERPT]
18224        [FOLDED]
18225        "
18226    });
18227    cx.simulate_keystroke("up");
18228    cx.assert_excerpts_with_selections(indoc! {"
18229        [EXCERPT]
18230        [FOLDED]
18231        [EXCERPT]
18232        a1
18233        b1
18234        ˇ[EXCERPT]
18235        [FOLDED]
18236        [EXCERPT]
18237        [FOLDED]
18238        "
18239    });
18240    cx.simulate_keystroke("up");
18241    cx.assert_excerpts_with_selections(indoc! {"
18242        [EXCERPT]
18243        [FOLDED]
18244        [EXCERPT]
18245        a1
18246        ˇb1
18247        [EXCERPT]
18248        [FOLDED]
18249        [EXCERPT]
18250        [FOLDED]
18251        "
18252    });
18253    cx.simulate_keystroke("up");
18254    cx.assert_excerpts_with_selections(indoc! {"
18255        [EXCERPT]
18256        [FOLDED]
18257        [EXCERPT]
18258        ˇa1
18259        b1
18260        [EXCERPT]
18261        [FOLDED]
18262        [EXCERPT]
18263        [FOLDED]
18264        "
18265    });
18266    for _ in 0..5 {
18267        cx.simulate_keystroke("up");
18268        cx.assert_excerpts_with_selections(indoc! {"
18269            [EXCERPT]
18270            ˇ[FOLDED]
18271            [EXCERPT]
18272            a1
18273            b1
18274            [EXCERPT]
18275            [FOLDED]
18276            [EXCERPT]
18277            [FOLDED]
18278            "
18279        });
18280    }
18281}
18282
18283#[gpui::test]
18284async fn test_inline_completion_text(cx: &mut TestAppContext) {
18285    init_test(cx, |_| {});
18286
18287    // Simple insertion
18288    assert_highlighted_edits(
18289        "Hello, world!",
18290        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18291        true,
18292        cx,
18293        |highlighted_edits, cx| {
18294            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18295            assert_eq!(highlighted_edits.highlights.len(), 1);
18296            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18297            assert_eq!(
18298                highlighted_edits.highlights[0].1.background_color,
18299                Some(cx.theme().status().created_background)
18300            );
18301        },
18302    )
18303    .await;
18304
18305    // Replacement
18306    assert_highlighted_edits(
18307        "This is a test.",
18308        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18309        false,
18310        cx,
18311        |highlighted_edits, cx| {
18312            assert_eq!(highlighted_edits.text, "That is a test.");
18313            assert_eq!(highlighted_edits.highlights.len(), 1);
18314            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18315            assert_eq!(
18316                highlighted_edits.highlights[0].1.background_color,
18317                Some(cx.theme().status().created_background)
18318            );
18319        },
18320    )
18321    .await;
18322
18323    // Multiple edits
18324    assert_highlighted_edits(
18325        "Hello, world!",
18326        vec![
18327            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18328            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18329        ],
18330        false,
18331        cx,
18332        |highlighted_edits, cx| {
18333            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18334            assert_eq!(highlighted_edits.highlights.len(), 2);
18335            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18336            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18337            assert_eq!(
18338                highlighted_edits.highlights[0].1.background_color,
18339                Some(cx.theme().status().created_background)
18340            );
18341            assert_eq!(
18342                highlighted_edits.highlights[1].1.background_color,
18343                Some(cx.theme().status().created_background)
18344            );
18345        },
18346    )
18347    .await;
18348
18349    // Multiple lines with edits
18350    assert_highlighted_edits(
18351        "First line\nSecond line\nThird line\nFourth line",
18352        vec![
18353            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18354            (
18355                Point::new(2, 0)..Point::new(2, 10),
18356                "New third line".to_string(),
18357            ),
18358            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18359        ],
18360        false,
18361        cx,
18362        |highlighted_edits, cx| {
18363            assert_eq!(
18364                highlighted_edits.text,
18365                "Second modified\nNew third line\nFourth updated line"
18366            );
18367            assert_eq!(highlighted_edits.highlights.len(), 3);
18368            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18369            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18370            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18371            for highlight in &highlighted_edits.highlights {
18372                assert_eq!(
18373                    highlight.1.background_color,
18374                    Some(cx.theme().status().created_background)
18375                );
18376            }
18377        },
18378    )
18379    .await;
18380}
18381
18382#[gpui::test]
18383async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18384    init_test(cx, |_| {});
18385
18386    // Deletion
18387    assert_highlighted_edits(
18388        "Hello, world!",
18389        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18390        true,
18391        cx,
18392        |highlighted_edits, cx| {
18393            assert_eq!(highlighted_edits.text, "Hello, world!");
18394            assert_eq!(highlighted_edits.highlights.len(), 1);
18395            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18396            assert_eq!(
18397                highlighted_edits.highlights[0].1.background_color,
18398                Some(cx.theme().status().deleted_background)
18399            );
18400        },
18401    )
18402    .await;
18403
18404    // Insertion
18405    assert_highlighted_edits(
18406        "Hello, world!",
18407        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18408        true,
18409        cx,
18410        |highlighted_edits, cx| {
18411            assert_eq!(highlighted_edits.highlights.len(), 1);
18412            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18413            assert_eq!(
18414                highlighted_edits.highlights[0].1.background_color,
18415                Some(cx.theme().status().created_background)
18416            );
18417        },
18418    )
18419    .await;
18420}
18421
18422async fn assert_highlighted_edits(
18423    text: &str,
18424    edits: Vec<(Range<Point>, String)>,
18425    include_deletions: bool,
18426    cx: &mut TestAppContext,
18427    assertion_fn: impl Fn(HighlightedText, &App),
18428) {
18429    let window = cx.add_window(|window, cx| {
18430        let buffer = MultiBuffer::build_simple(text, cx);
18431        Editor::new(EditorMode::full(), buffer, None, window, cx)
18432    });
18433    let cx = &mut VisualTestContext::from_window(*window, cx);
18434
18435    let (buffer, snapshot) = window
18436        .update(cx, |editor, _window, cx| {
18437            (
18438                editor.buffer().clone(),
18439                editor.buffer().read(cx).snapshot(cx),
18440            )
18441        })
18442        .unwrap();
18443
18444    let edits = edits
18445        .into_iter()
18446        .map(|(range, edit)| {
18447            (
18448                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18449                edit,
18450            )
18451        })
18452        .collect::<Vec<_>>();
18453
18454    let text_anchor_edits = edits
18455        .clone()
18456        .into_iter()
18457        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18458        .collect::<Vec<_>>();
18459
18460    let edit_preview = window
18461        .update(cx, |_, _window, cx| {
18462            buffer
18463                .read(cx)
18464                .as_singleton()
18465                .unwrap()
18466                .read(cx)
18467                .preview_edits(text_anchor_edits.into(), cx)
18468        })
18469        .unwrap()
18470        .await;
18471
18472    cx.update(|_window, cx| {
18473        let highlighted_edits = inline_completion_edit_text(
18474            &snapshot.as_singleton().unwrap().2,
18475            &edits,
18476            &edit_preview,
18477            include_deletions,
18478            cx,
18479        );
18480        assertion_fn(highlighted_edits, cx)
18481    });
18482}
18483
18484#[track_caller]
18485fn assert_breakpoint(
18486    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18487    path: &Arc<Path>,
18488    expected: Vec<(u32, Breakpoint)>,
18489) {
18490    if expected.len() == 0usize {
18491        assert!(!breakpoints.contains_key(path), "{}", path.display());
18492    } else {
18493        let mut breakpoint = breakpoints
18494            .get(path)
18495            .unwrap()
18496            .into_iter()
18497            .map(|breakpoint| {
18498                (
18499                    breakpoint.row,
18500                    Breakpoint {
18501                        message: breakpoint.message.clone(),
18502                        state: breakpoint.state,
18503                        condition: breakpoint.condition.clone(),
18504                        hit_condition: breakpoint.hit_condition.clone(),
18505                    },
18506                )
18507            })
18508            .collect::<Vec<_>>();
18509
18510        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18511
18512        assert_eq!(expected, breakpoint);
18513    }
18514}
18515
18516fn add_log_breakpoint_at_cursor(
18517    editor: &mut Editor,
18518    log_message: &str,
18519    window: &mut Window,
18520    cx: &mut Context<Editor>,
18521) {
18522    let (anchor, bp) = editor
18523        .breakpoints_at_cursors(window, cx)
18524        .first()
18525        .and_then(|(anchor, bp)| {
18526            if let Some(bp) = bp {
18527                Some((*anchor, bp.clone()))
18528            } else {
18529                None
18530            }
18531        })
18532        .unwrap_or_else(|| {
18533            let cursor_position: Point = editor.selections.newest(cx).head();
18534
18535            let breakpoint_position = editor
18536                .snapshot(window, cx)
18537                .display_snapshot
18538                .buffer_snapshot
18539                .anchor_before(Point::new(cursor_position.row, 0));
18540
18541            (breakpoint_position, Breakpoint::new_log(&log_message))
18542        });
18543
18544    editor.edit_breakpoint_at_anchor(
18545        anchor,
18546        bp,
18547        BreakpointEditAction::EditLogMessage(log_message.into()),
18548        cx,
18549    );
18550}
18551
18552#[gpui::test]
18553async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18554    init_test(cx, |_| {});
18555
18556    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18557    let fs = FakeFs::new(cx.executor());
18558    fs.insert_tree(
18559        path!("/a"),
18560        json!({
18561            "main.rs": sample_text,
18562        }),
18563    )
18564    .await;
18565    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18566    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18567    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18568
18569    let fs = FakeFs::new(cx.executor());
18570    fs.insert_tree(
18571        path!("/a"),
18572        json!({
18573            "main.rs": sample_text,
18574        }),
18575    )
18576    .await;
18577    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18578    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18579    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18580    let worktree_id = workspace
18581        .update(cx, |workspace, _window, cx| {
18582            workspace.project().update(cx, |project, cx| {
18583                project.worktrees(cx).next().unwrap().read(cx).id()
18584            })
18585        })
18586        .unwrap();
18587
18588    let buffer = project
18589        .update(cx, |project, cx| {
18590            project.open_buffer((worktree_id, "main.rs"), cx)
18591        })
18592        .await
18593        .unwrap();
18594
18595    let (editor, cx) = cx.add_window_view(|window, cx| {
18596        Editor::new(
18597            EditorMode::full(),
18598            MultiBuffer::build_from_buffer(buffer, cx),
18599            Some(project.clone()),
18600            window,
18601            cx,
18602        )
18603    });
18604
18605    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18606    let abs_path = project.read_with(cx, |project, cx| {
18607        project
18608            .absolute_path(&project_path, cx)
18609            .map(|path_buf| Arc::from(path_buf.to_owned()))
18610            .unwrap()
18611    });
18612
18613    // assert we can add breakpoint on the first line
18614    editor.update_in(cx, |editor, window, cx| {
18615        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18616        editor.move_to_end(&MoveToEnd, window, cx);
18617        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18618    });
18619
18620    let breakpoints = editor.update(cx, |editor, cx| {
18621        editor
18622            .breakpoint_store()
18623            .as_ref()
18624            .unwrap()
18625            .read(cx)
18626            .all_breakpoints(cx)
18627            .clone()
18628    });
18629
18630    assert_eq!(1, breakpoints.len());
18631    assert_breakpoint(
18632        &breakpoints,
18633        &abs_path,
18634        vec![
18635            (0, Breakpoint::new_standard()),
18636            (3, Breakpoint::new_standard()),
18637        ],
18638    );
18639
18640    editor.update_in(cx, |editor, window, cx| {
18641        editor.move_to_beginning(&MoveToBeginning, window, cx);
18642        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18643    });
18644
18645    let breakpoints = editor.update(cx, |editor, cx| {
18646        editor
18647            .breakpoint_store()
18648            .as_ref()
18649            .unwrap()
18650            .read(cx)
18651            .all_breakpoints(cx)
18652            .clone()
18653    });
18654
18655    assert_eq!(1, breakpoints.len());
18656    assert_breakpoint(
18657        &breakpoints,
18658        &abs_path,
18659        vec![(3, Breakpoint::new_standard())],
18660    );
18661
18662    editor.update_in(cx, |editor, window, cx| {
18663        editor.move_to_end(&MoveToEnd, window, cx);
18664        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18665    });
18666
18667    let breakpoints = editor.update(cx, |editor, cx| {
18668        editor
18669            .breakpoint_store()
18670            .as_ref()
18671            .unwrap()
18672            .read(cx)
18673            .all_breakpoints(cx)
18674            .clone()
18675    });
18676
18677    assert_eq!(0, breakpoints.len());
18678    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18679}
18680
18681#[gpui::test]
18682async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18683    init_test(cx, |_| {});
18684
18685    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18686
18687    let fs = FakeFs::new(cx.executor());
18688    fs.insert_tree(
18689        path!("/a"),
18690        json!({
18691            "main.rs": sample_text,
18692        }),
18693    )
18694    .await;
18695    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18696    let (workspace, cx) =
18697        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18698
18699    let worktree_id = workspace.update(cx, |workspace, cx| {
18700        workspace.project().update(cx, |project, cx| {
18701            project.worktrees(cx).next().unwrap().read(cx).id()
18702        })
18703    });
18704
18705    let buffer = project
18706        .update(cx, |project, cx| {
18707            project.open_buffer((worktree_id, "main.rs"), cx)
18708        })
18709        .await
18710        .unwrap();
18711
18712    let (editor, cx) = cx.add_window_view(|window, cx| {
18713        Editor::new(
18714            EditorMode::full(),
18715            MultiBuffer::build_from_buffer(buffer, cx),
18716            Some(project.clone()),
18717            window,
18718            cx,
18719        )
18720    });
18721
18722    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18723    let abs_path = project.read_with(cx, |project, cx| {
18724        project
18725            .absolute_path(&project_path, cx)
18726            .map(|path_buf| Arc::from(path_buf.to_owned()))
18727            .unwrap()
18728    });
18729
18730    editor.update_in(cx, |editor, window, cx| {
18731        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18732    });
18733
18734    let breakpoints = editor.update(cx, |editor, cx| {
18735        editor
18736            .breakpoint_store()
18737            .as_ref()
18738            .unwrap()
18739            .read(cx)
18740            .all_breakpoints(cx)
18741            .clone()
18742    });
18743
18744    assert_breakpoint(
18745        &breakpoints,
18746        &abs_path,
18747        vec![(0, Breakpoint::new_log("hello world"))],
18748    );
18749
18750    // Removing a log message from a log breakpoint should remove it
18751    editor.update_in(cx, |editor, window, cx| {
18752        add_log_breakpoint_at_cursor(editor, "", window, cx);
18753    });
18754
18755    let breakpoints = editor.update(cx, |editor, cx| {
18756        editor
18757            .breakpoint_store()
18758            .as_ref()
18759            .unwrap()
18760            .read(cx)
18761            .all_breakpoints(cx)
18762            .clone()
18763    });
18764
18765    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18766
18767    editor.update_in(cx, |editor, window, cx| {
18768        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18769        editor.move_to_end(&MoveToEnd, window, cx);
18770        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18771        // Not adding a log message to a standard breakpoint shouldn't remove it
18772        add_log_breakpoint_at_cursor(editor, "", 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_breakpoint(
18786        &breakpoints,
18787        &abs_path,
18788        vec![
18789            (0, Breakpoint::new_standard()),
18790            (3, Breakpoint::new_standard()),
18791        ],
18792    );
18793
18794    editor.update_in(cx, |editor, window, cx| {
18795        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18796    });
18797
18798    let breakpoints = editor.update(cx, |editor, cx| {
18799        editor
18800            .breakpoint_store()
18801            .as_ref()
18802            .unwrap()
18803            .read(cx)
18804            .all_breakpoints(cx)
18805            .clone()
18806    });
18807
18808    assert_breakpoint(
18809        &breakpoints,
18810        &abs_path,
18811        vec![
18812            (0, Breakpoint::new_standard()),
18813            (3, Breakpoint::new_log("hello world")),
18814        ],
18815    );
18816
18817    editor.update_in(cx, |editor, window, cx| {
18818        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18819    });
18820
18821    let breakpoints = editor.update(cx, |editor, cx| {
18822        editor
18823            .breakpoint_store()
18824            .as_ref()
18825            .unwrap()
18826            .read(cx)
18827            .all_breakpoints(cx)
18828            .clone()
18829    });
18830
18831    assert_breakpoint(
18832        &breakpoints,
18833        &abs_path,
18834        vec![
18835            (0, Breakpoint::new_standard()),
18836            (3, Breakpoint::new_log("hello Earth!!")),
18837        ],
18838    );
18839}
18840
18841/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18842/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18843/// or when breakpoints were placed out of order. This tests for a regression too
18844#[gpui::test]
18845async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18846    init_test(cx, |_| {});
18847
18848    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18849    let fs = FakeFs::new(cx.executor());
18850    fs.insert_tree(
18851        path!("/a"),
18852        json!({
18853            "main.rs": sample_text,
18854        }),
18855    )
18856    .await;
18857    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18858    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18859    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18860
18861    let fs = FakeFs::new(cx.executor());
18862    fs.insert_tree(
18863        path!("/a"),
18864        json!({
18865            "main.rs": sample_text,
18866        }),
18867    )
18868    .await;
18869    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18870    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18871    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18872    let worktree_id = workspace
18873        .update(cx, |workspace, _window, cx| {
18874            workspace.project().update(cx, |project, cx| {
18875                project.worktrees(cx).next().unwrap().read(cx).id()
18876            })
18877        })
18878        .unwrap();
18879
18880    let buffer = project
18881        .update(cx, |project, cx| {
18882            project.open_buffer((worktree_id, "main.rs"), cx)
18883        })
18884        .await
18885        .unwrap();
18886
18887    let (editor, cx) = cx.add_window_view(|window, cx| {
18888        Editor::new(
18889            EditorMode::full(),
18890            MultiBuffer::build_from_buffer(buffer, cx),
18891            Some(project.clone()),
18892            window,
18893            cx,
18894        )
18895    });
18896
18897    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18898    let abs_path = project.read_with(cx, |project, cx| {
18899        project
18900            .absolute_path(&project_path, cx)
18901            .map(|path_buf| Arc::from(path_buf.to_owned()))
18902            .unwrap()
18903    });
18904
18905    // assert we can add breakpoint on the first line
18906    editor.update_in(cx, |editor, window, cx| {
18907        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18908        editor.move_to_end(&MoveToEnd, window, cx);
18909        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18910        editor.move_up(&MoveUp, window, cx);
18911        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18912    });
18913
18914    let breakpoints = editor.update(cx, |editor, cx| {
18915        editor
18916            .breakpoint_store()
18917            .as_ref()
18918            .unwrap()
18919            .read(cx)
18920            .all_breakpoints(cx)
18921            .clone()
18922    });
18923
18924    assert_eq!(1, breakpoints.len());
18925    assert_breakpoint(
18926        &breakpoints,
18927        &abs_path,
18928        vec![
18929            (0, Breakpoint::new_standard()),
18930            (2, Breakpoint::new_standard()),
18931            (3, Breakpoint::new_standard()),
18932        ],
18933    );
18934
18935    editor.update_in(cx, |editor, window, cx| {
18936        editor.move_to_beginning(&MoveToBeginning, window, cx);
18937        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18938        editor.move_to_end(&MoveToEnd, window, cx);
18939        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18940        // Disabling a breakpoint that doesn't exist should do nothing
18941        editor.move_up(&MoveUp, window, cx);
18942        editor.move_up(&MoveUp, window, cx);
18943        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18944    });
18945
18946    let breakpoints = editor.update(cx, |editor, cx| {
18947        editor
18948            .breakpoint_store()
18949            .as_ref()
18950            .unwrap()
18951            .read(cx)
18952            .all_breakpoints(cx)
18953            .clone()
18954    });
18955
18956    let disable_breakpoint = {
18957        let mut bp = Breakpoint::new_standard();
18958        bp.state = BreakpointState::Disabled;
18959        bp
18960    };
18961
18962    assert_eq!(1, breakpoints.len());
18963    assert_breakpoint(
18964        &breakpoints,
18965        &abs_path,
18966        vec![
18967            (0, disable_breakpoint.clone()),
18968            (2, Breakpoint::new_standard()),
18969            (3, disable_breakpoint.clone()),
18970        ],
18971    );
18972
18973    editor.update_in(cx, |editor, window, cx| {
18974        editor.move_to_beginning(&MoveToBeginning, window, cx);
18975        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18976        editor.move_to_end(&MoveToEnd, window, cx);
18977        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18978        editor.move_up(&MoveUp, window, cx);
18979        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18980    });
18981
18982    let breakpoints = editor.update(cx, |editor, cx| {
18983        editor
18984            .breakpoint_store()
18985            .as_ref()
18986            .unwrap()
18987            .read(cx)
18988            .all_breakpoints(cx)
18989            .clone()
18990    });
18991
18992    assert_eq!(1, breakpoints.len());
18993    assert_breakpoint(
18994        &breakpoints,
18995        &abs_path,
18996        vec![
18997            (0, Breakpoint::new_standard()),
18998            (2, disable_breakpoint),
18999            (3, Breakpoint::new_standard()),
19000        ],
19001    );
19002}
19003
19004#[gpui::test]
19005async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
19006    init_test(cx, |_| {});
19007    let capabilities = lsp::ServerCapabilities {
19008        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
19009            prepare_provider: Some(true),
19010            work_done_progress_options: Default::default(),
19011        })),
19012        ..Default::default()
19013    };
19014    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19015
19016    cx.set_state(indoc! {"
19017        struct Fˇoo {}
19018    "});
19019
19020    cx.update_editor(|editor, _, cx| {
19021        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19022        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19023        editor.highlight_background::<DocumentHighlightRead>(
19024            &[highlight_range],
19025            |c| c.editor_document_highlight_read_background,
19026            cx,
19027        );
19028    });
19029
19030    let mut prepare_rename_handler = cx
19031        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
19032            move |_, _, _| async move {
19033                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
19034                    start: lsp::Position {
19035                        line: 0,
19036                        character: 7,
19037                    },
19038                    end: lsp::Position {
19039                        line: 0,
19040                        character: 10,
19041                    },
19042                })))
19043            },
19044        );
19045    let prepare_rename_task = cx
19046        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19047        .expect("Prepare rename was not started");
19048    prepare_rename_handler.next().await.unwrap();
19049    prepare_rename_task.await.expect("Prepare rename failed");
19050
19051    let mut rename_handler =
19052        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19053            let edit = lsp::TextEdit {
19054                range: lsp::Range {
19055                    start: lsp::Position {
19056                        line: 0,
19057                        character: 7,
19058                    },
19059                    end: lsp::Position {
19060                        line: 0,
19061                        character: 10,
19062                    },
19063                },
19064                new_text: "FooRenamed".to_string(),
19065            };
19066            Ok(Some(lsp::WorkspaceEdit::new(
19067                // Specify the same edit twice
19068                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
19069            )))
19070        });
19071    let rename_task = cx
19072        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19073        .expect("Confirm rename was not started");
19074    rename_handler.next().await.unwrap();
19075    rename_task.await.expect("Confirm rename failed");
19076    cx.run_until_parked();
19077
19078    // Despite two edits, only one is actually applied as those are identical
19079    cx.assert_editor_state(indoc! {"
19080        struct FooRenamedˇ {}
19081    "});
19082}
19083
19084#[gpui::test]
19085async fn test_rename_without_prepare(cx: &mut TestAppContext) {
19086    init_test(cx, |_| {});
19087    // These capabilities indicate that the server does not support prepare rename.
19088    let capabilities = lsp::ServerCapabilities {
19089        rename_provider: Some(lsp::OneOf::Left(true)),
19090        ..Default::default()
19091    };
19092    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
19093
19094    cx.set_state(indoc! {"
19095        struct Fˇoo {}
19096    "});
19097
19098    cx.update_editor(|editor, _window, cx| {
19099        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
19100        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
19101        editor.highlight_background::<DocumentHighlightRead>(
19102            &[highlight_range],
19103            |c| c.editor_document_highlight_read_background,
19104            cx,
19105        );
19106    });
19107
19108    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
19109        .expect("Prepare rename was not started")
19110        .await
19111        .expect("Prepare rename failed");
19112
19113    let mut rename_handler =
19114        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
19115            let edit = lsp::TextEdit {
19116                range: lsp::Range {
19117                    start: lsp::Position {
19118                        line: 0,
19119                        character: 7,
19120                    },
19121                    end: lsp::Position {
19122                        line: 0,
19123                        character: 10,
19124                    },
19125                },
19126                new_text: "FooRenamed".to_string(),
19127            };
19128            Ok(Some(lsp::WorkspaceEdit::new(
19129                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
19130            )))
19131        });
19132    let rename_task = cx
19133        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
19134        .expect("Confirm rename was not started");
19135    rename_handler.next().await.unwrap();
19136    rename_task.await.expect("Confirm rename failed");
19137    cx.run_until_parked();
19138
19139    // Correct range is renamed, as `surrounding_word` is used to find it.
19140    cx.assert_editor_state(indoc! {"
19141        struct FooRenamedˇ {}
19142    "});
19143}
19144
19145#[gpui::test]
19146async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
19147    init_test(cx, |_| {});
19148    let mut cx = EditorTestContext::new(cx).await;
19149
19150    let language = Arc::new(
19151        Language::new(
19152            LanguageConfig::default(),
19153            Some(tree_sitter_html::LANGUAGE.into()),
19154        )
19155        .with_brackets_query(
19156            r#"
19157            ("<" @open "/>" @close)
19158            ("</" @open ">" @close)
19159            ("<" @open ">" @close)
19160            ("\"" @open "\"" @close)
19161            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
19162        "#,
19163        )
19164        .unwrap(),
19165    );
19166    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
19167
19168    cx.set_state(indoc! {"
19169        <span>ˇ</span>
19170    "});
19171    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19172    cx.assert_editor_state(indoc! {"
19173        <span>
19174        ˇ
19175        </span>
19176    "});
19177
19178    cx.set_state(indoc! {"
19179        <span><span></span>ˇ</span>
19180    "});
19181    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19182    cx.assert_editor_state(indoc! {"
19183        <span><span></span>
19184        ˇ</span>
19185    "});
19186
19187    cx.set_state(indoc! {"
19188        <span>ˇ
19189        </span>
19190    "});
19191    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
19192    cx.assert_editor_state(indoc! {"
19193        <span>
19194        ˇ
19195        </span>
19196    "});
19197}
19198
19199#[gpui::test(iterations = 10)]
19200async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
19201    init_test(cx, |_| {});
19202
19203    let fs = FakeFs::new(cx.executor());
19204    fs.insert_tree(
19205        path!("/dir"),
19206        json!({
19207            "a.ts": "a",
19208        }),
19209    )
19210    .await;
19211
19212    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
19213    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19214    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19215
19216    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19217    language_registry.add(Arc::new(Language::new(
19218        LanguageConfig {
19219            name: "TypeScript".into(),
19220            matcher: LanguageMatcher {
19221                path_suffixes: vec!["ts".to_string()],
19222                ..Default::default()
19223            },
19224            ..Default::default()
19225        },
19226        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19227    )));
19228    let mut fake_language_servers = language_registry.register_fake_lsp(
19229        "TypeScript",
19230        FakeLspAdapter {
19231            capabilities: lsp::ServerCapabilities {
19232                code_lens_provider: Some(lsp::CodeLensOptions {
19233                    resolve_provider: Some(true),
19234                }),
19235                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19236                    commands: vec!["_the/command".to_string()],
19237                    ..lsp::ExecuteCommandOptions::default()
19238                }),
19239                ..lsp::ServerCapabilities::default()
19240            },
19241            ..FakeLspAdapter::default()
19242        },
19243    );
19244
19245    let (buffer, _handle) = project
19246        .update(cx, |p, cx| {
19247            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19248        })
19249        .await
19250        .unwrap();
19251    cx.executor().run_until_parked();
19252
19253    let fake_server = fake_language_servers.next().await.unwrap();
19254
19255    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19256    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19257    drop(buffer_snapshot);
19258    let actions = cx
19259        .update_window(*workspace, |_, window, cx| {
19260            project.code_actions(&buffer, anchor..anchor, window, cx)
19261        })
19262        .unwrap();
19263
19264    fake_server
19265        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19266            Ok(Some(vec![
19267                lsp::CodeLens {
19268                    range: lsp::Range::default(),
19269                    command: Some(lsp::Command {
19270                        title: "Code lens command".to_owned(),
19271                        command: "_the/command".to_owned(),
19272                        arguments: None,
19273                    }),
19274                    data: None,
19275                },
19276                lsp::CodeLens {
19277                    range: lsp::Range::default(),
19278                    command: Some(lsp::Command {
19279                        title: "Command not in capabilities".to_owned(),
19280                        command: "not in capabilities".to_owned(),
19281                        arguments: None,
19282                    }),
19283                    data: None,
19284                },
19285                lsp::CodeLens {
19286                    range: lsp::Range {
19287                        start: lsp::Position {
19288                            line: 1,
19289                            character: 1,
19290                        },
19291                        end: lsp::Position {
19292                            line: 1,
19293                            character: 1,
19294                        },
19295                    },
19296                    command: Some(lsp::Command {
19297                        title: "Command not in range".to_owned(),
19298                        command: "_the/command".to_owned(),
19299                        arguments: None,
19300                    }),
19301                    data: None,
19302                },
19303            ]))
19304        })
19305        .next()
19306        .await;
19307
19308    let actions = actions.await.unwrap();
19309    assert_eq!(
19310        actions.len(),
19311        1,
19312        "Should have only one valid action for the 0..0 range"
19313    );
19314    let action = actions[0].clone();
19315    let apply = project.update(cx, |project, cx| {
19316        project.apply_code_action(buffer.clone(), action, true, cx)
19317    });
19318
19319    // Resolving the code action does not populate its edits. In absence of
19320    // edits, we must execute the given command.
19321    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19322        |mut lens, _| async move {
19323            let lens_command = lens.command.as_mut().expect("should have a command");
19324            assert_eq!(lens_command.title, "Code lens command");
19325            lens_command.arguments = Some(vec![json!("the-argument")]);
19326            Ok(lens)
19327        },
19328    );
19329
19330    // While executing the command, the language server sends the editor
19331    // a `workspaceEdit` request.
19332    fake_server
19333        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19334            let fake = fake_server.clone();
19335            move |params, _| {
19336                assert_eq!(params.command, "_the/command");
19337                let fake = fake.clone();
19338                async move {
19339                    fake.server
19340                        .request::<lsp::request::ApplyWorkspaceEdit>(
19341                            lsp::ApplyWorkspaceEditParams {
19342                                label: None,
19343                                edit: lsp::WorkspaceEdit {
19344                                    changes: Some(
19345                                        [(
19346                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19347                                            vec![lsp::TextEdit {
19348                                                range: lsp::Range::new(
19349                                                    lsp::Position::new(0, 0),
19350                                                    lsp::Position::new(0, 0),
19351                                                ),
19352                                                new_text: "X".into(),
19353                                            }],
19354                                        )]
19355                                        .into_iter()
19356                                        .collect(),
19357                                    ),
19358                                    ..Default::default()
19359                                },
19360                            },
19361                        )
19362                        .await
19363                        .into_response()
19364                        .unwrap();
19365                    Ok(Some(json!(null)))
19366                }
19367            }
19368        })
19369        .next()
19370        .await;
19371
19372    // Applying the code lens command returns a project transaction containing the edits
19373    // sent by the language server in its `workspaceEdit` request.
19374    let transaction = apply.await.unwrap();
19375    assert!(transaction.0.contains_key(&buffer));
19376    buffer.update(cx, |buffer, cx| {
19377        assert_eq!(buffer.text(), "Xa");
19378        buffer.undo(cx);
19379        assert_eq!(buffer.text(), "a");
19380    });
19381}
19382
19383#[gpui::test]
19384async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19385    init_test(cx, |_| {});
19386
19387    let fs = FakeFs::new(cx.executor());
19388    let main_text = r#"fn main() {
19389println!("1");
19390println!("2");
19391println!("3");
19392println!("4");
19393println!("5");
19394}"#;
19395    let lib_text = "mod foo {}";
19396    fs.insert_tree(
19397        path!("/a"),
19398        json!({
19399            "lib.rs": lib_text,
19400            "main.rs": main_text,
19401        }),
19402    )
19403    .await;
19404
19405    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19406    let (workspace, cx) =
19407        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19408    let worktree_id = workspace.update(cx, |workspace, cx| {
19409        workspace.project().update(cx, |project, cx| {
19410            project.worktrees(cx).next().unwrap().read(cx).id()
19411        })
19412    });
19413
19414    let expected_ranges = vec![
19415        Point::new(0, 0)..Point::new(0, 0),
19416        Point::new(1, 0)..Point::new(1, 1),
19417        Point::new(2, 0)..Point::new(2, 2),
19418        Point::new(3, 0)..Point::new(3, 3),
19419    ];
19420
19421    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19422    let editor_1 = workspace
19423        .update_in(cx, |workspace, window, cx| {
19424            workspace.open_path(
19425                (worktree_id, "main.rs"),
19426                Some(pane_1.downgrade()),
19427                true,
19428                window,
19429                cx,
19430            )
19431        })
19432        .unwrap()
19433        .await
19434        .downcast::<Editor>()
19435        .unwrap();
19436    pane_1.update(cx, |pane, cx| {
19437        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19438        open_editor.update(cx, |editor, cx| {
19439            assert_eq!(
19440                editor.display_text(cx),
19441                main_text,
19442                "Original main.rs text on initial open",
19443            );
19444            assert_eq!(
19445                editor
19446                    .selections
19447                    .all::<Point>(cx)
19448                    .into_iter()
19449                    .map(|s| s.range())
19450                    .collect::<Vec<_>>(),
19451                vec![Point::zero()..Point::zero()],
19452                "Default selections on initial open",
19453            );
19454        })
19455    });
19456    editor_1.update_in(cx, |editor, window, cx| {
19457        editor.change_selections(None, window, cx, |s| {
19458            s.select_ranges(expected_ranges.clone());
19459        });
19460    });
19461
19462    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19463        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19464    });
19465    let editor_2 = workspace
19466        .update_in(cx, |workspace, window, cx| {
19467            workspace.open_path(
19468                (worktree_id, "main.rs"),
19469                Some(pane_2.downgrade()),
19470                true,
19471                window,
19472                cx,
19473            )
19474        })
19475        .unwrap()
19476        .await
19477        .downcast::<Editor>()
19478        .unwrap();
19479    pane_2.update(cx, |pane, cx| {
19480        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19481        open_editor.update(cx, |editor, cx| {
19482            assert_eq!(
19483                editor.display_text(cx),
19484                main_text,
19485                "Original main.rs text on initial open in another panel",
19486            );
19487            assert_eq!(
19488                editor
19489                    .selections
19490                    .all::<Point>(cx)
19491                    .into_iter()
19492                    .map(|s| s.range())
19493                    .collect::<Vec<_>>(),
19494                vec![Point::zero()..Point::zero()],
19495                "Default selections on initial open in another panel",
19496            );
19497        })
19498    });
19499
19500    editor_2.update_in(cx, |editor, window, cx| {
19501        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19502    });
19503
19504    let _other_editor_1 = workspace
19505        .update_in(cx, |workspace, window, cx| {
19506            workspace.open_path(
19507                (worktree_id, "lib.rs"),
19508                Some(pane_1.downgrade()),
19509                true,
19510                window,
19511                cx,
19512            )
19513        })
19514        .unwrap()
19515        .await
19516        .downcast::<Editor>()
19517        .unwrap();
19518    pane_1
19519        .update_in(cx, |pane, window, cx| {
19520            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19521                .unwrap()
19522        })
19523        .await
19524        .unwrap();
19525    drop(editor_1);
19526    pane_1.update(cx, |pane, cx| {
19527        pane.active_item()
19528            .unwrap()
19529            .downcast::<Editor>()
19530            .unwrap()
19531            .update(cx, |editor, cx| {
19532                assert_eq!(
19533                    editor.display_text(cx),
19534                    lib_text,
19535                    "Other file should be open and active",
19536                );
19537            });
19538        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19539    });
19540
19541    let _other_editor_2 = workspace
19542        .update_in(cx, |workspace, window, cx| {
19543            workspace.open_path(
19544                (worktree_id, "lib.rs"),
19545                Some(pane_2.downgrade()),
19546                true,
19547                window,
19548                cx,
19549            )
19550        })
19551        .unwrap()
19552        .await
19553        .downcast::<Editor>()
19554        .unwrap();
19555    pane_2
19556        .update_in(cx, |pane, window, cx| {
19557            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19558                .unwrap()
19559        })
19560        .await
19561        .unwrap();
19562    drop(editor_2);
19563    pane_2.update(cx, |pane, cx| {
19564        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19565        open_editor.update(cx, |editor, cx| {
19566            assert_eq!(
19567                editor.display_text(cx),
19568                lib_text,
19569                "Other file should be open and active in another panel too",
19570            );
19571        });
19572        assert_eq!(
19573            pane.items().count(),
19574            1,
19575            "No other editors should be open in another pane",
19576        );
19577    });
19578
19579    let _editor_1_reopened = workspace
19580        .update_in(cx, |workspace, window, cx| {
19581            workspace.open_path(
19582                (worktree_id, "main.rs"),
19583                Some(pane_1.downgrade()),
19584                true,
19585                window,
19586                cx,
19587            )
19588        })
19589        .unwrap()
19590        .await
19591        .downcast::<Editor>()
19592        .unwrap();
19593    let _editor_2_reopened = workspace
19594        .update_in(cx, |workspace, window, cx| {
19595            workspace.open_path(
19596                (worktree_id, "main.rs"),
19597                Some(pane_2.downgrade()),
19598                true,
19599                window,
19600                cx,
19601            )
19602        })
19603        .unwrap()
19604        .await
19605        .downcast::<Editor>()
19606        .unwrap();
19607    pane_1.update(cx, |pane, cx| {
19608        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19609        open_editor.update(cx, |editor, cx| {
19610            assert_eq!(
19611                editor.display_text(cx),
19612                main_text,
19613                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19614            );
19615            assert_eq!(
19616                editor
19617                    .selections
19618                    .all::<Point>(cx)
19619                    .into_iter()
19620                    .map(|s| s.range())
19621                    .collect::<Vec<_>>(),
19622                expected_ranges,
19623                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19624            );
19625        })
19626    });
19627    pane_2.update(cx, |pane, cx| {
19628        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19629        open_editor.update(cx, |editor, cx| {
19630            assert_eq!(
19631                editor.display_text(cx),
19632                r#"fn main() {
19633⋯rintln!("1");
19634⋯intln!("2");
19635⋯ntln!("3");
19636println!("4");
19637println!("5");
19638}"#,
19639                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19640            );
19641            assert_eq!(
19642                editor
19643                    .selections
19644                    .all::<Point>(cx)
19645                    .into_iter()
19646                    .map(|s| s.range())
19647                    .collect::<Vec<_>>(),
19648                vec![Point::zero()..Point::zero()],
19649                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19650            );
19651        })
19652    });
19653}
19654
19655#[gpui::test]
19656async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19657    init_test(cx, |_| {});
19658
19659    let fs = FakeFs::new(cx.executor());
19660    let main_text = r#"fn main() {
19661println!("1");
19662println!("2");
19663println!("3");
19664println!("4");
19665println!("5");
19666}"#;
19667    let lib_text = "mod foo {}";
19668    fs.insert_tree(
19669        path!("/a"),
19670        json!({
19671            "lib.rs": lib_text,
19672            "main.rs": main_text,
19673        }),
19674    )
19675    .await;
19676
19677    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19678    let (workspace, cx) =
19679        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19680    let worktree_id = workspace.update(cx, |workspace, cx| {
19681        workspace.project().update(cx, |project, cx| {
19682            project.worktrees(cx).next().unwrap().read(cx).id()
19683        })
19684    });
19685
19686    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19687    let editor = workspace
19688        .update_in(cx, |workspace, window, cx| {
19689            workspace.open_path(
19690                (worktree_id, "main.rs"),
19691                Some(pane.downgrade()),
19692                true,
19693                window,
19694                cx,
19695            )
19696        })
19697        .unwrap()
19698        .await
19699        .downcast::<Editor>()
19700        .unwrap();
19701    pane.update(cx, |pane, cx| {
19702        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19703        open_editor.update(cx, |editor, cx| {
19704            assert_eq!(
19705                editor.display_text(cx),
19706                main_text,
19707                "Original main.rs text on initial open",
19708            );
19709        })
19710    });
19711    editor.update_in(cx, |editor, window, cx| {
19712        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19713    });
19714
19715    cx.update_global(|store: &mut SettingsStore, cx| {
19716        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19717            s.restore_on_file_reopen = Some(false);
19718        });
19719    });
19720    editor.update_in(cx, |editor, window, cx| {
19721        editor.fold_ranges(
19722            vec![
19723                Point::new(1, 0)..Point::new(1, 1),
19724                Point::new(2, 0)..Point::new(2, 2),
19725                Point::new(3, 0)..Point::new(3, 3),
19726            ],
19727            false,
19728            window,
19729            cx,
19730        );
19731    });
19732    pane.update_in(cx, |pane, window, cx| {
19733        pane.close_all_items(&CloseAllItems::default(), window, cx)
19734            .unwrap()
19735    })
19736    .await
19737    .unwrap();
19738    pane.update(cx, |pane, _| {
19739        assert!(pane.active_item().is_none());
19740    });
19741    cx.update_global(|store: &mut SettingsStore, cx| {
19742        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19743            s.restore_on_file_reopen = Some(true);
19744        });
19745    });
19746
19747    let _editor_reopened = workspace
19748        .update_in(cx, |workspace, window, cx| {
19749            workspace.open_path(
19750                (worktree_id, "main.rs"),
19751                Some(pane.downgrade()),
19752                true,
19753                window,
19754                cx,
19755            )
19756        })
19757        .unwrap()
19758        .await
19759        .downcast::<Editor>()
19760        .unwrap();
19761    pane.update(cx, |pane, cx| {
19762        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19763        open_editor.update(cx, |editor, cx| {
19764            assert_eq!(
19765                editor.display_text(cx),
19766                main_text,
19767                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19768            );
19769        })
19770    });
19771}
19772
19773#[gpui::test]
19774async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19775    struct EmptyModalView {
19776        focus_handle: gpui::FocusHandle,
19777    }
19778    impl EventEmitter<DismissEvent> for EmptyModalView {}
19779    impl Render for EmptyModalView {
19780        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19781            div()
19782        }
19783    }
19784    impl Focusable for EmptyModalView {
19785        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19786            self.focus_handle.clone()
19787        }
19788    }
19789    impl workspace::ModalView for EmptyModalView {}
19790    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19791        EmptyModalView {
19792            focus_handle: cx.focus_handle(),
19793        }
19794    }
19795
19796    init_test(cx, |_| {});
19797
19798    let fs = FakeFs::new(cx.executor());
19799    let project = Project::test(fs, [], cx).await;
19800    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19801    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19802    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19803    let editor = cx.new_window_entity(|window, cx| {
19804        Editor::new(
19805            EditorMode::full(),
19806            buffer,
19807            Some(project.clone()),
19808            window,
19809            cx,
19810        )
19811    });
19812    workspace
19813        .update(cx, |workspace, window, cx| {
19814            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19815        })
19816        .unwrap();
19817    editor.update_in(cx, |editor, window, cx| {
19818        editor.open_context_menu(&OpenContextMenu, window, cx);
19819        assert!(editor.mouse_context_menu.is_some());
19820    });
19821    workspace
19822        .update(cx, |workspace, window, cx| {
19823            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19824        })
19825        .unwrap();
19826    cx.read(|cx| {
19827        assert!(editor.read(cx).mouse_context_menu.is_none());
19828    });
19829}
19830
19831#[gpui::test]
19832async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
19833    init_test(cx, |_| {});
19834
19835    let fs = FakeFs::new(cx.executor());
19836    fs.insert_file(path!("/file.html"), Default::default())
19837        .await;
19838
19839    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
19840
19841    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19842    let html_language = Arc::new(Language::new(
19843        LanguageConfig {
19844            name: "HTML".into(),
19845            matcher: LanguageMatcher {
19846                path_suffixes: vec!["html".to_string()],
19847                ..LanguageMatcher::default()
19848            },
19849            brackets: BracketPairConfig {
19850                pairs: vec![BracketPair {
19851                    start: "<".into(),
19852                    end: ">".into(),
19853                    close: true,
19854                    ..Default::default()
19855                }],
19856                ..Default::default()
19857            },
19858            ..Default::default()
19859        },
19860        Some(tree_sitter_html::LANGUAGE.into()),
19861    ));
19862    language_registry.add(html_language);
19863    let mut fake_servers = language_registry.register_fake_lsp(
19864        "HTML",
19865        FakeLspAdapter {
19866            capabilities: lsp::ServerCapabilities {
19867                completion_provider: Some(lsp::CompletionOptions {
19868                    resolve_provider: Some(true),
19869                    ..Default::default()
19870                }),
19871                ..Default::default()
19872            },
19873            ..Default::default()
19874        },
19875    );
19876
19877    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19878    let cx = &mut VisualTestContext::from_window(*workspace, cx);
19879
19880    let worktree_id = workspace
19881        .update(cx, |workspace, _window, cx| {
19882            workspace.project().update(cx, |project, cx| {
19883                project.worktrees(cx).next().unwrap().read(cx).id()
19884            })
19885        })
19886        .unwrap();
19887    project
19888        .update(cx, |project, cx| {
19889            project.open_local_buffer_with_lsp(path!("/file.html"), cx)
19890        })
19891        .await
19892        .unwrap();
19893    let editor = workspace
19894        .update(cx, |workspace, window, cx| {
19895            workspace.open_path((worktree_id, "file.html"), None, true, window, cx)
19896        })
19897        .unwrap()
19898        .await
19899        .unwrap()
19900        .downcast::<Editor>()
19901        .unwrap();
19902
19903    let fake_server = fake_servers.next().await.unwrap();
19904    editor.update_in(cx, |editor, window, cx| {
19905        editor.set_text("<ad></ad>", window, cx);
19906        editor.change_selections(None, window, cx, |selections| {
19907            selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
19908        });
19909        let Some((buffer, _)) = editor
19910            .buffer
19911            .read(cx)
19912            .text_anchor_for_position(editor.selections.newest_anchor().start, cx)
19913        else {
19914            panic!("Failed to get buffer for selection position");
19915        };
19916        let buffer = buffer.read(cx);
19917        let buffer_id = buffer.remote_id();
19918        let opening_range =
19919            buffer.anchor_before(Point::new(0, 1))..buffer.anchor_after(Point::new(0, 3));
19920        let closing_range =
19921            buffer.anchor_before(Point::new(0, 6))..buffer.anchor_after(Point::new(0, 8));
19922        let mut linked_ranges = HashMap::default();
19923        linked_ranges.insert(
19924            buffer_id,
19925            vec![(opening_range.clone(), vec![closing_range.clone()])],
19926        );
19927        editor.linked_edit_ranges = LinkedEditingRanges(linked_ranges);
19928    });
19929    let mut completion_handle =
19930        fake_server.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
19931            Ok(Some(lsp::CompletionResponse::Array(vec![
19932                lsp::CompletionItem {
19933                    label: "head".to_string(),
19934                    text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19935                        lsp::InsertReplaceEdit {
19936                            new_text: "head".to_string(),
19937                            insert: lsp::Range::new(
19938                                lsp::Position::new(0, 1),
19939                                lsp::Position::new(0, 3),
19940                            ),
19941                            replace: lsp::Range::new(
19942                                lsp::Position::new(0, 1),
19943                                lsp::Position::new(0, 3),
19944                            ),
19945                        },
19946                    )),
19947                    ..Default::default()
19948                },
19949            ])))
19950        });
19951    editor.update_in(cx, |editor, window, cx| {
19952        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
19953    });
19954    cx.run_until_parked();
19955    completion_handle.next().await.unwrap();
19956    editor.update(cx, |editor, _| {
19957        assert!(
19958            editor.context_menu_visible(),
19959            "Completion menu should be visible"
19960        );
19961    });
19962    editor.update_in(cx, |editor, window, cx| {
19963        editor.confirm_completion(&ConfirmCompletion::default(), window, cx)
19964    });
19965    cx.executor().run_until_parked();
19966    editor.update(cx, |editor, cx| {
19967        assert_eq!(editor.text(cx), "<head></head>");
19968    });
19969}
19970
19971#[gpui::test]
19972async fn test_invisible_worktree_servers(cx: &mut TestAppContext) {
19973    init_test(cx, |_| {});
19974
19975    let fs = FakeFs::new(cx.executor());
19976    fs.insert_tree(
19977        path!("/root"),
19978        json!({
19979            "a": {
19980                "main.rs": "fn main() {}",
19981            },
19982            "foo": {
19983                "bar": {
19984                    "external_file.rs": "pub mod external {}",
19985                }
19986            }
19987        }),
19988    )
19989    .await;
19990
19991    let project = Project::test(fs, [path!("/root/a").as_ref()], cx).await;
19992    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
19993    language_registry.add(rust_lang());
19994    let _fake_servers = language_registry.register_fake_lsp(
19995        "Rust",
19996        FakeLspAdapter {
19997            ..FakeLspAdapter::default()
19998        },
19999    );
20000    let (workspace, cx) =
20001        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
20002    let worktree_id = workspace.update(cx, |workspace, cx| {
20003        workspace.project().update(cx, |project, cx| {
20004            project.worktrees(cx).next().unwrap().read(cx).id()
20005        })
20006    });
20007
20008    let assert_language_servers_count =
20009        |expected: usize, context: &str, cx: &mut VisualTestContext| {
20010            project.update(cx, |project, cx| {
20011                let current = project
20012                    .lsp_store()
20013                    .read(cx)
20014                    .as_local()
20015                    .unwrap()
20016                    .language_servers
20017                    .len();
20018                assert_eq!(expected, current, "{context}");
20019            });
20020        };
20021
20022    assert_language_servers_count(
20023        0,
20024        "No servers should be running before any file is open",
20025        cx,
20026    );
20027    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
20028    let main_editor = workspace
20029        .update_in(cx, |workspace, window, cx| {
20030            workspace.open_path(
20031                (worktree_id, "main.rs"),
20032                Some(pane.downgrade()),
20033                true,
20034                window,
20035                cx,
20036            )
20037        })
20038        .unwrap()
20039        .await
20040        .downcast::<Editor>()
20041        .unwrap();
20042    pane.update(cx, |pane, cx| {
20043        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20044        open_editor.update(cx, |editor, cx| {
20045            assert_eq!(
20046                editor.display_text(cx),
20047                "fn main() {}",
20048                "Original main.rs text on initial open",
20049            );
20050        });
20051        assert_eq!(open_editor, main_editor);
20052    });
20053    assert_language_servers_count(1, "First *.rs file starts a language server", cx);
20054
20055    let external_editor = workspace
20056        .update_in(cx, |workspace, window, cx| {
20057            workspace.open_abs_path(
20058                PathBuf::from("/root/foo/bar/external_file.rs"),
20059                OpenOptions::default(),
20060                window,
20061                cx,
20062            )
20063        })
20064        .await
20065        .expect("opening external file")
20066        .downcast::<Editor>()
20067        .expect("downcasted external file's open element to editor");
20068    pane.update(cx, |pane, cx| {
20069        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20070        open_editor.update(cx, |editor, cx| {
20071            assert_eq!(
20072                editor.display_text(cx),
20073                "pub mod external {}",
20074                "External file is open now",
20075            );
20076        });
20077        assert_eq!(open_editor, external_editor);
20078    });
20079    assert_language_servers_count(
20080        1,
20081        "Second, external, *.rs file should join the existing server",
20082        cx,
20083    );
20084
20085    pane.update_in(cx, |pane, window, cx| {
20086        pane.close_active_item(&CloseActiveItem::default(), window, cx)
20087    })
20088    .unwrap()
20089    .await
20090    .unwrap();
20091    pane.update_in(cx, |pane, window, cx| {
20092        pane.navigate_backward(window, cx);
20093    });
20094    cx.run_until_parked();
20095    pane.update(cx, |pane, cx| {
20096        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
20097        open_editor.update(cx, |editor, cx| {
20098            assert_eq!(
20099                editor.display_text(cx),
20100                "pub mod external {}",
20101                "External file is open now",
20102            );
20103        });
20104    });
20105    assert_language_servers_count(
20106        1,
20107        "After closing and reopening (with navigate back) of an external file, no extra language servers should appear",
20108        cx,
20109    );
20110
20111    cx.update(|_, cx| {
20112        workspace::reload(&workspace::Reload::default(), cx);
20113    });
20114    assert_language_servers_count(
20115        1,
20116        "After reloading the worktree with local and external files opened, only one project should be started",
20117        cx,
20118    );
20119}
20120
20121fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
20122    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
20123    point..point
20124}
20125
20126fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
20127    let (text, ranges) = marked_text_ranges(marked_text, true);
20128    assert_eq!(editor.text(cx), text);
20129    assert_eq!(
20130        editor.selections.ranges(cx),
20131        ranges,
20132        "Assert selections are {}",
20133        marked_text
20134    );
20135}
20136
20137pub fn handle_signature_help_request(
20138    cx: &mut EditorLspTestContext,
20139    mocked_response: lsp::SignatureHelp,
20140) -> impl Future<Output = ()> + use<> {
20141    let mut request =
20142        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
20143            let mocked_response = mocked_response.clone();
20144            async move { Ok(Some(mocked_response)) }
20145        });
20146
20147    async move {
20148        request.next().await;
20149    }
20150}
20151
20152/// Handle completion request passing a marked string specifying where the completion
20153/// should be triggered from using '|' character, what range should be replaced, and what completions
20154/// should be returned using '<' and '>' to delimit the range.
20155///
20156/// Also see `handle_completion_request_with_insert_and_replace`.
20157#[track_caller]
20158pub fn handle_completion_request(
20159    cx: &mut EditorLspTestContext,
20160    marked_string: &str,
20161    completions: Vec<&'static str>,
20162    counter: Arc<AtomicUsize>,
20163) -> impl Future<Output = ()> {
20164    let complete_from_marker: TextRangeMarker = '|'.into();
20165    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20166    let (_, mut marked_ranges) = marked_text_ranges_by(
20167        marked_string,
20168        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20169    );
20170
20171    let complete_from_position =
20172        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20173    let replace_range =
20174        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20175
20176    let mut request =
20177        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20178            let completions = completions.clone();
20179            counter.fetch_add(1, atomic::Ordering::Release);
20180            async move {
20181                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20182                assert_eq!(
20183                    params.text_document_position.position,
20184                    complete_from_position
20185                );
20186                Ok(Some(lsp::CompletionResponse::Array(
20187                    completions
20188                        .iter()
20189                        .map(|completion_text| lsp::CompletionItem {
20190                            label: completion_text.to_string(),
20191                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
20192                                range: replace_range,
20193                                new_text: completion_text.to_string(),
20194                            })),
20195                            ..Default::default()
20196                        })
20197                        .collect(),
20198                )))
20199            }
20200        });
20201
20202    async move {
20203        request.next().await;
20204    }
20205}
20206
20207/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
20208/// given instead, which also contains an `insert` range.
20209///
20210/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
20211/// that is, `replace_range.start..cursor_pos`.
20212pub fn handle_completion_request_with_insert_and_replace(
20213    cx: &mut EditorLspTestContext,
20214    marked_string: &str,
20215    completions: Vec<&'static str>,
20216    counter: Arc<AtomicUsize>,
20217) -> impl Future<Output = ()> {
20218    let complete_from_marker: TextRangeMarker = '|'.into();
20219    let replace_range_marker: TextRangeMarker = ('<', '>').into();
20220    let (_, mut marked_ranges) = marked_text_ranges_by(
20221        marked_string,
20222        vec![complete_from_marker.clone(), replace_range_marker.clone()],
20223    );
20224
20225    let complete_from_position =
20226        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
20227    let replace_range =
20228        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
20229
20230    let mut request =
20231        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
20232            let completions = completions.clone();
20233            counter.fetch_add(1, atomic::Ordering::Release);
20234            async move {
20235                assert_eq!(params.text_document_position.text_document.uri, url.clone());
20236                assert_eq!(
20237                    params.text_document_position.position, complete_from_position,
20238                    "marker `|` position doesn't match",
20239                );
20240                Ok(Some(lsp::CompletionResponse::Array(
20241                    completions
20242                        .iter()
20243                        .map(|completion_text| lsp::CompletionItem {
20244                            label: completion_text.to_string(),
20245                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
20246                                lsp::InsertReplaceEdit {
20247                                    insert: lsp::Range {
20248                                        start: replace_range.start,
20249                                        end: complete_from_position,
20250                                    },
20251                                    replace: replace_range,
20252                                    new_text: completion_text.to_string(),
20253                                },
20254                            )),
20255                            ..Default::default()
20256                        })
20257                        .collect(),
20258                )))
20259            }
20260        });
20261
20262    async move {
20263        request.next().await;
20264    }
20265}
20266
20267fn handle_resolve_completion_request(
20268    cx: &mut EditorLspTestContext,
20269    edits: Option<Vec<(&'static str, &'static str)>>,
20270) -> impl Future<Output = ()> {
20271    let edits = edits.map(|edits| {
20272        edits
20273            .iter()
20274            .map(|(marked_string, new_text)| {
20275                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
20276                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
20277                lsp::TextEdit::new(replace_range, new_text.to_string())
20278            })
20279            .collect::<Vec<_>>()
20280    });
20281
20282    let mut request =
20283        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
20284            let edits = edits.clone();
20285            async move {
20286                Ok(lsp::CompletionItem {
20287                    additional_text_edits: edits,
20288                    ..Default::default()
20289                })
20290            }
20291        });
20292
20293    async move {
20294        request.next().await;
20295    }
20296}
20297
20298pub(crate) fn update_test_language_settings(
20299    cx: &mut TestAppContext,
20300    f: impl Fn(&mut AllLanguageSettingsContent),
20301) {
20302    cx.update(|cx| {
20303        SettingsStore::update_global(cx, |store, cx| {
20304            store.update_user_settings::<AllLanguageSettings>(cx, f);
20305        });
20306    });
20307}
20308
20309pub(crate) fn update_test_project_settings(
20310    cx: &mut TestAppContext,
20311    f: impl Fn(&mut ProjectSettings),
20312) {
20313    cx.update(|cx| {
20314        SettingsStore::update_global(cx, |store, cx| {
20315            store.update_user_settings::<ProjectSettings>(cx, f);
20316        });
20317    });
20318}
20319
20320pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
20321    cx.update(|cx| {
20322        assets::Assets.load_test_fonts(cx);
20323        let store = SettingsStore::test(cx);
20324        cx.set_global(store);
20325        theme::init(theme::LoadThemes::JustBase, cx);
20326        release_channel::init(SemanticVersion::default(), cx);
20327        client::init_settings(cx);
20328        language::init(cx);
20329        Project::init_settings(cx);
20330        workspace::init_settings(cx);
20331        crate::init(cx);
20332    });
20333
20334    update_test_language_settings(cx, f);
20335}
20336
20337#[track_caller]
20338fn assert_hunk_revert(
20339    not_reverted_text_with_selections: &str,
20340    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
20341    expected_reverted_text_with_selections: &str,
20342    base_text: &str,
20343    cx: &mut EditorLspTestContext,
20344) {
20345    cx.set_state(not_reverted_text_with_selections);
20346    cx.set_head_text(base_text);
20347    cx.executor().run_until_parked();
20348
20349    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
20350        let snapshot = editor.snapshot(window, cx);
20351        let reverted_hunk_statuses = snapshot
20352            .buffer_snapshot
20353            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
20354            .map(|hunk| hunk.status().kind)
20355            .collect::<Vec<_>>();
20356
20357        editor.git_restore(&Default::default(), window, cx);
20358        reverted_hunk_statuses
20359    });
20360    cx.executor().run_until_parked();
20361    cx.assert_editor_state(expected_reverted_text_with_selections);
20362    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
20363}