editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  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        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.defaults.tab_size = NonZeroU32::new(4)
 2924    });
 2925
 2926    let language = Arc::new(
 2927        Language::new(
 2928            LanguageConfig::default(),
 2929            Some(tree_sitter_rust::LANGUAGE.into()),
 2930        )
 2931        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2932        .unwrap(),
 2933    );
 2934
 2935    let mut cx = EditorTestContext::new(cx).await;
 2936    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2937    cx.set_state(indoc! {"
 2938        fn a() {
 2939            if b {
 2940        \t ˇc
 2941            }
 2942        }
 2943    "});
 2944
 2945    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2946    cx.assert_editor_state(indoc! {"
 2947        fn a() {
 2948            if b {
 2949                ˇc
 2950            }
 2951        }
 2952    "});
 2953}
 2954
 2955#[gpui::test]
 2956async fn test_indent_outdent(cx: &mut TestAppContext) {
 2957    init_test(cx, |settings| {
 2958        settings.defaults.tab_size = NonZeroU32::new(4);
 2959    });
 2960
 2961    let mut cx = EditorTestContext::new(cx).await;
 2962
 2963    cx.set_state(indoc! {"
 2964          «oneˇ» «twoˇ»
 2965        three
 2966         four
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970            «oneˇ» «twoˇ»
 2971        three
 2972         four
 2973    "});
 2974
 2975    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2976    cx.assert_editor_state(indoc! {"
 2977        «oneˇ» «twoˇ»
 2978        three
 2979         four
 2980    "});
 2981
 2982    // select across line ending
 2983    cx.set_state(indoc! {"
 2984        one two
 2985        t«hree
 2986        ˇ» four
 2987    "});
 2988    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2989    cx.assert_editor_state(indoc! {"
 2990        one two
 2991            t«hree
 2992        ˇ» four
 2993    "});
 2994
 2995    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2996    cx.assert_editor_state(indoc! {"
 2997        one two
 2998        t«hree
 2999        ˇ» four
 3000    "});
 3001
 3002    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3003    cx.set_state(indoc! {"
 3004        one two
 3005        ˇthree
 3006            four
 3007    "});
 3008    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3009    cx.assert_editor_state(indoc! {"
 3010        one two
 3011            ˇthree
 3012            four
 3013    "});
 3014
 3015    cx.set_state(indoc! {"
 3016        one two
 3017        ˇ    three
 3018            four
 3019    "});
 3020    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3021    cx.assert_editor_state(indoc! {"
 3022        one two
 3023        ˇthree
 3024            four
 3025    "});
 3026}
 3027
 3028#[gpui::test]
 3029async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3030    init_test(cx, |settings| {
 3031        settings.defaults.hard_tabs = Some(true);
 3032    });
 3033
 3034    let mut cx = EditorTestContext::new(cx).await;
 3035
 3036    // select two ranges on one line
 3037    cx.set_state(indoc! {"
 3038        «oneˇ» «twoˇ»
 3039        three
 3040        four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        \t«oneˇ» «twoˇ»
 3045        three
 3046        four
 3047    "});
 3048    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        \t\t«oneˇ» «twoˇ»
 3051        three
 3052        four
 3053    "});
 3054    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3055    cx.assert_editor_state(indoc! {"
 3056        \t«oneˇ» «twoˇ»
 3057        three
 3058        four
 3059    "});
 3060    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3061    cx.assert_editor_state(indoc! {"
 3062        «oneˇ» «twoˇ»
 3063        three
 3064        four
 3065    "});
 3066
 3067    // select across a line ending
 3068    cx.set_state(indoc! {"
 3069        one two
 3070        t«hree
 3071        ˇ»four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        one two
 3076        \tt«hree
 3077        ˇ»four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        one two
 3082        \t\tt«hree
 3083        ˇ»four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        one two
 3088        \tt«hree
 3089        ˇ»four
 3090    "});
 3091    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3092    cx.assert_editor_state(indoc! {"
 3093        one two
 3094        t«hree
 3095        ˇ»four
 3096    "});
 3097
 3098    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3099    cx.set_state(indoc! {"
 3100        one two
 3101        ˇthree
 3102        four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        ˇthree
 3108        four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        \tˇthree
 3114        four
 3115    "});
 3116    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3117    cx.assert_editor_state(indoc! {"
 3118        one two
 3119        ˇthree
 3120        four
 3121    "});
 3122}
 3123
 3124#[gpui::test]
 3125fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3126    init_test(cx, |settings| {
 3127        settings.languages.extend([
 3128            (
 3129                "TOML".into(),
 3130                LanguageSettingsContent {
 3131                    tab_size: NonZeroU32::new(2),
 3132                    ..Default::default()
 3133                },
 3134            ),
 3135            (
 3136                "Rust".into(),
 3137                LanguageSettingsContent {
 3138                    tab_size: NonZeroU32::new(4),
 3139                    ..Default::default()
 3140                },
 3141            ),
 3142        ]);
 3143    });
 3144
 3145    let toml_language = Arc::new(Language::new(
 3146        LanguageConfig {
 3147            name: "TOML".into(),
 3148            ..Default::default()
 3149        },
 3150        None,
 3151    ));
 3152    let rust_language = Arc::new(Language::new(
 3153        LanguageConfig {
 3154            name: "Rust".into(),
 3155            ..Default::default()
 3156        },
 3157        None,
 3158    ));
 3159
 3160    let toml_buffer =
 3161        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3162    let rust_buffer =
 3163        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3164    let multibuffer = cx.new(|cx| {
 3165        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3166        multibuffer.push_excerpts(
 3167            toml_buffer.clone(),
 3168            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3169            cx,
 3170        );
 3171        multibuffer.push_excerpts(
 3172            rust_buffer.clone(),
 3173            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3174            cx,
 3175        );
 3176        multibuffer
 3177    });
 3178
 3179    cx.add_window(|window, cx| {
 3180        let mut editor = build_editor(multibuffer, window, cx);
 3181
 3182        assert_eq!(
 3183            editor.text(cx),
 3184            indoc! {"
 3185                a = 1
 3186                b = 2
 3187
 3188                const c: usize = 3;
 3189            "}
 3190        );
 3191
 3192        select_ranges(
 3193            &mut editor,
 3194            indoc! {"
 3195                «aˇ» = 1
 3196                b = 2
 3197
 3198                «const c:ˇ» usize = 3;
 3199            "},
 3200            window,
 3201            cx,
 3202        );
 3203
 3204        editor.tab(&Tab, window, cx);
 3205        assert_text_with_selections(
 3206            &mut editor,
 3207            indoc! {"
 3208                  «aˇ» = 1
 3209                b = 2
 3210
 3211                    «const c:ˇ» usize = 3;
 3212            "},
 3213            cx,
 3214        );
 3215        editor.backtab(&Backtab, window, cx);
 3216        assert_text_with_selections(
 3217            &mut editor,
 3218            indoc! {"
 3219                «aˇ» = 1
 3220                b = 2
 3221
 3222                «const c:ˇ» usize = 3;
 3223            "},
 3224            cx,
 3225        );
 3226
 3227        editor
 3228    });
 3229}
 3230
 3231#[gpui::test]
 3232async fn test_backspace(cx: &mut TestAppContext) {
 3233    init_test(cx, |_| {});
 3234
 3235    let mut cx = EditorTestContext::new(cx).await;
 3236
 3237    // Basic backspace
 3238    cx.set_state(indoc! {"
 3239        onˇe two three
 3240        fou«rˇ» five six
 3241        seven «ˇeight nine
 3242        »ten
 3243    "});
 3244    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3245    cx.assert_editor_state(indoc! {"
 3246        oˇe two three
 3247        fouˇ five six
 3248        seven ˇten
 3249    "});
 3250
 3251    // Test backspace inside and around indents
 3252    cx.set_state(indoc! {"
 3253        zero
 3254            ˇone
 3255                ˇtwo
 3256            ˇ ˇ ˇ  three
 3257        ˇ  ˇ  four
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        zero
 3262        ˇone
 3263            ˇtwo
 3264        ˇ  threeˇ  four
 3265    "});
 3266}
 3267
 3268#[gpui::test]
 3269async fn test_delete(cx: &mut TestAppContext) {
 3270    init_test(cx, |_| {});
 3271
 3272    let mut cx = EditorTestContext::new(cx).await;
 3273    cx.set_state(indoc! {"
 3274        onˇe two three
 3275        fou«rˇ» five six
 3276        seven «ˇeight nine
 3277        »ten
 3278    "});
 3279    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3280    cx.assert_editor_state(indoc! {"
 3281        onˇ two three
 3282        fouˇ five six
 3283        seven ˇten
 3284    "});
 3285}
 3286
 3287#[gpui::test]
 3288fn test_delete_line(cx: &mut TestAppContext) {
 3289    init_test(cx, |_| {});
 3290
 3291    let editor = cx.add_window(|window, cx| {
 3292        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3293        build_editor(buffer, window, cx)
 3294    });
 3295    _ = editor.update(cx, |editor, window, cx| {
 3296        editor.change_selections(None, window, cx, |s| {
 3297            s.select_display_ranges([
 3298                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3299                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3300                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3301            ])
 3302        });
 3303        editor.delete_line(&DeleteLine, window, cx);
 3304        assert_eq!(editor.display_text(cx), "ghi");
 3305        assert_eq!(
 3306            editor.selections.display_ranges(cx),
 3307            vec![
 3308                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3309                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3310            ]
 3311        );
 3312    });
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3322            ])
 3323        });
 3324        editor.delete_line(&DeleteLine, window, cx);
 3325        assert_eq!(editor.display_text(cx), "ghi\n");
 3326        assert_eq!(
 3327            editor.selections.display_ranges(cx),
 3328            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3329        );
 3330    });
 3331}
 3332
 3333#[gpui::test]
 3334fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3335    init_test(cx, |_| {});
 3336
 3337    cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3339        let mut editor = build_editor(buffer.clone(), window, cx);
 3340        let buffer = buffer.read(cx).as_singleton().unwrap();
 3341
 3342        assert_eq!(
 3343            editor.selections.ranges::<Point>(cx),
 3344            &[Point::new(0, 0)..Point::new(0, 0)]
 3345        );
 3346
 3347        // When on single line, replace newline at end by space
 3348        editor.join_lines(&JoinLines, window, cx);
 3349        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3350        assert_eq!(
 3351            editor.selections.ranges::<Point>(cx),
 3352            &[Point::new(0, 3)..Point::new(0, 3)]
 3353        );
 3354
 3355        // When multiple lines are selected, remove newlines that are spanned by the selection
 3356        editor.change_selections(None, window, cx, |s| {
 3357            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3358        });
 3359        editor.join_lines(&JoinLines, window, cx);
 3360        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3361        assert_eq!(
 3362            editor.selections.ranges::<Point>(cx),
 3363            &[Point::new(0, 11)..Point::new(0, 11)]
 3364        );
 3365
 3366        // Undo should be transactional
 3367        editor.undo(&Undo, window, cx);
 3368        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3369        assert_eq!(
 3370            editor.selections.ranges::<Point>(cx),
 3371            &[Point::new(0, 5)..Point::new(2, 2)]
 3372        );
 3373
 3374        // When joining an empty line don't insert a space
 3375        editor.change_selections(None, window, cx, |s| {
 3376            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3377        });
 3378        editor.join_lines(&JoinLines, window, cx);
 3379        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3380        assert_eq!(
 3381            editor.selections.ranges::<Point>(cx),
 3382            [Point::new(2, 3)..Point::new(2, 3)]
 3383        );
 3384
 3385        // We can remove trailing newlines
 3386        editor.join_lines(&JoinLines, window, cx);
 3387        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3388        assert_eq!(
 3389            editor.selections.ranges::<Point>(cx),
 3390            [Point::new(2, 3)..Point::new(2, 3)]
 3391        );
 3392
 3393        // We don't blow up on the last line
 3394        editor.join_lines(&JoinLines, window, cx);
 3395        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3396        assert_eq!(
 3397            editor.selections.ranges::<Point>(cx),
 3398            [Point::new(2, 3)..Point::new(2, 3)]
 3399        );
 3400
 3401        // reset to test indentation
 3402        editor.buffer.update(cx, |buffer, cx| {
 3403            buffer.edit(
 3404                [
 3405                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3406                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3407                ],
 3408                None,
 3409                cx,
 3410            )
 3411        });
 3412
 3413        // We remove any leading spaces
 3414        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3415        editor.change_selections(None, window, cx, |s| {
 3416            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3417        });
 3418        editor.join_lines(&JoinLines, window, cx);
 3419        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3420
 3421        // We don't insert a space for a line containing only spaces
 3422        editor.join_lines(&JoinLines, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3424
 3425        // We ignore any leading tabs
 3426        editor.join_lines(&JoinLines, window, cx);
 3427        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3428
 3429        editor
 3430    });
 3431}
 3432
 3433#[gpui::test]
 3434fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3435    init_test(cx, |_| {});
 3436
 3437    cx.add_window(|window, cx| {
 3438        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3439        let mut editor = build_editor(buffer.clone(), window, cx);
 3440        let buffer = buffer.read(cx).as_singleton().unwrap();
 3441
 3442        editor.change_selections(None, window, cx, |s| {
 3443            s.select_ranges([
 3444                Point::new(0, 2)..Point::new(1, 1),
 3445                Point::new(1, 2)..Point::new(1, 2),
 3446                Point::new(3, 1)..Point::new(3, 2),
 3447            ])
 3448        });
 3449
 3450        editor.join_lines(&JoinLines, window, cx);
 3451        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3452
 3453        assert_eq!(
 3454            editor.selections.ranges::<Point>(cx),
 3455            [
 3456                Point::new(0, 7)..Point::new(0, 7),
 3457                Point::new(1, 3)..Point::new(1, 3)
 3458            ]
 3459        );
 3460        editor
 3461    });
 3462}
 3463
 3464#[gpui::test]
 3465async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3466    init_test(cx, |_| {});
 3467
 3468    let mut cx = EditorTestContext::new(cx).await;
 3469
 3470    let diff_base = r#"
 3471        Line 0
 3472        Line 1
 3473        Line 2
 3474        Line 3
 3475        "#
 3476    .unindent();
 3477
 3478    cx.set_state(
 3479        &r#"
 3480        ˇLine 0
 3481        Line 1
 3482        Line 2
 3483        Line 3
 3484        "#
 3485        .unindent(),
 3486    );
 3487
 3488    cx.set_head_text(&diff_base);
 3489    executor.run_until_parked();
 3490
 3491    // Join lines
 3492    cx.update_editor(|editor, window, cx| {
 3493        editor.join_lines(&JoinLines, window, cx);
 3494    });
 3495    executor.run_until_parked();
 3496
 3497    cx.assert_editor_state(
 3498        &r#"
 3499        Line 0ˇ Line 1
 3500        Line 2
 3501        Line 3
 3502        "#
 3503        .unindent(),
 3504    );
 3505    // Join again
 3506    cx.update_editor(|editor, window, cx| {
 3507        editor.join_lines(&JoinLines, window, cx);
 3508    });
 3509    executor.run_until_parked();
 3510
 3511    cx.assert_editor_state(
 3512        &r#"
 3513        Line 0 Line 1ˇ Line 2
 3514        Line 3
 3515        "#
 3516        .unindent(),
 3517    );
 3518}
 3519
 3520#[gpui::test]
 3521async fn test_custom_newlines_cause_no_false_positive_diffs(
 3522    executor: BackgroundExecutor,
 3523    cx: &mut TestAppContext,
 3524) {
 3525    init_test(cx, |_| {});
 3526    let mut cx = EditorTestContext::new(cx).await;
 3527    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3528    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3529    executor.run_until_parked();
 3530
 3531    cx.update_editor(|editor, window, cx| {
 3532        let snapshot = editor.snapshot(window, cx);
 3533        assert_eq!(
 3534            snapshot
 3535                .buffer_snapshot
 3536                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3537                .collect::<Vec<_>>(),
 3538            Vec::new(),
 3539            "Should not have any diffs for files with custom newlines"
 3540        );
 3541    });
 3542}
 3543
 3544#[gpui::test]
 3545async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3546    init_test(cx, |_| {});
 3547
 3548    let mut cx = EditorTestContext::new(cx).await;
 3549
 3550    // Test sort_lines_case_insensitive()
 3551    cx.set_state(indoc! {"
 3552        «z
 3553        y
 3554        x
 3555        Z
 3556        Y
 3557        Xˇ»
 3558    "});
 3559    cx.update_editor(|e, window, cx| {
 3560        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3561    });
 3562    cx.assert_editor_state(indoc! {"
 3563        «x
 3564        X
 3565        y
 3566        Y
 3567        z
 3568        Zˇ»
 3569    "});
 3570
 3571    // Test reverse_lines()
 3572    cx.set_state(indoc! {"
 3573        «5
 3574        4
 3575        3
 3576        2
 3577        1ˇ»
 3578    "});
 3579    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «1
 3582        2
 3583        3
 3584        4
 3585        5ˇ»
 3586    "});
 3587
 3588    // Skip testing shuffle_line()
 3589
 3590    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3591    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3592
 3593    // Don't manipulate when cursor is on single line, but expand the selection
 3594    cx.set_state(indoc! {"
 3595        ddˇdd
 3596        ccc
 3597        bb
 3598        a
 3599    "});
 3600    cx.update_editor(|e, window, cx| {
 3601        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3602    });
 3603    cx.assert_editor_state(indoc! {"
 3604        «ddddˇ»
 3605        ccc
 3606        bb
 3607        a
 3608    "});
 3609
 3610    // Basic manipulate case
 3611    // Start selection moves to column 0
 3612    // End of selection shrinks to fit shorter line
 3613    cx.set_state(indoc! {"
 3614        dd«d
 3615        ccc
 3616        bb
 3617        aaaaaˇ»
 3618    "});
 3619    cx.update_editor(|e, window, cx| {
 3620        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3621    });
 3622    cx.assert_editor_state(indoc! {"
 3623        «aaaaa
 3624        bb
 3625        ccc
 3626        dddˇ»
 3627    "});
 3628
 3629    // Manipulate case with newlines
 3630    cx.set_state(indoc! {"
 3631        dd«d
 3632        ccc
 3633
 3634        bb
 3635        aaaaa
 3636
 3637        ˇ»
 3638    "});
 3639    cx.update_editor(|e, window, cx| {
 3640        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3641    });
 3642    cx.assert_editor_state(indoc! {"
 3643        «
 3644
 3645        aaaaa
 3646        bb
 3647        ccc
 3648        dddˇ»
 3649
 3650    "});
 3651
 3652    // Adding new line
 3653    cx.set_state(indoc! {"
 3654        aa«a
 3655        bbˇ»b
 3656    "});
 3657    cx.update_editor(|e, window, cx| {
 3658        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3659    });
 3660    cx.assert_editor_state(indoc! {"
 3661        «aaa
 3662        bbb
 3663        added_lineˇ»
 3664    "});
 3665
 3666    // Removing line
 3667    cx.set_state(indoc! {"
 3668        aa«a
 3669        bbbˇ»
 3670    "});
 3671    cx.update_editor(|e, window, cx| {
 3672        e.manipulate_lines(window, cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «aaaˇ»
 3678    "});
 3679
 3680    // Removing all lines
 3681    cx.set_state(indoc! {"
 3682        aa«a
 3683        bbbˇ»
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.manipulate_lines(window, cx, |lines| {
 3687            lines.drain(..);
 3688        })
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        ˇ
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Consider continuous selection as single selection
 3702    cx.set_state(indoc! {"
 3703        Aaa«aa
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «Aaaaa
 3713        ccc
 3714        bb
 3715        aaaaaˇ»
 3716    "});
 3717
 3718    cx.set_state(indoc! {"
 3719        Aaa«aa
 3720        cˇ»c«c
 3721        bb
 3722        aaaˇ»aa
 3723    "});
 3724    cx.update_editor(|e, window, cx| {
 3725        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3726    });
 3727    cx.assert_editor_state(indoc! {"
 3728        «Aaaaa
 3729        ccc
 3730        bbˇ»
 3731    "});
 3732
 3733    // Consider non continuous selection as distinct dedup operations
 3734    cx.set_state(indoc! {"
 3735        «aaaaa
 3736        bb
 3737        aaaaa
 3738        aaaaaˇ»
 3739
 3740        aaa«aaˇ»
 3741    "});
 3742    cx.update_editor(|e, window, cx| {
 3743        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        «aaaaa
 3747        bbˇ»
 3748
 3749        «aaaaaˇ»
 3750    "});
 3751}
 3752
 3753#[gpui::test]
 3754async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3755    init_test(cx, |_| {});
 3756
 3757    let mut cx = EditorTestContext::new(cx).await;
 3758
 3759    cx.set_state(indoc! {"
 3760        «Aaa
 3761        aAa
 3762        Aaaˇ»
 3763    "});
 3764    cx.update_editor(|e, window, cx| {
 3765        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3766    });
 3767    cx.assert_editor_state(indoc! {"
 3768        «Aaa
 3769        aAaˇ»
 3770    "});
 3771
 3772    cx.set_state(indoc! {"
 3773        «Aaa
 3774        aAa
 3775        aaAˇ»
 3776    "});
 3777    cx.update_editor(|e, window, cx| {
 3778        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3779    });
 3780    cx.assert_editor_state(indoc! {"
 3781        «Aaaˇ»
 3782    "});
 3783}
 3784
 3785#[gpui::test]
 3786async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3787    init_test(cx, |_| {});
 3788
 3789    let mut cx = EditorTestContext::new(cx).await;
 3790
 3791    // Manipulate with multiple selections on a single line
 3792    cx.set_state(indoc! {"
 3793        dd«dd
 3794        cˇ»c«c
 3795        bb
 3796        aaaˇ»aa
 3797    "});
 3798    cx.update_editor(|e, window, cx| {
 3799        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3800    });
 3801    cx.assert_editor_state(indoc! {"
 3802        «aaaaa
 3803        bb
 3804        ccc
 3805        ddddˇ»
 3806    "});
 3807
 3808    // Manipulate with multiple disjoin selections
 3809    cx.set_state(indoc! {"
 3810 3811        4
 3812        3
 3813        2
 3814        1ˇ»
 3815
 3816        dd«dd
 3817        ccc
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «1
 3826        2
 3827        3
 3828        4
 3829        5ˇ»
 3830
 3831        «aaaaa
 3832        bb
 3833        ccc
 3834        ddddˇ»
 3835    "});
 3836
 3837    // Adding lines on each selection
 3838    cx.set_state(indoc! {"
 3839 3840        1ˇ»
 3841
 3842        bb«bb
 3843        aaaˇ»aa
 3844    "});
 3845    cx.update_editor(|e, window, cx| {
 3846        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3847    });
 3848    cx.assert_editor_state(indoc! {"
 3849        «2
 3850        1
 3851        added lineˇ»
 3852
 3853        «bbbb
 3854        aaaaa
 3855        added lineˇ»
 3856    "});
 3857
 3858    // Removing lines on each selection
 3859    cx.set_state(indoc! {"
 3860 3861        1ˇ»
 3862
 3863        bb«bb
 3864        aaaˇ»aa
 3865    "});
 3866    cx.update_editor(|e, window, cx| {
 3867        e.manipulate_lines(window, cx, |lines| {
 3868            lines.pop();
 3869        })
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2ˇ»
 3873
 3874        «bbbbˇ»
 3875    "});
 3876}
 3877
 3878#[gpui::test]
 3879async fn test_toggle_case(cx: &mut TestAppContext) {
 3880    init_test(cx, |_| {});
 3881
 3882    let mut cx = EditorTestContext::new(cx).await;
 3883
 3884    // If all lower case -> upper case
 3885    cx.set_state(indoc! {"
 3886        «hello worldˇ»
 3887    "});
 3888    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3889    cx.assert_editor_state(indoc! {"
 3890        «HELLO WORLDˇ»
 3891    "});
 3892
 3893    // If all upper case -> lower case
 3894    cx.set_state(indoc! {"
 3895        «HELLO WORLDˇ»
 3896    "});
 3897    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3898    cx.assert_editor_state(indoc! {"
 3899        «hello worldˇ»
 3900    "});
 3901
 3902    // If any upper case characters are identified -> lower case
 3903    // This matches JetBrains IDEs
 3904    cx.set_state(indoc! {"
 3905        «hEllo worldˇ»
 3906    "});
 3907    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3908    cx.assert_editor_state(indoc! {"
 3909        «hello worldˇ»
 3910    "});
 3911}
 3912
 3913#[gpui::test]
 3914async fn test_manipulate_text(cx: &mut TestAppContext) {
 3915    init_test(cx, |_| {});
 3916
 3917    let mut cx = EditorTestContext::new(cx).await;
 3918
 3919    // Test convert_to_upper_case()
 3920    cx.set_state(indoc! {"
 3921        «hello worldˇ»
 3922    "});
 3923    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3924    cx.assert_editor_state(indoc! {"
 3925        «HELLO WORLDˇ»
 3926    "});
 3927
 3928    // Test convert_to_lower_case()
 3929    cx.set_state(indoc! {"
 3930        «HELLO WORLDˇ»
 3931    "});
 3932    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3933    cx.assert_editor_state(indoc! {"
 3934        «hello worldˇ»
 3935    "});
 3936
 3937    // Test multiple line, single selection case
 3938    cx.set_state(indoc! {"
 3939        «The quick brown
 3940        fox jumps over
 3941        the lazy dogˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3944    cx.assert_editor_state(indoc! {"
 3945        «The Quick Brown
 3946        Fox Jumps Over
 3947        The Lazy Dogˇ»
 3948    "});
 3949
 3950    // Test multiple line, single selection case
 3951    cx.set_state(indoc! {"
 3952        «The quick brown
 3953        fox jumps over
 3954        the lazy dogˇ»
 3955    "});
 3956    cx.update_editor(|e, window, cx| {
 3957        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3958    });
 3959    cx.assert_editor_state(indoc! {"
 3960        «TheQuickBrown
 3961        FoxJumpsOver
 3962        TheLazyDogˇ»
 3963    "});
 3964
 3965    // From here on out, test more complex cases of manipulate_text()
 3966
 3967    // Test no selection case - should affect words cursors are in
 3968    // Cursor at beginning, middle, and end of word
 3969    cx.set_state(indoc! {"
 3970        ˇhello big beauˇtiful worldˇ
 3971    "});
 3972    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3973    cx.assert_editor_state(indoc! {"
 3974        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3975    "});
 3976
 3977    // Test multiple selections on a single line and across multiple lines
 3978    cx.set_state(indoc! {"
 3979        «Theˇ» quick «brown
 3980        foxˇ» jumps «overˇ»
 3981        the «lazyˇ» dog
 3982    "});
 3983    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3984    cx.assert_editor_state(indoc! {"
 3985        «THEˇ» quick «BROWN
 3986        FOXˇ» jumps «OVERˇ»
 3987        the «LAZYˇ» dog
 3988    "});
 3989
 3990    // Test case where text length grows
 3991    cx.set_state(indoc! {"
 3992        «tschüߡ»
 3993    "});
 3994    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3995    cx.assert_editor_state(indoc! {"
 3996        «TSCHÜSSˇ»
 3997    "});
 3998
 3999    // Test to make sure we don't crash when text shrinks
 4000    cx.set_state(indoc! {"
 4001        aaa_bbbˇ
 4002    "});
 4003    cx.update_editor(|e, window, cx| {
 4004        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4005    });
 4006    cx.assert_editor_state(indoc! {"
 4007        «aaaBbbˇ»
 4008    "});
 4009
 4010    // Test to make sure we all aware of the fact that each word can grow and shrink
 4011    // Final selections should be aware of this fact
 4012    cx.set_state(indoc! {"
 4013        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4014    "});
 4015    cx.update_editor(|e, window, cx| {
 4016        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4017    });
 4018    cx.assert_editor_state(indoc! {"
 4019        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4020    "});
 4021
 4022    cx.set_state(indoc! {"
 4023        «hElLo, WoRld!ˇ»
 4024    "});
 4025    cx.update_editor(|e, window, cx| {
 4026        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4027    });
 4028    cx.assert_editor_state(indoc! {"
 4029        «HeLlO, wOrLD!ˇ»
 4030    "});
 4031}
 4032
 4033#[gpui::test]
 4034fn test_duplicate_line(cx: &mut TestAppContext) {
 4035    init_test(cx, |_| {});
 4036
 4037    let editor = cx.add_window(|window, cx| {
 4038        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4039        build_editor(buffer, window, cx)
 4040    });
 4041    _ = editor.update(cx, |editor, window, cx| {
 4042        editor.change_selections(None, window, cx, |s| {
 4043            s.select_display_ranges([
 4044                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4045                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4046                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4047                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4048            ])
 4049        });
 4050        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4051        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4052        assert_eq!(
 4053            editor.selections.display_ranges(cx),
 4054            vec![
 4055                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4056                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4057                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4058                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4059            ]
 4060        );
 4061    });
 4062
 4063    let editor = cx.add_window(|window, cx| {
 4064        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4065        build_editor(buffer, window, cx)
 4066    });
 4067    _ = editor.update(cx, |editor, window, cx| {
 4068        editor.change_selections(None, window, cx, |s| {
 4069            s.select_display_ranges([
 4070                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4071                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4072            ])
 4073        });
 4074        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4075        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4076        assert_eq!(
 4077            editor.selections.display_ranges(cx),
 4078            vec![
 4079                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4080                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4081            ]
 4082        );
 4083    });
 4084
 4085    // With `move_upwards` the selections stay in place, except for
 4086    // the lines inserted above them
 4087    let editor = cx.add_window(|window, cx| {
 4088        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4089        build_editor(buffer, window, cx)
 4090    });
 4091    _ = editor.update(cx, |editor, window, cx| {
 4092        editor.change_selections(None, window, cx, |s| {
 4093            s.select_display_ranges([
 4094                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4095                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4096                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4097                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4098            ])
 4099        });
 4100        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4101        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4102        assert_eq!(
 4103            editor.selections.display_ranges(cx),
 4104            vec![
 4105                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4106                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4107                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4108                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4109            ]
 4110        );
 4111    });
 4112
 4113    let editor = cx.add_window(|window, cx| {
 4114        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4115        build_editor(buffer, window, cx)
 4116    });
 4117    _ = editor.update(cx, |editor, window, cx| {
 4118        editor.change_selections(None, window, cx, |s| {
 4119            s.select_display_ranges([
 4120                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4121                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4122            ])
 4123        });
 4124        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4125        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4126        assert_eq!(
 4127            editor.selections.display_ranges(cx),
 4128            vec![
 4129                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4130                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4131            ]
 4132        );
 4133    });
 4134
 4135    let editor = cx.add_window(|window, cx| {
 4136        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4137        build_editor(buffer, window, cx)
 4138    });
 4139    _ = editor.update(cx, |editor, window, cx| {
 4140        editor.change_selections(None, window, cx, |s| {
 4141            s.select_display_ranges([
 4142                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4143                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4144            ])
 4145        });
 4146        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4147        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4148        assert_eq!(
 4149            editor.selections.display_ranges(cx),
 4150            vec![
 4151                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4152                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4153            ]
 4154        );
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159fn test_move_line_up_down(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let editor = cx.add_window(|window, cx| {
 4163        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4164        build_editor(buffer, window, cx)
 4165    });
 4166    _ = editor.update(cx, |editor, window, cx| {
 4167        editor.fold_creases(
 4168            vec![
 4169                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4170                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4171                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4172            ],
 4173            true,
 4174            window,
 4175            cx,
 4176        );
 4177        editor.change_selections(None, window, cx, |s| {
 4178            s.select_display_ranges([
 4179                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4180                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4181                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4182                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4183            ])
 4184        });
 4185        assert_eq!(
 4186            editor.display_text(cx),
 4187            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4188        );
 4189
 4190        editor.move_line_up(&MoveLineUp, window, cx);
 4191        assert_eq!(
 4192            editor.display_text(cx),
 4193            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4194        );
 4195        assert_eq!(
 4196            editor.selections.display_ranges(cx),
 4197            vec![
 4198                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4199                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4200                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4201                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4202            ]
 4203        );
 4204    });
 4205
 4206    _ = editor.update(cx, |editor, window, cx| {
 4207        editor.move_line_down(&MoveLineDown, window, cx);
 4208        assert_eq!(
 4209            editor.display_text(cx),
 4210            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4211        );
 4212        assert_eq!(
 4213            editor.selections.display_ranges(cx),
 4214            vec![
 4215                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4216                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4217                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4218                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4219            ]
 4220        );
 4221    });
 4222
 4223    _ = editor.update(cx, |editor, window, cx| {
 4224        editor.move_line_down(&MoveLineDown, window, cx);
 4225        assert_eq!(
 4226            editor.display_text(cx),
 4227            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4228        );
 4229        assert_eq!(
 4230            editor.selections.display_ranges(cx),
 4231            vec![
 4232                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4233                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4234                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4235                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4236            ]
 4237        );
 4238    });
 4239
 4240    _ = editor.update(cx, |editor, window, cx| {
 4241        editor.move_line_up(&MoveLineUp, window, cx);
 4242        assert_eq!(
 4243            editor.display_text(cx),
 4244            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4245        );
 4246        assert_eq!(
 4247            editor.selections.display_ranges(cx),
 4248            vec![
 4249                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4250                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4251                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4252                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4253            ]
 4254        );
 4255    });
 4256}
 4257
 4258#[gpui::test]
 4259fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4260    init_test(cx, |_| {});
 4261
 4262    let editor = cx.add_window(|window, cx| {
 4263        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4264        build_editor(buffer, window, cx)
 4265    });
 4266    _ = editor.update(cx, |editor, window, cx| {
 4267        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4268        editor.insert_blocks(
 4269            [BlockProperties {
 4270                style: BlockStyle::Fixed,
 4271                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4272                height: Some(1),
 4273                render: Arc::new(|_| div().into_any()),
 4274                priority: 0,
 4275            }],
 4276            Some(Autoscroll::fit()),
 4277            cx,
 4278        );
 4279        editor.change_selections(None, window, cx, |s| {
 4280            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4281        });
 4282        editor.move_line_down(&MoveLineDown, window, cx);
 4283    });
 4284}
 4285
 4286#[gpui::test]
 4287async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4288    init_test(cx, |_| {});
 4289
 4290    let mut cx = EditorTestContext::new(cx).await;
 4291    cx.set_state(
 4292        &"
 4293            ˇzero
 4294            one
 4295            two
 4296            three
 4297            four
 4298            five
 4299        "
 4300        .unindent(),
 4301    );
 4302
 4303    // Create a four-line block that replaces three lines of text.
 4304    cx.update_editor(|editor, window, cx| {
 4305        let snapshot = editor.snapshot(window, cx);
 4306        let snapshot = &snapshot.buffer_snapshot;
 4307        let placement = BlockPlacement::Replace(
 4308            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4309        );
 4310        editor.insert_blocks(
 4311            [BlockProperties {
 4312                placement,
 4313                height: Some(4),
 4314                style: BlockStyle::Sticky,
 4315                render: Arc::new(|_| gpui::div().into_any_element()),
 4316                priority: 0,
 4317            }],
 4318            None,
 4319            cx,
 4320        );
 4321    });
 4322
 4323    // Move down so that the cursor touches the block.
 4324    cx.update_editor(|editor, window, cx| {
 4325        editor.move_down(&Default::default(), window, cx);
 4326    });
 4327    cx.assert_editor_state(
 4328        &"
 4329            zero
 4330            «one
 4331            two
 4332            threeˇ»
 4333            four
 4334            five
 4335        "
 4336        .unindent(),
 4337    );
 4338
 4339    // Move down past the block.
 4340    cx.update_editor(|editor, window, cx| {
 4341        editor.move_down(&Default::default(), window, cx);
 4342    });
 4343    cx.assert_editor_state(
 4344        &"
 4345            zero
 4346            one
 4347            two
 4348            three
 4349            ˇfour
 4350            five
 4351        "
 4352        .unindent(),
 4353    );
 4354}
 4355
 4356#[gpui::test]
 4357fn test_transpose(cx: &mut TestAppContext) {
 4358    init_test(cx, |_| {});
 4359
 4360    _ = cx.add_window(|window, cx| {
 4361        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4362        editor.set_style(EditorStyle::default(), window, cx);
 4363        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4364        editor.transpose(&Default::default(), window, cx);
 4365        assert_eq!(editor.text(cx), "bac");
 4366        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4367
 4368        editor.transpose(&Default::default(), window, cx);
 4369        assert_eq!(editor.text(cx), "bca");
 4370        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4371
 4372        editor.transpose(&Default::default(), window, cx);
 4373        assert_eq!(editor.text(cx), "bac");
 4374        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4375
 4376        editor
 4377    });
 4378
 4379    _ = cx.add_window(|window, cx| {
 4380        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4381        editor.set_style(EditorStyle::default(), window, cx);
 4382        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4383        editor.transpose(&Default::default(), window, cx);
 4384        assert_eq!(editor.text(cx), "acb\nde");
 4385        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4386
 4387        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4388        editor.transpose(&Default::default(), window, cx);
 4389        assert_eq!(editor.text(cx), "acbd\ne");
 4390        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4391
 4392        editor.transpose(&Default::default(), window, cx);
 4393        assert_eq!(editor.text(cx), "acbde\n");
 4394        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4395
 4396        editor.transpose(&Default::default(), window, cx);
 4397        assert_eq!(editor.text(cx), "acbd\ne");
 4398        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4399
 4400        editor
 4401    });
 4402
 4403    _ = cx.add_window(|window, cx| {
 4404        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4405        editor.set_style(EditorStyle::default(), window, cx);
 4406        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4407        editor.transpose(&Default::default(), window, cx);
 4408        assert_eq!(editor.text(cx), "bacd\ne");
 4409        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4410
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "bcade\n");
 4413        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "bcda\ne");
 4417        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4418
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "bcade\n");
 4421        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4422
 4423        editor.transpose(&Default::default(), window, cx);
 4424        assert_eq!(editor.text(cx), "bcaed\n");
 4425        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4426
 4427        editor
 4428    });
 4429
 4430    _ = cx.add_window(|window, cx| {
 4431        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4432        editor.set_style(EditorStyle::default(), window, cx);
 4433        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "🏀🍐✋");
 4436        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4437
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "🏀✋🍐");
 4440        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4441
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "🏀🍐✋");
 4444        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4445
 4446        editor
 4447    });
 4448}
 4449
 4450#[gpui::test]
 4451async fn test_rewrap(cx: &mut TestAppContext) {
 4452    init_test(cx, |settings| {
 4453        settings.languages.extend([
 4454            (
 4455                "Markdown".into(),
 4456                LanguageSettingsContent {
 4457                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4458                    ..Default::default()
 4459                },
 4460            ),
 4461            (
 4462                "Plain Text".into(),
 4463                LanguageSettingsContent {
 4464                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4465                    ..Default::default()
 4466                },
 4467            ),
 4468        ])
 4469    });
 4470
 4471    let mut cx = EditorTestContext::new(cx).await;
 4472
 4473    let language_with_c_comments = Arc::new(Language::new(
 4474        LanguageConfig {
 4475            line_comments: vec!["// ".into()],
 4476            ..LanguageConfig::default()
 4477        },
 4478        None,
 4479    ));
 4480    let language_with_pound_comments = Arc::new(Language::new(
 4481        LanguageConfig {
 4482            line_comments: vec!["# ".into()],
 4483            ..LanguageConfig::default()
 4484        },
 4485        None,
 4486    ));
 4487    let markdown_language = Arc::new(Language::new(
 4488        LanguageConfig {
 4489            name: "Markdown".into(),
 4490            ..LanguageConfig::default()
 4491        },
 4492        None,
 4493    ));
 4494    let language_with_doc_comments = Arc::new(Language::new(
 4495        LanguageConfig {
 4496            line_comments: vec!["// ".into(), "/// ".into()],
 4497            ..LanguageConfig::default()
 4498        },
 4499        Some(tree_sitter_rust::LANGUAGE.into()),
 4500    ));
 4501
 4502    let plaintext_language = Arc::new(Language::new(
 4503        LanguageConfig {
 4504            name: "Plain Text".into(),
 4505            ..LanguageConfig::default()
 4506        },
 4507        None,
 4508    ));
 4509
 4510    assert_rewrap(
 4511        indoc! {"
 4512            // ˇ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.
 4513        "},
 4514        indoc! {"
 4515            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4516            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4517            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4518            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4519            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4520            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4521            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4522            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4523            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4524            // porttitor id. Aliquam id accumsan eros.
 4525        "},
 4526        language_with_c_comments.clone(),
 4527        &mut cx,
 4528    );
 4529
 4530    // Test that rewrapping works inside of a selection
 4531    assert_rewrap(
 4532        indoc! {"
 4533            «// 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.ˇ»
 4534        "},
 4535        indoc! {"
 4536            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4537            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4538            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4539            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4540            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4541            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4542            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4543            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4544            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4545            // porttitor id. Aliquam id accumsan eros.ˇ»
 4546        "},
 4547        language_with_c_comments.clone(),
 4548        &mut cx,
 4549    );
 4550
 4551    // Test that cursors that expand to the same region are collapsed.
 4552    assert_rewrap(
 4553        indoc! {"
 4554            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4555            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4556            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4557            // ˇ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.
 4558        "},
 4559        indoc! {"
 4560            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4561            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4562            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4563            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4564            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4565            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4566            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4567            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4568            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4569            // porttitor id. Aliquam id accumsan eros.
 4570        "},
 4571        language_with_c_comments.clone(),
 4572        &mut cx,
 4573    );
 4574
 4575    // Test that non-contiguous selections are treated separately.
 4576    assert_rewrap(
 4577        indoc! {"
 4578            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4579            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4580            //
 4581            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4582            // ˇ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.
 4583        "},
 4584        indoc! {"
 4585            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4586            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4587            // auctor, eu lacinia sapien scelerisque.
 4588            //
 4589            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4590            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4591            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4592            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4593            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4594            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4595            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4596        "},
 4597        language_with_c_comments.clone(),
 4598        &mut cx,
 4599    );
 4600
 4601    // Test that different comment prefixes are supported.
 4602    assert_rewrap(
 4603        indoc! {"
 4604            # ˇ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.
 4605        "},
 4606        indoc! {"
 4607            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4608            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4609            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4610            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4611            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4612            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4613            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4614            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4615            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4616            # accumsan eros.
 4617        "},
 4618        language_with_pound_comments.clone(),
 4619        &mut cx,
 4620    );
 4621
 4622    // Test that rewrapping is ignored outside of comments in most languages.
 4623    assert_rewrap(
 4624        indoc! {"
 4625            /// Adds two numbers.
 4626            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4627            fn add(a: u32, b: u32) -> u32 {
 4628                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ˇ
 4629            }
 4630        "},
 4631        indoc! {"
 4632            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4633            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4634            fn add(a: u32, b: u32) -> u32 {
 4635                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ˇ
 4636            }
 4637        "},
 4638        language_with_doc_comments.clone(),
 4639        &mut cx,
 4640    );
 4641
 4642    // Test that rewrapping works in Markdown and Plain Text languages.
 4643    assert_rewrap(
 4644        indoc! {"
 4645            # Hello
 4646
 4647            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.
 4648        "},
 4649        indoc! {"
 4650            # Hello
 4651
 4652            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4653            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4654            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4655            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4656            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4657            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4658            Integer sit amet scelerisque nisi.
 4659        "},
 4660        markdown_language,
 4661        &mut cx,
 4662    );
 4663
 4664    assert_rewrap(
 4665        indoc! {"
 4666            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.
 4667        "},
 4668        indoc! {"
 4669            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4670            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4671            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4672            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4673            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4674            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4675            Integer sit amet scelerisque nisi.
 4676        "},
 4677        plaintext_language,
 4678        &mut cx,
 4679    );
 4680
 4681    // Test rewrapping unaligned comments in a selection.
 4682    assert_rewrap(
 4683        indoc! {"
 4684            fn foo() {
 4685                if true {
 4686            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4687            // Praesent semper egestas tellus id dignissim.ˇ»
 4688                    do_something();
 4689                } else {
 4690                    //
 4691                }
 4692            }
 4693        "},
 4694        indoc! {"
 4695            fn foo() {
 4696                if true {
 4697            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4698                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4699                    // egestas tellus id dignissim.ˇ»
 4700                    do_something();
 4701                } else {
 4702                    //
 4703                }
 4704            }
 4705        "},
 4706        language_with_doc_comments.clone(),
 4707        &mut cx,
 4708    );
 4709
 4710    assert_rewrap(
 4711        indoc! {"
 4712            fn foo() {
 4713                if true {
 4714            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4715            // Praesent semper egestas tellus id dignissim.»
 4716                    do_something();
 4717                } else {
 4718                    //
 4719                }
 4720
 4721            }
 4722        "},
 4723        indoc! {"
 4724            fn foo() {
 4725                if true {
 4726            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4727                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4728                    // egestas tellus id dignissim.»
 4729                    do_something();
 4730                } else {
 4731                    //
 4732                }
 4733
 4734            }
 4735        "},
 4736        language_with_doc_comments.clone(),
 4737        &mut cx,
 4738    );
 4739
 4740    #[track_caller]
 4741    fn assert_rewrap(
 4742        unwrapped_text: &str,
 4743        wrapped_text: &str,
 4744        language: Arc<Language>,
 4745        cx: &mut EditorTestContext,
 4746    ) {
 4747        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4748        cx.set_state(unwrapped_text);
 4749        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4750        cx.assert_editor_state(wrapped_text);
 4751    }
 4752}
 4753
 4754#[gpui::test]
 4755async fn test_hard_wrap(cx: &mut TestAppContext) {
 4756    init_test(cx, |_| {});
 4757    let mut cx = EditorTestContext::new(cx).await;
 4758
 4759    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4760    cx.update_editor(|editor, _, cx| {
 4761        editor.set_hard_wrap(Some(14), cx);
 4762    });
 4763
 4764    cx.set_state(indoc!(
 4765        "
 4766        one two three ˇ
 4767        "
 4768    ));
 4769    cx.simulate_input("four");
 4770    cx.run_until_parked();
 4771
 4772    cx.assert_editor_state(indoc!(
 4773        "
 4774        one two three
 4775        fourˇ
 4776        "
 4777    ));
 4778
 4779    cx.update_editor(|editor, window, cx| {
 4780        editor.newline(&Default::default(), window, cx);
 4781    });
 4782    cx.run_until_parked();
 4783    cx.assert_editor_state(indoc!(
 4784        "
 4785        one two three
 4786        four
 4787        ˇ
 4788        "
 4789    ));
 4790
 4791    cx.simulate_input("five");
 4792    cx.run_until_parked();
 4793    cx.assert_editor_state(indoc!(
 4794        "
 4795        one two three
 4796        four
 4797        fiveˇ
 4798        "
 4799    ));
 4800
 4801    cx.update_editor(|editor, window, cx| {
 4802        editor.newline(&Default::default(), window, cx);
 4803    });
 4804    cx.run_until_parked();
 4805    cx.simulate_input("# ");
 4806    cx.run_until_parked();
 4807    cx.assert_editor_state(indoc!(
 4808        "
 4809        one two three
 4810        four
 4811        five
 4812        # ˇ
 4813        "
 4814    ));
 4815
 4816    cx.update_editor(|editor, window, cx| {
 4817        editor.newline(&Default::default(), window, cx);
 4818    });
 4819    cx.run_until_parked();
 4820    cx.assert_editor_state(indoc!(
 4821        "
 4822        one two three
 4823        four
 4824        five
 4825        #\x20
 4826 4827        "
 4828    ));
 4829
 4830    cx.simulate_input(" 6");
 4831    cx.run_until_parked();
 4832    cx.assert_editor_state(indoc!(
 4833        "
 4834        one two three
 4835        four
 4836        five
 4837        #
 4838        # 6ˇ
 4839        "
 4840    ));
 4841}
 4842
 4843#[gpui::test]
 4844async fn test_clipboard(cx: &mut TestAppContext) {
 4845    init_test(cx, |_| {});
 4846
 4847    let mut cx = EditorTestContext::new(cx).await;
 4848
 4849    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4850    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4851    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4852
 4853    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4854    cx.set_state("two ˇfour ˇsix ˇ");
 4855    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4856    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4857
 4858    // Paste again but with only two cursors. Since the number of cursors doesn't
 4859    // match the number of slices in the clipboard, the entire clipboard text
 4860    // is pasted at each cursor.
 4861    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4862    cx.update_editor(|e, window, cx| {
 4863        e.handle_input("( ", window, cx);
 4864        e.paste(&Paste, window, cx);
 4865        e.handle_input(") ", window, cx);
 4866    });
 4867    cx.assert_editor_state(
 4868        &([
 4869            "( one✅ ",
 4870            "three ",
 4871            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4872            "three ",
 4873            "five ) ˇ",
 4874        ]
 4875        .join("\n")),
 4876    );
 4877
 4878    // Cut with three selections, one of which is full-line.
 4879    cx.set_state(indoc! {"
 4880        1«2ˇ»3
 4881        4ˇ567
 4882        «8ˇ»9"});
 4883    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4884    cx.assert_editor_state(indoc! {"
 4885        1ˇ3
 4886        ˇ9"});
 4887
 4888    // Paste with three selections, noticing how the copied selection that was full-line
 4889    // gets inserted before the second cursor.
 4890    cx.set_state(indoc! {"
 4891        1ˇ3
 4892 4893        «oˇ»ne"});
 4894    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4895    cx.assert_editor_state(indoc! {"
 4896        12ˇ3
 4897        4567
 4898 4899        8ˇne"});
 4900
 4901    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4902    cx.set_state(indoc! {"
 4903        The quick brown
 4904        fox juˇmps over
 4905        the lazy dog"});
 4906    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4907    assert_eq!(
 4908        cx.read_from_clipboard()
 4909            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4910        Some("fox jumps over\n".to_string())
 4911    );
 4912
 4913    // Paste with three selections, noticing how the copied full-line selection is inserted
 4914    // before the empty selections but replaces the selection that is non-empty.
 4915    cx.set_state(indoc! {"
 4916        Tˇhe quick brown
 4917        «foˇ»x jumps over
 4918        tˇhe lazy dog"});
 4919    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4920    cx.assert_editor_state(indoc! {"
 4921        fox jumps over
 4922        Tˇhe quick brown
 4923        fox jumps over
 4924        ˇx jumps over
 4925        fox jumps over
 4926        tˇhe lazy dog"});
 4927}
 4928
 4929#[gpui::test]
 4930async fn test_copy_trim(cx: &mut TestAppContext) {
 4931    init_test(cx, |_| {});
 4932
 4933    let mut cx = EditorTestContext::new(cx).await;
 4934    cx.set_state(
 4935        r#"            «for selection in selections.iter() {
 4936            let mut start = selection.start;
 4937            let mut end = selection.end;
 4938            let is_entire_line = selection.is_empty();
 4939            if is_entire_line {
 4940                start = Point::new(start.row, 0);ˇ»
 4941                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4942            }
 4943        "#,
 4944    );
 4945    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4946    assert_eq!(
 4947        cx.read_from_clipboard()
 4948            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4949        Some(
 4950            "for selection in selections.iter() {
 4951            let mut start = selection.start;
 4952            let mut end = selection.end;
 4953            let is_entire_line = selection.is_empty();
 4954            if is_entire_line {
 4955                start = Point::new(start.row, 0);"
 4956                .to_string()
 4957        ),
 4958        "Regular copying preserves all indentation selected",
 4959    );
 4960    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4961    assert_eq!(
 4962        cx.read_from_clipboard()
 4963            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4964        Some(
 4965            "for selection in selections.iter() {
 4966let mut start = selection.start;
 4967let mut end = selection.end;
 4968let is_entire_line = selection.is_empty();
 4969if is_entire_line {
 4970    start = Point::new(start.row, 0);"
 4971                .to_string()
 4972        ),
 4973        "Copying with stripping should strip all leading whitespaces"
 4974    );
 4975
 4976    cx.set_state(
 4977        r#"       «     for selection in selections.iter() {
 4978            let mut start = selection.start;
 4979            let mut end = selection.end;
 4980            let is_entire_line = selection.is_empty();
 4981            if is_entire_line {
 4982                start = Point::new(start.row, 0);ˇ»
 4983                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4984            }
 4985        "#,
 4986    );
 4987    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4988    assert_eq!(
 4989        cx.read_from_clipboard()
 4990            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4991        Some(
 4992            "     for selection in selections.iter() {
 4993            let mut start = selection.start;
 4994            let mut end = selection.end;
 4995            let is_entire_line = selection.is_empty();
 4996            if is_entire_line {
 4997                start = Point::new(start.row, 0);"
 4998                .to_string()
 4999        ),
 5000        "Regular copying preserves all indentation selected",
 5001    );
 5002    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5003    assert_eq!(
 5004        cx.read_from_clipboard()
 5005            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5006        Some(
 5007            "for selection in selections.iter() {
 5008let mut start = selection.start;
 5009let mut end = selection.end;
 5010let is_entire_line = selection.is_empty();
 5011if is_entire_line {
 5012    start = Point::new(start.row, 0);"
 5013                .to_string()
 5014        ),
 5015        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5016    );
 5017
 5018    cx.set_state(
 5019        r#"       «ˇ     for selection in selections.iter() {
 5020            let mut start = selection.start;
 5021            let mut end = selection.end;
 5022            let is_entire_line = selection.is_empty();
 5023            if is_entire_line {
 5024                start = Point::new(start.row, 0);»
 5025                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5026            }
 5027        "#,
 5028    );
 5029    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5030    assert_eq!(
 5031        cx.read_from_clipboard()
 5032            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5033        Some(
 5034            "     for selection in selections.iter() {
 5035            let mut start = selection.start;
 5036            let mut end = selection.end;
 5037            let is_entire_line = selection.is_empty();
 5038            if is_entire_line {
 5039                start = Point::new(start.row, 0);"
 5040                .to_string()
 5041        ),
 5042        "Regular copying for reverse selection works the same",
 5043    );
 5044    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5045    assert_eq!(
 5046        cx.read_from_clipboard()
 5047            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5048        Some(
 5049            "for selection in selections.iter() {
 5050let mut start = selection.start;
 5051let mut end = selection.end;
 5052let is_entire_line = selection.is_empty();
 5053if is_entire_line {
 5054    start = Point::new(start.row, 0);"
 5055                .to_string()
 5056        ),
 5057        "Copying with stripping for reverse selection works the same"
 5058    );
 5059
 5060    cx.set_state(
 5061        r#"            for selection «in selections.iter() {
 5062            let mut start = selection.start;
 5063            let mut end = selection.end;
 5064            let is_entire_line = selection.is_empty();
 5065            if is_entire_line {
 5066                start = Point::new(start.row, 0);ˇ»
 5067                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5068            }
 5069        "#,
 5070    );
 5071    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5072    assert_eq!(
 5073        cx.read_from_clipboard()
 5074            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5075        Some(
 5076            "in selections.iter() {
 5077            let mut start = selection.start;
 5078            let mut end = selection.end;
 5079            let is_entire_line = selection.is_empty();
 5080            if is_entire_line {
 5081                start = Point::new(start.row, 0);"
 5082                .to_string()
 5083        ),
 5084        "When selecting past the indent, the copying works as usual",
 5085    );
 5086    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5087    assert_eq!(
 5088        cx.read_from_clipboard()
 5089            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5090        Some(
 5091            "in selections.iter() {
 5092            let mut start = selection.start;
 5093            let mut end = selection.end;
 5094            let is_entire_line = selection.is_empty();
 5095            if is_entire_line {
 5096                start = Point::new(start.row, 0);"
 5097                .to_string()
 5098        ),
 5099        "When selecting past the indent, nothing is trimmed"
 5100    );
 5101}
 5102
 5103#[gpui::test]
 5104async fn test_paste_multiline(cx: &mut TestAppContext) {
 5105    init_test(cx, |_| {});
 5106
 5107    let mut cx = EditorTestContext::new(cx).await;
 5108    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5109
 5110    // Cut an indented block, without the leading whitespace.
 5111    cx.set_state(indoc! {"
 5112        const a: B = (
 5113            c(),
 5114            «d(
 5115                e,
 5116                f
 5117            )ˇ»
 5118        );
 5119    "});
 5120    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5121    cx.assert_editor_state(indoc! {"
 5122        const a: B = (
 5123            c(),
 5124            ˇ
 5125        );
 5126    "});
 5127
 5128    // Paste it at the same position.
 5129    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5130    cx.assert_editor_state(indoc! {"
 5131        const a: B = (
 5132            c(),
 5133            d(
 5134                e,
 5135                f
 5136 5137        );
 5138    "});
 5139
 5140    // Paste it at a line with a lower indent level.
 5141    cx.set_state(indoc! {"
 5142        ˇ
 5143        const a: B = (
 5144            c(),
 5145        );
 5146    "});
 5147    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5148    cx.assert_editor_state(indoc! {"
 5149        d(
 5150            e,
 5151            f
 5152 5153        const a: B = (
 5154            c(),
 5155        );
 5156    "});
 5157
 5158    // Cut an indented block, with the leading whitespace.
 5159    cx.set_state(indoc! {"
 5160        const a: B = (
 5161            c(),
 5162        «    d(
 5163                e,
 5164                f
 5165            )
 5166        ˇ»);
 5167    "});
 5168    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5169    cx.assert_editor_state(indoc! {"
 5170        const a: B = (
 5171            c(),
 5172        ˇ);
 5173    "});
 5174
 5175    // Paste it at the same position.
 5176    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5177    cx.assert_editor_state(indoc! {"
 5178        const a: B = (
 5179            c(),
 5180            d(
 5181                e,
 5182                f
 5183            )
 5184        ˇ);
 5185    "});
 5186
 5187    // Paste it at a line with a higher indent level.
 5188    cx.set_state(indoc! {"
 5189        const a: B = (
 5190            c(),
 5191            d(
 5192                e,
 5193 5194            )
 5195        );
 5196    "});
 5197    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5198    cx.assert_editor_state(indoc! {"
 5199        const a: B = (
 5200            c(),
 5201            d(
 5202                e,
 5203                f    d(
 5204                    e,
 5205                    f
 5206                )
 5207        ˇ
 5208            )
 5209        );
 5210    "});
 5211
 5212    // Copy an indented block, starting mid-line
 5213    cx.set_state(indoc! {"
 5214        const a: B = (
 5215            c(),
 5216            somethin«g(
 5217                e,
 5218                f
 5219            )ˇ»
 5220        );
 5221    "});
 5222    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5223
 5224    // Paste it on a line with a lower indent level
 5225    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5226    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5227    cx.assert_editor_state(indoc! {"
 5228        const a: B = (
 5229            c(),
 5230            something(
 5231                e,
 5232                f
 5233            )
 5234        );
 5235        g(
 5236            e,
 5237            f
 5238"});
 5239}
 5240
 5241#[gpui::test]
 5242async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5243    init_test(cx, |_| {});
 5244
 5245    cx.write_to_clipboard(ClipboardItem::new_string(
 5246        "    d(\n        e\n    );\n".into(),
 5247    ));
 5248
 5249    let mut cx = EditorTestContext::new(cx).await;
 5250    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5251
 5252    cx.set_state(indoc! {"
 5253        fn a() {
 5254            b();
 5255            if c() {
 5256                ˇ
 5257            }
 5258        }
 5259    "});
 5260
 5261    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5262    cx.assert_editor_state(indoc! {"
 5263        fn a() {
 5264            b();
 5265            if c() {
 5266                d(
 5267                    e
 5268                );
 5269        ˇ
 5270            }
 5271        }
 5272    "});
 5273
 5274    cx.set_state(indoc! {"
 5275        fn a() {
 5276            b();
 5277            ˇ
 5278        }
 5279    "});
 5280
 5281    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5282    cx.assert_editor_state(indoc! {"
 5283        fn a() {
 5284            b();
 5285            d(
 5286                e
 5287            );
 5288        ˇ
 5289        }
 5290    "});
 5291}
 5292
 5293#[gpui::test]
 5294fn test_select_all(cx: &mut TestAppContext) {
 5295    init_test(cx, |_| {});
 5296
 5297    let editor = cx.add_window(|window, cx| {
 5298        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5299        build_editor(buffer, window, cx)
 5300    });
 5301    _ = editor.update(cx, |editor, window, cx| {
 5302        editor.select_all(&SelectAll, window, cx);
 5303        assert_eq!(
 5304            editor.selections.display_ranges(cx),
 5305            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5306        );
 5307    });
 5308}
 5309
 5310#[gpui::test]
 5311fn test_select_line(cx: &mut TestAppContext) {
 5312    init_test(cx, |_| {});
 5313
 5314    let editor = cx.add_window(|window, cx| {
 5315        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5316        build_editor(buffer, window, cx)
 5317    });
 5318    _ = editor.update(cx, |editor, window, cx| {
 5319        editor.change_selections(None, window, cx, |s| {
 5320            s.select_display_ranges([
 5321                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5322                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5323                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5324                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5325            ])
 5326        });
 5327        editor.select_line(&SelectLine, window, cx);
 5328        assert_eq!(
 5329            editor.selections.display_ranges(cx),
 5330            vec![
 5331                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5332                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5333            ]
 5334        );
 5335    });
 5336
 5337    _ = editor.update(cx, |editor, window, cx| {
 5338        editor.select_line(&SelectLine, window, cx);
 5339        assert_eq!(
 5340            editor.selections.display_ranges(cx),
 5341            vec![
 5342                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5343                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5344            ]
 5345        );
 5346    });
 5347
 5348    _ = editor.update(cx, |editor, window, cx| {
 5349        editor.select_line(&SelectLine, window, cx);
 5350        assert_eq!(
 5351            editor.selections.display_ranges(cx),
 5352            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5353        );
 5354    });
 5355}
 5356
 5357#[gpui::test]
 5358async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5359    init_test(cx, |_| {});
 5360    let mut cx = EditorTestContext::new(cx).await;
 5361
 5362    #[track_caller]
 5363    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5364        cx.set_state(initial_state);
 5365        cx.update_editor(|e, window, cx| {
 5366            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5367        });
 5368        cx.assert_editor_state(expected_state);
 5369    }
 5370
 5371    // Selection starts and ends at the middle of lines, left-to-right
 5372    test(
 5373        &mut cx,
 5374        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5375        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5376    );
 5377    // Same thing, right-to-left
 5378    test(
 5379        &mut cx,
 5380        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5381        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5382    );
 5383
 5384    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5385    test(
 5386        &mut cx,
 5387        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5388        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5389    );
 5390    // Same thing, right-to-left
 5391    test(
 5392        &mut cx,
 5393        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5394        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5395    );
 5396
 5397    // Whole buffer, left-to-right, last line ends with newline
 5398    test(
 5399        &mut cx,
 5400        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5401        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5402    );
 5403    // Same thing, right-to-left
 5404    test(
 5405        &mut cx,
 5406        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5407        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5408    );
 5409
 5410    // Starts at the end of a line, ends at the start of another
 5411    test(
 5412        &mut cx,
 5413        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5414        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5415    );
 5416}
 5417
 5418#[gpui::test]
 5419async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5420    init_test(cx, |_| {});
 5421
 5422    let editor = cx.add_window(|window, cx| {
 5423        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5424        build_editor(buffer, window, cx)
 5425    });
 5426
 5427    // setup
 5428    _ = editor.update(cx, |editor, window, cx| {
 5429        editor.fold_creases(
 5430            vec![
 5431                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5432                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5433                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5434            ],
 5435            true,
 5436            window,
 5437            cx,
 5438        );
 5439        assert_eq!(
 5440            editor.display_text(cx),
 5441            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5442        );
 5443    });
 5444
 5445    _ = editor.update(cx, |editor, window, cx| {
 5446        editor.change_selections(None, window, cx, |s| {
 5447            s.select_display_ranges([
 5448                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5449                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5450                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5451                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5452            ])
 5453        });
 5454        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5455        assert_eq!(
 5456            editor.display_text(cx),
 5457            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5458        );
 5459    });
 5460    EditorTestContext::for_editor(editor, cx)
 5461        .await
 5462        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5463
 5464    _ = editor.update(cx, |editor, window, cx| {
 5465        editor.change_selections(None, window, cx, |s| {
 5466            s.select_display_ranges([
 5467                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5468            ])
 5469        });
 5470        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5471        assert_eq!(
 5472            editor.display_text(cx),
 5473            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5474        );
 5475        assert_eq!(
 5476            editor.selections.display_ranges(cx),
 5477            [
 5478                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5479                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5480                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5481                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5482                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5483                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5484                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5485            ]
 5486        );
 5487    });
 5488    EditorTestContext::for_editor(editor, cx)
 5489        .await
 5490        .assert_editor_state(
 5491            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5492        );
 5493}
 5494
 5495#[gpui::test]
 5496async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5497    init_test(cx, |_| {});
 5498
 5499    let mut cx = EditorTestContext::new(cx).await;
 5500
 5501    cx.set_state(indoc!(
 5502        r#"abc
 5503           defˇghi
 5504
 5505           jk
 5506           nlmo
 5507           "#
 5508    ));
 5509
 5510    cx.update_editor(|editor, window, cx| {
 5511        editor.add_selection_above(&Default::default(), window, cx);
 5512    });
 5513
 5514    cx.assert_editor_state(indoc!(
 5515        r#"abcˇ
 5516           defˇghi
 5517
 5518           jk
 5519           nlmo
 5520           "#
 5521    ));
 5522
 5523    cx.update_editor(|editor, window, cx| {
 5524        editor.add_selection_above(&Default::default(), window, cx);
 5525    });
 5526
 5527    cx.assert_editor_state(indoc!(
 5528        r#"abcˇ
 5529            defˇghi
 5530
 5531            jk
 5532            nlmo
 5533            "#
 5534    ));
 5535
 5536    cx.update_editor(|editor, window, cx| {
 5537        editor.add_selection_below(&Default::default(), window, cx);
 5538    });
 5539
 5540    cx.assert_editor_state(indoc!(
 5541        r#"abc
 5542           defˇghi
 5543
 5544           jk
 5545           nlmo
 5546           "#
 5547    ));
 5548
 5549    cx.update_editor(|editor, window, cx| {
 5550        editor.undo_selection(&Default::default(), window, cx);
 5551    });
 5552
 5553    cx.assert_editor_state(indoc!(
 5554        r#"abcˇ
 5555           defˇghi
 5556
 5557           jk
 5558           nlmo
 5559           "#
 5560    ));
 5561
 5562    cx.update_editor(|editor, window, cx| {
 5563        editor.redo_selection(&Default::default(), window, cx);
 5564    });
 5565
 5566    cx.assert_editor_state(indoc!(
 5567        r#"abc
 5568           defˇghi
 5569
 5570           jk
 5571           nlmo
 5572           "#
 5573    ));
 5574
 5575    cx.update_editor(|editor, window, cx| {
 5576        editor.add_selection_below(&Default::default(), window, cx);
 5577    });
 5578
 5579    cx.assert_editor_state(indoc!(
 5580        r#"abc
 5581           defˇghi
 5582
 5583           jk
 5584           nlmˇo
 5585           "#
 5586    ));
 5587
 5588    cx.update_editor(|editor, window, cx| {
 5589        editor.add_selection_below(&Default::default(), window, cx);
 5590    });
 5591
 5592    cx.assert_editor_state(indoc!(
 5593        r#"abc
 5594           defˇghi
 5595
 5596           jk
 5597           nlmˇo
 5598           "#
 5599    ));
 5600
 5601    // change selections
 5602    cx.set_state(indoc!(
 5603        r#"abc
 5604           def«ˇg»hi
 5605
 5606           jk
 5607           nlmo
 5608           "#
 5609    ));
 5610
 5611    cx.update_editor(|editor, window, cx| {
 5612        editor.add_selection_below(&Default::default(), window, cx);
 5613    });
 5614
 5615    cx.assert_editor_state(indoc!(
 5616        r#"abc
 5617           def«ˇg»hi
 5618
 5619           jk
 5620           nlm«ˇo»
 5621           "#
 5622    ));
 5623
 5624    cx.update_editor(|editor, window, cx| {
 5625        editor.add_selection_below(&Default::default(), window, cx);
 5626    });
 5627
 5628    cx.assert_editor_state(indoc!(
 5629        r#"abc
 5630           def«ˇg»hi
 5631
 5632           jk
 5633           nlm«ˇo»
 5634           "#
 5635    ));
 5636
 5637    cx.update_editor(|editor, window, cx| {
 5638        editor.add_selection_above(&Default::default(), window, cx);
 5639    });
 5640
 5641    cx.assert_editor_state(indoc!(
 5642        r#"abc
 5643           def«ˇg»hi
 5644
 5645           jk
 5646           nlmo
 5647           "#
 5648    ));
 5649
 5650    cx.update_editor(|editor, window, cx| {
 5651        editor.add_selection_above(&Default::default(), window, cx);
 5652    });
 5653
 5654    cx.assert_editor_state(indoc!(
 5655        r#"abc
 5656           def«ˇg»hi
 5657
 5658           jk
 5659           nlmo
 5660           "#
 5661    ));
 5662
 5663    // Change selections again
 5664    cx.set_state(indoc!(
 5665        r#"a«bc
 5666           defgˇ»hi
 5667
 5668           jk
 5669           nlmo
 5670           "#
 5671    ));
 5672
 5673    cx.update_editor(|editor, window, cx| {
 5674        editor.add_selection_below(&Default::default(), window, cx);
 5675    });
 5676
 5677    cx.assert_editor_state(indoc!(
 5678        r#"a«bcˇ»
 5679           d«efgˇ»hi
 5680
 5681           j«kˇ»
 5682           nlmo
 5683           "#
 5684    ));
 5685
 5686    cx.update_editor(|editor, window, cx| {
 5687        editor.add_selection_below(&Default::default(), window, cx);
 5688    });
 5689    cx.assert_editor_state(indoc!(
 5690        r#"a«bcˇ»
 5691           d«efgˇ»hi
 5692
 5693           j«kˇ»
 5694           n«lmoˇ»
 5695           "#
 5696    ));
 5697    cx.update_editor(|editor, window, cx| {
 5698        editor.add_selection_above(&Default::default(), window, cx);
 5699    });
 5700
 5701    cx.assert_editor_state(indoc!(
 5702        r#"a«bcˇ»
 5703           d«efgˇ»hi
 5704
 5705           j«kˇ»
 5706           nlmo
 5707           "#
 5708    ));
 5709
 5710    // Change selections again
 5711    cx.set_state(indoc!(
 5712        r#"abc
 5713           d«ˇefghi
 5714
 5715           jk
 5716           nlm»o
 5717           "#
 5718    ));
 5719
 5720    cx.update_editor(|editor, window, cx| {
 5721        editor.add_selection_above(&Default::default(), window, cx);
 5722    });
 5723
 5724    cx.assert_editor_state(indoc!(
 5725        r#"a«ˇbc»
 5726           d«ˇef»ghi
 5727
 5728           j«ˇk»
 5729           n«ˇlm»o
 5730           "#
 5731    ));
 5732
 5733    cx.update_editor(|editor, window, cx| {
 5734        editor.add_selection_below(&Default::default(), window, cx);
 5735    });
 5736
 5737    cx.assert_editor_state(indoc!(
 5738        r#"abc
 5739           d«ˇef»ghi
 5740
 5741           j«ˇk»
 5742           n«ˇlm»o
 5743           "#
 5744    ));
 5745}
 5746
 5747#[gpui::test]
 5748async fn test_select_next(cx: &mut TestAppContext) {
 5749    init_test(cx, |_| {});
 5750
 5751    let mut cx = EditorTestContext::new(cx).await;
 5752    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5753
 5754    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5755        .unwrap();
 5756    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5757
 5758    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5759        .unwrap();
 5760    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5761
 5762    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5763    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5764
 5765    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5766    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5767
 5768    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5769        .unwrap();
 5770    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5771
 5772    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5773        .unwrap();
 5774    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5775}
 5776
 5777#[gpui::test]
 5778async fn test_select_all_matches(cx: &mut TestAppContext) {
 5779    init_test(cx, |_| {});
 5780
 5781    let mut cx = EditorTestContext::new(cx).await;
 5782
 5783    // Test caret-only selections
 5784    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5785    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5786        .unwrap();
 5787    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5788
 5789    // Test left-to-right selections
 5790    cx.set_state("abc\n«abcˇ»\nabc");
 5791    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5792        .unwrap();
 5793    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5794
 5795    // Test right-to-left selections
 5796    cx.set_state("abc\n«ˇabc»\nabc");
 5797    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5798        .unwrap();
 5799    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5800
 5801    // Test selecting whitespace with caret selection
 5802    cx.set_state("abc\nˇ   abc\nabc");
 5803    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5804        .unwrap();
 5805    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5806
 5807    // Test selecting whitespace with left-to-right selection
 5808    cx.set_state("abc\n«ˇ  »abc\nabc");
 5809    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5810        .unwrap();
 5811    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5812
 5813    // Test no matches with right-to-left selection
 5814    cx.set_state("abc\n«  ˇ»abc\nabc");
 5815    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5816        .unwrap();
 5817    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5818}
 5819
 5820#[gpui::test]
 5821async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5822    init_test(cx, |_| {});
 5823
 5824    let mut cx = EditorTestContext::new(cx).await;
 5825    cx.set_state(
 5826        r#"let foo = 2;
 5827lˇet foo = 2;
 5828let fooˇ = 2;
 5829let foo = 2;
 5830let foo = ˇ2;"#,
 5831    );
 5832
 5833    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5834        .unwrap();
 5835    cx.assert_editor_state(
 5836        r#"let foo = 2;
 5837«letˇ» foo = 2;
 5838let «fooˇ» = 2;
 5839let foo = 2;
 5840let foo = «2ˇ»;"#,
 5841    );
 5842
 5843    // noop for multiple selections with different contents
 5844    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5845        .unwrap();
 5846    cx.assert_editor_state(
 5847        r#"let foo = 2;
 5848«letˇ» foo = 2;
 5849let «fooˇ» = 2;
 5850let foo = 2;
 5851let foo = «2ˇ»;"#,
 5852    );
 5853}
 5854
 5855#[gpui::test]
 5856async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5857    init_test(cx, |_| {});
 5858
 5859    let mut cx =
 5860        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5861
 5862    cx.assert_editor_state(indoc! {"
 5863        ˇbbb
 5864        ccc
 5865
 5866        bbb
 5867        ccc
 5868        "});
 5869    cx.dispatch_action(SelectPrevious::default());
 5870    cx.assert_editor_state(indoc! {"
 5871                «bbbˇ»
 5872                ccc
 5873
 5874                bbb
 5875                ccc
 5876                "});
 5877    cx.dispatch_action(SelectPrevious::default());
 5878    cx.assert_editor_state(indoc! {"
 5879                «bbbˇ»
 5880                ccc
 5881
 5882                «bbbˇ»
 5883                ccc
 5884                "});
 5885}
 5886
 5887#[gpui::test]
 5888async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5889    init_test(cx, |_| {});
 5890
 5891    let mut cx = EditorTestContext::new(cx).await;
 5892    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5893
 5894    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5895        .unwrap();
 5896    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5897
 5898    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5899        .unwrap();
 5900    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5901
 5902    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5903    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5904
 5905    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5906    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5907
 5908    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5909        .unwrap();
 5910    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5911
 5912    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5913        .unwrap();
 5914    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5915
 5916    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5917        .unwrap();
 5918    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5919}
 5920
 5921#[gpui::test]
 5922async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5923    init_test(cx, |_| {});
 5924
 5925    let mut cx = EditorTestContext::new(cx).await;
 5926    cx.set_state("");
 5927
 5928    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5929        .unwrap();
 5930    cx.assert_editor_state("«aˇ»");
 5931    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5932        .unwrap();
 5933    cx.assert_editor_state("«aˇ»");
 5934}
 5935
 5936#[gpui::test]
 5937async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5938    init_test(cx, |_| {});
 5939
 5940    let mut cx = EditorTestContext::new(cx).await;
 5941    cx.set_state(
 5942        r#"let foo = 2;
 5943lˇet foo = 2;
 5944let fooˇ = 2;
 5945let foo = 2;
 5946let foo = ˇ2;"#,
 5947    );
 5948
 5949    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5950        .unwrap();
 5951    cx.assert_editor_state(
 5952        r#"let foo = 2;
 5953«letˇ» foo = 2;
 5954let «fooˇ» = 2;
 5955let foo = 2;
 5956let foo = «2ˇ»;"#,
 5957    );
 5958
 5959    // noop for multiple selections with different contents
 5960    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5961        .unwrap();
 5962    cx.assert_editor_state(
 5963        r#"let foo = 2;
 5964«letˇ» foo = 2;
 5965let «fooˇ» = 2;
 5966let foo = 2;
 5967let foo = «2ˇ»;"#,
 5968    );
 5969}
 5970
 5971#[gpui::test]
 5972async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5973    init_test(cx, |_| {});
 5974
 5975    let mut cx = EditorTestContext::new(cx).await;
 5976    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5977
 5978    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5979        .unwrap();
 5980    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5981
 5982    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5983        .unwrap();
 5984    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5985
 5986    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5987    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5988
 5989    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5990    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5991
 5992    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5993        .unwrap();
 5994    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5995
 5996    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5997        .unwrap();
 5998    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5999}
 6000
 6001#[gpui::test]
 6002async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6003    init_test(cx, |_| {});
 6004
 6005    let language = Arc::new(Language::new(
 6006        LanguageConfig::default(),
 6007        Some(tree_sitter_rust::LANGUAGE.into()),
 6008    ));
 6009
 6010    let text = r#"
 6011        use mod1::mod2::{mod3, mod4};
 6012
 6013        fn fn_1(param1: bool, param2: &str) {
 6014            let var1 = "text";
 6015        }
 6016    "#
 6017    .unindent();
 6018
 6019    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6020    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6021    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6022
 6023    editor
 6024        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6025        .await;
 6026
 6027    editor.update_in(cx, |editor, window, cx| {
 6028        editor.change_selections(None, window, cx, |s| {
 6029            s.select_display_ranges([
 6030                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6031                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6032                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6033            ]);
 6034        });
 6035        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6036    });
 6037    editor.update(cx, |editor, cx| {
 6038        assert_text_with_selections(
 6039            editor,
 6040            indoc! {r#"
 6041                use mod1::mod2::{mod3, «mod4ˇ»};
 6042
 6043                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6044                    let var1 = "«ˇtext»";
 6045                }
 6046            "#},
 6047            cx,
 6048        );
 6049    });
 6050
 6051    editor.update_in(cx, |editor, window, cx| {
 6052        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6053    });
 6054    editor.update(cx, |editor, cx| {
 6055        assert_text_with_selections(
 6056            editor,
 6057            indoc! {r#"
 6058                use mod1::mod2::«{mod3, mod4}ˇ»;
 6059
 6060                «ˇfn fn_1(param1: bool, param2: &str) {
 6061                    let var1 = "text";
 6062 6063            "#},
 6064            cx,
 6065        );
 6066    });
 6067
 6068    editor.update_in(cx, |editor, window, cx| {
 6069        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6070    });
 6071    assert_eq!(
 6072        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6073        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6074    );
 6075
 6076    // Trying to expand the selected syntax node one more time has no effect.
 6077    editor.update_in(cx, |editor, window, cx| {
 6078        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6079    });
 6080    assert_eq!(
 6081        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6082        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6083    );
 6084
 6085    editor.update_in(cx, |editor, window, cx| {
 6086        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6087    });
 6088    editor.update(cx, |editor, cx| {
 6089        assert_text_with_selections(
 6090            editor,
 6091            indoc! {r#"
 6092                use mod1::mod2::«{mod3, mod4}ˇ»;
 6093
 6094                «ˇfn fn_1(param1: bool, param2: &str) {
 6095                    let var1 = "text";
 6096 6097            "#},
 6098            cx,
 6099        );
 6100    });
 6101
 6102    editor.update_in(cx, |editor, window, cx| {
 6103        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6104    });
 6105    editor.update(cx, |editor, cx| {
 6106        assert_text_with_selections(
 6107            editor,
 6108            indoc! {r#"
 6109                use mod1::mod2::{mod3, «mod4ˇ»};
 6110
 6111                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6112                    let var1 = "«ˇtext»";
 6113                }
 6114            "#},
 6115            cx,
 6116        );
 6117    });
 6118
 6119    editor.update_in(cx, |editor, window, cx| {
 6120        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6121    });
 6122    editor.update(cx, |editor, cx| {
 6123        assert_text_with_selections(
 6124            editor,
 6125            indoc! {r#"
 6126                use mod1::mod2::{mod3, mo«ˇ»d4};
 6127
 6128                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6129                    let var1 = "te«ˇ»xt";
 6130                }
 6131            "#},
 6132            cx,
 6133        );
 6134    });
 6135
 6136    // Trying to shrink the selected syntax node one more time has no effect.
 6137    editor.update_in(cx, |editor, window, cx| {
 6138        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6139    });
 6140    editor.update_in(cx, |editor, _, cx| {
 6141        assert_text_with_selections(
 6142            editor,
 6143            indoc! {r#"
 6144                use mod1::mod2::{mod3, mo«ˇ»d4};
 6145
 6146                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6147                    let var1 = "te«ˇ»xt";
 6148                }
 6149            "#},
 6150            cx,
 6151        );
 6152    });
 6153
 6154    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6155    // a fold.
 6156    editor.update_in(cx, |editor, window, cx| {
 6157        editor.fold_creases(
 6158            vec![
 6159                Crease::simple(
 6160                    Point::new(0, 21)..Point::new(0, 24),
 6161                    FoldPlaceholder::test(),
 6162                ),
 6163                Crease::simple(
 6164                    Point::new(3, 20)..Point::new(3, 22),
 6165                    FoldPlaceholder::test(),
 6166                ),
 6167            ],
 6168            true,
 6169            window,
 6170            cx,
 6171        );
 6172        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6173    });
 6174    editor.update(cx, |editor, cx| {
 6175        assert_text_with_selections(
 6176            editor,
 6177            indoc! {r#"
 6178                use mod1::mod2::«{mod3, mod4}ˇ»;
 6179
 6180                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6181                    «ˇlet var1 = "text";»
 6182                }
 6183            "#},
 6184            cx,
 6185        );
 6186    });
 6187}
 6188
 6189#[gpui::test]
 6190async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6191    init_test(cx, |_| {});
 6192
 6193    let base_text = r#"
 6194        impl A {
 6195            // this is an uncommitted comment
 6196
 6197            fn b() {
 6198                c();
 6199            }
 6200
 6201            // this is another uncommitted comment
 6202
 6203            fn d() {
 6204                // e
 6205                // f
 6206            }
 6207        }
 6208
 6209        fn g() {
 6210            // h
 6211        }
 6212    "#
 6213    .unindent();
 6214
 6215    let text = r#"
 6216        ˇimpl A {
 6217
 6218            fn b() {
 6219                c();
 6220            }
 6221
 6222            fn d() {
 6223                // e
 6224                // f
 6225            }
 6226        }
 6227
 6228        fn g() {
 6229            // h
 6230        }
 6231    "#
 6232    .unindent();
 6233
 6234    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6235    cx.set_state(&text);
 6236    cx.set_head_text(&base_text);
 6237    cx.update_editor(|editor, window, cx| {
 6238        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6239    });
 6240
 6241    cx.assert_state_with_diff(
 6242        "
 6243        ˇimpl A {
 6244      -     // this is an uncommitted comment
 6245
 6246            fn b() {
 6247                c();
 6248            }
 6249
 6250      -     // this is another uncommitted comment
 6251      -
 6252            fn d() {
 6253                // e
 6254                // f
 6255            }
 6256        }
 6257
 6258        fn g() {
 6259            // h
 6260        }
 6261    "
 6262        .unindent(),
 6263    );
 6264
 6265    let expected_display_text = "
 6266        impl A {
 6267            // this is an uncommitted comment
 6268
 6269            fn b() {
 6270 6271            }
 6272
 6273            // this is another uncommitted comment
 6274
 6275            fn d() {
 6276 6277            }
 6278        }
 6279
 6280        fn g() {
 6281 6282        }
 6283        "
 6284    .unindent();
 6285
 6286    cx.update_editor(|editor, window, cx| {
 6287        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6288        assert_eq!(editor.display_text(cx), expected_display_text);
 6289    });
 6290}
 6291
 6292#[gpui::test]
 6293async fn test_autoindent(cx: &mut TestAppContext) {
 6294    init_test(cx, |_| {});
 6295
 6296    let language = Arc::new(
 6297        Language::new(
 6298            LanguageConfig {
 6299                brackets: BracketPairConfig {
 6300                    pairs: vec![
 6301                        BracketPair {
 6302                            start: "{".to_string(),
 6303                            end: "}".to_string(),
 6304                            close: false,
 6305                            surround: false,
 6306                            newline: true,
 6307                        },
 6308                        BracketPair {
 6309                            start: "(".to_string(),
 6310                            end: ")".to_string(),
 6311                            close: false,
 6312                            surround: false,
 6313                            newline: true,
 6314                        },
 6315                    ],
 6316                    ..Default::default()
 6317                },
 6318                ..Default::default()
 6319            },
 6320            Some(tree_sitter_rust::LANGUAGE.into()),
 6321        )
 6322        .with_indents_query(
 6323            r#"
 6324                (_ "(" ")" @end) @indent
 6325                (_ "{" "}" @end) @indent
 6326            "#,
 6327        )
 6328        .unwrap(),
 6329    );
 6330
 6331    let text = "fn a() {}";
 6332
 6333    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6334    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6335    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6336    editor
 6337        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6338        .await;
 6339
 6340    editor.update_in(cx, |editor, window, cx| {
 6341        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6342        editor.newline(&Newline, window, cx);
 6343        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6344        assert_eq!(
 6345            editor.selections.ranges(cx),
 6346            &[
 6347                Point::new(1, 4)..Point::new(1, 4),
 6348                Point::new(3, 4)..Point::new(3, 4),
 6349                Point::new(5, 0)..Point::new(5, 0)
 6350            ]
 6351        );
 6352    });
 6353}
 6354
 6355#[gpui::test]
 6356async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6357    init_test(cx, |_| {});
 6358
 6359    {
 6360        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6361        cx.set_state(indoc! {"
 6362            impl A {
 6363
 6364                fn b() {}
 6365
 6366            «fn c() {
 6367
 6368            }ˇ»
 6369            }
 6370        "});
 6371
 6372        cx.update_editor(|editor, window, cx| {
 6373            editor.autoindent(&Default::default(), window, cx);
 6374        });
 6375
 6376        cx.assert_editor_state(indoc! {"
 6377            impl A {
 6378
 6379                fn b() {}
 6380
 6381                «fn c() {
 6382
 6383                }ˇ»
 6384            }
 6385        "});
 6386    }
 6387
 6388    {
 6389        let mut cx = EditorTestContext::new_multibuffer(
 6390            cx,
 6391            [indoc! { "
 6392                impl A {
 6393                «
 6394                // a
 6395                fn b(){}
 6396                »
 6397                «
 6398                    }
 6399                    fn c(){}
 6400                »
 6401            "}],
 6402        );
 6403
 6404        let buffer = cx.update_editor(|editor, _, cx| {
 6405            let buffer = editor.buffer().update(cx, |buffer, _| {
 6406                buffer.all_buffers().iter().next().unwrap().clone()
 6407            });
 6408            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6409            buffer
 6410        });
 6411
 6412        cx.run_until_parked();
 6413        cx.update_editor(|editor, window, cx| {
 6414            editor.select_all(&Default::default(), window, cx);
 6415            editor.autoindent(&Default::default(), window, cx)
 6416        });
 6417        cx.run_until_parked();
 6418
 6419        cx.update(|_, cx| {
 6420            assert_eq!(
 6421                buffer.read(cx).text(),
 6422                indoc! { "
 6423                    impl A {
 6424
 6425                        // a
 6426                        fn b(){}
 6427
 6428
 6429                    }
 6430                    fn c(){}
 6431
 6432                " }
 6433            )
 6434        });
 6435    }
 6436}
 6437
 6438#[gpui::test]
 6439async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6440    init_test(cx, |_| {});
 6441
 6442    let mut cx = EditorTestContext::new(cx).await;
 6443
 6444    let language = Arc::new(Language::new(
 6445        LanguageConfig {
 6446            brackets: BracketPairConfig {
 6447                pairs: vec![
 6448                    BracketPair {
 6449                        start: "{".to_string(),
 6450                        end: "}".to_string(),
 6451                        close: true,
 6452                        surround: true,
 6453                        newline: true,
 6454                    },
 6455                    BracketPair {
 6456                        start: "(".to_string(),
 6457                        end: ")".to_string(),
 6458                        close: true,
 6459                        surround: true,
 6460                        newline: true,
 6461                    },
 6462                    BracketPair {
 6463                        start: "/*".to_string(),
 6464                        end: " */".to_string(),
 6465                        close: true,
 6466                        surround: true,
 6467                        newline: true,
 6468                    },
 6469                    BracketPair {
 6470                        start: "[".to_string(),
 6471                        end: "]".to_string(),
 6472                        close: false,
 6473                        surround: false,
 6474                        newline: true,
 6475                    },
 6476                    BracketPair {
 6477                        start: "\"".to_string(),
 6478                        end: "\"".to_string(),
 6479                        close: true,
 6480                        surround: true,
 6481                        newline: false,
 6482                    },
 6483                    BracketPair {
 6484                        start: "<".to_string(),
 6485                        end: ">".to_string(),
 6486                        close: false,
 6487                        surround: true,
 6488                        newline: true,
 6489                    },
 6490                ],
 6491                ..Default::default()
 6492            },
 6493            autoclose_before: "})]".to_string(),
 6494            ..Default::default()
 6495        },
 6496        Some(tree_sitter_rust::LANGUAGE.into()),
 6497    ));
 6498
 6499    cx.language_registry().add(language.clone());
 6500    cx.update_buffer(|buffer, cx| {
 6501        buffer.set_language(Some(language), cx);
 6502    });
 6503
 6504    cx.set_state(
 6505        &r#"
 6506            🏀ˇ
 6507            εˇ
 6508            ❤️ˇ
 6509        "#
 6510        .unindent(),
 6511    );
 6512
 6513    // autoclose multiple nested brackets at multiple cursors
 6514    cx.update_editor(|editor, window, cx| {
 6515        editor.handle_input("{", window, cx);
 6516        editor.handle_input("{", window, cx);
 6517        editor.handle_input("{", window, cx);
 6518    });
 6519    cx.assert_editor_state(
 6520        &"
 6521            🏀{{{ˇ}}}
 6522            ε{{{ˇ}}}
 6523            ❤️{{{ˇ}}}
 6524        "
 6525        .unindent(),
 6526    );
 6527
 6528    // insert a different closing bracket
 6529    cx.update_editor(|editor, window, cx| {
 6530        editor.handle_input(")", window, cx);
 6531    });
 6532    cx.assert_editor_state(
 6533        &"
 6534            🏀{{{)ˇ}}}
 6535            ε{{{)ˇ}}}
 6536            ❤️{{{)ˇ}}}
 6537        "
 6538        .unindent(),
 6539    );
 6540
 6541    // skip over the auto-closed brackets when typing a closing bracket
 6542    cx.update_editor(|editor, window, cx| {
 6543        editor.move_right(&MoveRight, window, cx);
 6544        editor.handle_input("}", window, cx);
 6545        editor.handle_input("}", window, cx);
 6546        editor.handle_input("}", window, cx);
 6547    });
 6548    cx.assert_editor_state(
 6549        &"
 6550            🏀{{{)}}}}ˇ
 6551            ε{{{)}}}}ˇ
 6552            ❤️{{{)}}}}ˇ
 6553        "
 6554        .unindent(),
 6555    );
 6556
 6557    // autoclose multi-character pairs
 6558    cx.set_state(
 6559        &"
 6560            ˇ
 6561            ˇ
 6562        "
 6563        .unindent(),
 6564    );
 6565    cx.update_editor(|editor, window, cx| {
 6566        editor.handle_input("/", window, cx);
 6567        editor.handle_input("*", window, cx);
 6568    });
 6569    cx.assert_editor_state(
 6570        &"
 6571            /*ˇ */
 6572            /*ˇ */
 6573        "
 6574        .unindent(),
 6575    );
 6576
 6577    // one cursor autocloses a multi-character pair, one cursor
 6578    // does not autoclose.
 6579    cx.set_state(
 6580        &"
 6581 6582            ˇ
 6583        "
 6584        .unindent(),
 6585    );
 6586    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6587    cx.assert_editor_state(
 6588        &"
 6589            /*ˇ */
 6590 6591        "
 6592        .unindent(),
 6593    );
 6594
 6595    // Don't autoclose if the next character isn't whitespace and isn't
 6596    // listed in the language's "autoclose_before" section.
 6597    cx.set_state("ˇa b");
 6598    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6599    cx.assert_editor_state("{ˇa b");
 6600
 6601    // Don't autoclose if `close` is false for the bracket pair
 6602    cx.set_state("ˇ");
 6603    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6604    cx.assert_editor_state("");
 6605
 6606    // Surround with brackets if text is selected
 6607    cx.set_state("«aˇ» b");
 6608    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6609    cx.assert_editor_state("{«aˇ»} b");
 6610
 6611    // Autoclose when not immediately after a word character
 6612    cx.set_state("a ˇ");
 6613    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6614    cx.assert_editor_state("a \"ˇ\"");
 6615
 6616    // Autoclose pair where the start and end characters are the same
 6617    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6618    cx.assert_editor_state("a \"\"ˇ");
 6619
 6620    // Don't autoclose when immediately after a word character
 6621    cx.set_state("");
 6622    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6623    cx.assert_editor_state("a\"ˇ");
 6624
 6625    // Do autoclose when after a non-word character
 6626    cx.set_state("");
 6627    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6628    cx.assert_editor_state("{\"ˇ\"");
 6629
 6630    // Non identical pairs autoclose regardless of preceding character
 6631    cx.set_state("");
 6632    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6633    cx.assert_editor_state("a{ˇ}");
 6634
 6635    // Don't autoclose pair if autoclose is disabled
 6636    cx.set_state("ˇ");
 6637    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6638    cx.assert_editor_state("");
 6639
 6640    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6641    cx.set_state("«aˇ» b");
 6642    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6643    cx.assert_editor_state("<«aˇ»> b");
 6644}
 6645
 6646#[gpui::test]
 6647async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6648    init_test(cx, |settings| {
 6649        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6650    });
 6651
 6652    let mut cx = EditorTestContext::new(cx).await;
 6653
 6654    let language = Arc::new(Language::new(
 6655        LanguageConfig {
 6656            brackets: BracketPairConfig {
 6657                pairs: vec![
 6658                    BracketPair {
 6659                        start: "{".to_string(),
 6660                        end: "}".to_string(),
 6661                        close: true,
 6662                        surround: true,
 6663                        newline: true,
 6664                    },
 6665                    BracketPair {
 6666                        start: "(".to_string(),
 6667                        end: ")".to_string(),
 6668                        close: true,
 6669                        surround: true,
 6670                        newline: true,
 6671                    },
 6672                    BracketPair {
 6673                        start: "[".to_string(),
 6674                        end: "]".to_string(),
 6675                        close: false,
 6676                        surround: false,
 6677                        newline: true,
 6678                    },
 6679                ],
 6680                ..Default::default()
 6681            },
 6682            autoclose_before: "})]".to_string(),
 6683            ..Default::default()
 6684        },
 6685        Some(tree_sitter_rust::LANGUAGE.into()),
 6686    ));
 6687
 6688    cx.language_registry().add(language.clone());
 6689    cx.update_buffer(|buffer, cx| {
 6690        buffer.set_language(Some(language), cx);
 6691    });
 6692
 6693    cx.set_state(
 6694        &"
 6695            ˇ
 6696            ˇ
 6697            ˇ
 6698        "
 6699        .unindent(),
 6700    );
 6701
 6702    // ensure only matching closing brackets are skipped over
 6703    cx.update_editor(|editor, window, cx| {
 6704        editor.handle_input("}", window, cx);
 6705        editor.move_left(&MoveLeft, window, cx);
 6706        editor.handle_input(")", window, cx);
 6707        editor.move_left(&MoveLeft, window, cx);
 6708    });
 6709    cx.assert_editor_state(
 6710        &"
 6711            ˇ)}
 6712            ˇ)}
 6713            ˇ)}
 6714        "
 6715        .unindent(),
 6716    );
 6717
 6718    // skip-over closing brackets at multiple cursors
 6719    cx.update_editor(|editor, window, cx| {
 6720        editor.handle_input(")", window, cx);
 6721        editor.handle_input("}", window, cx);
 6722    });
 6723    cx.assert_editor_state(
 6724        &"
 6725            )}ˇ
 6726            )}ˇ
 6727            )}ˇ
 6728        "
 6729        .unindent(),
 6730    );
 6731
 6732    // ignore non-close brackets
 6733    cx.update_editor(|editor, window, cx| {
 6734        editor.handle_input("]", window, cx);
 6735        editor.move_left(&MoveLeft, window, cx);
 6736        editor.handle_input("]", window, cx);
 6737    });
 6738    cx.assert_editor_state(
 6739        &"
 6740            )}]ˇ]
 6741            )}]ˇ]
 6742            )}]ˇ]
 6743        "
 6744        .unindent(),
 6745    );
 6746}
 6747
 6748#[gpui::test]
 6749async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6750    init_test(cx, |_| {});
 6751
 6752    let mut cx = EditorTestContext::new(cx).await;
 6753
 6754    let html_language = Arc::new(
 6755        Language::new(
 6756            LanguageConfig {
 6757                name: "HTML".into(),
 6758                brackets: BracketPairConfig {
 6759                    pairs: vec![
 6760                        BracketPair {
 6761                            start: "<".into(),
 6762                            end: ">".into(),
 6763                            close: true,
 6764                            ..Default::default()
 6765                        },
 6766                        BracketPair {
 6767                            start: "{".into(),
 6768                            end: "}".into(),
 6769                            close: true,
 6770                            ..Default::default()
 6771                        },
 6772                        BracketPair {
 6773                            start: "(".into(),
 6774                            end: ")".into(),
 6775                            close: true,
 6776                            ..Default::default()
 6777                        },
 6778                    ],
 6779                    ..Default::default()
 6780                },
 6781                autoclose_before: "})]>".into(),
 6782                ..Default::default()
 6783            },
 6784            Some(tree_sitter_html::LANGUAGE.into()),
 6785        )
 6786        .with_injection_query(
 6787            r#"
 6788            (script_element
 6789                (raw_text) @injection.content
 6790                (#set! injection.language "javascript"))
 6791            "#,
 6792        )
 6793        .unwrap(),
 6794    );
 6795
 6796    let javascript_language = Arc::new(Language::new(
 6797        LanguageConfig {
 6798            name: "JavaScript".into(),
 6799            brackets: BracketPairConfig {
 6800                pairs: vec![
 6801                    BracketPair {
 6802                        start: "/*".into(),
 6803                        end: " */".into(),
 6804                        close: true,
 6805                        ..Default::default()
 6806                    },
 6807                    BracketPair {
 6808                        start: "{".into(),
 6809                        end: "}".into(),
 6810                        close: true,
 6811                        ..Default::default()
 6812                    },
 6813                    BracketPair {
 6814                        start: "(".into(),
 6815                        end: ")".into(),
 6816                        close: true,
 6817                        ..Default::default()
 6818                    },
 6819                ],
 6820                ..Default::default()
 6821            },
 6822            autoclose_before: "})]>".into(),
 6823            ..Default::default()
 6824        },
 6825        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6826    ));
 6827
 6828    cx.language_registry().add(html_language.clone());
 6829    cx.language_registry().add(javascript_language.clone());
 6830
 6831    cx.update_buffer(|buffer, cx| {
 6832        buffer.set_language(Some(html_language), cx);
 6833    });
 6834
 6835    cx.set_state(
 6836        &r#"
 6837            <body>ˇ
 6838                <script>
 6839                    var x = 1;ˇ
 6840                </script>
 6841            </body>ˇ
 6842        "#
 6843        .unindent(),
 6844    );
 6845
 6846    // Precondition: different languages are active at different locations.
 6847    cx.update_editor(|editor, window, cx| {
 6848        let snapshot = editor.snapshot(window, cx);
 6849        let cursors = editor.selections.ranges::<usize>(cx);
 6850        let languages = cursors
 6851            .iter()
 6852            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6853            .collect::<Vec<_>>();
 6854        assert_eq!(
 6855            languages,
 6856            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6857        );
 6858    });
 6859
 6860    // Angle brackets autoclose in HTML, but not JavaScript.
 6861    cx.update_editor(|editor, window, cx| {
 6862        editor.handle_input("<", window, cx);
 6863        editor.handle_input("a", window, cx);
 6864    });
 6865    cx.assert_editor_state(
 6866        &r#"
 6867            <body><aˇ>
 6868                <script>
 6869                    var x = 1;<aˇ
 6870                </script>
 6871            </body><aˇ>
 6872        "#
 6873        .unindent(),
 6874    );
 6875
 6876    // Curly braces and parens autoclose in both HTML and JavaScript.
 6877    cx.update_editor(|editor, window, cx| {
 6878        editor.handle_input(" b=", window, cx);
 6879        editor.handle_input("{", window, cx);
 6880        editor.handle_input("c", window, cx);
 6881        editor.handle_input("(", window, cx);
 6882    });
 6883    cx.assert_editor_state(
 6884        &r#"
 6885            <body><a b={c(ˇ)}>
 6886                <script>
 6887                    var x = 1;<a b={c(ˇ)}
 6888                </script>
 6889            </body><a b={c(ˇ)}>
 6890        "#
 6891        .unindent(),
 6892    );
 6893
 6894    // Brackets that were already autoclosed are skipped.
 6895    cx.update_editor(|editor, window, cx| {
 6896        editor.handle_input(")", window, cx);
 6897        editor.handle_input("d", window, cx);
 6898        editor.handle_input("}", window, cx);
 6899    });
 6900    cx.assert_editor_state(
 6901        &r#"
 6902            <body><a b={c()d}ˇ>
 6903                <script>
 6904                    var x = 1;<a b={c()d}ˇ
 6905                </script>
 6906            </body><a b={c()d}ˇ>
 6907        "#
 6908        .unindent(),
 6909    );
 6910    cx.update_editor(|editor, window, cx| {
 6911        editor.handle_input(">", window, cx);
 6912    });
 6913    cx.assert_editor_state(
 6914        &r#"
 6915            <body><a b={c()d}>ˇ
 6916                <script>
 6917                    var x = 1;<a b={c()d}>ˇ
 6918                </script>
 6919            </body><a b={c()d}>ˇ
 6920        "#
 6921        .unindent(),
 6922    );
 6923
 6924    // Reset
 6925    cx.set_state(
 6926        &r#"
 6927            <body>ˇ
 6928                <script>
 6929                    var x = 1;ˇ
 6930                </script>
 6931            </body>ˇ
 6932        "#
 6933        .unindent(),
 6934    );
 6935
 6936    cx.update_editor(|editor, window, cx| {
 6937        editor.handle_input("<", window, cx);
 6938    });
 6939    cx.assert_editor_state(
 6940        &r#"
 6941            <body><ˇ>
 6942                <script>
 6943                    var x = 1;<ˇ
 6944                </script>
 6945            </body><ˇ>
 6946        "#
 6947        .unindent(),
 6948    );
 6949
 6950    // When backspacing, the closing angle brackets are removed.
 6951    cx.update_editor(|editor, window, cx| {
 6952        editor.backspace(&Backspace, window, cx);
 6953    });
 6954    cx.assert_editor_state(
 6955        &r#"
 6956            <body>ˇ
 6957                <script>
 6958                    var x = 1;ˇ
 6959                </script>
 6960            </body>ˇ
 6961        "#
 6962        .unindent(),
 6963    );
 6964
 6965    // Block comments autoclose in JavaScript, but not HTML.
 6966    cx.update_editor(|editor, window, cx| {
 6967        editor.handle_input("/", window, cx);
 6968        editor.handle_input("*", window, cx);
 6969    });
 6970    cx.assert_editor_state(
 6971        &r#"
 6972            <body>/*ˇ
 6973                <script>
 6974                    var x = 1;/*ˇ */
 6975                </script>
 6976            </body>/*ˇ
 6977        "#
 6978        .unindent(),
 6979    );
 6980}
 6981
 6982#[gpui::test]
 6983async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6984    init_test(cx, |_| {});
 6985
 6986    let mut cx = EditorTestContext::new(cx).await;
 6987
 6988    let rust_language = Arc::new(
 6989        Language::new(
 6990            LanguageConfig {
 6991                name: "Rust".into(),
 6992                brackets: serde_json::from_value(json!([
 6993                    { "start": "{", "end": "}", "close": true, "newline": true },
 6994                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6995                ]))
 6996                .unwrap(),
 6997                autoclose_before: "})]>".into(),
 6998                ..Default::default()
 6999            },
 7000            Some(tree_sitter_rust::LANGUAGE.into()),
 7001        )
 7002        .with_override_query("(string_literal) @string")
 7003        .unwrap(),
 7004    );
 7005
 7006    cx.language_registry().add(rust_language.clone());
 7007    cx.update_buffer(|buffer, cx| {
 7008        buffer.set_language(Some(rust_language), cx);
 7009    });
 7010
 7011    cx.set_state(
 7012        &r#"
 7013            let x = ˇ
 7014        "#
 7015        .unindent(),
 7016    );
 7017
 7018    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7019    cx.update_editor(|editor, window, cx| {
 7020        editor.handle_input("\"", window, cx);
 7021    });
 7022    cx.assert_editor_state(
 7023        &r#"
 7024            let x = "ˇ"
 7025        "#
 7026        .unindent(),
 7027    );
 7028
 7029    // Inserting another quotation mark. The cursor moves across the existing
 7030    // automatically-inserted quotation mark.
 7031    cx.update_editor(|editor, window, cx| {
 7032        editor.handle_input("\"", window, cx);
 7033    });
 7034    cx.assert_editor_state(
 7035        &r#"
 7036            let x = ""ˇ
 7037        "#
 7038        .unindent(),
 7039    );
 7040
 7041    // Reset
 7042    cx.set_state(
 7043        &r#"
 7044            let x = ˇ
 7045        "#
 7046        .unindent(),
 7047    );
 7048
 7049    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7050    cx.update_editor(|editor, window, cx| {
 7051        editor.handle_input("\"", window, cx);
 7052        editor.handle_input(" ", window, cx);
 7053        editor.move_left(&Default::default(), window, cx);
 7054        editor.handle_input("\\", window, cx);
 7055        editor.handle_input("\"", window, cx);
 7056    });
 7057    cx.assert_editor_state(
 7058        &r#"
 7059            let x = "\"ˇ "
 7060        "#
 7061        .unindent(),
 7062    );
 7063
 7064    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7065    // mark. Nothing is inserted.
 7066    cx.update_editor(|editor, window, cx| {
 7067        editor.move_right(&Default::default(), window, cx);
 7068        editor.handle_input("\"", window, cx);
 7069    });
 7070    cx.assert_editor_state(
 7071        &r#"
 7072            let x = "\" "ˇ
 7073        "#
 7074        .unindent(),
 7075    );
 7076}
 7077
 7078#[gpui::test]
 7079async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7080    init_test(cx, |_| {});
 7081
 7082    let language = Arc::new(Language::new(
 7083        LanguageConfig {
 7084            brackets: BracketPairConfig {
 7085                pairs: vec![
 7086                    BracketPair {
 7087                        start: "{".to_string(),
 7088                        end: "}".to_string(),
 7089                        close: true,
 7090                        surround: true,
 7091                        newline: true,
 7092                    },
 7093                    BracketPair {
 7094                        start: "/* ".to_string(),
 7095                        end: "*/".to_string(),
 7096                        close: true,
 7097                        surround: true,
 7098                        ..Default::default()
 7099                    },
 7100                ],
 7101                ..Default::default()
 7102            },
 7103            ..Default::default()
 7104        },
 7105        Some(tree_sitter_rust::LANGUAGE.into()),
 7106    ));
 7107
 7108    let text = r#"
 7109        a
 7110        b
 7111        c
 7112    "#
 7113    .unindent();
 7114
 7115    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7116    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7117    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7118    editor
 7119        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7120        .await;
 7121
 7122    editor.update_in(cx, |editor, window, cx| {
 7123        editor.change_selections(None, window, cx, |s| {
 7124            s.select_display_ranges([
 7125                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7126                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7127                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7128            ])
 7129        });
 7130
 7131        editor.handle_input("{", window, cx);
 7132        editor.handle_input("{", window, cx);
 7133        editor.handle_input("{", window, cx);
 7134        assert_eq!(
 7135            editor.text(cx),
 7136            "
 7137                {{{a}}}
 7138                {{{b}}}
 7139                {{{c}}}
 7140            "
 7141            .unindent()
 7142        );
 7143        assert_eq!(
 7144            editor.selections.display_ranges(cx),
 7145            [
 7146                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7147                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7148                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7149            ]
 7150        );
 7151
 7152        editor.undo(&Undo, window, cx);
 7153        editor.undo(&Undo, window, cx);
 7154        editor.undo(&Undo, window, cx);
 7155        assert_eq!(
 7156            editor.text(cx),
 7157            "
 7158                a
 7159                b
 7160                c
 7161            "
 7162            .unindent()
 7163        );
 7164        assert_eq!(
 7165            editor.selections.display_ranges(cx),
 7166            [
 7167                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7168                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7169                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7170            ]
 7171        );
 7172
 7173        // Ensure inserting the first character of a multi-byte bracket pair
 7174        // doesn't surround the selections with the bracket.
 7175        editor.handle_input("/", window, cx);
 7176        assert_eq!(
 7177            editor.text(cx),
 7178            "
 7179                /
 7180                /
 7181                /
 7182            "
 7183            .unindent()
 7184        );
 7185        assert_eq!(
 7186            editor.selections.display_ranges(cx),
 7187            [
 7188                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7189                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7190                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7191            ]
 7192        );
 7193
 7194        editor.undo(&Undo, window, cx);
 7195        assert_eq!(
 7196            editor.text(cx),
 7197            "
 7198                a
 7199                b
 7200                c
 7201            "
 7202            .unindent()
 7203        );
 7204        assert_eq!(
 7205            editor.selections.display_ranges(cx),
 7206            [
 7207                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7208                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7209                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7210            ]
 7211        );
 7212
 7213        // Ensure inserting the last character of a multi-byte bracket pair
 7214        // doesn't surround the selections with the bracket.
 7215        editor.handle_input("*", window, cx);
 7216        assert_eq!(
 7217            editor.text(cx),
 7218            "
 7219                *
 7220                *
 7221                *
 7222            "
 7223            .unindent()
 7224        );
 7225        assert_eq!(
 7226            editor.selections.display_ranges(cx),
 7227            [
 7228                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7229                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7230                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7231            ]
 7232        );
 7233    });
 7234}
 7235
 7236#[gpui::test]
 7237async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7238    init_test(cx, |_| {});
 7239
 7240    let language = Arc::new(Language::new(
 7241        LanguageConfig {
 7242            brackets: BracketPairConfig {
 7243                pairs: vec![BracketPair {
 7244                    start: "{".to_string(),
 7245                    end: "}".to_string(),
 7246                    close: true,
 7247                    surround: true,
 7248                    newline: true,
 7249                }],
 7250                ..Default::default()
 7251            },
 7252            autoclose_before: "}".to_string(),
 7253            ..Default::default()
 7254        },
 7255        Some(tree_sitter_rust::LANGUAGE.into()),
 7256    ));
 7257
 7258    let text = r#"
 7259        a
 7260        b
 7261        c
 7262    "#
 7263    .unindent();
 7264
 7265    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7266    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7267    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7268    editor
 7269        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7270        .await;
 7271
 7272    editor.update_in(cx, |editor, window, cx| {
 7273        editor.change_selections(None, window, cx, |s| {
 7274            s.select_ranges([
 7275                Point::new(0, 1)..Point::new(0, 1),
 7276                Point::new(1, 1)..Point::new(1, 1),
 7277                Point::new(2, 1)..Point::new(2, 1),
 7278            ])
 7279        });
 7280
 7281        editor.handle_input("{", window, cx);
 7282        editor.handle_input("{", window, cx);
 7283        editor.handle_input("_", window, cx);
 7284        assert_eq!(
 7285            editor.text(cx),
 7286            "
 7287                a{{_}}
 7288                b{{_}}
 7289                c{{_}}
 7290            "
 7291            .unindent()
 7292        );
 7293        assert_eq!(
 7294            editor.selections.ranges::<Point>(cx),
 7295            [
 7296                Point::new(0, 4)..Point::new(0, 4),
 7297                Point::new(1, 4)..Point::new(1, 4),
 7298                Point::new(2, 4)..Point::new(2, 4)
 7299            ]
 7300        );
 7301
 7302        editor.backspace(&Default::default(), window, cx);
 7303        editor.backspace(&Default::default(), window, cx);
 7304        assert_eq!(
 7305            editor.text(cx),
 7306            "
 7307                a{}
 7308                b{}
 7309                c{}
 7310            "
 7311            .unindent()
 7312        );
 7313        assert_eq!(
 7314            editor.selections.ranges::<Point>(cx),
 7315            [
 7316                Point::new(0, 2)..Point::new(0, 2),
 7317                Point::new(1, 2)..Point::new(1, 2),
 7318                Point::new(2, 2)..Point::new(2, 2)
 7319            ]
 7320        );
 7321
 7322        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7323        assert_eq!(
 7324            editor.text(cx),
 7325            "
 7326                a
 7327                b
 7328                c
 7329            "
 7330            .unindent()
 7331        );
 7332        assert_eq!(
 7333            editor.selections.ranges::<Point>(cx),
 7334            [
 7335                Point::new(0, 1)..Point::new(0, 1),
 7336                Point::new(1, 1)..Point::new(1, 1),
 7337                Point::new(2, 1)..Point::new(2, 1)
 7338            ]
 7339        );
 7340    });
 7341}
 7342
 7343#[gpui::test]
 7344async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7345    init_test(cx, |settings| {
 7346        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7347    });
 7348
 7349    let mut cx = EditorTestContext::new(cx).await;
 7350
 7351    let language = Arc::new(Language::new(
 7352        LanguageConfig {
 7353            brackets: BracketPairConfig {
 7354                pairs: vec![
 7355                    BracketPair {
 7356                        start: "{".to_string(),
 7357                        end: "}".to_string(),
 7358                        close: true,
 7359                        surround: true,
 7360                        newline: true,
 7361                    },
 7362                    BracketPair {
 7363                        start: "(".to_string(),
 7364                        end: ")".to_string(),
 7365                        close: true,
 7366                        surround: true,
 7367                        newline: true,
 7368                    },
 7369                    BracketPair {
 7370                        start: "[".to_string(),
 7371                        end: "]".to_string(),
 7372                        close: false,
 7373                        surround: true,
 7374                        newline: true,
 7375                    },
 7376                ],
 7377                ..Default::default()
 7378            },
 7379            autoclose_before: "})]".to_string(),
 7380            ..Default::default()
 7381        },
 7382        Some(tree_sitter_rust::LANGUAGE.into()),
 7383    ));
 7384
 7385    cx.language_registry().add(language.clone());
 7386    cx.update_buffer(|buffer, cx| {
 7387        buffer.set_language(Some(language), cx);
 7388    });
 7389
 7390    cx.set_state(
 7391        &"
 7392            {(ˇ)}
 7393            [[ˇ]]
 7394            {(ˇ)}
 7395        "
 7396        .unindent(),
 7397    );
 7398
 7399    cx.update_editor(|editor, window, cx| {
 7400        editor.backspace(&Default::default(), window, cx);
 7401        editor.backspace(&Default::default(), window, cx);
 7402    });
 7403
 7404    cx.assert_editor_state(
 7405        &"
 7406            ˇ
 7407            ˇ]]
 7408            ˇ
 7409        "
 7410        .unindent(),
 7411    );
 7412
 7413    cx.update_editor(|editor, window, cx| {
 7414        editor.handle_input("{", window, cx);
 7415        editor.handle_input("{", window, cx);
 7416        editor.move_right(&MoveRight, window, cx);
 7417        editor.move_right(&MoveRight, window, cx);
 7418        editor.move_left(&MoveLeft, window, cx);
 7419        editor.move_left(&MoveLeft, window, cx);
 7420        editor.backspace(&Default::default(), window, cx);
 7421    });
 7422
 7423    cx.assert_editor_state(
 7424        &"
 7425            {ˇ}
 7426            {ˇ}]]
 7427            {ˇ}
 7428        "
 7429        .unindent(),
 7430    );
 7431
 7432    cx.update_editor(|editor, window, cx| {
 7433        editor.backspace(&Default::default(), window, cx);
 7434    });
 7435
 7436    cx.assert_editor_state(
 7437        &"
 7438            ˇ
 7439            ˇ]]
 7440            ˇ
 7441        "
 7442        .unindent(),
 7443    );
 7444}
 7445
 7446#[gpui::test]
 7447async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7448    init_test(cx, |_| {});
 7449
 7450    let language = Arc::new(Language::new(
 7451        LanguageConfig::default(),
 7452        Some(tree_sitter_rust::LANGUAGE.into()),
 7453    ));
 7454
 7455    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7456    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7457    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7458    editor
 7459        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7460        .await;
 7461
 7462    editor.update_in(cx, |editor, window, cx| {
 7463        editor.set_auto_replace_emoji_shortcode(true);
 7464
 7465        editor.handle_input("Hello ", window, cx);
 7466        editor.handle_input(":wave", window, cx);
 7467        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7468
 7469        editor.handle_input(":", window, cx);
 7470        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7471
 7472        editor.handle_input(" :smile", window, cx);
 7473        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7474
 7475        editor.handle_input(":", window, cx);
 7476        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7477
 7478        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7479        editor.handle_input(":wave", window, cx);
 7480        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7481
 7482        editor.handle_input(":", window, cx);
 7483        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7484
 7485        editor.handle_input(":1", window, cx);
 7486        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7487
 7488        editor.handle_input(":", window, cx);
 7489        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7490
 7491        // Ensure shortcode does not get replaced when it is part of a word
 7492        editor.handle_input(" Test:wave", window, cx);
 7493        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7494
 7495        editor.handle_input(":", window, cx);
 7496        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7497
 7498        editor.set_auto_replace_emoji_shortcode(false);
 7499
 7500        // Ensure shortcode does not get replaced when auto replace is off
 7501        editor.handle_input(" :wave", window, cx);
 7502        assert_eq!(
 7503            editor.text(cx),
 7504            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7505        );
 7506
 7507        editor.handle_input(":", window, cx);
 7508        assert_eq!(
 7509            editor.text(cx),
 7510            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7511        );
 7512    });
 7513}
 7514
 7515#[gpui::test]
 7516async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7517    init_test(cx, |_| {});
 7518
 7519    let (text, insertion_ranges) = marked_text_ranges(
 7520        indoc! {"
 7521            ˇ
 7522        "},
 7523        false,
 7524    );
 7525
 7526    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7527    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7528
 7529    _ = editor.update_in(cx, |editor, window, cx| {
 7530        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7531
 7532        editor
 7533            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7534            .unwrap();
 7535
 7536        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7537            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7538            assert_eq!(editor.text(cx), expected_text);
 7539            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7540        }
 7541
 7542        assert(
 7543            editor,
 7544            cx,
 7545            indoc! {"
 7546            type «» =•
 7547            "},
 7548        );
 7549
 7550        assert!(editor.context_menu_visible(), "There should be a matches");
 7551    });
 7552}
 7553
 7554#[gpui::test]
 7555async fn test_snippets(cx: &mut TestAppContext) {
 7556    init_test(cx, |_| {});
 7557
 7558    let (text, insertion_ranges) = marked_text_ranges(
 7559        indoc! {"
 7560            a.ˇ b
 7561            a.ˇ b
 7562            a.ˇ b
 7563        "},
 7564        false,
 7565    );
 7566
 7567    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7568    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7569
 7570    editor.update_in(cx, |editor, window, cx| {
 7571        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7572
 7573        editor
 7574            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7575            .unwrap();
 7576
 7577        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7578            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7579            assert_eq!(editor.text(cx), expected_text);
 7580            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7581        }
 7582
 7583        assert(
 7584            editor,
 7585            cx,
 7586            indoc! {"
 7587                a.f(«one», two, «three») b
 7588                a.f(«one», two, «three») b
 7589                a.f(«one», two, «three») b
 7590            "},
 7591        );
 7592
 7593        // Can't move earlier than the first tab stop
 7594        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7595        assert(
 7596            editor,
 7597            cx,
 7598            indoc! {"
 7599                a.f(«one», two, «three») b
 7600                a.f(«one», two, «three») b
 7601                a.f(«one», two, «three») b
 7602            "},
 7603        );
 7604
 7605        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7606        assert(
 7607            editor,
 7608            cx,
 7609            indoc! {"
 7610                a.f(one, «two», three) b
 7611                a.f(one, «two», three) b
 7612                a.f(one, «two», three) b
 7613            "},
 7614        );
 7615
 7616        editor.move_to_prev_snippet_tabstop(window, cx);
 7617        assert(
 7618            editor,
 7619            cx,
 7620            indoc! {"
 7621                a.f(«one», two, «three») b
 7622                a.f(«one», two, «three») b
 7623                a.f(«one», two, «three») b
 7624            "},
 7625        );
 7626
 7627        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7628        assert(
 7629            editor,
 7630            cx,
 7631            indoc! {"
 7632                a.f(one, «two», three) b
 7633                a.f(one, «two», three) b
 7634                a.f(one, «two», three) b
 7635            "},
 7636        );
 7637        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7638        assert(
 7639            editor,
 7640            cx,
 7641            indoc! {"
 7642                a.f(one, two, three)ˇ b
 7643                a.f(one, two, three)ˇ b
 7644                a.f(one, two, three)ˇ b
 7645            "},
 7646        );
 7647
 7648        // As soon as the last tab stop is reached, snippet state is gone
 7649        editor.move_to_prev_snippet_tabstop(window, cx);
 7650        assert(
 7651            editor,
 7652            cx,
 7653            indoc! {"
 7654                a.f(one, two, three)ˇ b
 7655                a.f(one, two, three)ˇ b
 7656                a.f(one, two, three)ˇ b
 7657            "},
 7658        );
 7659    });
 7660}
 7661
 7662#[gpui::test]
 7663async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7664    init_test(cx, |_| {});
 7665
 7666    let fs = FakeFs::new(cx.executor());
 7667    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7668
 7669    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7670
 7671    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7672    language_registry.add(rust_lang());
 7673    let mut fake_servers = language_registry.register_fake_lsp(
 7674        "Rust",
 7675        FakeLspAdapter {
 7676            capabilities: lsp::ServerCapabilities {
 7677                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7678                ..Default::default()
 7679            },
 7680            ..Default::default()
 7681        },
 7682    );
 7683
 7684    let buffer = project
 7685        .update(cx, |project, cx| {
 7686            project.open_local_buffer(path!("/file.rs"), cx)
 7687        })
 7688        .await
 7689        .unwrap();
 7690
 7691    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7692    let (editor, cx) = cx.add_window_view(|window, cx| {
 7693        build_editor_with_project(project.clone(), buffer, window, cx)
 7694    });
 7695    editor.update_in(cx, |editor, window, cx| {
 7696        editor.set_text("one\ntwo\nthree\n", window, cx)
 7697    });
 7698    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7699
 7700    cx.executor().start_waiting();
 7701    let fake_server = fake_servers.next().await.unwrap();
 7702
 7703    let save = editor
 7704        .update_in(cx, |editor, window, cx| {
 7705            editor.save(true, project.clone(), window, cx)
 7706        })
 7707        .unwrap();
 7708    fake_server
 7709        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7710            assert_eq!(
 7711                params.text_document.uri,
 7712                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7713            );
 7714            assert_eq!(params.options.tab_size, 4);
 7715            Ok(Some(vec![lsp::TextEdit::new(
 7716                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7717                ", ".to_string(),
 7718            )]))
 7719        })
 7720        .next()
 7721        .await;
 7722    cx.executor().start_waiting();
 7723    save.await;
 7724
 7725    assert_eq!(
 7726        editor.update(cx, |editor, cx| editor.text(cx)),
 7727        "one, two\nthree\n"
 7728    );
 7729    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7730
 7731    editor.update_in(cx, |editor, window, cx| {
 7732        editor.set_text("one\ntwo\nthree\n", window, cx)
 7733    });
 7734    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7735
 7736    // Ensure we can still save even if formatting hangs.
 7737    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7738        move |params, _| async move {
 7739            assert_eq!(
 7740                params.text_document.uri,
 7741                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7742            );
 7743            futures::future::pending::<()>().await;
 7744            unreachable!()
 7745        },
 7746    );
 7747    let save = editor
 7748        .update_in(cx, |editor, window, cx| {
 7749            editor.save(true, project.clone(), window, cx)
 7750        })
 7751        .unwrap();
 7752    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7753    cx.executor().start_waiting();
 7754    save.await;
 7755    assert_eq!(
 7756        editor.update(cx, |editor, cx| editor.text(cx)),
 7757        "one\ntwo\nthree\n"
 7758    );
 7759    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7760
 7761    // For non-dirty buffer, no formatting request should be sent
 7762    let save = editor
 7763        .update_in(cx, |editor, window, cx| {
 7764            editor.save(true, project.clone(), window, cx)
 7765        })
 7766        .unwrap();
 7767    let _pending_format_request = fake_server
 7768        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7769            panic!("Should not be invoked on non-dirty buffer");
 7770        })
 7771        .next();
 7772    cx.executor().start_waiting();
 7773    save.await;
 7774
 7775    // Set rust language override and assert overridden tabsize is sent to language server
 7776    update_test_language_settings(cx, |settings| {
 7777        settings.languages.insert(
 7778            "Rust".into(),
 7779            LanguageSettingsContent {
 7780                tab_size: NonZeroU32::new(8),
 7781                ..Default::default()
 7782            },
 7783        );
 7784    });
 7785
 7786    editor.update_in(cx, |editor, window, cx| {
 7787        editor.set_text("somehting_new\n", window, cx)
 7788    });
 7789    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7790    let save = editor
 7791        .update_in(cx, |editor, window, cx| {
 7792            editor.save(true, project.clone(), window, cx)
 7793        })
 7794        .unwrap();
 7795    fake_server
 7796        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7797            assert_eq!(
 7798                params.text_document.uri,
 7799                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7800            );
 7801            assert_eq!(params.options.tab_size, 8);
 7802            Ok(Some(vec![]))
 7803        })
 7804        .next()
 7805        .await;
 7806    cx.executor().start_waiting();
 7807    save.await;
 7808}
 7809
 7810#[gpui::test]
 7811async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7812    init_test(cx, |_| {});
 7813
 7814    let cols = 4;
 7815    let rows = 10;
 7816    let sample_text_1 = sample_text(rows, cols, 'a');
 7817    assert_eq!(
 7818        sample_text_1,
 7819        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7820    );
 7821    let sample_text_2 = sample_text(rows, cols, 'l');
 7822    assert_eq!(
 7823        sample_text_2,
 7824        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7825    );
 7826    let sample_text_3 = sample_text(rows, cols, 'v');
 7827    assert_eq!(
 7828        sample_text_3,
 7829        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7830    );
 7831
 7832    let fs = FakeFs::new(cx.executor());
 7833    fs.insert_tree(
 7834        path!("/a"),
 7835        json!({
 7836            "main.rs": sample_text_1,
 7837            "other.rs": sample_text_2,
 7838            "lib.rs": sample_text_3,
 7839        }),
 7840    )
 7841    .await;
 7842
 7843    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7844    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7845    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7846
 7847    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7848    language_registry.add(rust_lang());
 7849    let mut fake_servers = language_registry.register_fake_lsp(
 7850        "Rust",
 7851        FakeLspAdapter {
 7852            capabilities: lsp::ServerCapabilities {
 7853                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7854                ..Default::default()
 7855            },
 7856            ..Default::default()
 7857        },
 7858    );
 7859
 7860    let worktree = project.update(cx, |project, cx| {
 7861        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7862        assert_eq!(worktrees.len(), 1);
 7863        worktrees.pop().unwrap()
 7864    });
 7865    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7866
 7867    let buffer_1 = project
 7868        .update(cx, |project, cx| {
 7869            project.open_buffer((worktree_id, "main.rs"), cx)
 7870        })
 7871        .await
 7872        .unwrap();
 7873    let buffer_2 = project
 7874        .update(cx, |project, cx| {
 7875            project.open_buffer((worktree_id, "other.rs"), cx)
 7876        })
 7877        .await
 7878        .unwrap();
 7879    let buffer_3 = project
 7880        .update(cx, |project, cx| {
 7881            project.open_buffer((worktree_id, "lib.rs"), cx)
 7882        })
 7883        .await
 7884        .unwrap();
 7885
 7886    let multi_buffer = cx.new(|cx| {
 7887        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7888        multi_buffer.push_excerpts(
 7889            buffer_1.clone(),
 7890            [
 7891                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7892                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7893                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7894            ],
 7895            cx,
 7896        );
 7897        multi_buffer.push_excerpts(
 7898            buffer_2.clone(),
 7899            [
 7900                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7901                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7902                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7903            ],
 7904            cx,
 7905        );
 7906        multi_buffer.push_excerpts(
 7907            buffer_3.clone(),
 7908            [
 7909                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7910                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7911                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7912            ],
 7913            cx,
 7914        );
 7915        multi_buffer
 7916    });
 7917    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7918        Editor::new(
 7919            EditorMode::Full,
 7920            multi_buffer,
 7921            Some(project.clone()),
 7922            window,
 7923            cx,
 7924        )
 7925    });
 7926
 7927    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7928        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7929            s.select_ranges(Some(1..2))
 7930        });
 7931        editor.insert("|one|two|three|", window, cx);
 7932    });
 7933    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7934    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7935        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7936            s.select_ranges(Some(60..70))
 7937        });
 7938        editor.insert("|four|five|six|", window, cx);
 7939    });
 7940    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7941
 7942    // First two buffers should be edited, but not the third one.
 7943    assert_eq!(
 7944        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7945        "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}",
 7946    );
 7947    buffer_1.update(cx, |buffer, _| {
 7948        assert!(buffer.is_dirty());
 7949        assert_eq!(
 7950            buffer.text(),
 7951            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7952        )
 7953    });
 7954    buffer_2.update(cx, |buffer, _| {
 7955        assert!(buffer.is_dirty());
 7956        assert_eq!(
 7957            buffer.text(),
 7958            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7959        )
 7960    });
 7961    buffer_3.update(cx, |buffer, _| {
 7962        assert!(!buffer.is_dirty());
 7963        assert_eq!(buffer.text(), sample_text_3,)
 7964    });
 7965    cx.executor().run_until_parked();
 7966
 7967    cx.executor().start_waiting();
 7968    let save = multi_buffer_editor
 7969        .update_in(cx, |editor, window, cx| {
 7970            editor.save(true, project.clone(), window, cx)
 7971        })
 7972        .unwrap();
 7973
 7974    let fake_server = fake_servers.next().await.unwrap();
 7975    fake_server
 7976        .server
 7977        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7978            Ok(Some(vec![lsp::TextEdit::new(
 7979                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7980                format!("[{} formatted]", params.text_document.uri),
 7981            )]))
 7982        })
 7983        .detach();
 7984    save.await;
 7985
 7986    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7987    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7988    assert_eq!(
 7989        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7990        uri!(
 7991            "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}"
 7992        ),
 7993    );
 7994    buffer_1.update(cx, |buffer, _| {
 7995        assert!(!buffer.is_dirty());
 7996        assert_eq!(
 7997            buffer.text(),
 7998            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7999        )
 8000    });
 8001    buffer_2.update(cx, |buffer, _| {
 8002        assert!(!buffer.is_dirty());
 8003        assert_eq!(
 8004            buffer.text(),
 8005            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8006        )
 8007    });
 8008    buffer_3.update(cx, |buffer, _| {
 8009        assert!(!buffer.is_dirty());
 8010        assert_eq!(buffer.text(), sample_text_3,)
 8011    });
 8012}
 8013
 8014#[gpui::test]
 8015async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8016    init_test(cx, |_| {});
 8017
 8018    let fs = FakeFs::new(cx.executor());
 8019    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8020
 8021    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8022
 8023    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8024    language_registry.add(rust_lang());
 8025    let mut fake_servers = language_registry.register_fake_lsp(
 8026        "Rust",
 8027        FakeLspAdapter {
 8028            capabilities: lsp::ServerCapabilities {
 8029                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8030                ..Default::default()
 8031            },
 8032            ..Default::default()
 8033        },
 8034    );
 8035
 8036    let buffer = project
 8037        .update(cx, |project, cx| {
 8038            project.open_local_buffer(path!("/file.rs"), cx)
 8039        })
 8040        .await
 8041        .unwrap();
 8042
 8043    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8044    let (editor, cx) = cx.add_window_view(|window, cx| {
 8045        build_editor_with_project(project.clone(), buffer, window, cx)
 8046    });
 8047    editor.update_in(cx, |editor, window, cx| {
 8048        editor.set_text("one\ntwo\nthree\n", window, cx)
 8049    });
 8050    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8051
 8052    cx.executor().start_waiting();
 8053    let fake_server = fake_servers.next().await.unwrap();
 8054
 8055    let save = editor
 8056        .update_in(cx, |editor, window, cx| {
 8057            editor.save(true, project.clone(), window, cx)
 8058        })
 8059        .unwrap();
 8060    fake_server
 8061        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8062            assert_eq!(
 8063                params.text_document.uri,
 8064                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8065            );
 8066            assert_eq!(params.options.tab_size, 4);
 8067            Ok(Some(vec![lsp::TextEdit::new(
 8068                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8069                ", ".to_string(),
 8070            )]))
 8071        })
 8072        .next()
 8073        .await;
 8074    cx.executor().start_waiting();
 8075    save.await;
 8076    assert_eq!(
 8077        editor.update(cx, |editor, cx| editor.text(cx)),
 8078        "one, two\nthree\n"
 8079    );
 8080    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8081
 8082    editor.update_in(cx, |editor, window, cx| {
 8083        editor.set_text("one\ntwo\nthree\n", window, cx)
 8084    });
 8085    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8086
 8087    // Ensure we can still save even if formatting hangs.
 8088    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8089        move |params, _| async move {
 8090            assert_eq!(
 8091                params.text_document.uri,
 8092                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8093            );
 8094            futures::future::pending::<()>().await;
 8095            unreachable!()
 8096        },
 8097    );
 8098    let save = editor
 8099        .update_in(cx, |editor, window, cx| {
 8100            editor.save(true, project.clone(), window, cx)
 8101        })
 8102        .unwrap();
 8103    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8104    cx.executor().start_waiting();
 8105    save.await;
 8106    assert_eq!(
 8107        editor.update(cx, |editor, cx| editor.text(cx)),
 8108        "one\ntwo\nthree\n"
 8109    );
 8110    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8111
 8112    // For non-dirty buffer, no formatting request should be sent
 8113    let save = editor
 8114        .update_in(cx, |editor, window, cx| {
 8115            editor.save(true, project.clone(), window, cx)
 8116        })
 8117        .unwrap();
 8118    let _pending_format_request = fake_server
 8119        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8120            panic!("Should not be invoked on non-dirty buffer");
 8121        })
 8122        .next();
 8123    cx.executor().start_waiting();
 8124    save.await;
 8125
 8126    // Set Rust language override and assert overridden tabsize is sent to language server
 8127    update_test_language_settings(cx, |settings| {
 8128        settings.languages.insert(
 8129            "Rust".into(),
 8130            LanguageSettingsContent {
 8131                tab_size: NonZeroU32::new(8),
 8132                ..Default::default()
 8133            },
 8134        );
 8135    });
 8136
 8137    editor.update_in(cx, |editor, window, cx| {
 8138        editor.set_text("somehting_new\n", window, cx)
 8139    });
 8140    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8141    let save = editor
 8142        .update_in(cx, |editor, window, cx| {
 8143            editor.save(true, project.clone(), window, cx)
 8144        })
 8145        .unwrap();
 8146    fake_server
 8147        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8148            assert_eq!(
 8149                params.text_document.uri,
 8150                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8151            );
 8152            assert_eq!(params.options.tab_size, 8);
 8153            Ok(Some(vec![]))
 8154        })
 8155        .next()
 8156        .await;
 8157    cx.executor().start_waiting();
 8158    save.await;
 8159}
 8160
 8161#[gpui::test]
 8162async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8163    init_test(cx, |settings| {
 8164        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8165            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8166        ))
 8167    });
 8168
 8169    let fs = FakeFs::new(cx.executor());
 8170    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8171
 8172    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8173
 8174    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8175    language_registry.add(Arc::new(Language::new(
 8176        LanguageConfig {
 8177            name: "Rust".into(),
 8178            matcher: LanguageMatcher {
 8179                path_suffixes: vec!["rs".to_string()],
 8180                ..Default::default()
 8181            },
 8182            ..LanguageConfig::default()
 8183        },
 8184        Some(tree_sitter_rust::LANGUAGE.into()),
 8185    )));
 8186    update_test_language_settings(cx, |settings| {
 8187        // Enable Prettier formatting for the same buffer, and ensure
 8188        // LSP is called instead of Prettier.
 8189        settings.defaults.prettier = Some(PrettierSettings {
 8190            allowed: true,
 8191            ..PrettierSettings::default()
 8192        });
 8193    });
 8194    let mut fake_servers = language_registry.register_fake_lsp(
 8195        "Rust",
 8196        FakeLspAdapter {
 8197            capabilities: lsp::ServerCapabilities {
 8198                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8199                ..Default::default()
 8200            },
 8201            ..Default::default()
 8202        },
 8203    );
 8204
 8205    let buffer = project
 8206        .update(cx, |project, cx| {
 8207            project.open_local_buffer(path!("/file.rs"), cx)
 8208        })
 8209        .await
 8210        .unwrap();
 8211
 8212    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8213    let (editor, cx) = cx.add_window_view(|window, cx| {
 8214        build_editor_with_project(project.clone(), buffer, window, cx)
 8215    });
 8216    editor.update_in(cx, |editor, window, cx| {
 8217        editor.set_text("one\ntwo\nthree\n", window, cx)
 8218    });
 8219
 8220    cx.executor().start_waiting();
 8221    let fake_server = fake_servers.next().await.unwrap();
 8222
 8223    let format = editor
 8224        .update_in(cx, |editor, window, cx| {
 8225            editor.perform_format(
 8226                project.clone(),
 8227                FormatTrigger::Manual,
 8228                FormatTarget::Buffers,
 8229                window,
 8230                cx,
 8231            )
 8232        })
 8233        .unwrap();
 8234    fake_server
 8235        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8236            assert_eq!(
 8237                params.text_document.uri,
 8238                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8239            );
 8240            assert_eq!(params.options.tab_size, 4);
 8241            Ok(Some(vec![lsp::TextEdit::new(
 8242                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8243                ", ".to_string(),
 8244            )]))
 8245        })
 8246        .next()
 8247        .await;
 8248    cx.executor().start_waiting();
 8249    format.await;
 8250    assert_eq!(
 8251        editor.update(cx, |editor, cx| editor.text(cx)),
 8252        "one, two\nthree\n"
 8253    );
 8254
 8255    editor.update_in(cx, |editor, window, cx| {
 8256        editor.set_text("one\ntwo\nthree\n", window, cx)
 8257    });
 8258    // Ensure we don't lock if formatting hangs.
 8259    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8260        move |params, _| async move {
 8261            assert_eq!(
 8262                params.text_document.uri,
 8263                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8264            );
 8265            futures::future::pending::<()>().await;
 8266            unreachable!()
 8267        },
 8268    );
 8269    let format = editor
 8270        .update_in(cx, |editor, window, cx| {
 8271            editor.perform_format(
 8272                project,
 8273                FormatTrigger::Manual,
 8274                FormatTarget::Buffers,
 8275                window,
 8276                cx,
 8277            )
 8278        })
 8279        .unwrap();
 8280    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8281    cx.executor().start_waiting();
 8282    format.await;
 8283    assert_eq!(
 8284        editor.update(cx, |editor, cx| editor.text(cx)),
 8285        "one\ntwo\nthree\n"
 8286    );
 8287}
 8288
 8289#[gpui::test]
 8290async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8291    init_test(cx, |settings| {
 8292        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8293            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8294        ))
 8295    });
 8296
 8297    let fs = FakeFs::new(cx.executor());
 8298    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8299
 8300    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8301
 8302    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8303    language_registry.add(Arc::new(Language::new(
 8304        LanguageConfig {
 8305            name: "TypeScript".into(),
 8306            matcher: LanguageMatcher {
 8307                path_suffixes: vec!["ts".to_string()],
 8308                ..Default::default()
 8309            },
 8310            ..LanguageConfig::default()
 8311        },
 8312        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8313    )));
 8314    update_test_language_settings(cx, |settings| {
 8315        settings.defaults.prettier = Some(PrettierSettings {
 8316            allowed: true,
 8317            ..PrettierSettings::default()
 8318        });
 8319    });
 8320    let mut fake_servers = language_registry.register_fake_lsp(
 8321        "TypeScript",
 8322        FakeLspAdapter {
 8323            capabilities: lsp::ServerCapabilities {
 8324                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8325                ..Default::default()
 8326            },
 8327            ..Default::default()
 8328        },
 8329    );
 8330
 8331    let buffer = project
 8332        .update(cx, |project, cx| {
 8333            project.open_local_buffer(path!("/file.ts"), cx)
 8334        })
 8335        .await
 8336        .unwrap();
 8337
 8338    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8339    let (editor, cx) = cx.add_window_view(|window, cx| {
 8340        build_editor_with_project(project.clone(), buffer, window, cx)
 8341    });
 8342    editor.update_in(cx, |editor, window, cx| {
 8343        editor.set_text(
 8344            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8345            window,
 8346            cx,
 8347        )
 8348    });
 8349
 8350    cx.executor().start_waiting();
 8351    let fake_server = fake_servers.next().await.unwrap();
 8352
 8353    let format = editor
 8354        .update_in(cx, |editor, window, cx| {
 8355            editor.perform_code_action_kind(
 8356                project.clone(),
 8357                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8358                window,
 8359                cx,
 8360            )
 8361        })
 8362        .unwrap();
 8363    fake_server
 8364        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8365            assert_eq!(
 8366                params.text_document.uri,
 8367                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8368            );
 8369            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8370                lsp::CodeAction {
 8371                    title: "Organize Imports".to_string(),
 8372                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8373                    edit: Some(lsp::WorkspaceEdit {
 8374                        changes: Some(
 8375                            [(
 8376                                params.text_document.uri.clone(),
 8377                                vec![lsp::TextEdit::new(
 8378                                    lsp::Range::new(
 8379                                        lsp::Position::new(1, 0),
 8380                                        lsp::Position::new(2, 0),
 8381                                    ),
 8382                                    "".to_string(),
 8383                                )],
 8384                            )]
 8385                            .into_iter()
 8386                            .collect(),
 8387                        ),
 8388                        ..Default::default()
 8389                    }),
 8390                    ..Default::default()
 8391                },
 8392            )]))
 8393        })
 8394        .next()
 8395        .await;
 8396    cx.executor().start_waiting();
 8397    format.await;
 8398    assert_eq!(
 8399        editor.update(cx, |editor, cx| editor.text(cx)),
 8400        "import { a } from 'module';\n\nconst x = a;\n"
 8401    );
 8402
 8403    editor.update_in(cx, |editor, window, cx| {
 8404        editor.set_text(
 8405            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8406            window,
 8407            cx,
 8408        )
 8409    });
 8410    // Ensure we don't lock if code action hangs.
 8411    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8412        move |params, _| async move {
 8413            assert_eq!(
 8414                params.text_document.uri,
 8415                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8416            );
 8417            futures::future::pending::<()>().await;
 8418            unreachable!()
 8419        },
 8420    );
 8421    let format = editor
 8422        .update_in(cx, |editor, window, cx| {
 8423            editor.perform_code_action_kind(
 8424                project,
 8425                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8426                window,
 8427                cx,
 8428            )
 8429        })
 8430        .unwrap();
 8431    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8432    cx.executor().start_waiting();
 8433    format.await;
 8434    assert_eq!(
 8435        editor.update(cx, |editor, cx| editor.text(cx)),
 8436        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8437    );
 8438}
 8439
 8440#[gpui::test]
 8441async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8442    init_test(cx, |_| {});
 8443
 8444    let mut cx = EditorLspTestContext::new_rust(
 8445        lsp::ServerCapabilities {
 8446            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8447            ..Default::default()
 8448        },
 8449        cx,
 8450    )
 8451    .await;
 8452
 8453    cx.set_state(indoc! {"
 8454        one.twoˇ
 8455    "});
 8456
 8457    // The format request takes a long time. When it completes, it inserts
 8458    // a newline and an indent before the `.`
 8459    cx.lsp
 8460        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8461            let executor = cx.background_executor().clone();
 8462            async move {
 8463                executor.timer(Duration::from_millis(100)).await;
 8464                Ok(Some(vec![lsp::TextEdit {
 8465                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8466                    new_text: "\n    ".into(),
 8467                }]))
 8468            }
 8469        });
 8470
 8471    // Submit a format request.
 8472    let format_1 = cx
 8473        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8474        .unwrap();
 8475    cx.executor().run_until_parked();
 8476
 8477    // Submit a second format request.
 8478    let format_2 = cx
 8479        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8480        .unwrap();
 8481    cx.executor().run_until_parked();
 8482
 8483    // Wait for both format requests to complete
 8484    cx.executor().advance_clock(Duration::from_millis(200));
 8485    cx.executor().start_waiting();
 8486    format_1.await.unwrap();
 8487    cx.executor().start_waiting();
 8488    format_2.await.unwrap();
 8489
 8490    // The formatting edits only happens once.
 8491    cx.assert_editor_state(indoc! {"
 8492        one
 8493            .twoˇ
 8494    "});
 8495}
 8496
 8497#[gpui::test]
 8498async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8499    init_test(cx, |settings| {
 8500        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8501    });
 8502
 8503    let mut cx = EditorLspTestContext::new_rust(
 8504        lsp::ServerCapabilities {
 8505            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8506            ..Default::default()
 8507        },
 8508        cx,
 8509    )
 8510    .await;
 8511
 8512    // Set up a buffer white some trailing whitespace and no trailing newline.
 8513    cx.set_state(
 8514        &[
 8515            "one ",   //
 8516            "twoˇ",   //
 8517            "three ", //
 8518            "four",   //
 8519        ]
 8520        .join("\n"),
 8521    );
 8522
 8523    // Submit a format request.
 8524    let format = cx
 8525        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8526        .unwrap();
 8527
 8528    // Record which buffer changes have been sent to the language server
 8529    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8530    cx.lsp
 8531        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8532            let buffer_changes = buffer_changes.clone();
 8533            move |params, _| {
 8534                buffer_changes.lock().extend(
 8535                    params
 8536                        .content_changes
 8537                        .into_iter()
 8538                        .map(|e| (e.range.unwrap(), e.text)),
 8539                );
 8540            }
 8541        });
 8542
 8543    // Handle formatting requests to the language server.
 8544    cx.lsp
 8545        .set_request_handler::<lsp::request::Formatting, _, _>({
 8546            let buffer_changes = buffer_changes.clone();
 8547            move |_, _| {
 8548                // When formatting is requested, trailing whitespace has already been stripped,
 8549                // and the trailing newline has already been added.
 8550                assert_eq!(
 8551                    &buffer_changes.lock()[1..],
 8552                    &[
 8553                        (
 8554                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8555                            "".into()
 8556                        ),
 8557                        (
 8558                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8559                            "".into()
 8560                        ),
 8561                        (
 8562                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8563                            "\n".into()
 8564                        ),
 8565                    ]
 8566                );
 8567
 8568                // Insert blank lines between each line of the buffer.
 8569                async move {
 8570                    Ok(Some(vec![
 8571                        lsp::TextEdit {
 8572                            range: lsp::Range::new(
 8573                                lsp::Position::new(1, 0),
 8574                                lsp::Position::new(1, 0),
 8575                            ),
 8576                            new_text: "\n".into(),
 8577                        },
 8578                        lsp::TextEdit {
 8579                            range: lsp::Range::new(
 8580                                lsp::Position::new(2, 0),
 8581                                lsp::Position::new(2, 0),
 8582                            ),
 8583                            new_text: "\n".into(),
 8584                        },
 8585                    ]))
 8586                }
 8587            }
 8588        });
 8589
 8590    // After formatting the buffer, the trailing whitespace is stripped,
 8591    // a newline is appended, and the edits provided by the language server
 8592    // have been applied.
 8593    format.await.unwrap();
 8594    cx.assert_editor_state(
 8595        &[
 8596            "one",   //
 8597            "",      //
 8598            "twoˇ",  //
 8599            "",      //
 8600            "three", //
 8601            "four",  //
 8602            "",      //
 8603        ]
 8604        .join("\n"),
 8605    );
 8606
 8607    // Undoing the formatting undoes the trailing whitespace removal, the
 8608    // trailing newline, and the LSP edits.
 8609    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8610    cx.assert_editor_state(
 8611        &[
 8612            "one ",   //
 8613            "twoˇ",   //
 8614            "three ", //
 8615            "four",   //
 8616        ]
 8617        .join("\n"),
 8618    );
 8619}
 8620
 8621#[gpui::test]
 8622async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8623    cx: &mut TestAppContext,
 8624) {
 8625    init_test(cx, |_| {});
 8626
 8627    cx.update(|cx| {
 8628        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8629            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8630                settings.auto_signature_help = Some(true);
 8631            });
 8632        });
 8633    });
 8634
 8635    let mut cx = EditorLspTestContext::new_rust(
 8636        lsp::ServerCapabilities {
 8637            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8638                ..Default::default()
 8639            }),
 8640            ..Default::default()
 8641        },
 8642        cx,
 8643    )
 8644    .await;
 8645
 8646    let language = Language::new(
 8647        LanguageConfig {
 8648            name: "Rust".into(),
 8649            brackets: BracketPairConfig {
 8650                pairs: vec![
 8651                    BracketPair {
 8652                        start: "{".to_string(),
 8653                        end: "}".to_string(),
 8654                        close: true,
 8655                        surround: true,
 8656                        newline: true,
 8657                    },
 8658                    BracketPair {
 8659                        start: "(".to_string(),
 8660                        end: ")".to_string(),
 8661                        close: true,
 8662                        surround: true,
 8663                        newline: true,
 8664                    },
 8665                    BracketPair {
 8666                        start: "/*".to_string(),
 8667                        end: " */".to_string(),
 8668                        close: true,
 8669                        surround: true,
 8670                        newline: true,
 8671                    },
 8672                    BracketPair {
 8673                        start: "[".to_string(),
 8674                        end: "]".to_string(),
 8675                        close: false,
 8676                        surround: false,
 8677                        newline: true,
 8678                    },
 8679                    BracketPair {
 8680                        start: "\"".to_string(),
 8681                        end: "\"".to_string(),
 8682                        close: true,
 8683                        surround: true,
 8684                        newline: false,
 8685                    },
 8686                    BracketPair {
 8687                        start: "<".to_string(),
 8688                        end: ">".to_string(),
 8689                        close: false,
 8690                        surround: true,
 8691                        newline: true,
 8692                    },
 8693                ],
 8694                ..Default::default()
 8695            },
 8696            autoclose_before: "})]".to_string(),
 8697            ..Default::default()
 8698        },
 8699        Some(tree_sitter_rust::LANGUAGE.into()),
 8700    );
 8701    let language = Arc::new(language);
 8702
 8703    cx.language_registry().add(language.clone());
 8704    cx.update_buffer(|buffer, cx| {
 8705        buffer.set_language(Some(language), cx);
 8706    });
 8707
 8708    cx.set_state(
 8709        &r#"
 8710            fn main() {
 8711                sampleˇ
 8712            }
 8713        "#
 8714        .unindent(),
 8715    );
 8716
 8717    cx.update_editor(|editor, window, cx| {
 8718        editor.handle_input("(", window, cx);
 8719    });
 8720    cx.assert_editor_state(
 8721        &"
 8722            fn main() {
 8723                sample(ˇ)
 8724            }
 8725        "
 8726        .unindent(),
 8727    );
 8728
 8729    let mocked_response = lsp::SignatureHelp {
 8730        signatures: vec![lsp::SignatureInformation {
 8731            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8732            documentation: None,
 8733            parameters: Some(vec![
 8734                lsp::ParameterInformation {
 8735                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8736                    documentation: None,
 8737                },
 8738                lsp::ParameterInformation {
 8739                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8740                    documentation: None,
 8741                },
 8742            ]),
 8743            active_parameter: None,
 8744        }],
 8745        active_signature: Some(0),
 8746        active_parameter: Some(0),
 8747    };
 8748    handle_signature_help_request(&mut cx, mocked_response).await;
 8749
 8750    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8751        .await;
 8752
 8753    cx.editor(|editor, _, _| {
 8754        let signature_help_state = editor.signature_help_state.popover().cloned();
 8755        assert_eq!(
 8756            signature_help_state.unwrap().label,
 8757            "param1: u8, param2: u8"
 8758        );
 8759    });
 8760}
 8761
 8762#[gpui::test]
 8763async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8764    init_test(cx, |_| {});
 8765
 8766    cx.update(|cx| {
 8767        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8768            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8769                settings.auto_signature_help = Some(false);
 8770                settings.show_signature_help_after_edits = Some(false);
 8771            });
 8772        });
 8773    });
 8774
 8775    let mut cx = EditorLspTestContext::new_rust(
 8776        lsp::ServerCapabilities {
 8777            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8778                ..Default::default()
 8779            }),
 8780            ..Default::default()
 8781        },
 8782        cx,
 8783    )
 8784    .await;
 8785
 8786    let language = Language::new(
 8787        LanguageConfig {
 8788            name: "Rust".into(),
 8789            brackets: BracketPairConfig {
 8790                pairs: vec![
 8791                    BracketPair {
 8792                        start: "{".to_string(),
 8793                        end: "}".to_string(),
 8794                        close: true,
 8795                        surround: true,
 8796                        newline: true,
 8797                    },
 8798                    BracketPair {
 8799                        start: "(".to_string(),
 8800                        end: ")".to_string(),
 8801                        close: true,
 8802                        surround: true,
 8803                        newline: true,
 8804                    },
 8805                    BracketPair {
 8806                        start: "/*".to_string(),
 8807                        end: " */".to_string(),
 8808                        close: true,
 8809                        surround: true,
 8810                        newline: true,
 8811                    },
 8812                    BracketPair {
 8813                        start: "[".to_string(),
 8814                        end: "]".to_string(),
 8815                        close: false,
 8816                        surround: false,
 8817                        newline: true,
 8818                    },
 8819                    BracketPair {
 8820                        start: "\"".to_string(),
 8821                        end: "\"".to_string(),
 8822                        close: true,
 8823                        surround: true,
 8824                        newline: false,
 8825                    },
 8826                    BracketPair {
 8827                        start: "<".to_string(),
 8828                        end: ">".to_string(),
 8829                        close: false,
 8830                        surround: true,
 8831                        newline: true,
 8832                    },
 8833                ],
 8834                ..Default::default()
 8835            },
 8836            autoclose_before: "})]".to_string(),
 8837            ..Default::default()
 8838        },
 8839        Some(tree_sitter_rust::LANGUAGE.into()),
 8840    );
 8841    let language = Arc::new(language);
 8842
 8843    cx.language_registry().add(language.clone());
 8844    cx.update_buffer(|buffer, cx| {
 8845        buffer.set_language(Some(language), cx);
 8846    });
 8847
 8848    // Ensure that signature_help is not called when no signature help is enabled.
 8849    cx.set_state(
 8850        &r#"
 8851            fn main() {
 8852                sampleˇ
 8853            }
 8854        "#
 8855        .unindent(),
 8856    );
 8857    cx.update_editor(|editor, window, cx| {
 8858        editor.handle_input("(", window, cx);
 8859    });
 8860    cx.assert_editor_state(
 8861        &"
 8862            fn main() {
 8863                sample(ˇ)
 8864            }
 8865        "
 8866        .unindent(),
 8867    );
 8868    cx.editor(|editor, _, _| {
 8869        assert!(editor.signature_help_state.task().is_none());
 8870    });
 8871
 8872    let mocked_response = lsp::SignatureHelp {
 8873        signatures: vec![lsp::SignatureInformation {
 8874            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8875            documentation: None,
 8876            parameters: Some(vec![
 8877                lsp::ParameterInformation {
 8878                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8879                    documentation: None,
 8880                },
 8881                lsp::ParameterInformation {
 8882                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8883                    documentation: None,
 8884                },
 8885            ]),
 8886            active_parameter: None,
 8887        }],
 8888        active_signature: Some(0),
 8889        active_parameter: Some(0),
 8890    };
 8891
 8892    // Ensure that signature_help is called when enabled afte edits
 8893    cx.update(|_, cx| {
 8894        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8895            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8896                settings.auto_signature_help = Some(false);
 8897                settings.show_signature_help_after_edits = Some(true);
 8898            });
 8899        });
 8900    });
 8901    cx.set_state(
 8902        &r#"
 8903            fn main() {
 8904                sampleˇ
 8905            }
 8906        "#
 8907        .unindent(),
 8908    );
 8909    cx.update_editor(|editor, window, cx| {
 8910        editor.handle_input("(", window, cx);
 8911    });
 8912    cx.assert_editor_state(
 8913        &"
 8914            fn main() {
 8915                sample(ˇ)
 8916            }
 8917        "
 8918        .unindent(),
 8919    );
 8920    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8921    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8922        .await;
 8923    cx.update_editor(|editor, _, _| {
 8924        let signature_help_state = editor.signature_help_state.popover().cloned();
 8925        assert!(signature_help_state.is_some());
 8926        assert_eq!(
 8927            signature_help_state.unwrap().label,
 8928            "param1: u8, param2: u8"
 8929        );
 8930        editor.signature_help_state = SignatureHelpState::default();
 8931    });
 8932
 8933    // Ensure that signature_help is called when auto signature help override is enabled
 8934    cx.update(|_, cx| {
 8935        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8936            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8937                settings.auto_signature_help = Some(true);
 8938                settings.show_signature_help_after_edits = Some(false);
 8939            });
 8940        });
 8941    });
 8942    cx.set_state(
 8943        &r#"
 8944            fn main() {
 8945                sampleˇ
 8946            }
 8947        "#
 8948        .unindent(),
 8949    );
 8950    cx.update_editor(|editor, window, cx| {
 8951        editor.handle_input("(", window, cx);
 8952    });
 8953    cx.assert_editor_state(
 8954        &"
 8955            fn main() {
 8956                sample(ˇ)
 8957            }
 8958        "
 8959        .unindent(),
 8960    );
 8961    handle_signature_help_request(&mut cx, mocked_response).await;
 8962    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8963        .await;
 8964    cx.editor(|editor, _, _| {
 8965        let signature_help_state = editor.signature_help_state.popover().cloned();
 8966        assert!(signature_help_state.is_some());
 8967        assert_eq!(
 8968            signature_help_state.unwrap().label,
 8969            "param1: u8, param2: u8"
 8970        );
 8971    });
 8972}
 8973
 8974#[gpui::test]
 8975async fn test_signature_help(cx: &mut TestAppContext) {
 8976    init_test(cx, |_| {});
 8977    cx.update(|cx| {
 8978        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8979            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8980                settings.auto_signature_help = Some(true);
 8981            });
 8982        });
 8983    });
 8984
 8985    let mut cx = EditorLspTestContext::new_rust(
 8986        lsp::ServerCapabilities {
 8987            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8988                ..Default::default()
 8989            }),
 8990            ..Default::default()
 8991        },
 8992        cx,
 8993    )
 8994    .await;
 8995
 8996    // A test that directly calls `show_signature_help`
 8997    cx.update_editor(|editor, window, cx| {
 8998        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8999    });
 9000
 9001    let mocked_response = lsp::SignatureHelp {
 9002        signatures: vec![lsp::SignatureInformation {
 9003            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9004            documentation: None,
 9005            parameters: Some(vec![
 9006                lsp::ParameterInformation {
 9007                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9008                    documentation: None,
 9009                },
 9010                lsp::ParameterInformation {
 9011                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9012                    documentation: None,
 9013                },
 9014            ]),
 9015            active_parameter: None,
 9016        }],
 9017        active_signature: Some(0),
 9018        active_parameter: Some(0),
 9019    };
 9020    handle_signature_help_request(&mut cx, mocked_response).await;
 9021
 9022    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9023        .await;
 9024
 9025    cx.editor(|editor, _, _| {
 9026        let signature_help_state = editor.signature_help_state.popover().cloned();
 9027        assert!(signature_help_state.is_some());
 9028        assert_eq!(
 9029            signature_help_state.unwrap().label,
 9030            "param1: u8, param2: u8"
 9031        );
 9032    });
 9033
 9034    // When exiting outside from inside the brackets, `signature_help` is closed.
 9035    cx.set_state(indoc! {"
 9036        fn main() {
 9037            sample(ˇ);
 9038        }
 9039
 9040        fn sample(param1: u8, param2: u8) {}
 9041    "});
 9042
 9043    cx.update_editor(|editor, window, cx| {
 9044        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9045    });
 9046
 9047    let mocked_response = lsp::SignatureHelp {
 9048        signatures: Vec::new(),
 9049        active_signature: None,
 9050        active_parameter: None,
 9051    };
 9052    handle_signature_help_request(&mut cx, mocked_response).await;
 9053
 9054    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9055        .await;
 9056
 9057    cx.editor(|editor, _, _| {
 9058        assert!(!editor.signature_help_state.is_shown());
 9059    });
 9060
 9061    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9062    cx.set_state(indoc! {"
 9063        fn main() {
 9064            sample(ˇ);
 9065        }
 9066
 9067        fn sample(param1: u8, param2: u8) {}
 9068    "});
 9069
 9070    let mocked_response = lsp::SignatureHelp {
 9071        signatures: vec![lsp::SignatureInformation {
 9072            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9073            documentation: None,
 9074            parameters: Some(vec![
 9075                lsp::ParameterInformation {
 9076                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9077                    documentation: None,
 9078                },
 9079                lsp::ParameterInformation {
 9080                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9081                    documentation: None,
 9082                },
 9083            ]),
 9084            active_parameter: None,
 9085        }],
 9086        active_signature: Some(0),
 9087        active_parameter: Some(0),
 9088    };
 9089    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9090    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9091        .await;
 9092    cx.editor(|editor, _, _| {
 9093        assert!(editor.signature_help_state.is_shown());
 9094    });
 9095
 9096    // Restore the popover with more parameter input
 9097    cx.set_state(indoc! {"
 9098        fn main() {
 9099            sample(param1, param2ˇ);
 9100        }
 9101
 9102        fn sample(param1: u8, param2: u8) {}
 9103    "});
 9104
 9105    let mocked_response = lsp::SignatureHelp {
 9106        signatures: vec![lsp::SignatureInformation {
 9107            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9108            documentation: None,
 9109            parameters: Some(vec![
 9110                lsp::ParameterInformation {
 9111                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9112                    documentation: None,
 9113                },
 9114                lsp::ParameterInformation {
 9115                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9116                    documentation: None,
 9117                },
 9118            ]),
 9119            active_parameter: None,
 9120        }],
 9121        active_signature: Some(0),
 9122        active_parameter: Some(1),
 9123    };
 9124    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9125    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9126        .await;
 9127
 9128    // When selecting a range, the popover is gone.
 9129    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9130    cx.update_editor(|editor, window, cx| {
 9131        editor.change_selections(None, window, cx, |s| {
 9132            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9133        })
 9134    });
 9135    cx.assert_editor_state(indoc! {"
 9136        fn main() {
 9137            sample(param1, «ˇparam2»);
 9138        }
 9139
 9140        fn sample(param1: u8, param2: u8) {}
 9141    "});
 9142    cx.editor(|editor, _, _| {
 9143        assert!(!editor.signature_help_state.is_shown());
 9144    });
 9145
 9146    // When unselecting again, the popover is back if within the brackets.
 9147    cx.update_editor(|editor, window, cx| {
 9148        editor.change_selections(None, window, cx, |s| {
 9149            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9150        })
 9151    });
 9152    cx.assert_editor_state(indoc! {"
 9153        fn main() {
 9154            sample(param1, ˇparam2);
 9155        }
 9156
 9157        fn sample(param1: u8, param2: u8) {}
 9158    "});
 9159    handle_signature_help_request(&mut cx, mocked_response).await;
 9160    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9161        .await;
 9162    cx.editor(|editor, _, _| {
 9163        assert!(editor.signature_help_state.is_shown());
 9164    });
 9165
 9166    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9167    cx.update_editor(|editor, window, cx| {
 9168        editor.change_selections(None, window, cx, |s| {
 9169            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9170            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9171        })
 9172    });
 9173    cx.assert_editor_state(indoc! {"
 9174        fn main() {
 9175            sample(param1, ˇparam2);
 9176        }
 9177
 9178        fn sample(param1: u8, param2: u8) {}
 9179    "});
 9180
 9181    let mocked_response = lsp::SignatureHelp {
 9182        signatures: vec![lsp::SignatureInformation {
 9183            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9184            documentation: None,
 9185            parameters: Some(vec![
 9186                lsp::ParameterInformation {
 9187                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9188                    documentation: None,
 9189                },
 9190                lsp::ParameterInformation {
 9191                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9192                    documentation: None,
 9193                },
 9194            ]),
 9195            active_parameter: None,
 9196        }],
 9197        active_signature: Some(0),
 9198        active_parameter: Some(1),
 9199    };
 9200    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9201    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9202        .await;
 9203    cx.update_editor(|editor, _, cx| {
 9204        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9205    });
 9206    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9207        .await;
 9208    cx.update_editor(|editor, window, cx| {
 9209        editor.change_selections(None, window, cx, |s| {
 9210            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9211        })
 9212    });
 9213    cx.assert_editor_state(indoc! {"
 9214        fn main() {
 9215            sample(param1, «ˇparam2»);
 9216        }
 9217
 9218        fn sample(param1: u8, param2: u8) {}
 9219    "});
 9220    cx.update_editor(|editor, window, cx| {
 9221        editor.change_selections(None, window, cx, |s| {
 9222            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9223        })
 9224    });
 9225    cx.assert_editor_state(indoc! {"
 9226        fn main() {
 9227            sample(param1, ˇparam2);
 9228        }
 9229
 9230        fn sample(param1: u8, param2: u8) {}
 9231    "});
 9232    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9233        .await;
 9234}
 9235
 9236#[gpui::test]
 9237async fn test_completion_mode(cx: &mut TestAppContext) {
 9238    init_test(cx, |_| {});
 9239    let mut cx = EditorLspTestContext::new_rust(
 9240        lsp::ServerCapabilities {
 9241            completion_provider: Some(lsp::CompletionOptions {
 9242                resolve_provider: Some(true),
 9243                ..Default::default()
 9244            }),
 9245            ..Default::default()
 9246        },
 9247        cx,
 9248    )
 9249    .await;
 9250
 9251    struct Run {
 9252        run_description: &'static str,
 9253        initial_state: String,
 9254        buffer_marked_text: String,
 9255        completion_text: &'static str,
 9256        expected_with_insert_mode: String,
 9257        expected_with_replace_mode: String,
 9258        expected_with_replace_subsequence_mode: String,
 9259        expected_with_replace_suffix_mode: String,
 9260    }
 9261
 9262    let runs = [
 9263        Run {
 9264            run_description: "Start of word matches completion text",
 9265            initial_state: "before ediˇ after".into(),
 9266            buffer_marked_text: "before <edi|> after".into(),
 9267            completion_text: "editor",
 9268            expected_with_insert_mode: "before editorˇ after".into(),
 9269            expected_with_replace_mode: "before editorˇ after".into(),
 9270            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9271            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9272        },
 9273        Run {
 9274            run_description: "Accept same text at the middle of the word",
 9275            initial_state: "before ediˇtor after".into(),
 9276            buffer_marked_text: "before <edi|tor> after".into(),
 9277            completion_text: "editor",
 9278            expected_with_insert_mode: "before editorˇtor after".into(),
 9279            expected_with_replace_mode: "before ediˇtor after".into(),
 9280            expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
 9281            expected_with_replace_suffix_mode: "before ediˇtor after".into(),
 9282        },
 9283        Run {
 9284            run_description: "End of word matches completion text -- cursor at end",
 9285            initial_state: "before torˇ after".into(),
 9286            buffer_marked_text: "before <tor|> after".into(),
 9287            completion_text: "editor",
 9288            expected_with_insert_mode: "before editorˇ after".into(),
 9289            expected_with_replace_mode: "before editorˇ after".into(),
 9290            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9291            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9292        },
 9293        Run {
 9294            run_description: "End of word matches completion text -- cursor at start",
 9295            initial_state: "before ˇtor after".into(),
 9296            buffer_marked_text: "before <|tor> after".into(),
 9297            completion_text: "editor",
 9298            expected_with_insert_mode: "before editorˇtor after".into(),
 9299            expected_with_replace_mode: "before editorˇ after".into(),
 9300            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9301            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9302        },
 9303        Run {
 9304            run_description: "Prepend text containing whitespace",
 9305            initial_state: "pˇfield: bool".into(),
 9306            buffer_marked_text: "<p|field>: bool".into(),
 9307            completion_text: "pub ",
 9308            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9309            expected_with_replace_mode: "pub ˇ: bool".into(),
 9310            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9311            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9312        },
 9313        Run {
 9314            run_description: "Add element to start of list",
 9315            initial_state: "[element_ˇelement_2]".into(),
 9316            buffer_marked_text: "[<element_|element_2>]".into(),
 9317            completion_text: "element_1",
 9318            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9319            expected_with_replace_mode: "[element_1ˇ]".into(),
 9320            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9321            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9322        },
 9323        Run {
 9324            run_description: "Add element to start of list -- first and second elements are equal",
 9325            initial_state: "[elˇelement]".into(),
 9326            buffer_marked_text: "[<el|element>]".into(),
 9327            completion_text: "element",
 9328            expected_with_insert_mode: "[elementˇelement]".into(),
 9329            expected_with_replace_mode: "[elˇement]".into(),
 9330            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9331            expected_with_replace_suffix_mode: "[elˇement]".into(),
 9332        },
 9333        Run {
 9334            run_description: "Ends with matching suffix",
 9335            initial_state: "SubˇError".into(),
 9336            buffer_marked_text: "<Sub|Error>".into(),
 9337            completion_text: "SubscriptionError",
 9338            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9339            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9340            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9341            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9342        },
 9343        Run {
 9344            run_description: "Suffix is a subsequence -- contiguous",
 9345            initial_state: "SubˇErr".into(),
 9346            buffer_marked_text: "<Sub|Err>".into(),
 9347            completion_text: "SubscriptionError",
 9348            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9349            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9350            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9351            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9352        },
 9353        Run {
 9354            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9355            initial_state: "Suˇscrirr".into(),
 9356            buffer_marked_text: "<Su|scrirr>".into(),
 9357            completion_text: "SubscriptionError",
 9358            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9359            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9360            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9361            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9362        },
 9363        Run {
 9364            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9365            initial_state: "foo(indˇix)".into(),
 9366            buffer_marked_text: "foo(<ind|ix>)".into(),
 9367            completion_text: "node_index",
 9368            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9369            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9370            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9371            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9372        },
 9373    ];
 9374
 9375    for run in runs {
 9376        let run_variations = [
 9377            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9378            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9379            (
 9380                LspInsertMode::ReplaceSubsequence,
 9381                run.expected_with_replace_subsequence_mode,
 9382            ),
 9383            (
 9384                LspInsertMode::ReplaceSuffix,
 9385                run.expected_with_replace_suffix_mode,
 9386            ),
 9387        ];
 9388
 9389        for (lsp_insert_mode, expected_text) in run_variations {
 9390            eprintln!(
 9391                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9392                run.run_description,
 9393            );
 9394
 9395            update_test_language_settings(&mut cx, |settings| {
 9396                settings.defaults.completions = Some(CompletionSettings {
 9397                    lsp_insert_mode,
 9398                    words: WordsCompletionMode::Disabled,
 9399                    lsp: true,
 9400                    lsp_fetch_timeout_ms: 0,
 9401                });
 9402            });
 9403
 9404            cx.set_state(&run.initial_state);
 9405            cx.update_editor(|editor, window, cx| {
 9406                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9407            });
 9408
 9409            let counter = Arc::new(AtomicUsize::new(0));
 9410            handle_completion_request_with_insert_and_replace(
 9411                &mut cx,
 9412                &run.buffer_marked_text,
 9413                vec![run.completion_text],
 9414                counter.clone(),
 9415            )
 9416            .await;
 9417            cx.condition(|editor, _| editor.context_menu_visible())
 9418                .await;
 9419            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9420
 9421            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9422                editor
 9423                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9424                    .unwrap()
 9425            });
 9426            cx.assert_editor_state(&expected_text);
 9427            handle_resolve_completion_request(&mut cx, None).await;
 9428            apply_additional_edits.await.unwrap();
 9429        }
 9430    }
 9431}
 9432
 9433#[gpui::test]
 9434async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9435    init_test(cx, |_| {});
 9436    let mut cx = EditorLspTestContext::new_rust(
 9437        lsp::ServerCapabilities {
 9438            completion_provider: Some(lsp::CompletionOptions {
 9439                resolve_provider: Some(true),
 9440                ..Default::default()
 9441            }),
 9442            ..Default::default()
 9443        },
 9444        cx,
 9445    )
 9446    .await;
 9447
 9448    let initial_state = "SubˇError";
 9449    let buffer_marked_text = "<Sub|Error>";
 9450    let completion_text = "SubscriptionError";
 9451    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9452    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9453
 9454    update_test_language_settings(&mut cx, |settings| {
 9455        settings.defaults.completions = Some(CompletionSettings {
 9456            words: WordsCompletionMode::Disabled,
 9457            // set the opposite here to ensure that the action is overriding the default behavior
 9458            lsp_insert_mode: LspInsertMode::Insert,
 9459            lsp: true,
 9460            lsp_fetch_timeout_ms: 0,
 9461        });
 9462    });
 9463
 9464    cx.set_state(initial_state);
 9465    cx.update_editor(|editor, window, cx| {
 9466        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9467    });
 9468
 9469    let counter = Arc::new(AtomicUsize::new(0));
 9470    handle_completion_request_with_insert_and_replace(
 9471        &mut cx,
 9472        &buffer_marked_text,
 9473        vec![completion_text],
 9474        counter.clone(),
 9475    )
 9476    .await;
 9477    cx.condition(|editor, _| editor.context_menu_visible())
 9478        .await;
 9479    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9480
 9481    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9482        editor
 9483            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9484            .unwrap()
 9485    });
 9486    cx.assert_editor_state(&expected_with_replace_mode);
 9487    handle_resolve_completion_request(&mut cx, None).await;
 9488    apply_additional_edits.await.unwrap();
 9489
 9490    update_test_language_settings(&mut cx, |settings| {
 9491        settings.defaults.completions = Some(CompletionSettings {
 9492            words: WordsCompletionMode::Disabled,
 9493            // set the opposite here to ensure that the action is overriding the default behavior
 9494            lsp_insert_mode: LspInsertMode::Replace,
 9495            lsp: true,
 9496            lsp_fetch_timeout_ms: 0,
 9497        });
 9498    });
 9499
 9500    cx.set_state(initial_state);
 9501    cx.update_editor(|editor, window, cx| {
 9502        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9503    });
 9504    handle_completion_request_with_insert_and_replace(
 9505        &mut cx,
 9506        &buffer_marked_text,
 9507        vec![completion_text],
 9508        counter.clone(),
 9509    )
 9510    .await;
 9511    cx.condition(|editor, _| editor.context_menu_visible())
 9512        .await;
 9513    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9514
 9515    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9516        editor
 9517            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9518            .unwrap()
 9519    });
 9520    cx.assert_editor_state(&expected_with_insert_mode);
 9521    handle_resolve_completion_request(&mut cx, None).await;
 9522    apply_additional_edits.await.unwrap();
 9523}
 9524
 9525#[gpui::test]
 9526async fn test_completion(cx: &mut TestAppContext) {
 9527    init_test(cx, |_| {});
 9528
 9529    let mut cx = EditorLspTestContext::new_rust(
 9530        lsp::ServerCapabilities {
 9531            completion_provider: Some(lsp::CompletionOptions {
 9532                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9533                resolve_provider: Some(true),
 9534                ..Default::default()
 9535            }),
 9536            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9537            ..Default::default()
 9538        },
 9539        cx,
 9540    )
 9541    .await;
 9542    let counter = Arc::new(AtomicUsize::new(0));
 9543
 9544    cx.set_state(indoc! {"
 9545        oneˇ
 9546        two
 9547        three
 9548    "});
 9549    cx.simulate_keystroke(".");
 9550    handle_completion_request(
 9551        &mut cx,
 9552        indoc! {"
 9553            one.|<>
 9554            two
 9555            three
 9556        "},
 9557        vec!["first_completion", "second_completion"],
 9558        counter.clone(),
 9559    )
 9560    .await;
 9561    cx.condition(|editor, _| editor.context_menu_visible())
 9562        .await;
 9563    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9564
 9565    let _handler = handle_signature_help_request(
 9566        &mut cx,
 9567        lsp::SignatureHelp {
 9568            signatures: vec![lsp::SignatureInformation {
 9569                label: "test signature".to_string(),
 9570                documentation: None,
 9571                parameters: Some(vec![lsp::ParameterInformation {
 9572                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9573                    documentation: None,
 9574                }]),
 9575                active_parameter: None,
 9576            }],
 9577            active_signature: None,
 9578            active_parameter: None,
 9579        },
 9580    );
 9581    cx.update_editor(|editor, window, cx| {
 9582        assert!(
 9583            !editor.signature_help_state.is_shown(),
 9584            "No signature help was called for"
 9585        );
 9586        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9587    });
 9588    cx.run_until_parked();
 9589    cx.update_editor(|editor, _, _| {
 9590        assert!(
 9591            !editor.signature_help_state.is_shown(),
 9592            "No signature help should be shown when completions menu is open"
 9593        );
 9594    });
 9595
 9596    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9597        editor.context_menu_next(&Default::default(), window, cx);
 9598        editor
 9599            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9600            .unwrap()
 9601    });
 9602    cx.assert_editor_state(indoc! {"
 9603        one.second_completionˇ
 9604        two
 9605        three
 9606    "});
 9607
 9608    handle_resolve_completion_request(
 9609        &mut cx,
 9610        Some(vec![
 9611            (
 9612                //This overlaps with the primary completion edit which is
 9613                //misbehavior from the LSP spec, test that we filter it out
 9614                indoc! {"
 9615                    one.second_ˇcompletion
 9616                    two
 9617                    threeˇ
 9618                "},
 9619                "overlapping additional edit",
 9620            ),
 9621            (
 9622                indoc! {"
 9623                    one.second_completion
 9624                    two
 9625                    threeˇ
 9626                "},
 9627                "\nadditional edit",
 9628            ),
 9629        ]),
 9630    )
 9631    .await;
 9632    apply_additional_edits.await.unwrap();
 9633    cx.assert_editor_state(indoc! {"
 9634        one.second_completionˇ
 9635        two
 9636        three
 9637        additional edit
 9638    "});
 9639
 9640    cx.set_state(indoc! {"
 9641        one.second_completion
 9642        twoˇ
 9643        threeˇ
 9644        additional edit
 9645    "});
 9646    cx.simulate_keystroke(" ");
 9647    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9648    cx.simulate_keystroke("s");
 9649    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9650
 9651    cx.assert_editor_state(indoc! {"
 9652        one.second_completion
 9653        two sˇ
 9654        three sˇ
 9655        additional edit
 9656    "});
 9657    handle_completion_request(
 9658        &mut cx,
 9659        indoc! {"
 9660            one.second_completion
 9661            two s
 9662            three <s|>
 9663            additional edit
 9664        "},
 9665        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9666        counter.clone(),
 9667    )
 9668    .await;
 9669    cx.condition(|editor, _| editor.context_menu_visible())
 9670        .await;
 9671    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9672
 9673    cx.simulate_keystroke("i");
 9674
 9675    handle_completion_request(
 9676        &mut cx,
 9677        indoc! {"
 9678            one.second_completion
 9679            two si
 9680            three <si|>
 9681            additional edit
 9682        "},
 9683        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9684        counter.clone(),
 9685    )
 9686    .await;
 9687    cx.condition(|editor, _| editor.context_menu_visible())
 9688        .await;
 9689    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9690
 9691    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9692        editor
 9693            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9694            .unwrap()
 9695    });
 9696    cx.assert_editor_state(indoc! {"
 9697        one.second_completion
 9698        two sixth_completionˇ
 9699        three sixth_completionˇ
 9700        additional edit
 9701    "});
 9702
 9703    apply_additional_edits.await.unwrap();
 9704
 9705    update_test_language_settings(&mut cx, |settings| {
 9706        settings.defaults.show_completions_on_input = Some(false);
 9707    });
 9708    cx.set_state("editorˇ");
 9709    cx.simulate_keystroke(".");
 9710    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9711    cx.simulate_keystrokes("c l o");
 9712    cx.assert_editor_state("editor.cloˇ");
 9713    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9714    cx.update_editor(|editor, window, cx| {
 9715        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9716    });
 9717    handle_completion_request(
 9718        &mut cx,
 9719        "editor.<clo|>",
 9720        vec!["close", "clobber"],
 9721        counter.clone(),
 9722    )
 9723    .await;
 9724    cx.condition(|editor, _| editor.context_menu_visible())
 9725        .await;
 9726    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9727
 9728    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9729        editor
 9730            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9731            .unwrap()
 9732    });
 9733    cx.assert_editor_state("editor.closeˇ");
 9734    handle_resolve_completion_request(&mut cx, None).await;
 9735    apply_additional_edits.await.unwrap();
 9736}
 9737
 9738#[gpui::test]
 9739async fn test_word_completion(cx: &mut TestAppContext) {
 9740    let lsp_fetch_timeout_ms = 10;
 9741    init_test(cx, |language_settings| {
 9742        language_settings.defaults.completions = Some(CompletionSettings {
 9743            words: WordsCompletionMode::Fallback,
 9744            lsp: true,
 9745            lsp_fetch_timeout_ms: 10,
 9746            lsp_insert_mode: LspInsertMode::Insert,
 9747        });
 9748    });
 9749
 9750    let mut cx = EditorLspTestContext::new_rust(
 9751        lsp::ServerCapabilities {
 9752            completion_provider: Some(lsp::CompletionOptions {
 9753                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9754                ..lsp::CompletionOptions::default()
 9755            }),
 9756            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9757            ..lsp::ServerCapabilities::default()
 9758        },
 9759        cx,
 9760    )
 9761    .await;
 9762
 9763    let throttle_completions = Arc::new(AtomicBool::new(false));
 9764
 9765    let lsp_throttle_completions = throttle_completions.clone();
 9766    let _completion_requests_handler =
 9767        cx.lsp
 9768            .server
 9769            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9770                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9771                let cx = cx.clone();
 9772                async move {
 9773                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9774                        cx.background_executor()
 9775                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9776                            .await;
 9777                    }
 9778                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9779                        lsp::CompletionItem {
 9780                            label: "first".into(),
 9781                            ..lsp::CompletionItem::default()
 9782                        },
 9783                        lsp::CompletionItem {
 9784                            label: "last".into(),
 9785                            ..lsp::CompletionItem::default()
 9786                        },
 9787                    ])))
 9788                }
 9789            });
 9790
 9791    cx.set_state(indoc! {"
 9792        oneˇ
 9793        two
 9794        three
 9795    "});
 9796    cx.simulate_keystroke(".");
 9797    cx.executor().run_until_parked();
 9798    cx.condition(|editor, _| editor.context_menu_visible())
 9799        .await;
 9800    cx.update_editor(|editor, window, cx| {
 9801        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9802        {
 9803            assert_eq!(
 9804                completion_menu_entries(&menu),
 9805                &["first", "last"],
 9806                "When LSP server is fast to reply, no fallback word completions are used"
 9807            );
 9808        } else {
 9809            panic!("expected completion menu to be open");
 9810        }
 9811        editor.cancel(&Cancel, window, cx);
 9812    });
 9813    cx.executor().run_until_parked();
 9814    cx.condition(|editor, _| !editor.context_menu_visible())
 9815        .await;
 9816
 9817    throttle_completions.store(true, atomic::Ordering::Release);
 9818    cx.simulate_keystroke(".");
 9819    cx.executor()
 9820        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9821    cx.executor().run_until_parked();
 9822    cx.condition(|editor, _| editor.context_menu_visible())
 9823        .await;
 9824    cx.update_editor(|editor, _, _| {
 9825        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9826        {
 9827            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9828                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9829        } else {
 9830            panic!("expected completion menu to be open");
 9831        }
 9832    });
 9833}
 9834
 9835#[gpui::test]
 9836async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9837    init_test(cx, |language_settings| {
 9838        language_settings.defaults.completions = Some(CompletionSettings {
 9839            words: WordsCompletionMode::Enabled,
 9840            lsp: true,
 9841            lsp_fetch_timeout_ms: 0,
 9842            lsp_insert_mode: LspInsertMode::Insert,
 9843        });
 9844    });
 9845
 9846    let mut cx = EditorLspTestContext::new_rust(
 9847        lsp::ServerCapabilities {
 9848            completion_provider: Some(lsp::CompletionOptions {
 9849                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9850                ..lsp::CompletionOptions::default()
 9851            }),
 9852            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9853            ..lsp::ServerCapabilities::default()
 9854        },
 9855        cx,
 9856    )
 9857    .await;
 9858
 9859    let _completion_requests_handler =
 9860        cx.lsp
 9861            .server
 9862            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9863                Ok(Some(lsp::CompletionResponse::Array(vec![
 9864                    lsp::CompletionItem {
 9865                        label: "first".into(),
 9866                        ..lsp::CompletionItem::default()
 9867                    },
 9868                    lsp::CompletionItem {
 9869                        label: "last".into(),
 9870                        ..lsp::CompletionItem::default()
 9871                    },
 9872                ])))
 9873            });
 9874
 9875    cx.set_state(indoc! {"ˇ
 9876        first
 9877        last
 9878        second
 9879    "});
 9880    cx.simulate_keystroke(".");
 9881    cx.executor().run_until_parked();
 9882    cx.condition(|editor, _| editor.context_menu_visible())
 9883        .await;
 9884    cx.update_editor(|editor, _, _| {
 9885        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9886        {
 9887            assert_eq!(
 9888                completion_menu_entries(&menu),
 9889                &["first", "last", "second"],
 9890                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9891            );
 9892        } else {
 9893            panic!("expected completion menu to be open");
 9894        }
 9895    });
 9896}
 9897
 9898#[gpui::test]
 9899async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9900    init_test(cx, |language_settings| {
 9901        language_settings.defaults.completions = Some(CompletionSettings {
 9902            words: WordsCompletionMode::Disabled,
 9903            lsp: true,
 9904            lsp_fetch_timeout_ms: 0,
 9905            lsp_insert_mode: LspInsertMode::Insert,
 9906        });
 9907    });
 9908
 9909    let mut cx = EditorLspTestContext::new_rust(
 9910        lsp::ServerCapabilities {
 9911            completion_provider: Some(lsp::CompletionOptions {
 9912                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9913                ..lsp::CompletionOptions::default()
 9914            }),
 9915            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9916            ..lsp::ServerCapabilities::default()
 9917        },
 9918        cx,
 9919    )
 9920    .await;
 9921
 9922    let _completion_requests_handler =
 9923        cx.lsp
 9924            .server
 9925            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9926                panic!("LSP completions should not be queried when dealing with word completions")
 9927            });
 9928
 9929    cx.set_state(indoc! {"ˇ
 9930        first
 9931        last
 9932        second
 9933    "});
 9934    cx.update_editor(|editor, window, cx| {
 9935        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9936    });
 9937    cx.executor().run_until_parked();
 9938    cx.condition(|editor, _| editor.context_menu_visible())
 9939        .await;
 9940    cx.update_editor(|editor, _, _| {
 9941        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9942        {
 9943            assert_eq!(
 9944                completion_menu_entries(&menu),
 9945                &["first", "last", "second"],
 9946                "`ShowWordCompletions` action should show word completions"
 9947            );
 9948        } else {
 9949            panic!("expected completion menu to be open");
 9950        }
 9951    });
 9952
 9953    cx.simulate_keystroke("l");
 9954    cx.executor().run_until_parked();
 9955    cx.condition(|editor, _| editor.context_menu_visible())
 9956        .await;
 9957    cx.update_editor(|editor, _, _| {
 9958        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9959        {
 9960            assert_eq!(
 9961                completion_menu_entries(&menu),
 9962                &["last"],
 9963                "After showing word completions, further editing should filter them and not query the LSP"
 9964            );
 9965        } else {
 9966            panic!("expected completion menu to be open");
 9967        }
 9968    });
 9969}
 9970
 9971#[gpui::test]
 9972async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9973    init_test(cx, |language_settings| {
 9974        language_settings.defaults.completions = Some(CompletionSettings {
 9975            words: WordsCompletionMode::Fallback,
 9976            lsp: false,
 9977            lsp_fetch_timeout_ms: 0,
 9978            lsp_insert_mode: LspInsertMode::Insert,
 9979        });
 9980    });
 9981
 9982    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9983
 9984    cx.set_state(indoc! {"ˇ
 9985        0_usize
 9986        let
 9987        33
 9988        4.5f32
 9989    "});
 9990    cx.update_editor(|editor, window, cx| {
 9991        editor.show_completions(&ShowCompletions::default(), window, cx);
 9992    });
 9993    cx.executor().run_until_parked();
 9994    cx.condition(|editor, _| editor.context_menu_visible())
 9995        .await;
 9996    cx.update_editor(|editor, window, cx| {
 9997        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9998        {
 9999            assert_eq!(
10000                completion_menu_entries(&menu),
10001                &["let"],
10002                "With no digits in the completion query, no digits should be in the word completions"
10003            );
10004        } else {
10005            panic!("expected completion menu to be open");
10006        }
10007        editor.cancel(&Cancel, window, cx);
10008    });
10009
10010    cx.set_state(indoc! {"10011        0_usize
10012        let
10013        3
10014        33.35f32
10015    "});
10016    cx.update_editor(|editor, window, cx| {
10017        editor.show_completions(&ShowCompletions::default(), window, cx);
10018    });
10019    cx.executor().run_until_parked();
10020    cx.condition(|editor, _| editor.context_menu_visible())
10021        .await;
10022    cx.update_editor(|editor, _, _| {
10023        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10024        {
10025            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10026                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10027        } else {
10028            panic!("expected completion menu to be open");
10029        }
10030    });
10031}
10032
10033fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10034    let position = || lsp::Position {
10035        line: params.text_document_position.position.line,
10036        character: params.text_document_position.position.character,
10037    };
10038    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10039        range: lsp::Range {
10040            start: position(),
10041            end: position(),
10042        },
10043        new_text: text.to_string(),
10044    }))
10045}
10046
10047#[gpui::test]
10048async fn test_multiline_completion(cx: &mut TestAppContext) {
10049    init_test(cx, |_| {});
10050
10051    let fs = FakeFs::new(cx.executor());
10052    fs.insert_tree(
10053        path!("/a"),
10054        json!({
10055            "main.ts": "a",
10056        }),
10057    )
10058    .await;
10059
10060    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10061    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10062    let typescript_language = Arc::new(Language::new(
10063        LanguageConfig {
10064            name: "TypeScript".into(),
10065            matcher: LanguageMatcher {
10066                path_suffixes: vec!["ts".to_string()],
10067                ..LanguageMatcher::default()
10068            },
10069            line_comments: vec!["// ".into()],
10070            ..LanguageConfig::default()
10071        },
10072        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10073    ));
10074    language_registry.add(typescript_language.clone());
10075    let mut fake_servers = language_registry.register_fake_lsp(
10076        "TypeScript",
10077        FakeLspAdapter {
10078            capabilities: lsp::ServerCapabilities {
10079                completion_provider: Some(lsp::CompletionOptions {
10080                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10081                    ..lsp::CompletionOptions::default()
10082                }),
10083                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10084                ..lsp::ServerCapabilities::default()
10085            },
10086            // Emulate vtsls label generation
10087            label_for_completion: Some(Box::new(|item, _| {
10088                let text = if let Some(description) = item
10089                    .label_details
10090                    .as_ref()
10091                    .and_then(|label_details| label_details.description.as_ref())
10092                {
10093                    format!("{} {}", item.label, description)
10094                } else if let Some(detail) = &item.detail {
10095                    format!("{} {}", item.label, detail)
10096                } else {
10097                    item.label.clone()
10098                };
10099                let len = text.len();
10100                Some(language::CodeLabel {
10101                    text,
10102                    runs: Vec::new(),
10103                    filter_range: 0..len,
10104                })
10105            })),
10106            ..FakeLspAdapter::default()
10107        },
10108    );
10109    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10110    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10111    let worktree_id = workspace
10112        .update(cx, |workspace, _window, cx| {
10113            workspace.project().update(cx, |project, cx| {
10114                project.worktrees(cx).next().unwrap().read(cx).id()
10115            })
10116        })
10117        .unwrap();
10118    let _buffer = project
10119        .update(cx, |project, cx| {
10120            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10121        })
10122        .await
10123        .unwrap();
10124    let editor = workspace
10125        .update(cx, |workspace, window, cx| {
10126            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10127        })
10128        .unwrap()
10129        .await
10130        .unwrap()
10131        .downcast::<Editor>()
10132        .unwrap();
10133    let fake_server = fake_servers.next().await.unwrap();
10134
10135    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10136    let multiline_label_2 = "a\nb\nc\n";
10137    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10138    let multiline_description = "d\ne\nf\n";
10139    let multiline_detail_2 = "g\nh\ni\n";
10140
10141    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10142        move |params, _| async move {
10143            Ok(Some(lsp::CompletionResponse::Array(vec![
10144                lsp::CompletionItem {
10145                    label: multiline_label.to_string(),
10146                    text_edit: gen_text_edit(&params, "new_text_1"),
10147                    ..lsp::CompletionItem::default()
10148                },
10149                lsp::CompletionItem {
10150                    label: "single line label 1".to_string(),
10151                    detail: Some(multiline_detail.to_string()),
10152                    text_edit: gen_text_edit(&params, "new_text_2"),
10153                    ..lsp::CompletionItem::default()
10154                },
10155                lsp::CompletionItem {
10156                    label: "single line label 2".to_string(),
10157                    label_details: Some(lsp::CompletionItemLabelDetails {
10158                        description: Some(multiline_description.to_string()),
10159                        detail: None,
10160                    }),
10161                    text_edit: gen_text_edit(&params, "new_text_2"),
10162                    ..lsp::CompletionItem::default()
10163                },
10164                lsp::CompletionItem {
10165                    label: multiline_label_2.to_string(),
10166                    detail: Some(multiline_detail_2.to_string()),
10167                    text_edit: gen_text_edit(&params, "new_text_3"),
10168                    ..lsp::CompletionItem::default()
10169                },
10170                lsp::CompletionItem {
10171                    label: "Label with many     spaces and \t but without newlines".to_string(),
10172                    detail: Some(
10173                        "Details with many     spaces and \t but without newlines".to_string(),
10174                    ),
10175                    text_edit: gen_text_edit(&params, "new_text_4"),
10176                    ..lsp::CompletionItem::default()
10177                },
10178            ])))
10179        },
10180    );
10181
10182    editor.update_in(cx, |editor, window, cx| {
10183        cx.focus_self(window);
10184        editor.move_to_end(&MoveToEnd, window, cx);
10185        editor.handle_input(".", window, cx);
10186    });
10187    cx.run_until_parked();
10188    completion_handle.next().await.unwrap();
10189
10190    editor.update(cx, |editor, _| {
10191        assert!(editor.context_menu_visible());
10192        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10193        {
10194            let completion_labels = menu
10195                .completions
10196                .borrow()
10197                .iter()
10198                .map(|c| c.label.text.clone())
10199                .collect::<Vec<_>>();
10200            assert_eq!(
10201                completion_labels,
10202                &[
10203                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10204                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10205                    "single line label 2 d e f ",
10206                    "a b c g h i ",
10207                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10208                ],
10209                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10210            );
10211
10212            for completion in menu
10213                .completions
10214                .borrow()
10215                .iter() {
10216                    assert_eq!(
10217                        completion.label.filter_range,
10218                        0..completion.label.text.len(),
10219                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10220                    );
10221                }
10222        } else {
10223            panic!("expected completion menu to be open");
10224        }
10225    });
10226}
10227
10228#[gpui::test]
10229async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10230    init_test(cx, |_| {});
10231    let mut cx = EditorLspTestContext::new_rust(
10232        lsp::ServerCapabilities {
10233            completion_provider: Some(lsp::CompletionOptions {
10234                trigger_characters: Some(vec![".".to_string()]),
10235                ..Default::default()
10236            }),
10237            ..Default::default()
10238        },
10239        cx,
10240    )
10241    .await;
10242    cx.lsp
10243        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10244            Ok(Some(lsp::CompletionResponse::Array(vec![
10245                lsp::CompletionItem {
10246                    label: "first".into(),
10247                    ..Default::default()
10248                },
10249                lsp::CompletionItem {
10250                    label: "last".into(),
10251                    ..Default::default()
10252                },
10253            ])))
10254        });
10255    cx.set_state("variableˇ");
10256    cx.simulate_keystroke(".");
10257    cx.executor().run_until_parked();
10258
10259    cx.update_editor(|editor, _, _| {
10260        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10261        {
10262            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
10263        } else {
10264            panic!("expected completion menu to be open");
10265        }
10266    });
10267
10268    cx.update_editor(|editor, window, cx| {
10269        editor.move_page_down(&MovePageDown::default(), window, cx);
10270        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10271        {
10272            assert!(
10273                menu.selected_item == 1,
10274                "expected PageDown to select the last item from the context menu"
10275            );
10276        } else {
10277            panic!("expected completion menu to stay open after PageDown");
10278        }
10279    });
10280
10281    cx.update_editor(|editor, window, cx| {
10282        editor.move_page_up(&MovePageUp::default(), window, cx);
10283        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10284        {
10285            assert!(
10286                menu.selected_item == 0,
10287                "expected PageUp to select the first item from the context menu"
10288            );
10289        } else {
10290            panic!("expected completion menu to stay open after PageUp");
10291        }
10292    });
10293}
10294
10295#[gpui::test]
10296async fn test_completion_sort(cx: &mut TestAppContext) {
10297    init_test(cx, |_| {});
10298    let mut cx = EditorLspTestContext::new_rust(
10299        lsp::ServerCapabilities {
10300            completion_provider: Some(lsp::CompletionOptions {
10301                trigger_characters: Some(vec![".".to_string()]),
10302                ..Default::default()
10303            }),
10304            ..Default::default()
10305        },
10306        cx,
10307    )
10308    .await;
10309    cx.lsp
10310        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10311            Ok(Some(lsp::CompletionResponse::Array(vec![
10312                lsp::CompletionItem {
10313                    label: "Range".into(),
10314                    sort_text: Some("a".into()),
10315                    ..Default::default()
10316                },
10317                lsp::CompletionItem {
10318                    label: "r".into(),
10319                    sort_text: Some("b".into()),
10320                    ..Default::default()
10321                },
10322                lsp::CompletionItem {
10323                    label: "ret".into(),
10324                    sort_text: Some("c".into()),
10325                    ..Default::default()
10326                },
10327                lsp::CompletionItem {
10328                    label: "return".into(),
10329                    sort_text: Some("d".into()),
10330                    ..Default::default()
10331                },
10332                lsp::CompletionItem {
10333                    label: "slice".into(),
10334                    sort_text: Some("d".into()),
10335                    ..Default::default()
10336                },
10337            ])))
10338        });
10339    cx.set_state("");
10340    cx.executor().run_until_parked();
10341    cx.update_editor(|editor, window, cx| {
10342        editor.show_completions(
10343            &ShowCompletions {
10344                trigger: Some("r".into()),
10345            },
10346            window,
10347            cx,
10348        );
10349    });
10350    cx.executor().run_until_parked();
10351
10352    cx.update_editor(|editor, _, _| {
10353        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10354        {
10355            assert_eq!(
10356                completion_menu_entries(&menu),
10357                &["r", "ret", "Range", "return"]
10358            );
10359        } else {
10360            panic!("expected completion menu to be open");
10361        }
10362    });
10363}
10364
10365#[gpui::test]
10366async fn test_as_is_completions(cx: &mut TestAppContext) {
10367    init_test(cx, |_| {});
10368    let mut cx = EditorLspTestContext::new_rust(
10369        lsp::ServerCapabilities {
10370            completion_provider: Some(lsp::CompletionOptions {
10371                ..Default::default()
10372            }),
10373            ..Default::default()
10374        },
10375        cx,
10376    )
10377    .await;
10378    cx.lsp
10379        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10380            Ok(Some(lsp::CompletionResponse::Array(vec![
10381                lsp::CompletionItem {
10382                    label: "unsafe".into(),
10383                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10384                        range: lsp::Range {
10385                            start: lsp::Position {
10386                                line: 1,
10387                                character: 2,
10388                            },
10389                            end: lsp::Position {
10390                                line: 1,
10391                                character: 3,
10392                            },
10393                        },
10394                        new_text: "unsafe".to_string(),
10395                    })),
10396                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
10397                    ..Default::default()
10398                },
10399            ])))
10400        });
10401    cx.set_state("fn a() {}\n");
10402    cx.executor().run_until_parked();
10403    cx.update_editor(|editor, window, cx| {
10404        editor.show_completions(
10405            &ShowCompletions {
10406                trigger: Some("\n".into()),
10407            },
10408            window,
10409            cx,
10410        );
10411    });
10412    cx.executor().run_until_parked();
10413
10414    cx.update_editor(|editor, window, cx| {
10415        editor.confirm_completion(&Default::default(), window, cx)
10416    });
10417    cx.executor().run_until_parked();
10418    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
10419}
10420
10421#[gpui::test]
10422async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10423    init_test(cx, |_| {});
10424
10425    let mut cx = EditorLspTestContext::new_rust(
10426        lsp::ServerCapabilities {
10427            completion_provider: Some(lsp::CompletionOptions {
10428                trigger_characters: Some(vec![".".to_string()]),
10429                resolve_provider: Some(true),
10430                ..Default::default()
10431            }),
10432            ..Default::default()
10433        },
10434        cx,
10435    )
10436    .await;
10437
10438    cx.set_state("fn main() { let a = 2ˇ; }");
10439    cx.simulate_keystroke(".");
10440    let completion_item = lsp::CompletionItem {
10441        label: "Some".into(),
10442        kind: Some(lsp::CompletionItemKind::SNIPPET),
10443        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10444        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10445            kind: lsp::MarkupKind::Markdown,
10446            value: "```rust\nSome(2)\n```".to_string(),
10447        })),
10448        deprecated: Some(false),
10449        sort_text: Some("Some".to_string()),
10450        filter_text: Some("Some".to_string()),
10451        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10452        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10453            range: lsp::Range {
10454                start: lsp::Position {
10455                    line: 0,
10456                    character: 22,
10457                },
10458                end: lsp::Position {
10459                    line: 0,
10460                    character: 22,
10461                },
10462            },
10463            new_text: "Some(2)".to_string(),
10464        })),
10465        additional_text_edits: Some(vec![lsp::TextEdit {
10466            range: lsp::Range {
10467                start: lsp::Position {
10468                    line: 0,
10469                    character: 20,
10470                },
10471                end: lsp::Position {
10472                    line: 0,
10473                    character: 22,
10474                },
10475            },
10476            new_text: "".to_string(),
10477        }]),
10478        ..Default::default()
10479    };
10480
10481    let closure_completion_item = completion_item.clone();
10482    let counter = Arc::new(AtomicUsize::new(0));
10483    let counter_clone = counter.clone();
10484    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10485        let task_completion_item = closure_completion_item.clone();
10486        counter_clone.fetch_add(1, atomic::Ordering::Release);
10487        async move {
10488            Ok(Some(lsp::CompletionResponse::Array(vec![
10489                task_completion_item,
10490            ])))
10491        }
10492    });
10493
10494    cx.condition(|editor, _| editor.context_menu_visible())
10495        .await;
10496    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10497    assert!(request.next().await.is_some());
10498    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10499
10500    cx.simulate_keystrokes("S o m");
10501    cx.condition(|editor, _| editor.context_menu_visible())
10502        .await;
10503    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10504    assert!(request.next().await.is_some());
10505    assert!(request.next().await.is_some());
10506    assert!(request.next().await.is_some());
10507    request.close();
10508    assert!(request.next().await.is_none());
10509    assert_eq!(
10510        counter.load(atomic::Ordering::Acquire),
10511        4,
10512        "With the completions menu open, only one LSP request should happen per input"
10513    );
10514}
10515
10516#[gpui::test]
10517async fn test_toggle_comment(cx: &mut TestAppContext) {
10518    init_test(cx, |_| {});
10519    let mut cx = EditorTestContext::new(cx).await;
10520    let language = Arc::new(Language::new(
10521        LanguageConfig {
10522            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10523            ..Default::default()
10524        },
10525        Some(tree_sitter_rust::LANGUAGE.into()),
10526    ));
10527    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10528
10529    // If multiple selections intersect a line, the line is only toggled once.
10530    cx.set_state(indoc! {"
10531        fn a() {
10532            «//b();
10533            ˇ»// «c();
10534            //ˇ»  d();
10535        }
10536    "});
10537
10538    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10539
10540    cx.assert_editor_state(indoc! {"
10541        fn a() {
10542            «b();
10543            c();
10544            ˇ» d();
10545        }
10546    "});
10547
10548    // The comment prefix is inserted at the same column for every line in a
10549    // selection.
10550    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10551
10552    cx.assert_editor_state(indoc! {"
10553        fn a() {
10554            // «b();
10555            // c();
10556            ˇ»//  d();
10557        }
10558    "});
10559
10560    // If a selection ends at the beginning of a line, that line is not toggled.
10561    cx.set_selections_state(indoc! {"
10562        fn a() {
10563            // b();
10564            «// c();
10565        ˇ»    //  d();
10566        }
10567    "});
10568
10569    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10570
10571    cx.assert_editor_state(indoc! {"
10572        fn a() {
10573            // b();
10574            «c();
10575        ˇ»    //  d();
10576        }
10577    "});
10578
10579    // If a selection span a single line and is empty, the line is toggled.
10580    cx.set_state(indoc! {"
10581        fn a() {
10582            a();
10583            b();
10584        ˇ
10585        }
10586    "});
10587
10588    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10589
10590    cx.assert_editor_state(indoc! {"
10591        fn a() {
10592            a();
10593            b();
10594        //•ˇ
10595        }
10596    "});
10597
10598    // If a selection span multiple lines, empty lines are not toggled.
10599    cx.set_state(indoc! {"
10600        fn a() {
10601            «a();
10602
10603            c();ˇ»
10604        }
10605    "});
10606
10607    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10608
10609    cx.assert_editor_state(indoc! {"
10610        fn a() {
10611            // «a();
10612
10613            // c();ˇ»
10614        }
10615    "});
10616
10617    // If a selection includes multiple comment prefixes, all lines are uncommented.
10618    cx.set_state(indoc! {"
10619        fn a() {
10620            «// a();
10621            /// b();
10622            //! c();ˇ»
10623        }
10624    "});
10625
10626    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10627
10628    cx.assert_editor_state(indoc! {"
10629        fn a() {
10630            «a();
10631            b();
10632            c();ˇ»
10633        }
10634    "});
10635}
10636
10637#[gpui::test]
10638async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10639    init_test(cx, |_| {});
10640    let mut cx = EditorTestContext::new(cx).await;
10641    let language = Arc::new(Language::new(
10642        LanguageConfig {
10643            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10644            ..Default::default()
10645        },
10646        Some(tree_sitter_rust::LANGUAGE.into()),
10647    ));
10648    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10649
10650    let toggle_comments = &ToggleComments {
10651        advance_downwards: false,
10652        ignore_indent: true,
10653    };
10654
10655    // If multiple selections intersect a line, the line is only toggled once.
10656    cx.set_state(indoc! {"
10657        fn a() {
10658        //    «b();
10659        //    c();
10660        //    ˇ» d();
10661        }
10662    "});
10663
10664    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10665
10666    cx.assert_editor_state(indoc! {"
10667        fn a() {
10668            «b();
10669            c();
10670            ˇ» d();
10671        }
10672    "});
10673
10674    // The comment prefix is inserted at the beginning of each line
10675    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10676
10677    cx.assert_editor_state(indoc! {"
10678        fn a() {
10679        //    «b();
10680        //    c();
10681        //    ˇ» d();
10682        }
10683    "});
10684
10685    // If a selection ends at the beginning of a line, that line is not toggled.
10686    cx.set_selections_state(indoc! {"
10687        fn a() {
10688        //    b();
10689        //    «c();
10690        ˇ»//     d();
10691        }
10692    "});
10693
10694    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10695
10696    cx.assert_editor_state(indoc! {"
10697        fn a() {
10698        //    b();
10699            «c();
10700        ˇ»//     d();
10701        }
10702    "});
10703
10704    // If a selection span a single line and is empty, the line is toggled.
10705    cx.set_state(indoc! {"
10706        fn a() {
10707            a();
10708            b();
10709        ˇ
10710        }
10711    "});
10712
10713    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10714
10715    cx.assert_editor_state(indoc! {"
10716        fn a() {
10717            a();
10718            b();
10719        //ˇ
10720        }
10721    "});
10722
10723    // If a selection span multiple lines, empty lines are not toggled.
10724    cx.set_state(indoc! {"
10725        fn a() {
10726            «a();
10727
10728            c();ˇ»
10729        }
10730    "});
10731
10732    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10733
10734    cx.assert_editor_state(indoc! {"
10735        fn a() {
10736        //    «a();
10737
10738        //    c();ˇ»
10739        }
10740    "});
10741
10742    // If a selection includes multiple comment prefixes, all lines are uncommented.
10743    cx.set_state(indoc! {"
10744        fn a() {
10745        //    «a();
10746        ///    b();
10747        //!    c();ˇ»
10748        }
10749    "});
10750
10751    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10752
10753    cx.assert_editor_state(indoc! {"
10754        fn a() {
10755            «a();
10756            b();
10757            c();ˇ»
10758        }
10759    "});
10760}
10761
10762#[gpui::test]
10763async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10764    init_test(cx, |_| {});
10765
10766    let language = Arc::new(Language::new(
10767        LanguageConfig {
10768            line_comments: vec!["// ".into()],
10769            ..Default::default()
10770        },
10771        Some(tree_sitter_rust::LANGUAGE.into()),
10772    ));
10773
10774    let mut cx = EditorTestContext::new(cx).await;
10775
10776    cx.language_registry().add(language.clone());
10777    cx.update_buffer(|buffer, cx| {
10778        buffer.set_language(Some(language), cx);
10779    });
10780
10781    let toggle_comments = &ToggleComments {
10782        advance_downwards: true,
10783        ignore_indent: false,
10784    };
10785
10786    // Single cursor on one line -> advance
10787    // Cursor moves horizontally 3 characters as well on non-blank line
10788    cx.set_state(indoc!(
10789        "fn a() {
10790             ˇdog();
10791             cat();
10792        }"
10793    ));
10794    cx.update_editor(|editor, window, cx| {
10795        editor.toggle_comments(toggle_comments, window, cx);
10796    });
10797    cx.assert_editor_state(indoc!(
10798        "fn a() {
10799             // dog();
10800             catˇ();
10801        }"
10802    ));
10803
10804    // Single selection on one line -> don't advance
10805    cx.set_state(indoc!(
10806        "fn a() {
10807             «dog()ˇ»;
10808             cat();
10809        }"
10810    ));
10811    cx.update_editor(|editor, window, cx| {
10812        editor.toggle_comments(toggle_comments, window, cx);
10813    });
10814    cx.assert_editor_state(indoc!(
10815        "fn a() {
10816             // «dog()ˇ»;
10817             cat();
10818        }"
10819    ));
10820
10821    // Multiple cursors on one line -> advance
10822    cx.set_state(indoc!(
10823        "fn a() {
10824             ˇdˇog();
10825             cat();
10826        }"
10827    ));
10828    cx.update_editor(|editor, window, cx| {
10829        editor.toggle_comments(toggle_comments, window, cx);
10830    });
10831    cx.assert_editor_state(indoc!(
10832        "fn a() {
10833             // dog();
10834             catˇ(ˇ);
10835        }"
10836    ));
10837
10838    // Multiple cursors on one line, with selection -> don't advance
10839    cx.set_state(indoc!(
10840        "fn a() {
10841             ˇdˇog«()ˇ»;
10842             cat();
10843        }"
10844    ));
10845    cx.update_editor(|editor, window, cx| {
10846        editor.toggle_comments(toggle_comments, window, cx);
10847    });
10848    cx.assert_editor_state(indoc!(
10849        "fn a() {
10850             // ˇdˇog«()ˇ»;
10851             cat();
10852        }"
10853    ));
10854
10855    // Single cursor on one line -> advance
10856    // Cursor moves to column 0 on blank line
10857    cx.set_state(indoc!(
10858        "fn a() {
10859             ˇdog();
10860
10861             cat();
10862        }"
10863    ));
10864    cx.update_editor(|editor, window, cx| {
10865        editor.toggle_comments(toggle_comments, window, cx);
10866    });
10867    cx.assert_editor_state(indoc!(
10868        "fn a() {
10869             // dog();
10870        ˇ
10871             cat();
10872        }"
10873    ));
10874
10875    // Single cursor on one line -> advance
10876    // Cursor starts and ends at column 0
10877    cx.set_state(indoc!(
10878        "fn a() {
10879         ˇ    dog();
10880             cat();
10881        }"
10882    ));
10883    cx.update_editor(|editor, window, cx| {
10884        editor.toggle_comments(toggle_comments, window, cx);
10885    });
10886    cx.assert_editor_state(indoc!(
10887        "fn a() {
10888             // dog();
10889         ˇ    cat();
10890        }"
10891    ));
10892}
10893
10894#[gpui::test]
10895async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10896    init_test(cx, |_| {});
10897
10898    let mut cx = EditorTestContext::new(cx).await;
10899
10900    let html_language = Arc::new(
10901        Language::new(
10902            LanguageConfig {
10903                name: "HTML".into(),
10904                block_comment: Some(("<!-- ".into(), " -->".into())),
10905                ..Default::default()
10906            },
10907            Some(tree_sitter_html::LANGUAGE.into()),
10908        )
10909        .with_injection_query(
10910            r#"
10911            (script_element
10912                (raw_text) @injection.content
10913                (#set! injection.language "javascript"))
10914            "#,
10915        )
10916        .unwrap(),
10917    );
10918
10919    let javascript_language = Arc::new(Language::new(
10920        LanguageConfig {
10921            name: "JavaScript".into(),
10922            line_comments: vec!["// ".into()],
10923            ..Default::default()
10924        },
10925        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10926    ));
10927
10928    cx.language_registry().add(html_language.clone());
10929    cx.language_registry().add(javascript_language.clone());
10930    cx.update_buffer(|buffer, cx| {
10931        buffer.set_language(Some(html_language), cx);
10932    });
10933
10934    // Toggle comments for empty selections
10935    cx.set_state(
10936        &r#"
10937            <p>A</p>ˇ
10938            <p>B</p>ˇ
10939            <p>C</p>ˇ
10940        "#
10941        .unindent(),
10942    );
10943    cx.update_editor(|editor, window, cx| {
10944        editor.toggle_comments(&ToggleComments::default(), window, cx)
10945    });
10946    cx.assert_editor_state(
10947        &r#"
10948            <!-- <p>A</p>ˇ -->
10949            <!-- <p>B</p>ˇ -->
10950            <!-- <p>C</p>ˇ -->
10951        "#
10952        .unindent(),
10953    );
10954    cx.update_editor(|editor, window, cx| {
10955        editor.toggle_comments(&ToggleComments::default(), window, cx)
10956    });
10957    cx.assert_editor_state(
10958        &r#"
10959            <p>A</p>ˇ
10960            <p>B</p>ˇ
10961            <p>C</p>ˇ
10962        "#
10963        .unindent(),
10964    );
10965
10966    // Toggle comments for mixture of empty and non-empty selections, where
10967    // multiple selections occupy a given line.
10968    cx.set_state(
10969        &r#"
10970            <p>A«</p>
10971            <p>ˇ»B</p>ˇ
10972            <p>C«</p>
10973            <p>ˇ»D</p>ˇ
10974        "#
10975        .unindent(),
10976    );
10977
10978    cx.update_editor(|editor, window, cx| {
10979        editor.toggle_comments(&ToggleComments::default(), window, cx)
10980    });
10981    cx.assert_editor_state(
10982        &r#"
10983            <!-- <p>A«</p>
10984            <p>ˇ»B</p>ˇ -->
10985            <!-- <p>C«</p>
10986            <p>ˇ»D</p>ˇ -->
10987        "#
10988        .unindent(),
10989    );
10990    cx.update_editor(|editor, window, cx| {
10991        editor.toggle_comments(&ToggleComments::default(), window, cx)
10992    });
10993    cx.assert_editor_state(
10994        &r#"
10995            <p>A«</p>
10996            <p>ˇ»B</p>ˇ
10997            <p>C«</p>
10998            <p>ˇ»D</p>ˇ
10999        "#
11000        .unindent(),
11001    );
11002
11003    // Toggle comments when different languages are active for different
11004    // selections.
11005    cx.set_state(
11006        &r#"
11007            ˇ<script>
11008                ˇvar x = new Y();
11009            ˇ</script>
11010        "#
11011        .unindent(),
11012    );
11013    cx.executor().run_until_parked();
11014    cx.update_editor(|editor, window, cx| {
11015        editor.toggle_comments(&ToggleComments::default(), window, cx)
11016    });
11017    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11018    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11019    cx.assert_editor_state(
11020        &r#"
11021            <!-- ˇ<script> -->
11022                // ˇvar x = new Y();
11023            <!-- ˇ</script> -->
11024        "#
11025        .unindent(),
11026    );
11027}
11028
11029#[gpui::test]
11030fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11031    init_test(cx, |_| {});
11032
11033    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11034    let multibuffer = cx.new(|cx| {
11035        let mut multibuffer = MultiBuffer::new(ReadWrite);
11036        multibuffer.push_excerpts(
11037            buffer.clone(),
11038            [
11039                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11040                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11041            ],
11042            cx,
11043        );
11044        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11045        multibuffer
11046    });
11047
11048    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11049    editor.update_in(cx, |editor, window, cx| {
11050        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11051        editor.change_selections(None, window, cx, |s| {
11052            s.select_ranges([
11053                Point::new(0, 0)..Point::new(0, 0),
11054                Point::new(1, 0)..Point::new(1, 0),
11055            ])
11056        });
11057
11058        editor.handle_input("X", window, cx);
11059        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11060        assert_eq!(
11061            editor.selections.ranges(cx),
11062            [
11063                Point::new(0, 1)..Point::new(0, 1),
11064                Point::new(1, 1)..Point::new(1, 1),
11065            ]
11066        );
11067
11068        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11069        editor.change_selections(None, window, cx, |s| {
11070            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11071        });
11072        editor.backspace(&Default::default(), window, cx);
11073        assert_eq!(editor.text(cx), "Xa\nbbb");
11074        assert_eq!(
11075            editor.selections.ranges(cx),
11076            [Point::new(1, 0)..Point::new(1, 0)]
11077        );
11078
11079        editor.change_selections(None, window, cx, |s| {
11080            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11081        });
11082        editor.backspace(&Default::default(), window, cx);
11083        assert_eq!(editor.text(cx), "X\nbb");
11084        assert_eq!(
11085            editor.selections.ranges(cx),
11086            [Point::new(0, 1)..Point::new(0, 1)]
11087        );
11088    });
11089}
11090
11091#[gpui::test]
11092fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11093    init_test(cx, |_| {});
11094
11095    let markers = vec![('[', ']').into(), ('(', ')').into()];
11096    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11097        indoc! {"
11098            [aaaa
11099            (bbbb]
11100            cccc)",
11101        },
11102        markers.clone(),
11103    );
11104    let excerpt_ranges = markers.into_iter().map(|marker| {
11105        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11106        ExcerptRange::new(context.clone())
11107    });
11108    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11109    let multibuffer = cx.new(|cx| {
11110        let mut multibuffer = MultiBuffer::new(ReadWrite);
11111        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11112        multibuffer
11113    });
11114
11115    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11116    editor.update_in(cx, |editor, window, cx| {
11117        let (expected_text, selection_ranges) = marked_text_ranges(
11118            indoc! {"
11119                aaaa
11120                bˇbbb
11121                bˇbbˇb
11122                cccc"
11123            },
11124            true,
11125        );
11126        assert_eq!(editor.text(cx), expected_text);
11127        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11128
11129        editor.handle_input("X", window, cx);
11130
11131        let (expected_text, expected_selections) = marked_text_ranges(
11132            indoc! {"
11133                aaaa
11134                bXˇbbXb
11135                bXˇbbXˇb
11136                cccc"
11137            },
11138            false,
11139        );
11140        assert_eq!(editor.text(cx), expected_text);
11141        assert_eq!(editor.selections.ranges(cx), expected_selections);
11142
11143        editor.newline(&Newline, window, cx);
11144        let (expected_text, expected_selections) = marked_text_ranges(
11145            indoc! {"
11146                aaaa
11147                bX
11148                ˇbbX
11149                b
11150                bX
11151                ˇbbX
11152                ˇb
11153                cccc"
11154            },
11155            false,
11156        );
11157        assert_eq!(editor.text(cx), expected_text);
11158        assert_eq!(editor.selections.ranges(cx), expected_selections);
11159    });
11160}
11161
11162#[gpui::test]
11163fn test_refresh_selections(cx: &mut TestAppContext) {
11164    init_test(cx, |_| {});
11165
11166    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11167    let mut excerpt1_id = None;
11168    let multibuffer = cx.new(|cx| {
11169        let mut multibuffer = MultiBuffer::new(ReadWrite);
11170        excerpt1_id = multibuffer
11171            .push_excerpts(
11172                buffer.clone(),
11173                [
11174                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11175                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11176                ],
11177                cx,
11178            )
11179            .into_iter()
11180            .next();
11181        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11182        multibuffer
11183    });
11184
11185    let editor = cx.add_window(|window, cx| {
11186        let mut editor = build_editor(multibuffer.clone(), window, cx);
11187        let snapshot = editor.snapshot(window, cx);
11188        editor.change_selections(None, window, cx, |s| {
11189            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11190        });
11191        editor.begin_selection(
11192            Point::new(2, 1).to_display_point(&snapshot),
11193            true,
11194            1,
11195            window,
11196            cx,
11197        );
11198        assert_eq!(
11199            editor.selections.ranges(cx),
11200            [
11201                Point::new(1, 3)..Point::new(1, 3),
11202                Point::new(2, 1)..Point::new(2, 1),
11203            ]
11204        );
11205        editor
11206    });
11207
11208    // Refreshing selections is a no-op when excerpts haven't changed.
11209    _ = editor.update(cx, |editor, window, cx| {
11210        editor.change_selections(None, window, cx, |s| s.refresh());
11211        assert_eq!(
11212            editor.selections.ranges(cx),
11213            [
11214                Point::new(1, 3)..Point::new(1, 3),
11215                Point::new(2, 1)..Point::new(2, 1),
11216            ]
11217        );
11218    });
11219
11220    multibuffer.update(cx, |multibuffer, cx| {
11221        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11222    });
11223    _ = editor.update(cx, |editor, window, cx| {
11224        // Removing an excerpt causes the first selection to become degenerate.
11225        assert_eq!(
11226            editor.selections.ranges(cx),
11227            [
11228                Point::new(0, 0)..Point::new(0, 0),
11229                Point::new(0, 1)..Point::new(0, 1)
11230            ]
11231        );
11232
11233        // Refreshing selections will relocate the first selection to the original buffer
11234        // location.
11235        editor.change_selections(None, window, cx, |s| s.refresh());
11236        assert_eq!(
11237            editor.selections.ranges(cx),
11238            [
11239                Point::new(0, 1)..Point::new(0, 1),
11240                Point::new(0, 3)..Point::new(0, 3)
11241            ]
11242        );
11243        assert!(editor.selections.pending_anchor().is_some());
11244    });
11245}
11246
11247#[gpui::test]
11248fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11249    init_test(cx, |_| {});
11250
11251    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11252    let mut excerpt1_id = None;
11253    let multibuffer = cx.new(|cx| {
11254        let mut multibuffer = MultiBuffer::new(ReadWrite);
11255        excerpt1_id = multibuffer
11256            .push_excerpts(
11257                buffer.clone(),
11258                [
11259                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11260                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11261                ],
11262                cx,
11263            )
11264            .into_iter()
11265            .next();
11266        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11267        multibuffer
11268    });
11269
11270    let editor = cx.add_window(|window, cx| {
11271        let mut editor = build_editor(multibuffer.clone(), window, cx);
11272        let snapshot = editor.snapshot(window, cx);
11273        editor.begin_selection(
11274            Point::new(1, 3).to_display_point(&snapshot),
11275            false,
11276            1,
11277            window,
11278            cx,
11279        );
11280        assert_eq!(
11281            editor.selections.ranges(cx),
11282            [Point::new(1, 3)..Point::new(1, 3)]
11283        );
11284        editor
11285    });
11286
11287    multibuffer.update(cx, |multibuffer, cx| {
11288        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11289    });
11290    _ = editor.update(cx, |editor, window, cx| {
11291        assert_eq!(
11292            editor.selections.ranges(cx),
11293            [Point::new(0, 0)..Point::new(0, 0)]
11294        );
11295
11296        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11297        editor.change_selections(None, window, cx, |s| s.refresh());
11298        assert_eq!(
11299            editor.selections.ranges(cx),
11300            [Point::new(0, 3)..Point::new(0, 3)]
11301        );
11302        assert!(editor.selections.pending_anchor().is_some());
11303    });
11304}
11305
11306#[gpui::test]
11307async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11308    init_test(cx, |_| {});
11309
11310    let language = Arc::new(
11311        Language::new(
11312            LanguageConfig {
11313                brackets: BracketPairConfig {
11314                    pairs: vec![
11315                        BracketPair {
11316                            start: "{".to_string(),
11317                            end: "}".to_string(),
11318                            close: true,
11319                            surround: true,
11320                            newline: true,
11321                        },
11322                        BracketPair {
11323                            start: "/* ".to_string(),
11324                            end: " */".to_string(),
11325                            close: true,
11326                            surround: true,
11327                            newline: true,
11328                        },
11329                    ],
11330                    ..Default::default()
11331                },
11332                ..Default::default()
11333            },
11334            Some(tree_sitter_rust::LANGUAGE.into()),
11335        )
11336        .with_indents_query("")
11337        .unwrap(),
11338    );
11339
11340    let text = concat!(
11341        "{   }\n",     //
11342        "  x\n",       //
11343        "  /*   */\n", //
11344        "x\n",         //
11345        "{{} }\n",     //
11346    );
11347
11348    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11349    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11350    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11351    editor
11352        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11353        .await;
11354
11355    editor.update_in(cx, |editor, window, cx| {
11356        editor.change_selections(None, window, cx, |s| {
11357            s.select_display_ranges([
11358                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11359                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11360                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11361            ])
11362        });
11363        editor.newline(&Newline, window, cx);
11364
11365        assert_eq!(
11366            editor.buffer().read(cx).read(cx).text(),
11367            concat!(
11368                "{ \n",    // Suppress rustfmt
11369                "\n",      //
11370                "}\n",     //
11371                "  x\n",   //
11372                "  /* \n", //
11373                "  \n",    //
11374                "  */\n",  //
11375                "x\n",     //
11376                "{{} \n",  //
11377                "}\n",     //
11378            )
11379        );
11380    });
11381}
11382
11383#[gpui::test]
11384fn test_highlighted_ranges(cx: &mut TestAppContext) {
11385    init_test(cx, |_| {});
11386
11387    let editor = cx.add_window(|window, cx| {
11388        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11389        build_editor(buffer.clone(), window, cx)
11390    });
11391
11392    _ = editor.update(cx, |editor, window, cx| {
11393        struct Type1;
11394        struct Type2;
11395
11396        let buffer = editor.buffer.read(cx).snapshot(cx);
11397
11398        let anchor_range =
11399            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11400
11401        editor.highlight_background::<Type1>(
11402            &[
11403                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11404                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11405                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11406                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11407            ],
11408            |_| Hsla::red(),
11409            cx,
11410        );
11411        editor.highlight_background::<Type2>(
11412            &[
11413                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11414                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11415                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11416                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11417            ],
11418            |_| Hsla::green(),
11419            cx,
11420        );
11421
11422        let snapshot = editor.snapshot(window, cx);
11423        let mut highlighted_ranges = editor.background_highlights_in_range(
11424            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11425            &snapshot,
11426            cx.theme().colors(),
11427        );
11428        // Enforce a consistent ordering based on color without relying on the ordering of the
11429        // highlight's `TypeId` which is non-executor.
11430        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11431        assert_eq!(
11432            highlighted_ranges,
11433            &[
11434                (
11435                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11436                    Hsla::red(),
11437                ),
11438                (
11439                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11440                    Hsla::red(),
11441                ),
11442                (
11443                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11444                    Hsla::green(),
11445                ),
11446                (
11447                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11448                    Hsla::green(),
11449                ),
11450            ]
11451        );
11452        assert_eq!(
11453            editor.background_highlights_in_range(
11454                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11455                &snapshot,
11456                cx.theme().colors(),
11457            ),
11458            &[(
11459                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11460                Hsla::red(),
11461            )]
11462        );
11463    });
11464}
11465
11466#[gpui::test]
11467async fn test_following(cx: &mut TestAppContext) {
11468    init_test(cx, |_| {});
11469
11470    let fs = FakeFs::new(cx.executor());
11471    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11472
11473    let buffer = project.update(cx, |project, cx| {
11474        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11475        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11476    });
11477    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11478    let follower = cx.update(|cx| {
11479        cx.open_window(
11480            WindowOptions {
11481                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11482                    gpui::Point::new(px(0.), px(0.)),
11483                    gpui::Point::new(px(10.), px(80.)),
11484                ))),
11485                ..Default::default()
11486            },
11487            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11488        )
11489        .unwrap()
11490    });
11491
11492    let is_still_following = Rc::new(RefCell::new(true));
11493    let follower_edit_event_count = Rc::new(RefCell::new(0));
11494    let pending_update = Rc::new(RefCell::new(None));
11495    let leader_entity = leader.root(cx).unwrap();
11496    let follower_entity = follower.root(cx).unwrap();
11497    _ = follower.update(cx, {
11498        let update = pending_update.clone();
11499        let is_still_following = is_still_following.clone();
11500        let follower_edit_event_count = follower_edit_event_count.clone();
11501        |_, window, cx| {
11502            cx.subscribe_in(
11503                &leader_entity,
11504                window,
11505                move |_, leader, event, window, cx| {
11506                    leader.read(cx).add_event_to_update_proto(
11507                        event,
11508                        &mut update.borrow_mut(),
11509                        window,
11510                        cx,
11511                    );
11512                },
11513            )
11514            .detach();
11515
11516            cx.subscribe_in(
11517                &follower_entity,
11518                window,
11519                move |_, _, event: &EditorEvent, _window, _cx| {
11520                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11521                        *is_still_following.borrow_mut() = false;
11522                    }
11523
11524                    if let EditorEvent::BufferEdited = event {
11525                        *follower_edit_event_count.borrow_mut() += 1;
11526                    }
11527                },
11528            )
11529            .detach();
11530        }
11531    });
11532
11533    // Update the selections only
11534    _ = leader.update(cx, |leader, window, cx| {
11535        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11536    });
11537    follower
11538        .update(cx, |follower, window, cx| {
11539            follower.apply_update_proto(
11540                &project,
11541                pending_update.borrow_mut().take().unwrap(),
11542                window,
11543                cx,
11544            )
11545        })
11546        .unwrap()
11547        .await
11548        .unwrap();
11549    _ = follower.update(cx, |follower, _, cx| {
11550        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11551    });
11552    assert!(*is_still_following.borrow());
11553    assert_eq!(*follower_edit_event_count.borrow(), 0);
11554
11555    // Update the scroll position only
11556    _ = leader.update(cx, |leader, window, cx| {
11557        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11558    });
11559    follower
11560        .update(cx, |follower, window, cx| {
11561            follower.apply_update_proto(
11562                &project,
11563                pending_update.borrow_mut().take().unwrap(),
11564                window,
11565                cx,
11566            )
11567        })
11568        .unwrap()
11569        .await
11570        .unwrap();
11571    assert_eq!(
11572        follower
11573            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11574            .unwrap(),
11575        gpui::Point::new(1.5, 3.5)
11576    );
11577    assert!(*is_still_following.borrow());
11578    assert_eq!(*follower_edit_event_count.borrow(), 0);
11579
11580    // Update the selections and scroll position. The follower's scroll position is updated
11581    // via autoscroll, not via the leader's exact scroll position.
11582    _ = leader.update(cx, |leader, window, cx| {
11583        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11584        leader.request_autoscroll(Autoscroll::newest(), cx);
11585        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11586    });
11587    follower
11588        .update(cx, |follower, window, cx| {
11589            follower.apply_update_proto(
11590                &project,
11591                pending_update.borrow_mut().take().unwrap(),
11592                window,
11593                cx,
11594            )
11595        })
11596        .unwrap()
11597        .await
11598        .unwrap();
11599    _ = follower.update(cx, |follower, _, cx| {
11600        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11601        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11602    });
11603    assert!(*is_still_following.borrow());
11604
11605    // Creating a pending selection that precedes another selection
11606    _ = leader.update(cx, |leader, window, cx| {
11607        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11608        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11609    });
11610    follower
11611        .update(cx, |follower, window, cx| {
11612            follower.apply_update_proto(
11613                &project,
11614                pending_update.borrow_mut().take().unwrap(),
11615                window,
11616                cx,
11617            )
11618        })
11619        .unwrap()
11620        .await
11621        .unwrap();
11622    _ = follower.update(cx, |follower, _, cx| {
11623        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11624    });
11625    assert!(*is_still_following.borrow());
11626
11627    // Extend the pending selection so that it surrounds another selection
11628    _ = leader.update(cx, |leader, window, cx| {
11629        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11630    });
11631    follower
11632        .update(cx, |follower, window, cx| {
11633            follower.apply_update_proto(
11634                &project,
11635                pending_update.borrow_mut().take().unwrap(),
11636                window,
11637                cx,
11638            )
11639        })
11640        .unwrap()
11641        .await
11642        .unwrap();
11643    _ = follower.update(cx, |follower, _, cx| {
11644        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11645    });
11646
11647    // Scrolling locally breaks the follow
11648    _ = follower.update(cx, |follower, window, cx| {
11649        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11650        follower.set_scroll_anchor(
11651            ScrollAnchor {
11652                anchor: top_anchor,
11653                offset: gpui::Point::new(0.0, 0.5),
11654            },
11655            window,
11656            cx,
11657        );
11658    });
11659    assert!(!(*is_still_following.borrow()));
11660}
11661
11662#[gpui::test]
11663async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11664    init_test(cx, |_| {});
11665
11666    let fs = FakeFs::new(cx.executor());
11667    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11668    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11669    let pane = workspace
11670        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11671        .unwrap();
11672
11673    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11674
11675    let leader = pane.update_in(cx, |_, window, cx| {
11676        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11677        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11678    });
11679
11680    // Start following the editor when it has no excerpts.
11681    let mut state_message =
11682        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11683    let workspace_entity = workspace.root(cx).unwrap();
11684    let follower_1 = cx
11685        .update_window(*workspace.deref(), |_, window, cx| {
11686            Editor::from_state_proto(
11687                workspace_entity,
11688                ViewId {
11689                    creator: Default::default(),
11690                    id: 0,
11691                },
11692                &mut state_message,
11693                window,
11694                cx,
11695            )
11696        })
11697        .unwrap()
11698        .unwrap()
11699        .await
11700        .unwrap();
11701
11702    let update_message = Rc::new(RefCell::new(None));
11703    follower_1.update_in(cx, {
11704        let update = update_message.clone();
11705        |_, window, cx| {
11706            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11707                leader.read(cx).add_event_to_update_proto(
11708                    event,
11709                    &mut update.borrow_mut(),
11710                    window,
11711                    cx,
11712                );
11713            })
11714            .detach();
11715        }
11716    });
11717
11718    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11719        (
11720            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11721            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11722        )
11723    });
11724
11725    // Insert some excerpts.
11726    leader.update(cx, |leader, cx| {
11727        leader.buffer.update(cx, |multibuffer, cx| {
11728            let excerpt_ids = multibuffer.push_excerpts(
11729                buffer_1.clone(),
11730                [
11731                    ExcerptRange::new(1..6),
11732                    ExcerptRange::new(12..15),
11733                    ExcerptRange::new(0..3),
11734                ],
11735                cx,
11736            );
11737            multibuffer.insert_excerpts_after(
11738                excerpt_ids[0],
11739                buffer_2.clone(),
11740                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11741                cx,
11742            );
11743        });
11744    });
11745
11746    // Apply the update of adding the excerpts.
11747    follower_1
11748        .update_in(cx, |follower, window, cx| {
11749            follower.apply_update_proto(
11750                &project,
11751                update_message.borrow().clone().unwrap(),
11752                window,
11753                cx,
11754            )
11755        })
11756        .await
11757        .unwrap();
11758    assert_eq!(
11759        follower_1.update(cx, |editor, cx| editor.text(cx)),
11760        leader.update(cx, |editor, cx| editor.text(cx))
11761    );
11762    update_message.borrow_mut().take();
11763
11764    // Start following separately after it already has excerpts.
11765    let mut state_message =
11766        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11767    let workspace_entity = workspace.root(cx).unwrap();
11768    let follower_2 = cx
11769        .update_window(*workspace.deref(), |_, window, cx| {
11770            Editor::from_state_proto(
11771                workspace_entity,
11772                ViewId {
11773                    creator: Default::default(),
11774                    id: 0,
11775                },
11776                &mut state_message,
11777                window,
11778                cx,
11779            )
11780        })
11781        .unwrap()
11782        .unwrap()
11783        .await
11784        .unwrap();
11785    assert_eq!(
11786        follower_2.update(cx, |editor, cx| editor.text(cx)),
11787        leader.update(cx, |editor, cx| editor.text(cx))
11788    );
11789
11790    // Remove some excerpts.
11791    leader.update(cx, |leader, cx| {
11792        leader.buffer.update(cx, |multibuffer, cx| {
11793            let excerpt_ids = multibuffer.excerpt_ids();
11794            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11795            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11796        });
11797    });
11798
11799    // Apply the update of removing the excerpts.
11800    follower_1
11801        .update_in(cx, |follower, window, cx| {
11802            follower.apply_update_proto(
11803                &project,
11804                update_message.borrow().clone().unwrap(),
11805                window,
11806                cx,
11807            )
11808        })
11809        .await
11810        .unwrap();
11811    follower_2
11812        .update_in(cx, |follower, window, cx| {
11813            follower.apply_update_proto(
11814                &project,
11815                update_message.borrow().clone().unwrap(),
11816                window,
11817                cx,
11818            )
11819        })
11820        .await
11821        .unwrap();
11822    update_message.borrow_mut().take();
11823    assert_eq!(
11824        follower_1.update(cx, |editor, cx| editor.text(cx)),
11825        leader.update(cx, |editor, cx| editor.text(cx))
11826    );
11827}
11828
11829#[gpui::test]
11830async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11831    init_test(cx, |_| {});
11832
11833    let mut cx = EditorTestContext::new(cx).await;
11834    let lsp_store =
11835        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11836
11837    cx.set_state(indoc! {"
11838        ˇfn func(abc def: i32) -> u32 {
11839        }
11840    "});
11841
11842    cx.update(|_, cx| {
11843        lsp_store.update(cx, |lsp_store, cx| {
11844            lsp_store
11845                .update_diagnostics(
11846                    LanguageServerId(0),
11847                    lsp::PublishDiagnosticsParams {
11848                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11849                        version: None,
11850                        diagnostics: vec![
11851                            lsp::Diagnostic {
11852                                range: lsp::Range::new(
11853                                    lsp::Position::new(0, 11),
11854                                    lsp::Position::new(0, 12),
11855                                ),
11856                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11857                                ..Default::default()
11858                            },
11859                            lsp::Diagnostic {
11860                                range: lsp::Range::new(
11861                                    lsp::Position::new(0, 12),
11862                                    lsp::Position::new(0, 15),
11863                                ),
11864                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11865                                ..Default::default()
11866                            },
11867                            lsp::Diagnostic {
11868                                range: lsp::Range::new(
11869                                    lsp::Position::new(0, 25),
11870                                    lsp::Position::new(0, 28),
11871                                ),
11872                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11873                                ..Default::default()
11874                            },
11875                        ],
11876                    },
11877                    &[],
11878                    cx,
11879                )
11880                .unwrap()
11881        });
11882    });
11883
11884    executor.run_until_parked();
11885
11886    cx.update_editor(|editor, window, cx| {
11887        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11888    });
11889
11890    cx.assert_editor_state(indoc! {"
11891        fn func(abc def: i32) -> ˇu32 {
11892        }
11893    "});
11894
11895    cx.update_editor(|editor, window, cx| {
11896        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11897    });
11898
11899    cx.assert_editor_state(indoc! {"
11900        fn func(abc ˇdef: i32) -> u32 {
11901        }
11902    "});
11903
11904    cx.update_editor(|editor, window, cx| {
11905        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11906    });
11907
11908    cx.assert_editor_state(indoc! {"
11909        fn func(abcˇ def: i32) -> u32 {
11910        }
11911    "});
11912
11913    cx.update_editor(|editor, window, cx| {
11914        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11915    });
11916
11917    cx.assert_editor_state(indoc! {"
11918        fn func(abc def: i32) -> ˇu32 {
11919        }
11920    "});
11921}
11922
11923#[gpui::test]
11924async fn cycle_through_same_place_diagnostics(
11925    executor: BackgroundExecutor,
11926    cx: &mut TestAppContext,
11927) {
11928    init_test(cx, |_| {});
11929
11930    let mut cx = EditorTestContext::new(cx).await;
11931    let lsp_store =
11932        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11933
11934    cx.set_state(indoc! {"
11935        ˇfn func(abc def: i32) -> u32 {
11936        }
11937    "});
11938
11939    cx.update(|_, cx| {
11940        lsp_store.update(cx, |lsp_store, cx| {
11941            lsp_store
11942                .update_diagnostics(
11943                    LanguageServerId(0),
11944                    lsp::PublishDiagnosticsParams {
11945                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11946                        version: None,
11947                        diagnostics: vec![
11948                            lsp::Diagnostic {
11949                                range: lsp::Range::new(
11950                                    lsp::Position::new(0, 11),
11951                                    lsp::Position::new(0, 12),
11952                                ),
11953                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11954                                ..Default::default()
11955                            },
11956                            lsp::Diagnostic {
11957                                range: lsp::Range::new(
11958                                    lsp::Position::new(0, 12),
11959                                    lsp::Position::new(0, 15),
11960                                ),
11961                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11962                                ..Default::default()
11963                            },
11964                            lsp::Diagnostic {
11965                                range: lsp::Range::new(
11966                                    lsp::Position::new(0, 12),
11967                                    lsp::Position::new(0, 15),
11968                                ),
11969                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11970                                ..Default::default()
11971                            },
11972                            lsp::Diagnostic {
11973                                range: lsp::Range::new(
11974                                    lsp::Position::new(0, 25),
11975                                    lsp::Position::new(0, 28),
11976                                ),
11977                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11978                                ..Default::default()
11979                            },
11980                        ],
11981                    },
11982                    &[],
11983                    cx,
11984                )
11985                .unwrap()
11986        });
11987    });
11988    executor.run_until_parked();
11989
11990    //// Backward
11991
11992    // Fourth diagnostic
11993    cx.update_editor(|editor, window, cx| {
11994        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11995    });
11996    cx.assert_editor_state(indoc! {"
11997        fn func(abc def: i32) -> ˇu32 {
11998        }
11999    "});
12000
12001    // Third diagnostic
12002    cx.update_editor(|editor, window, cx| {
12003        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12004    });
12005    cx.assert_editor_state(indoc! {"
12006        fn func(abc ˇdef: i32) -> u32 {
12007        }
12008    "});
12009
12010    // Second diagnostic, same place
12011    cx.update_editor(|editor, window, cx| {
12012        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12013    });
12014    cx.assert_editor_state(indoc! {"
12015        fn func(abc ˇdef: i32) -> u32 {
12016        }
12017    "});
12018
12019    // First diagnostic
12020    cx.update_editor(|editor, window, cx| {
12021        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12022    });
12023    cx.assert_editor_state(indoc! {"
12024        fn func(abcˇ def: i32) -> u32 {
12025        }
12026    "});
12027
12028    // Wrapped over, fourth diagnostic
12029    cx.update_editor(|editor, window, cx| {
12030        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12031    });
12032    cx.assert_editor_state(indoc! {"
12033        fn func(abc def: i32) -> ˇu32 {
12034        }
12035    "});
12036
12037    cx.update_editor(|editor, window, cx| {
12038        editor.move_to_beginning(&MoveToBeginning, window, cx);
12039    });
12040    cx.assert_editor_state(indoc! {"
12041        ˇfn func(abc def: i32) -> u32 {
12042        }
12043    "});
12044
12045    //// Forward
12046
12047    // First diagnostic
12048    cx.update_editor(|editor, window, cx| {
12049        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12050    });
12051    cx.assert_editor_state(indoc! {"
12052        fn func(abcˇ def: i32) -> u32 {
12053        }
12054    "});
12055
12056    // Second diagnostic
12057    cx.update_editor(|editor, window, cx| {
12058        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12059    });
12060    cx.assert_editor_state(indoc! {"
12061        fn func(abc ˇdef: i32) -> u32 {
12062        }
12063    "});
12064
12065    // Third diagnostic, same place
12066    cx.update_editor(|editor, window, cx| {
12067        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12068    });
12069    cx.assert_editor_state(indoc! {"
12070        fn func(abc ˇdef: i32) -> u32 {
12071        }
12072    "});
12073
12074    // Fourth diagnostic
12075    cx.update_editor(|editor, window, cx| {
12076        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12077    });
12078    cx.assert_editor_state(indoc! {"
12079        fn func(abc def: i32) -> ˇu32 {
12080        }
12081    "});
12082
12083    // Wrapped around, first diagnostic
12084    cx.update_editor(|editor, window, cx| {
12085        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12086    });
12087    cx.assert_editor_state(indoc! {"
12088        fn func(abcˇ def: i32) -> u32 {
12089        }
12090    "});
12091}
12092
12093#[gpui::test]
12094async fn active_diagnostics_dismiss_after_invalidation(
12095    executor: BackgroundExecutor,
12096    cx: &mut TestAppContext,
12097) {
12098    init_test(cx, |_| {});
12099
12100    let mut cx = EditorTestContext::new(cx).await;
12101    let lsp_store =
12102        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12103
12104    cx.set_state(indoc! {"
12105        ˇfn func(abc def: i32) -> u32 {
12106        }
12107    "});
12108
12109    let message = "Something's wrong!";
12110    cx.update(|_, cx| {
12111        lsp_store.update(cx, |lsp_store, cx| {
12112            lsp_store
12113                .update_diagnostics(
12114                    LanguageServerId(0),
12115                    lsp::PublishDiagnosticsParams {
12116                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12117                        version: None,
12118                        diagnostics: vec![lsp::Diagnostic {
12119                            range: lsp::Range::new(
12120                                lsp::Position::new(0, 11),
12121                                lsp::Position::new(0, 12),
12122                            ),
12123                            severity: Some(lsp::DiagnosticSeverity::ERROR),
12124                            message: message.to_string(),
12125                            ..Default::default()
12126                        }],
12127                    },
12128                    &[],
12129                    cx,
12130                )
12131                .unwrap()
12132        });
12133    });
12134    executor.run_until_parked();
12135
12136    cx.update_editor(|editor, window, cx| {
12137        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12138        assert_eq!(
12139            editor
12140                .active_diagnostics
12141                .as_ref()
12142                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
12143            Some(message),
12144            "Should have a diagnostics group activated"
12145        );
12146    });
12147    cx.assert_editor_state(indoc! {"
12148        fn func(abcˇ def: i32) -> u32 {
12149        }
12150    "});
12151
12152    cx.update(|_, cx| {
12153        lsp_store.update(cx, |lsp_store, cx| {
12154            lsp_store
12155                .update_diagnostics(
12156                    LanguageServerId(0),
12157                    lsp::PublishDiagnosticsParams {
12158                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12159                        version: None,
12160                        diagnostics: Vec::new(),
12161                    },
12162                    &[],
12163                    cx,
12164                )
12165                .unwrap()
12166        });
12167    });
12168    executor.run_until_parked();
12169    cx.update_editor(|editor, _, _| {
12170        assert_eq!(
12171            editor.active_diagnostics, None,
12172            "After no diagnostics set to the editor, no diagnostics should be active"
12173        );
12174    });
12175    cx.assert_editor_state(indoc! {"
12176        fn func(abcˇ def: i32) -> u32 {
12177        }
12178    "});
12179
12180    cx.update_editor(|editor, window, cx| {
12181        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12182        assert_eq!(
12183            editor.active_diagnostics, None,
12184            "Should be no diagnostics to go to and activate"
12185        );
12186    });
12187    cx.assert_editor_state(indoc! {"
12188        fn func(abcˇ def: i32) -> u32 {
12189        }
12190    "});
12191}
12192
12193#[gpui::test]
12194async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12195    init_test(cx, |_| {});
12196
12197    let mut cx = EditorTestContext::new(cx).await;
12198
12199    cx.set_state(indoc! {"
12200        fn func(abˇc def: i32) -> u32 {
12201        }
12202    "});
12203    let lsp_store =
12204        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12205
12206    cx.update(|_, cx| {
12207        lsp_store.update(cx, |lsp_store, cx| {
12208            lsp_store.update_diagnostics(
12209                LanguageServerId(0),
12210                lsp::PublishDiagnosticsParams {
12211                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12212                    version: None,
12213                    diagnostics: vec![lsp::Diagnostic {
12214                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12215                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12216                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12217                        ..Default::default()
12218                    }],
12219                },
12220                &[],
12221                cx,
12222            )
12223        })
12224    }).unwrap();
12225    cx.run_until_parked();
12226    cx.update_editor(|editor, window, cx| {
12227        hover_popover::hover(editor, &Default::default(), window, cx)
12228    });
12229    cx.run_until_parked();
12230    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12231}
12232
12233#[gpui::test]
12234async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12235    init_test(cx, |_| {});
12236
12237    let mut cx = EditorTestContext::new(cx).await;
12238
12239    let diff_base = r#"
12240        use some::mod;
12241
12242        const A: u32 = 42;
12243
12244        fn main() {
12245            println!("hello");
12246
12247            println!("world");
12248        }
12249        "#
12250    .unindent();
12251
12252    // Edits are modified, removed, modified, added
12253    cx.set_state(
12254        &r#"
12255        use some::modified;
12256
12257        ˇ
12258        fn main() {
12259            println!("hello there");
12260
12261            println!("around the");
12262            println!("world");
12263        }
12264        "#
12265        .unindent(),
12266    );
12267
12268    cx.set_head_text(&diff_base);
12269    executor.run_until_parked();
12270
12271    cx.update_editor(|editor, window, cx| {
12272        //Wrap around the bottom of the buffer
12273        for _ in 0..3 {
12274            editor.go_to_next_hunk(&GoToHunk, window, cx);
12275        }
12276    });
12277
12278    cx.assert_editor_state(
12279        &r#"
12280        ˇuse some::modified;
12281
12282
12283        fn main() {
12284            println!("hello there");
12285
12286            println!("around the");
12287            println!("world");
12288        }
12289        "#
12290        .unindent(),
12291    );
12292
12293    cx.update_editor(|editor, window, cx| {
12294        //Wrap around the top of the buffer
12295        for _ in 0..2 {
12296            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12297        }
12298    });
12299
12300    cx.assert_editor_state(
12301        &r#"
12302        use some::modified;
12303
12304
12305        fn main() {
12306        ˇ    println!("hello there");
12307
12308            println!("around the");
12309            println!("world");
12310        }
12311        "#
12312        .unindent(),
12313    );
12314
12315    cx.update_editor(|editor, window, cx| {
12316        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12317    });
12318
12319    cx.assert_editor_state(
12320        &r#"
12321        use some::modified;
12322
12323        ˇ
12324        fn main() {
12325            println!("hello there");
12326
12327            println!("around the");
12328            println!("world");
12329        }
12330        "#
12331        .unindent(),
12332    );
12333
12334    cx.update_editor(|editor, window, cx| {
12335        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12336    });
12337
12338    cx.assert_editor_state(
12339        &r#"
12340        ˇuse some::modified;
12341
12342
12343        fn main() {
12344            println!("hello there");
12345
12346            println!("around the");
12347            println!("world");
12348        }
12349        "#
12350        .unindent(),
12351    );
12352
12353    cx.update_editor(|editor, window, cx| {
12354        for _ in 0..2 {
12355            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12356        }
12357    });
12358
12359    cx.assert_editor_state(
12360        &r#"
12361        use some::modified;
12362
12363
12364        fn main() {
12365        ˇ    println!("hello there");
12366
12367            println!("around the");
12368            println!("world");
12369        }
12370        "#
12371        .unindent(),
12372    );
12373
12374    cx.update_editor(|editor, window, cx| {
12375        editor.fold(&Fold, window, cx);
12376    });
12377
12378    cx.update_editor(|editor, window, cx| {
12379        editor.go_to_next_hunk(&GoToHunk, window, cx);
12380    });
12381
12382    cx.assert_editor_state(
12383        &r#"
12384        ˇuse some::modified;
12385
12386
12387        fn main() {
12388            println!("hello there");
12389
12390            println!("around the");
12391            println!("world");
12392        }
12393        "#
12394        .unindent(),
12395    );
12396}
12397
12398#[test]
12399fn test_split_words() {
12400    fn split(text: &str) -> Vec<&str> {
12401        split_words(text).collect()
12402    }
12403
12404    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12405    assert_eq!(split("hello_world"), &["hello_", "world"]);
12406    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12407    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12408    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12409    assert_eq!(split("helloworld"), &["helloworld"]);
12410
12411    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12412}
12413
12414#[gpui::test]
12415async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12416    init_test(cx, |_| {});
12417
12418    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12419    let mut assert = |before, after| {
12420        let _state_context = cx.set_state(before);
12421        cx.run_until_parked();
12422        cx.update_editor(|editor, window, cx| {
12423            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12424        });
12425        cx.run_until_parked();
12426        cx.assert_editor_state(after);
12427    };
12428
12429    // Outside bracket jumps to outside of matching bracket
12430    assert("console.logˇ(var);", "console.log(var)ˇ;");
12431    assert("console.log(var)ˇ;", "console.logˇ(var);");
12432
12433    // Inside bracket jumps to inside of matching bracket
12434    assert("console.log(ˇvar);", "console.log(varˇ);");
12435    assert("console.log(varˇ);", "console.log(ˇvar);");
12436
12437    // When outside a bracket and inside, favor jumping to the inside bracket
12438    assert(
12439        "console.log('foo', [1, 2, 3]ˇ);",
12440        "console.log(ˇ'foo', [1, 2, 3]);",
12441    );
12442    assert(
12443        "console.log(ˇ'foo', [1, 2, 3]);",
12444        "console.log('foo', [1, 2, 3]ˇ);",
12445    );
12446
12447    // Bias forward if two options are equally likely
12448    assert(
12449        "let result = curried_fun()ˇ();",
12450        "let result = curried_fun()()ˇ;",
12451    );
12452
12453    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12454    assert(
12455        indoc! {"
12456            function test() {
12457                console.log('test')ˇ
12458            }"},
12459        indoc! {"
12460            function test() {
12461                console.logˇ('test')
12462            }"},
12463    );
12464}
12465
12466#[gpui::test]
12467async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12468    init_test(cx, |_| {});
12469
12470    let fs = FakeFs::new(cx.executor());
12471    fs.insert_tree(
12472        path!("/a"),
12473        json!({
12474            "main.rs": "fn main() { let a = 5; }",
12475            "other.rs": "// Test file",
12476        }),
12477    )
12478    .await;
12479    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12480
12481    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12482    language_registry.add(Arc::new(Language::new(
12483        LanguageConfig {
12484            name: "Rust".into(),
12485            matcher: LanguageMatcher {
12486                path_suffixes: vec!["rs".to_string()],
12487                ..Default::default()
12488            },
12489            brackets: BracketPairConfig {
12490                pairs: vec![BracketPair {
12491                    start: "{".to_string(),
12492                    end: "}".to_string(),
12493                    close: true,
12494                    surround: true,
12495                    newline: true,
12496                }],
12497                disabled_scopes_by_bracket_ix: Vec::new(),
12498            },
12499            ..Default::default()
12500        },
12501        Some(tree_sitter_rust::LANGUAGE.into()),
12502    )));
12503    let mut fake_servers = language_registry.register_fake_lsp(
12504        "Rust",
12505        FakeLspAdapter {
12506            capabilities: lsp::ServerCapabilities {
12507                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12508                    first_trigger_character: "{".to_string(),
12509                    more_trigger_character: None,
12510                }),
12511                ..Default::default()
12512            },
12513            ..Default::default()
12514        },
12515    );
12516
12517    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12518
12519    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12520
12521    let worktree_id = workspace
12522        .update(cx, |workspace, _, cx| {
12523            workspace.project().update(cx, |project, cx| {
12524                project.worktrees(cx).next().unwrap().read(cx).id()
12525            })
12526        })
12527        .unwrap();
12528
12529    let buffer = project
12530        .update(cx, |project, cx| {
12531            project.open_local_buffer(path!("/a/main.rs"), cx)
12532        })
12533        .await
12534        .unwrap();
12535    let editor_handle = workspace
12536        .update(cx, |workspace, window, cx| {
12537            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12538        })
12539        .unwrap()
12540        .await
12541        .unwrap()
12542        .downcast::<Editor>()
12543        .unwrap();
12544
12545    cx.executor().start_waiting();
12546    let fake_server = fake_servers.next().await.unwrap();
12547
12548    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12549        |params, _| async move {
12550            assert_eq!(
12551                params.text_document_position.text_document.uri,
12552                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12553            );
12554            assert_eq!(
12555                params.text_document_position.position,
12556                lsp::Position::new(0, 21),
12557            );
12558
12559            Ok(Some(vec![lsp::TextEdit {
12560                new_text: "]".to_string(),
12561                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12562            }]))
12563        },
12564    );
12565
12566    editor_handle.update_in(cx, |editor, window, cx| {
12567        window.focus(&editor.focus_handle(cx));
12568        editor.change_selections(None, window, cx, |s| {
12569            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12570        });
12571        editor.handle_input("{", window, cx);
12572    });
12573
12574    cx.executor().run_until_parked();
12575
12576    buffer.update(cx, |buffer, _| {
12577        assert_eq!(
12578            buffer.text(),
12579            "fn main() { let a = {5}; }",
12580            "No extra braces from on type formatting should appear in the buffer"
12581        )
12582    });
12583}
12584
12585#[gpui::test]
12586async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12587    init_test(cx, |_| {});
12588
12589    let fs = FakeFs::new(cx.executor());
12590    fs.insert_tree(
12591        path!("/a"),
12592        json!({
12593            "main.rs": "fn main() { let a = 5; }",
12594            "other.rs": "// Test file",
12595        }),
12596    )
12597    .await;
12598
12599    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12600
12601    let server_restarts = Arc::new(AtomicUsize::new(0));
12602    let closure_restarts = Arc::clone(&server_restarts);
12603    let language_server_name = "test language server";
12604    let language_name: LanguageName = "Rust".into();
12605
12606    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12607    language_registry.add(Arc::new(Language::new(
12608        LanguageConfig {
12609            name: language_name.clone(),
12610            matcher: LanguageMatcher {
12611                path_suffixes: vec!["rs".to_string()],
12612                ..Default::default()
12613            },
12614            ..Default::default()
12615        },
12616        Some(tree_sitter_rust::LANGUAGE.into()),
12617    )));
12618    let mut fake_servers = language_registry.register_fake_lsp(
12619        "Rust",
12620        FakeLspAdapter {
12621            name: language_server_name,
12622            initialization_options: Some(json!({
12623                "testOptionValue": true
12624            })),
12625            initializer: Some(Box::new(move |fake_server| {
12626                let task_restarts = Arc::clone(&closure_restarts);
12627                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12628                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12629                    futures::future::ready(Ok(()))
12630                });
12631            })),
12632            ..Default::default()
12633        },
12634    );
12635
12636    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12637    let _buffer = project
12638        .update(cx, |project, cx| {
12639            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12640        })
12641        .await
12642        .unwrap();
12643    let _fake_server = fake_servers.next().await.unwrap();
12644    update_test_language_settings(cx, |language_settings| {
12645        language_settings.languages.insert(
12646            language_name.clone(),
12647            LanguageSettingsContent {
12648                tab_size: NonZeroU32::new(8),
12649                ..Default::default()
12650            },
12651        );
12652    });
12653    cx.executor().run_until_parked();
12654    assert_eq!(
12655        server_restarts.load(atomic::Ordering::Acquire),
12656        0,
12657        "Should not restart LSP server on an unrelated change"
12658    );
12659
12660    update_test_project_settings(cx, |project_settings| {
12661        project_settings.lsp.insert(
12662            "Some other server name".into(),
12663            LspSettings {
12664                binary: None,
12665                settings: None,
12666                initialization_options: Some(json!({
12667                    "some other init value": false
12668                })),
12669                enable_lsp_tasks: false,
12670            },
12671        );
12672    });
12673    cx.executor().run_until_parked();
12674    assert_eq!(
12675        server_restarts.load(atomic::Ordering::Acquire),
12676        0,
12677        "Should not restart LSP server on an unrelated LSP settings change"
12678    );
12679
12680    update_test_project_settings(cx, |project_settings| {
12681        project_settings.lsp.insert(
12682            language_server_name.into(),
12683            LspSettings {
12684                binary: None,
12685                settings: None,
12686                initialization_options: Some(json!({
12687                    "anotherInitValue": false
12688                })),
12689                enable_lsp_tasks: false,
12690            },
12691        );
12692    });
12693    cx.executor().run_until_parked();
12694    assert_eq!(
12695        server_restarts.load(atomic::Ordering::Acquire),
12696        1,
12697        "Should restart LSP server on a related LSP settings change"
12698    );
12699
12700    update_test_project_settings(cx, |project_settings| {
12701        project_settings.lsp.insert(
12702            language_server_name.into(),
12703            LspSettings {
12704                binary: None,
12705                settings: None,
12706                initialization_options: Some(json!({
12707                    "anotherInitValue": false
12708                })),
12709                enable_lsp_tasks: false,
12710            },
12711        );
12712    });
12713    cx.executor().run_until_parked();
12714    assert_eq!(
12715        server_restarts.load(atomic::Ordering::Acquire),
12716        1,
12717        "Should not restart LSP server on a related LSP settings change that is the same"
12718    );
12719
12720    update_test_project_settings(cx, |project_settings| {
12721        project_settings.lsp.insert(
12722            language_server_name.into(),
12723            LspSettings {
12724                binary: None,
12725                settings: None,
12726                initialization_options: None,
12727                enable_lsp_tasks: false,
12728            },
12729        );
12730    });
12731    cx.executor().run_until_parked();
12732    assert_eq!(
12733        server_restarts.load(atomic::Ordering::Acquire),
12734        2,
12735        "Should restart LSP server on another related LSP settings change"
12736    );
12737}
12738
12739#[gpui::test]
12740async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12741    init_test(cx, |_| {});
12742
12743    let mut cx = EditorLspTestContext::new_rust(
12744        lsp::ServerCapabilities {
12745            completion_provider: Some(lsp::CompletionOptions {
12746                trigger_characters: Some(vec![".".to_string()]),
12747                resolve_provider: Some(true),
12748                ..Default::default()
12749            }),
12750            ..Default::default()
12751        },
12752        cx,
12753    )
12754    .await;
12755
12756    cx.set_state("fn main() { let a = 2ˇ; }");
12757    cx.simulate_keystroke(".");
12758    let completion_item = lsp::CompletionItem {
12759        label: "some".into(),
12760        kind: Some(lsp::CompletionItemKind::SNIPPET),
12761        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12762        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12763            kind: lsp::MarkupKind::Markdown,
12764            value: "```rust\nSome(2)\n```".to_string(),
12765        })),
12766        deprecated: Some(false),
12767        sort_text: Some("fffffff2".to_string()),
12768        filter_text: Some("some".to_string()),
12769        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12770        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12771            range: lsp::Range {
12772                start: lsp::Position {
12773                    line: 0,
12774                    character: 22,
12775                },
12776                end: lsp::Position {
12777                    line: 0,
12778                    character: 22,
12779                },
12780            },
12781            new_text: "Some(2)".to_string(),
12782        })),
12783        additional_text_edits: Some(vec![lsp::TextEdit {
12784            range: lsp::Range {
12785                start: lsp::Position {
12786                    line: 0,
12787                    character: 20,
12788                },
12789                end: lsp::Position {
12790                    line: 0,
12791                    character: 22,
12792                },
12793            },
12794            new_text: "".to_string(),
12795        }]),
12796        ..Default::default()
12797    };
12798
12799    let closure_completion_item = completion_item.clone();
12800    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12801        let task_completion_item = closure_completion_item.clone();
12802        async move {
12803            Ok(Some(lsp::CompletionResponse::Array(vec![
12804                task_completion_item,
12805            ])))
12806        }
12807    });
12808
12809    request.next().await;
12810
12811    cx.condition(|editor, _| editor.context_menu_visible())
12812        .await;
12813    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12814        editor
12815            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12816            .unwrap()
12817    });
12818    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12819
12820    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12821        let task_completion_item = completion_item.clone();
12822        async move { Ok(task_completion_item) }
12823    })
12824    .next()
12825    .await
12826    .unwrap();
12827    apply_additional_edits.await.unwrap();
12828    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12829}
12830
12831#[gpui::test]
12832async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12833    init_test(cx, |_| {});
12834
12835    let mut cx = EditorLspTestContext::new_rust(
12836        lsp::ServerCapabilities {
12837            completion_provider: Some(lsp::CompletionOptions {
12838                trigger_characters: Some(vec![".".to_string()]),
12839                resolve_provider: Some(true),
12840                ..Default::default()
12841            }),
12842            ..Default::default()
12843        },
12844        cx,
12845    )
12846    .await;
12847
12848    cx.set_state("fn main() { let a = 2ˇ; }");
12849    cx.simulate_keystroke(".");
12850
12851    let item1 = lsp::CompletionItem {
12852        label: "method id()".to_string(),
12853        filter_text: Some("id".to_string()),
12854        detail: None,
12855        documentation: None,
12856        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12857            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12858            new_text: ".id".to_string(),
12859        })),
12860        ..lsp::CompletionItem::default()
12861    };
12862
12863    let item2 = lsp::CompletionItem {
12864        label: "other".to_string(),
12865        filter_text: Some("other".to_string()),
12866        detail: None,
12867        documentation: None,
12868        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12869            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12870            new_text: ".other".to_string(),
12871        })),
12872        ..lsp::CompletionItem::default()
12873    };
12874
12875    let item1 = item1.clone();
12876    cx.set_request_handler::<lsp::request::Completion, _, _>({
12877        let item1 = item1.clone();
12878        move |_, _, _| {
12879            let item1 = item1.clone();
12880            let item2 = item2.clone();
12881            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12882        }
12883    })
12884    .next()
12885    .await;
12886
12887    cx.condition(|editor, _| editor.context_menu_visible())
12888        .await;
12889    cx.update_editor(|editor, _, _| {
12890        let context_menu = editor.context_menu.borrow_mut();
12891        let context_menu = context_menu
12892            .as_ref()
12893            .expect("Should have the context menu deployed");
12894        match context_menu {
12895            CodeContextMenu::Completions(completions_menu) => {
12896                let completions = completions_menu.completions.borrow_mut();
12897                assert_eq!(
12898                    completions
12899                        .iter()
12900                        .map(|completion| &completion.label.text)
12901                        .collect::<Vec<_>>(),
12902                    vec!["method id()", "other"]
12903                )
12904            }
12905            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12906        }
12907    });
12908
12909    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12910        let item1 = item1.clone();
12911        move |_, item_to_resolve, _| {
12912            let item1 = item1.clone();
12913            async move {
12914                if item1 == item_to_resolve {
12915                    Ok(lsp::CompletionItem {
12916                        label: "method id()".to_string(),
12917                        filter_text: Some("id".to_string()),
12918                        detail: Some("Now resolved!".to_string()),
12919                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12920                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12921                            range: lsp::Range::new(
12922                                lsp::Position::new(0, 22),
12923                                lsp::Position::new(0, 22),
12924                            ),
12925                            new_text: ".id".to_string(),
12926                        })),
12927                        ..lsp::CompletionItem::default()
12928                    })
12929                } else {
12930                    Ok(item_to_resolve)
12931                }
12932            }
12933        }
12934    })
12935    .next()
12936    .await
12937    .unwrap();
12938    cx.run_until_parked();
12939
12940    cx.update_editor(|editor, window, cx| {
12941        editor.context_menu_next(&Default::default(), window, cx);
12942    });
12943
12944    cx.update_editor(|editor, _, _| {
12945        let context_menu = editor.context_menu.borrow_mut();
12946        let context_menu = context_menu
12947            .as_ref()
12948            .expect("Should have the context menu deployed");
12949        match context_menu {
12950            CodeContextMenu::Completions(completions_menu) => {
12951                let completions = completions_menu.completions.borrow_mut();
12952                assert_eq!(
12953                    completions
12954                        .iter()
12955                        .map(|completion| &completion.label.text)
12956                        .collect::<Vec<_>>(),
12957                    vec!["method id() Now resolved!", "other"],
12958                    "Should update first completion label, but not second as the filter text did not match."
12959                );
12960            }
12961            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12962        }
12963    });
12964}
12965
12966#[gpui::test]
12967async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12968    init_test(cx, |_| {});
12969
12970    let mut cx = EditorLspTestContext::new_rust(
12971        lsp::ServerCapabilities {
12972            completion_provider: Some(lsp::CompletionOptions {
12973                trigger_characters: Some(vec![".".to_string()]),
12974                resolve_provider: Some(true),
12975                ..Default::default()
12976            }),
12977            ..Default::default()
12978        },
12979        cx,
12980    )
12981    .await;
12982
12983    cx.set_state("fn main() { let a = 2ˇ; }");
12984    cx.simulate_keystroke(".");
12985
12986    let unresolved_item_1 = lsp::CompletionItem {
12987        label: "id".to_string(),
12988        filter_text: Some("id".to_string()),
12989        detail: None,
12990        documentation: None,
12991        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12992            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12993            new_text: ".id".to_string(),
12994        })),
12995        ..lsp::CompletionItem::default()
12996    };
12997    let resolved_item_1 = lsp::CompletionItem {
12998        additional_text_edits: Some(vec![lsp::TextEdit {
12999            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13000            new_text: "!!".to_string(),
13001        }]),
13002        ..unresolved_item_1.clone()
13003    };
13004    let unresolved_item_2 = lsp::CompletionItem {
13005        label: "other".to_string(),
13006        filter_text: Some("other".to_string()),
13007        detail: None,
13008        documentation: None,
13009        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13010            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13011            new_text: ".other".to_string(),
13012        })),
13013        ..lsp::CompletionItem::default()
13014    };
13015    let resolved_item_2 = lsp::CompletionItem {
13016        additional_text_edits: Some(vec![lsp::TextEdit {
13017            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13018            new_text: "??".to_string(),
13019        }]),
13020        ..unresolved_item_2.clone()
13021    };
13022
13023    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13024    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13025    cx.lsp
13026        .server
13027        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13028            let unresolved_item_1 = unresolved_item_1.clone();
13029            let resolved_item_1 = resolved_item_1.clone();
13030            let unresolved_item_2 = unresolved_item_2.clone();
13031            let resolved_item_2 = resolved_item_2.clone();
13032            let resolve_requests_1 = resolve_requests_1.clone();
13033            let resolve_requests_2 = resolve_requests_2.clone();
13034            move |unresolved_request, _| {
13035                let unresolved_item_1 = unresolved_item_1.clone();
13036                let resolved_item_1 = resolved_item_1.clone();
13037                let unresolved_item_2 = unresolved_item_2.clone();
13038                let resolved_item_2 = resolved_item_2.clone();
13039                let resolve_requests_1 = resolve_requests_1.clone();
13040                let resolve_requests_2 = resolve_requests_2.clone();
13041                async move {
13042                    if unresolved_request == unresolved_item_1 {
13043                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13044                        Ok(resolved_item_1.clone())
13045                    } else if unresolved_request == unresolved_item_2 {
13046                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13047                        Ok(resolved_item_2.clone())
13048                    } else {
13049                        panic!("Unexpected completion item {unresolved_request:?}")
13050                    }
13051                }
13052            }
13053        })
13054        .detach();
13055
13056    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13057        let unresolved_item_1 = unresolved_item_1.clone();
13058        let unresolved_item_2 = unresolved_item_2.clone();
13059        async move {
13060            Ok(Some(lsp::CompletionResponse::Array(vec![
13061                unresolved_item_1,
13062                unresolved_item_2,
13063            ])))
13064        }
13065    })
13066    .next()
13067    .await;
13068
13069    cx.condition(|editor, _| editor.context_menu_visible())
13070        .await;
13071    cx.update_editor(|editor, _, _| {
13072        let context_menu = editor.context_menu.borrow_mut();
13073        let context_menu = context_menu
13074            .as_ref()
13075            .expect("Should have the context menu deployed");
13076        match context_menu {
13077            CodeContextMenu::Completions(completions_menu) => {
13078                let completions = completions_menu.completions.borrow_mut();
13079                assert_eq!(
13080                    completions
13081                        .iter()
13082                        .map(|completion| &completion.label.text)
13083                        .collect::<Vec<_>>(),
13084                    vec!["id", "other"]
13085                )
13086            }
13087            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13088        }
13089    });
13090    cx.run_until_parked();
13091
13092    cx.update_editor(|editor, window, cx| {
13093        editor.context_menu_next(&ContextMenuNext, window, cx);
13094    });
13095    cx.run_until_parked();
13096    cx.update_editor(|editor, window, cx| {
13097        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13098    });
13099    cx.run_until_parked();
13100    cx.update_editor(|editor, window, cx| {
13101        editor.context_menu_next(&ContextMenuNext, window, cx);
13102    });
13103    cx.run_until_parked();
13104    cx.update_editor(|editor, window, cx| {
13105        editor
13106            .compose_completion(&ComposeCompletion::default(), window, cx)
13107            .expect("No task returned")
13108    })
13109    .await
13110    .expect("Completion failed");
13111    cx.run_until_parked();
13112
13113    cx.update_editor(|editor, _, cx| {
13114        assert_eq!(
13115            resolve_requests_1.load(atomic::Ordering::Acquire),
13116            1,
13117            "Should always resolve once despite multiple selections"
13118        );
13119        assert_eq!(
13120            resolve_requests_2.load(atomic::Ordering::Acquire),
13121            1,
13122            "Should always resolve once after multiple selections and applying the completion"
13123        );
13124        assert_eq!(
13125            editor.text(cx),
13126            "fn main() { let a = ??.other; }",
13127            "Should use resolved data when applying the completion"
13128        );
13129    });
13130}
13131
13132#[gpui::test]
13133async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13134    init_test(cx, |_| {});
13135
13136    let item_0 = lsp::CompletionItem {
13137        label: "abs".into(),
13138        insert_text: Some("abs".into()),
13139        data: Some(json!({ "very": "special"})),
13140        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13141        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13142            lsp::InsertReplaceEdit {
13143                new_text: "abs".to_string(),
13144                insert: lsp::Range::default(),
13145                replace: lsp::Range::default(),
13146            },
13147        )),
13148        ..lsp::CompletionItem::default()
13149    };
13150    let items = iter::once(item_0.clone())
13151        .chain((11..51).map(|i| lsp::CompletionItem {
13152            label: format!("item_{}", i),
13153            insert_text: Some(format!("item_{}", i)),
13154            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13155            ..lsp::CompletionItem::default()
13156        }))
13157        .collect::<Vec<_>>();
13158
13159    let default_commit_characters = vec!["?".to_string()];
13160    let default_data = json!({ "default": "data"});
13161    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13162    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13163    let default_edit_range = lsp::Range {
13164        start: lsp::Position {
13165            line: 0,
13166            character: 5,
13167        },
13168        end: lsp::Position {
13169            line: 0,
13170            character: 5,
13171        },
13172    };
13173
13174    let mut cx = EditorLspTestContext::new_rust(
13175        lsp::ServerCapabilities {
13176            completion_provider: Some(lsp::CompletionOptions {
13177                trigger_characters: Some(vec![".".to_string()]),
13178                resolve_provider: Some(true),
13179                ..Default::default()
13180            }),
13181            ..Default::default()
13182        },
13183        cx,
13184    )
13185    .await;
13186
13187    cx.set_state("fn main() { let a = 2ˇ; }");
13188    cx.simulate_keystroke(".");
13189
13190    let completion_data = default_data.clone();
13191    let completion_characters = default_commit_characters.clone();
13192    let completion_items = items.clone();
13193    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13194        let default_data = completion_data.clone();
13195        let default_commit_characters = completion_characters.clone();
13196        let items = completion_items.clone();
13197        async move {
13198            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13199                items,
13200                item_defaults: Some(lsp::CompletionListItemDefaults {
13201                    data: Some(default_data.clone()),
13202                    commit_characters: Some(default_commit_characters.clone()),
13203                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13204                        default_edit_range,
13205                    )),
13206                    insert_text_format: Some(default_insert_text_format),
13207                    insert_text_mode: Some(default_insert_text_mode),
13208                }),
13209                ..lsp::CompletionList::default()
13210            })))
13211        }
13212    })
13213    .next()
13214    .await;
13215
13216    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13217    cx.lsp
13218        .server
13219        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13220            let closure_resolved_items = resolved_items.clone();
13221            move |item_to_resolve, _| {
13222                let closure_resolved_items = closure_resolved_items.clone();
13223                async move {
13224                    closure_resolved_items.lock().push(item_to_resolve.clone());
13225                    Ok(item_to_resolve)
13226                }
13227            }
13228        })
13229        .detach();
13230
13231    cx.condition(|editor, _| editor.context_menu_visible())
13232        .await;
13233    cx.run_until_parked();
13234    cx.update_editor(|editor, _, _| {
13235        let menu = editor.context_menu.borrow_mut();
13236        match menu.as_ref().expect("should have the completions menu") {
13237            CodeContextMenu::Completions(completions_menu) => {
13238                assert_eq!(
13239                    completions_menu
13240                        .entries
13241                        .borrow()
13242                        .iter()
13243                        .map(|mat| mat.string.clone())
13244                        .collect::<Vec<String>>(),
13245                    items
13246                        .iter()
13247                        .map(|completion| completion.label.clone())
13248                        .collect::<Vec<String>>()
13249                );
13250            }
13251            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13252        }
13253    });
13254    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13255    // with 4 from the end.
13256    assert_eq!(
13257        *resolved_items.lock(),
13258        [&items[0..16], &items[items.len() - 4..items.len()]]
13259            .concat()
13260            .iter()
13261            .cloned()
13262            .map(|mut item| {
13263                if item.data.is_none() {
13264                    item.data = Some(default_data.clone());
13265                }
13266                item
13267            })
13268            .collect::<Vec<lsp::CompletionItem>>(),
13269        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13270    );
13271    resolved_items.lock().clear();
13272
13273    cx.update_editor(|editor, window, cx| {
13274        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13275    });
13276    cx.run_until_parked();
13277    // Completions that have already been resolved are skipped.
13278    assert_eq!(
13279        *resolved_items.lock(),
13280        items[items.len() - 16..items.len() - 4]
13281            .iter()
13282            .cloned()
13283            .map(|mut item| {
13284                if item.data.is_none() {
13285                    item.data = Some(default_data.clone());
13286                }
13287                item
13288            })
13289            .collect::<Vec<lsp::CompletionItem>>()
13290    );
13291    resolved_items.lock().clear();
13292}
13293
13294#[gpui::test]
13295async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13296    init_test(cx, |_| {});
13297
13298    let mut cx = EditorLspTestContext::new(
13299        Language::new(
13300            LanguageConfig {
13301                matcher: LanguageMatcher {
13302                    path_suffixes: vec!["jsx".into()],
13303                    ..Default::default()
13304                },
13305                overrides: [(
13306                    "element".into(),
13307                    LanguageConfigOverride {
13308                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13309                        ..Default::default()
13310                    },
13311                )]
13312                .into_iter()
13313                .collect(),
13314                ..Default::default()
13315            },
13316            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13317        )
13318        .with_override_query("(jsx_self_closing_element) @element")
13319        .unwrap(),
13320        lsp::ServerCapabilities {
13321            completion_provider: Some(lsp::CompletionOptions {
13322                trigger_characters: Some(vec![":".to_string()]),
13323                ..Default::default()
13324            }),
13325            ..Default::default()
13326        },
13327        cx,
13328    )
13329    .await;
13330
13331    cx.lsp
13332        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13333            Ok(Some(lsp::CompletionResponse::Array(vec![
13334                lsp::CompletionItem {
13335                    label: "bg-blue".into(),
13336                    ..Default::default()
13337                },
13338                lsp::CompletionItem {
13339                    label: "bg-red".into(),
13340                    ..Default::default()
13341                },
13342                lsp::CompletionItem {
13343                    label: "bg-yellow".into(),
13344                    ..Default::default()
13345                },
13346            ])))
13347        });
13348
13349    cx.set_state(r#"<p class="bgˇ" />"#);
13350
13351    // Trigger completion when typing a dash, because the dash is an extra
13352    // word character in the 'element' scope, which contains the cursor.
13353    cx.simulate_keystroke("-");
13354    cx.executor().run_until_parked();
13355    cx.update_editor(|editor, _, _| {
13356        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13357        {
13358            assert_eq!(
13359                completion_menu_entries(&menu),
13360                &["bg-red", "bg-blue", "bg-yellow"]
13361            );
13362        } else {
13363            panic!("expected completion menu to be open");
13364        }
13365    });
13366
13367    cx.simulate_keystroke("l");
13368    cx.executor().run_until_parked();
13369    cx.update_editor(|editor, _, _| {
13370        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13371        {
13372            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13373        } else {
13374            panic!("expected completion menu to be open");
13375        }
13376    });
13377
13378    // When filtering completions, consider the character after the '-' to
13379    // be the start of a subword.
13380    cx.set_state(r#"<p class="yelˇ" />"#);
13381    cx.simulate_keystroke("l");
13382    cx.executor().run_until_parked();
13383    cx.update_editor(|editor, _, _| {
13384        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13385        {
13386            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13387        } else {
13388            panic!("expected completion menu to be open");
13389        }
13390    });
13391}
13392
13393fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13394    let entries = menu.entries.borrow();
13395    entries.iter().map(|mat| mat.string.clone()).collect()
13396}
13397
13398#[gpui::test]
13399async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13400    init_test(cx, |settings| {
13401        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13402            FormatterList(vec![Formatter::Prettier].into()),
13403        ))
13404    });
13405
13406    let fs = FakeFs::new(cx.executor());
13407    fs.insert_file(path!("/file.ts"), Default::default()).await;
13408
13409    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13410    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13411
13412    language_registry.add(Arc::new(Language::new(
13413        LanguageConfig {
13414            name: "TypeScript".into(),
13415            matcher: LanguageMatcher {
13416                path_suffixes: vec!["ts".to_string()],
13417                ..Default::default()
13418            },
13419            ..Default::default()
13420        },
13421        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13422    )));
13423    update_test_language_settings(cx, |settings| {
13424        settings.defaults.prettier = Some(PrettierSettings {
13425            allowed: true,
13426            ..PrettierSettings::default()
13427        });
13428    });
13429
13430    let test_plugin = "test_plugin";
13431    let _ = language_registry.register_fake_lsp(
13432        "TypeScript",
13433        FakeLspAdapter {
13434            prettier_plugins: vec![test_plugin],
13435            ..Default::default()
13436        },
13437    );
13438
13439    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13440    let buffer = project
13441        .update(cx, |project, cx| {
13442            project.open_local_buffer(path!("/file.ts"), cx)
13443        })
13444        .await
13445        .unwrap();
13446
13447    let buffer_text = "one\ntwo\nthree\n";
13448    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13449    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13450    editor.update_in(cx, |editor, window, cx| {
13451        editor.set_text(buffer_text, window, cx)
13452    });
13453
13454    editor
13455        .update_in(cx, |editor, window, cx| {
13456            editor.perform_format(
13457                project.clone(),
13458                FormatTrigger::Manual,
13459                FormatTarget::Buffers,
13460                window,
13461                cx,
13462            )
13463        })
13464        .unwrap()
13465        .await;
13466    assert_eq!(
13467        editor.update(cx, |editor, cx| editor.text(cx)),
13468        buffer_text.to_string() + prettier_format_suffix,
13469        "Test prettier formatting was not applied to the original buffer text",
13470    );
13471
13472    update_test_language_settings(cx, |settings| {
13473        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13474    });
13475    let format = editor.update_in(cx, |editor, window, cx| {
13476        editor.perform_format(
13477            project.clone(),
13478            FormatTrigger::Manual,
13479            FormatTarget::Buffers,
13480            window,
13481            cx,
13482        )
13483    });
13484    format.await.unwrap();
13485    assert_eq!(
13486        editor.update(cx, |editor, cx| editor.text(cx)),
13487        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13488        "Autoformatting (via test prettier) was not applied to the original buffer text",
13489    );
13490}
13491
13492#[gpui::test]
13493async fn test_addition_reverts(cx: &mut TestAppContext) {
13494    init_test(cx, |_| {});
13495    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13496    let base_text = indoc! {r#"
13497        struct Row;
13498        struct Row1;
13499        struct Row2;
13500
13501        struct Row4;
13502        struct Row5;
13503        struct Row6;
13504
13505        struct Row8;
13506        struct Row9;
13507        struct Row10;"#};
13508
13509    // When addition hunks are not adjacent to carets, no hunk revert is performed
13510    assert_hunk_revert(
13511        indoc! {r#"struct Row;
13512                   struct Row1;
13513                   struct Row1.1;
13514                   struct Row1.2;
13515                   struct Row2;ˇ
13516
13517                   struct Row4;
13518                   struct Row5;
13519                   struct Row6;
13520
13521                   struct Row8;
13522                   ˇstruct Row9;
13523                   struct Row9.1;
13524                   struct Row9.2;
13525                   struct Row9.3;
13526                   struct Row10;"#},
13527        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13528        indoc! {r#"struct Row;
13529                   struct Row1;
13530                   struct Row1.1;
13531                   struct Row1.2;
13532                   struct Row2;ˇ
13533
13534                   struct Row4;
13535                   struct Row5;
13536                   struct Row6;
13537
13538                   struct Row8;
13539                   ˇstruct Row9;
13540                   struct Row9.1;
13541                   struct Row9.2;
13542                   struct Row9.3;
13543                   struct Row10;"#},
13544        base_text,
13545        &mut cx,
13546    );
13547    // Same for selections
13548    assert_hunk_revert(
13549        indoc! {r#"struct Row;
13550                   struct Row1;
13551                   struct Row2;
13552                   struct Row2.1;
13553                   struct Row2.2;
13554                   «ˇ
13555                   struct Row4;
13556                   struct» Row5;
13557                   «struct Row6;
13558                   ˇ»
13559                   struct Row9.1;
13560                   struct Row9.2;
13561                   struct Row9.3;
13562                   struct Row8;
13563                   struct Row9;
13564                   struct Row10;"#},
13565        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13566        indoc! {r#"struct Row;
13567                   struct Row1;
13568                   struct Row2;
13569                   struct Row2.1;
13570                   struct Row2.2;
13571                   «ˇ
13572                   struct Row4;
13573                   struct» Row5;
13574                   «struct Row6;
13575                   ˇ»
13576                   struct Row9.1;
13577                   struct Row9.2;
13578                   struct Row9.3;
13579                   struct Row8;
13580                   struct Row9;
13581                   struct Row10;"#},
13582        base_text,
13583        &mut cx,
13584    );
13585
13586    // When carets and selections intersect the addition hunks, those are reverted.
13587    // Adjacent carets got merged.
13588    assert_hunk_revert(
13589        indoc! {r#"struct Row;
13590                   ˇ// something on the top
13591                   struct Row1;
13592                   struct Row2;
13593                   struct Roˇw3.1;
13594                   struct Row2.2;
13595                   struct Row2.3;ˇ
13596
13597                   struct Row4;
13598                   struct ˇRow5.1;
13599                   struct Row5.2;
13600                   struct «Rowˇ»5.3;
13601                   struct Row5;
13602                   struct Row6;
13603                   ˇ
13604                   struct Row9.1;
13605                   struct «Rowˇ»9.2;
13606                   struct «ˇRow»9.3;
13607                   struct Row8;
13608                   struct Row9;
13609                   «ˇ// something on bottom»
13610                   struct Row10;"#},
13611        vec![
13612            DiffHunkStatusKind::Added,
13613            DiffHunkStatusKind::Added,
13614            DiffHunkStatusKind::Added,
13615            DiffHunkStatusKind::Added,
13616            DiffHunkStatusKind::Added,
13617        ],
13618        indoc! {r#"struct Row;
13619                   ˇstruct Row1;
13620                   struct Row2;
13621                   ˇ
13622                   struct Row4;
13623                   ˇstruct Row5;
13624                   struct Row6;
13625                   ˇ
13626                   ˇstruct Row8;
13627                   struct Row9;
13628                   ˇstruct Row10;"#},
13629        base_text,
13630        &mut cx,
13631    );
13632}
13633
13634#[gpui::test]
13635async fn test_modification_reverts(cx: &mut TestAppContext) {
13636    init_test(cx, |_| {});
13637    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13638    let base_text = indoc! {r#"
13639        struct Row;
13640        struct Row1;
13641        struct Row2;
13642
13643        struct Row4;
13644        struct Row5;
13645        struct Row6;
13646
13647        struct Row8;
13648        struct Row9;
13649        struct Row10;"#};
13650
13651    // Modification hunks behave the same as the addition ones.
13652    assert_hunk_revert(
13653        indoc! {r#"struct Row;
13654                   struct Row1;
13655                   struct Row33;
13656                   ˇ
13657                   struct Row4;
13658                   struct Row5;
13659                   struct Row6;
13660                   ˇ
13661                   struct Row99;
13662                   struct Row9;
13663                   struct Row10;"#},
13664        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13665        indoc! {r#"struct Row;
13666                   struct Row1;
13667                   struct Row33;
13668                   ˇ
13669                   struct Row4;
13670                   struct Row5;
13671                   struct Row6;
13672                   ˇ
13673                   struct Row99;
13674                   struct Row9;
13675                   struct Row10;"#},
13676        base_text,
13677        &mut cx,
13678    );
13679    assert_hunk_revert(
13680        indoc! {r#"struct Row;
13681                   struct Row1;
13682                   struct Row33;
13683                   «ˇ
13684                   struct Row4;
13685                   struct» Row5;
13686                   «struct Row6;
13687                   ˇ»
13688                   struct Row99;
13689                   struct Row9;
13690                   struct Row10;"#},
13691        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13692        indoc! {r#"struct Row;
13693                   struct Row1;
13694                   struct Row33;
13695                   «ˇ
13696                   struct Row4;
13697                   struct» Row5;
13698                   «struct Row6;
13699                   ˇ»
13700                   struct Row99;
13701                   struct Row9;
13702                   struct Row10;"#},
13703        base_text,
13704        &mut cx,
13705    );
13706
13707    assert_hunk_revert(
13708        indoc! {r#"ˇstruct Row1.1;
13709                   struct Row1;
13710                   «ˇstr»uct Row22;
13711
13712                   struct ˇRow44;
13713                   struct Row5;
13714                   struct «Rˇ»ow66;ˇ
13715
13716                   «struˇ»ct Row88;
13717                   struct Row9;
13718                   struct Row1011;ˇ"#},
13719        vec![
13720            DiffHunkStatusKind::Modified,
13721            DiffHunkStatusKind::Modified,
13722            DiffHunkStatusKind::Modified,
13723            DiffHunkStatusKind::Modified,
13724            DiffHunkStatusKind::Modified,
13725            DiffHunkStatusKind::Modified,
13726        ],
13727        indoc! {r#"struct Row;
13728                   ˇstruct Row1;
13729                   struct Row2;
13730                   ˇ
13731                   struct Row4;
13732                   ˇstruct Row5;
13733                   struct Row6;
13734                   ˇ
13735                   struct Row8;
13736                   ˇstruct Row9;
13737                   struct Row10;ˇ"#},
13738        base_text,
13739        &mut cx,
13740    );
13741}
13742
13743#[gpui::test]
13744async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13745    init_test(cx, |_| {});
13746    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13747    let base_text = indoc! {r#"
13748        one
13749
13750        two
13751        three
13752        "#};
13753
13754    cx.set_head_text(base_text);
13755    cx.set_state("\nˇ\n");
13756    cx.executor().run_until_parked();
13757    cx.update_editor(|editor, _window, cx| {
13758        editor.expand_selected_diff_hunks(cx);
13759    });
13760    cx.executor().run_until_parked();
13761    cx.update_editor(|editor, window, cx| {
13762        editor.backspace(&Default::default(), window, cx);
13763    });
13764    cx.run_until_parked();
13765    cx.assert_state_with_diff(
13766        indoc! {r#"
13767
13768        - two
13769        - threeˇ
13770        +
13771        "#}
13772        .to_string(),
13773    );
13774}
13775
13776#[gpui::test]
13777async fn test_deletion_reverts(cx: &mut TestAppContext) {
13778    init_test(cx, |_| {});
13779    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13780    let base_text = indoc! {r#"struct Row;
13781struct Row1;
13782struct Row2;
13783
13784struct Row4;
13785struct Row5;
13786struct Row6;
13787
13788struct Row8;
13789struct Row9;
13790struct Row10;"#};
13791
13792    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13793    assert_hunk_revert(
13794        indoc! {r#"struct Row;
13795                   struct Row2;
13796
13797                   ˇstruct Row4;
13798                   struct Row5;
13799                   struct Row6;
13800                   ˇ
13801                   struct Row8;
13802                   struct Row10;"#},
13803        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13804        indoc! {r#"struct Row;
13805                   struct Row2;
13806
13807                   ˇstruct Row4;
13808                   struct Row5;
13809                   struct Row6;
13810                   ˇ
13811                   struct Row8;
13812                   struct Row10;"#},
13813        base_text,
13814        &mut cx,
13815    );
13816    assert_hunk_revert(
13817        indoc! {r#"struct Row;
13818                   struct Row2;
13819
13820                   «ˇstruct Row4;
13821                   struct» Row5;
13822                   «struct Row6;
13823                   ˇ»
13824                   struct Row8;
13825                   struct Row10;"#},
13826        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13827        indoc! {r#"struct Row;
13828                   struct Row2;
13829
13830                   «ˇstruct Row4;
13831                   struct» Row5;
13832                   «struct Row6;
13833                   ˇ»
13834                   struct Row8;
13835                   struct Row10;"#},
13836        base_text,
13837        &mut cx,
13838    );
13839
13840    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13841    assert_hunk_revert(
13842        indoc! {r#"struct Row;
13843                   ˇstruct Row2;
13844
13845                   struct Row4;
13846                   struct Row5;
13847                   struct Row6;
13848
13849                   struct Row8;ˇ
13850                   struct Row10;"#},
13851        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13852        indoc! {r#"struct Row;
13853                   struct Row1;
13854                   ˇstruct Row2;
13855
13856                   struct Row4;
13857                   struct Row5;
13858                   struct Row6;
13859
13860                   struct Row8;ˇ
13861                   struct Row9;
13862                   struct Row10;"#},
13863        base_text,
13864        &mut cx,
13865    );
13866    assert_hunk_revert(
13867        indoc! {r#"struct Row;
13868                   struct Row2«ˇ;
13869                   struct Row4;
13870                   struct» Row5;
13871                   «struct Row6;
13872
13873                   struct Row8;ˇ»
13874                   struct Row10;"#},
13875        vec![
13876            DiffHunkStatusKind::Deleted,
13877            DiffHunkStatusKind::Deleted,
13878            DiffHunkStatusKind::Deleted,
13879        ],
13880        indoc! {r#"struct Row;
13881                   struct Row1;
13882                   struct Row2«ˇ;
13883
13884                   struct Row4;
13885                   struct» Row5;
13886                   «struct Row6;
13887
13888                   struct Row8;ˇ»
13889                   struct Row9;
13890                   struct Row10;"#},
13891        base_text,
13892        &mut cx,
13893    );
13894}
13895
13896#[gpui::test]
13897async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13898    init_test(cx, |_| {});
13899
13900    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13901    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13902    let base_text_3 =
13903        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13904
13905    let text_1 = edit_first_char_of_every_line(base_text_1);
13906    let text_2 = edit_first_char_of_every_line(base_text_2);
13907    let text_3 = edit_first_char_of_every_line(base_text_3);
13908
13909    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13910    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13911    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13912
13913    let multibuffer = cx.new(|cx| {
13914        let mut multibuffer = MultiBuffer::new(ReadWrite);
13915        multibuffer.push_excerpts(
13916            buffer_1.clone(),
13917            [
13918                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13919                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13920                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13921            ],
13922            cx,
13923        );
13924        multibuffer.push_excerpts(
13925            buffer_2.clone(),
13926            [
13927                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13928                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13929                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13930            ],
13931            cx,
13932        );
13933        multibuffer.push_excerpts(
13934            buffer_3.clone(),
13935            [
13936                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13937                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13938                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13939            ],
13940            cx,
13941        );
13942        multibuffer
13943    });
13944
13945    let fs = FakeFs::new(cx.executor());
13946    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13947    let (editor, cx) = cx
13948        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13949    editor.update_in(cx, |editor, _window, cx| {
13950        for (buffer, diff_base) in [
13951            (buffer_1.clone(), base_text_1),
13952            (buffer_2.clone(), base_text_2),
13953            (buffer_3.clone(), base_text_3),
13954        ] {
13955            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13956            editor
13957                .buffer
13958                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13959        }
13960    });
13961    cx.executor().run_until_parked();
13962
13963    editor.update_in(cx, |editor, window, cx| {
13964        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}");
13965        editor.select_all(&SelectAll, window, cx);
13966        editor.git_restore(&Default::default(), window, cx);
13967    });
13968    cx.executor().run_until_parked();
13969
13970    // When all ranges are selected, all buffer hunks are reverted.
13971    editor.update(cx, |editor, cx| {
13972        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");
13973    });
13974    buffer_1.update(cx, |buffer, _| {
13975        assert_eq!(buffer.text(), base_text_1);
13976    });
13977    buffer_2.update(cx, |buffer, _| {
13978        assert_eq!(buffer.text(), base_text_2);
13979    });
13980    buffer_3.update(cx, |buffer, _| {
13981        assert_eq!(buffer.text(), base_text_3);
13982    });
13983
13984    editor.update_in(cx, |editor, window, cx| {
13985        editor.undo(&Default::default(), window, cx);
13986    });
13987
13988    editor.update_in(cx, |editor, window, cx| {
13989        editor.change_selections(None, window, cx, |s| {
13990            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13991        });
13992        editor.git_restore(&Default::default(), window, cx);
13993    });
13994
13995    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13996    // but not affect buffer_2 and its related excerpts.
13997    editor.update(cx, |editor, cx| {
13998        assert_eq!(
13999            editor.text(cx),
14000            "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}"
14001        );
14002    });
14003    buffer_1.update(cx, |buffer, _| {
14004        assert_eq!(buffer.text(), base_text_1);
14005    });
14006    buffer_2.update(cx, |buffer, _| {
14007        assert_eq!(
14008            buffer.text(),
14009            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14010        );
14011    });
14012    buffer_3.update(cx, |buffer, _| {
14013        assert_eq!(
14014            buffer.text(),
14015            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14016        );
14017    });
14018
14019    fn edit_first_char_of_every_line(text: &str) -> String {
14020        text.split('\n')
14021            .map(|line| format!("X{}", &line[1..]))
14022            .collect::<Vec<_>>()
14023            .join("\n")
14024    }
14025}
14026
14027#[gpui::test]
14028async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14029    init_test(cx, |_| {});
14030
14031    let cols = 4;
14032    let rows = 10;
14033    let sample_text_1 = sample_text(rows, cols, 'a');
14034    assert_eq!(
14035        sample_text_1,
14036        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14037    );
14038    let sample_text_2 = sample_text(rows, cols, 'l');
14039    assert_eq!(
14040        sample_text_2,
14041        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14042    );
14043    let sample_text_3 = sample_text(rows, cols, 'v');
14044    assert_eq!(
14045        sample_text_3,
14046        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14047    );
14048
14049    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14050    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14051    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14052
14053    let multi_buffer = cx.new(|cx| {
14054        let mut multibuffer = MultiBuffer::new(ReadWrite);
14055        multibuffer.push_excerpts(
14056            buffer_1.clone(),
14057            [
14058                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14059                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14060                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14061            ],
14062            cx,
14063        );
14064        multibuffer.push_excerpts(
14065            buffer_2.clone(),
14066            [
14067                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14068                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14069                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14070            ],
14071            cx,
14072        );
14073        multibuffer.push_excerpts(
14074            buffer_3.clone(),
14075            [
14076                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14077                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14078                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14079            ],
14080            cx,
14081        );
14082        multibuffer
14083    });
14084
14085    let fs = FakeFs::new(cx.executor());
14086    fs.insert_tree(
14087        "/a",
14088        json!({
14089            "main.rs": sample_text_1,
14090            "other.rs": sample_text_2,
14091            "lib.rs": sample_text_3,
14092        }),
14093    )
14094    .await;
14095    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14096    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14097    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14098    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14099        Editor::new(
14100            EditorMode::Full,
14101            multi_buffer,
14102            Some(project.clone()),
14103            window,
14104            cx,
14105        )
14106    });
14107    let multibuffer_item_id = workspace
14108        .update(cx, |workspace, window, cx| {
14109            assert!(
14110                workspace.active_item(cx).is_none(),
14111                "active item should be None before the first item is added"
14112            );
14113            workspace.add_item_to_active_pane(
14114                Box::new(multi_buffer_editor.clone()),
14115                None,
14116                true,
14117                window,
14118                cx,
14119            );
14120            let active_item = workspace
14121                .active_item(cx)
14122                .expect("should have an active item after adding the multi buffer");
14123            assert!(
14124                !active_item.is_singleton(cx),
14125                "A multi buffer was expected to active after adding"
14126            );
14127            active_item.item_id()
14128        })
14129        .unwrap();
14130    cx.executor().run_until_parked();
14131
14132    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14133        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14134            s.select_ranges(Some(1..2))
14135        });
14136        editor.open_excerpts(&OpenExcerpts, window, cx);
14137    });
14138    cx.executor().run_until_parked();
14139    let first_item_id = workspace
14140        .update(cx, |workspace, window, cx| {
14141            let active_item = workspace
14142                .active_item(cx)
14143                .expect("should have an active item after navigating into the 1st buffer");
14144            let first_item_id = active_item.item_id();
14145            assert_ne!(
14146                first_item_id, multibuffer_item_id,
14147                "Should navigate into the 1st buffer and activate it"
14148            );
14149            assert!(
14150                active_item.is_singleton(cx),
14151                "New active item should be a singleton buffer"
14152            );
14153            assert_eq!(
14154                active_item
14155                    .act_as::<Editor>(cx)
14156                    .expect("should have navigated into an editor for the 1st buffer")
14157                    .read(cx)
14158                    .text(cx),
14159                sample_text_1
14160            );
14161
14162            workspace
14163                .go_back(workspace.active_pane().downgrade(), window, cx)
14164                .detach_and_log_err(cx);
14165
14166            first_item_id
14167        })
14168        .unwrap();
14169    cx.executor().run_until_parked();
14170    workspace
14171        .update(cx, |workspace, _, cx| {
14172            let active_item = workspace
14173                .active_item(cx)
14174                .expect("should have an active item after navigating back");
14175            assert_eq!(
14176                active_item.item_id(),
14177                multibuffer_item_id,
14178                "Should navigate back to the multi buffer"
14179            );
14180            assert!(!active_item.is_singleton(cx));
14181        })
14182        .unwrap();
14183
14184    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14185        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14186            s.select_ranges(Some(39..40))
14187        });
14188        editor.open_excerpts(&OpenExcerpts, window, cx);
14189    });
14190    cx.executor().run_until_parked();
14191    let second_item_id = workspace
14192        .update(cx, |workspace, window, cx| {
14193            let active_item = workspace
14194                .active_item(cx)
14195                .expect("should have an active item after navigating into the 2nd buffer");
14196            let second_item_id = active_item.item_id();
14197            assert_ne!(
14198                second_item_id, multibuffer_item_id,
14199                "Should navigate away from the multibuffer"
14200            );
14201            assert_ne!(
14202                second_item_id, first_item_id,
14203                "Should navigate into the 2nd buffer and activate it"
14204            );
14205            assert!(
14206                active_item.is_singleton(cx),
14207                "New active item should be a singleton buffer"
14208            );
14209            assert_eq!(
14210                active_item
14211                    .act_as::<Editor>(cx)
14212                    .expect("should have navigated into an editor")
14213                    .read(cx)
14214                    .text(cx),
14215                sample_text_2
14216            );
14217
14218            workspace
14219                .go_back(workspace.active_pane().downgrade(), window, cx)
14220                .detach_and_log_err(cx);
14221
14222            second_item_id
14223        })
14224        .unwrap();
14225    cx.executor().run_until_parked();
14226    workspace
14227        .update(cx, |workspace, _, cx| {
14228            let active_item = workspace
14229                .active_item(cx)
14230                .expect("should have an active item after navigating back from the 2nd buffer");
14231            assert_eq!(
14232                active_item.item_id(),
14233                multibuffer_item_id,
14234                "Should navigate back from the 2nd buffer to the multi buffer"
14235            );
14236            assert!(!active_item.is_singleton(cx));
14237        })
14238        .unwrap();
14239
14240    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14241        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14242            s.select_ranges(Some(70..70))
14243        });
14244        editor.open_excerpts(&OpenExcerpts, window, cx);
14245    });
14246    cx.executor().run_until_parked();
14247    workspace
14248        .update(cx, |workspace, window, cx| {
14249            let active_item = workspace
14250                .active_item(cx)
14251                .expect("should have an active item after navigating into the 3rd buffer");
14252            let third_item_id = active_item.item_id();
14253            assert_ne!(
14254                third_item_id, multibuffer_item_id,
14255                "Should navigate into the 3rd buffer and activate it"
14256            );
14257            assert_ne!(third_item_id, first_item_id);
14258            assert_ne!(third_item_id, second_item_id);
14259            assert!(
14260                active_item.is_singleton(cx),
14261                "New active item should be a singleton buffer"
14262            );
14263            assert_eq!(
14264                active_item
14265                    .act_as::<Editor>(cx)
14266                    .expect("should have navigated into an editor")
14267                    .read(cx)
14268                    .text(cx),
14269                sample_text_3
14270            );
14271
14272            workspace
14273                .go_back(workspace.active_pane().downgrade(), window, cx)
14274                .detach_and_log_err(cx);
14275        })
14276        .unwrap();
14277    cx.executor().run_until_parked();
14278    workspace
14279        .update(cx, |workspace, _, cx| {
14280            let active_item = workspace
14281                .active_item(cx)
14282                .expect("should have an active item after navigating back from the 3rd buffer");
14283            assert_eq!(
14284                active_item.item_id(),
14285                multibuffer_item_id,
14286                "Should navigate back from the 3rd buffer to the multi buffer"
14287            );
14288            assert!(!active_item.is_singleton(cx));
14289        })
14290        .unwrap();
14291}
14292
14293#[gpui::test]
14294async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14295    init_test(cx, |_| {});
14296
14297    let mut cx = EditorTestContext::new(cx).await;
14298
14299    let diff_base = r#"
14300        use some::mod;
14301
14302        const A: u32 = 42;
14303
14304        fn main() {
14305            println!("hello");
14306
14307            println!("world");
14308        }
14309        "#
14310    .unindent();
14311
14312    cx.set_state(
14313        &r#"
14314        use some::modified;
14315
14316        ˇ
14317        fn main() {
14318            println!("hello there");
14319
14320            println!("around the");
14321            println!("world");
14322        }
14323        "#
14324        .unindent(),
14325    );
14326
14327    cx.set_head_text(&diff_base);
14328    executor.run_until_parked();
14329
14330    cx.update_editor(|editor, window, cx| {
14331        editor.go_to_next_hunk(&GoToHunk, window, cx);
14332        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14333    });
14334    executor.run_until_parked();
14335    cx.assert_state_with_diff(
14336        r#"
14337          use some::modified;
14338
14339
14340          fn main() {
14341        -     println!("hello");
14342        + ˇ    println!("hello there");
14343
14344              println!("around the");
14345              println!("world");
14346          }
14347        "#
14348        .unindent(),
14349    );
14350
14351    cx.update_editor(|editor, window, cx| {
14352        for _ in 0..2 {
14353            editor.go_to_next_hunk(&GoToHunk, window, cx);
14354            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14355        }
14356    });
14357    executor.run_until_parked();
14358    cx.assert_state_with_diff(
14359        r#"
14360        - use some::mod;
14361        + ˇuse some::modified;
14362
14363
14364          fn main() {
14365        -     println!("hello");
14366        +     println!("hello there");
14367
14368        +     println!("around the");
14369              println!("world");
14370          }
14371        "#
14372        .unindent(),
14373    );
14374
14375    cx.update_editor(|editor, window, cx| {
14376        editor.go_to_next_hunk(&GoToHunk, window, cx);
14377        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14378    });
14379    executor.run_until_parked();
14380    cx.assert_state_with_diff(
14381        r#"
14382        - use some::mod;
14383        + use some::modified;
14384
14385        - const A: u32 = 42;
14386          ˇ
14387          fn main() {
14388        -     println!("hello");
14389        +     println!("hello there");
14390
14391        +     println!("around the");
14392              println!("world");
14393          }
14394        "#
14395        .unindent(),
14396    );
14397
14398    cx.update_editor(|editor, window, cx| {
14399        editor.cancel(&Cancel, window, cx);
14400    });
14401
14402    cx.assert_state_with_diff(
14403        r#"
14404          use some::modified;
14405
14406          ˇ
14407          fn main() {
14408              println!("hello there");
14409
14410              println!("around the");
14411              println!("world");
14412          }
14413        "#
14414        .unindent(),
14415    );
14416}
14417
14418#[gpui::test]
14419async fn test_diff_base_change_with_expanded_diff_hunks(
14420    executor: BackgroundExecutor,
14421    cx: &mut TestAppContext,
14422) {
14423    init_test(cx, |_| {});
14424
14425    let mut cx = EditorTestContext::new(cx).await;
14426
14427    let diff_base = r#"
14428        use some::mod1;
14429        use some::mod2;
14430
14431        const A: u32 = 42;
14432        const B: u32 = 42;
14433        const C: u32 = 42;
14434
14435        fn main() {
14436            println!("hello");
14437
14438            println!("world");
14439        }
14440        "#
14441    .unindent();
14442
14443    cx.set_state(
14444        &r#"
14445        use some::mod2;
14446
14447        const A: u32 = 42;
14448        const C: u32 = 42;
14449
14450        fn main(ˇ) {
14451            //println!("hello");
14452
14453            println!("world");
14454            //
14455            //
14456        }
14457        "#
14458        .unindent(),
14459    );
14460
14461    cx.set_head_text(&diff_base);
14462    executor.run_until_parked();
14463
14464    cx.update_editor(|editor, window, cx| {
14465        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14466    });
14467    executor.run_until_parked();
14468    cx.assert_state_with_diff(
14469        r#"
14470        - use some::mod1;
14471          use some::mod2;
14472
14473          const A: u32 = 42;
14474        - const B: u32 = 42;
14475          const C: u32 = 42;
14476
14477          fn main(ˇ) {
14478        -     println!("hello");
14479        +     //println!("hello");
14480
14481              println!("world");
14482        +     //
14483        +     //
14484          }
14485        "#
14486        .unindent(),
14487    );
14488
14489    cx.set_head_text("new diff base!");
14490    executor.run_until_parked();
14491    cx.assert_state_with_diff(
14492        r#"
14493        - new diff base!
14494        + use some::mod2;
14495        +
14496        + const A: u32 = 42;
14497        + const C: u32 = 42;
14498        +
14499        + fn main(ˇ) {
14500        +     //println!("hello");
14501        +
14502        +     println!("world");
14503        +     //
14504        +     //
14505        + }
14506        "#
14507        .unindent(),
14508    );
14509}
14510
14511#[gpui::test]
14512async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14513    init_test(cx, |_| {});
14514
14515    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14516    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14517    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14518    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14519    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14520    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14521
14522    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14523    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14524    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14525
14526    let multi_buffer = cx.new(|cx| {
14527        let mut multibuffer = MultiBuffer::new(ReadWrite);
14528        multibuffer.push_excerpts(
14529            buffer_1.clone(),
14530            [
14531                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14532                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14533                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14534            ],
14535            cx,
14536        );
14537        multibuffer.push_excerpts(
14538            buffer_2.clone(),
14539            [
14540                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14541                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14542                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14543            ],
14544            cx,
14545        );
14546        multibuffer.push_excerpts(
14547            buffer_3.clone(),
14548            [
14549                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14550                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14551                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14552            ],
14553            cx,
14554        );
14555        multibuffer
14556    });
14557
14558    let editor =
14559        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14560    editor
14561        .update(cx, |editor, _window, cx| {
14562            for (buffer, diff_base) in [
14563                (buffer_1.clone(), file_1_old),
14564                (buffer_2.clone(), file_2_old),
14565                (buffer_3.clone(), file_3_old),
14566            ] {
14567                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14568                editor
14569                    .buffer
14570                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14571            }
14572        })
14573        .unwrap();
14574
14575    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14576    cx.run_until_parked();
14577
14578    cx.assert_editor_state(
14579        &"
14580            ˇaaa
14581            ccc
14582            ddd
14583
14584            ggg
14585            hhh
14586
14587
14588            lll
14589            mmm
14590            NNN
14591
14592            qqq
14593            rrr
14594
14595            uuu
14596            111
14597            222
14598            333
14599
14600            666
14601            777
14602
14603            000
14604            !!!"
14605        .unindent(),
14606    );
14607
14608    cx.update_editor(|editor, window, cx| {
14609        editor.select_all(&SelectAll, window, cx);
14610        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14611    });
14612    cx.executor().run_until_parked();
14613
14614    cx.assert_state_with_diff(
14615        "
14616            «aaa
14617          - bbb
14618            ccc
14619            ddd
14620
14621            ggg
14622            hhh
14623
14624
14625            lll
14626            mmm
14627          - nnn
14628          + NNN
14629
14630            qqq
14631            rrr
14632
14633            uuu
14634            111
14635            222
14636            333
14637
14638          + 666
14639            777
14640
14641            000
14642            !!!ˇ»"
14643            .unindent(),
14644    );
14645}
14646
14647#[gpui::test]
14648async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14649    init_test(cx, |_| {});
14650
14651    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14652    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14653
14654    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14655    let multi_buffer = cx.new(|cx| {
14656        let mut multibuffer = MultiBuffer::new(ReadWrite);
14657        multibuffer.push_excerpts(
14658            buffer.clone(),
14659            [
14660                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14661                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14662                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14663            ],
14664            cx,
14665        );
14666        multibuffer
14667    });
14668
14669    let editor =
14670        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14671    editor
14672        .update(cx, |editor, _window, cx| {
14673            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14674            editor
14675                .buffer
14676                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14677        })
14678        .unwrap();
14679
14680    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14681    cx.run_until_parked();
14682
14683    cx.update_editor(|editor, window, cx| {
14684        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14685    });
14686    cx.executor().run_until_parked();
14687
14688    // When the start of a hunk coincides with the start of its excerpt,
14689    // the hunk is expanded. When the start of a a hunk is earlier than
14690    // the start of its excerpt, the hunk is not expanded.
14691    cx.assert_state_with_diff(
14692        "
14693            ˇaaa
14694          - bbb
14695          + BBB
14696
14697          - ddd
14698          - eee
14699          + DDD
14700          + EEE
14701            fff
14702
14703            iii
14704        "
14705        .unindent(),
14706    );
14707}
14708
14709#[gpui::test]
14710async fn test_edits_around_expanded_insertion_hunks(
14711    executor: BackgroundExecutor,
14712    cx: &mut TestAppContext,
14713) {
14714    init_test(cx, |_| {});
14715
14716    let mut cx = EditorTestContext::new(cx).await;
14717
14718    let diff_base = r#"
14719        use some::mod1;
14720        use some::mod2;
14721
14722        const A: u32 = 42;
14723
14724        fn main() {
14725            println!("hello");
14726
14727            println!("world");
14728        }
14729        "#
14730    .unindent();
14731    executor.run_until_parked();
14732    cx.set_state(
14733        &r#"
14734        use some::mod1;
14735        use some::mod2;
14736
14737        const A: u32 = 42;
14738        const B: u32 = 42;
14739        const C: u32 = 42;
14740        ˇ
14741
14742        fn main() {
14743            println!("hello");
14744
14745            println!("world");
14746        }
14747        "#
14748        .unindent(),
14749    );
14750
14751    cx.set_head_text(&diff_base);
14752    executor.run_until_parked();
14753
14754    cx.update_editor(|editor, window, cx| {
14755        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14756    });
14757    executor.run_until_parked();
14758
14759    cx.assert_state_with_diff(
14760        r#"
14761        use some::mod1;
14762        use some::mod2;
14763
14764        const A: u32 = 42;
14765      + const B: u32 = 42;
14766      + const C: u32 = 42;
14767      + ˇ
14768
14769        fn main() {
14770            println!("hello");
14771
14772            println!("world");
14773        }
14774      "#
14775        .unindent(),
14776    );
14777
14778    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14779    executor.run_until_parked();
14780
14781    cx.assert_state_with_diff(
14782        r#"
14783        use some::mod1;
14784        use some::mod2;
14785
14786        const A: u32 = 42;
14787      + const B: u32 = 42;
14788      + const C: u32 = 42;
14789      + const D: u32 = 42;
14790      + ˇ
14791
14792        fn main() {
14793            println!("hello");
14794
14795            println!("world");
14796        }
14797      "#
14798        .unindent(),
14799    );
14800
14801    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14802    executor.run_until_parked();
14803
14804    cx.assert_state_with_diff(
14805        r#"
14806        use some::mod1;
14807        use some::mod2;
14808
14809        const A: u32 = 42;
14810      + const B: u32 = 42;
14811      + const C: u32 = 42;
14812      + const D: u32 = 42;
14813      + const E: u32 = 42;
14814      + ˇ
14815
14816        fn main() {
14817            println!("hello");
14818
14819            println!("world");
14820        }
14821      "#
14822        .unindent(),
14823    );
14824
14825    cx.update_editor(|editor, window, cx| {
14826        editor.delete_line(&DeleteLine, window, cx);
14827    });
14828    executor.run_until_parked();
14829
14830    cx.assert_state_with_diff(
14831        r#"
14832        use some::mod1;
14833        use some::mod2;
14834
14835        const A: u32 = 42;
14836      + const B: u32 = 42;
14837      + const C: u32 = 42;
14838      + const D: u32 = 42;
14839      + const E: u32 = 42;
14840        ˇ
14841        fn main() {
14842            println!("hello");
14843
14844            println!("world");
14845        }
14846      "#
14847        .unindent(),
14848    );
14849
14850    cx.update_editor(|editor, window, cx| {
14851        editor.move_up(&MoveUp, window, cx);
14852        editor.delete_line(&DeleteLine, window, cx);
14853        editor.move_up(&MoveUp, window, cx);
14854        editor.delete_line(&DeleteLine, window, cx);
14855        editor.move_up(&MoveUp, window, cx);
14856        editor.delete_line(&DeleteLine, window, cx);
14857    });
14858    executor.run_until_parked();
14859    cx.assert_state_with_diff(
14860        r#"
14861        use some::mod1;
14862        use some::mod2;
14863
14864        const A: u32 = 42;
14865      + const B: u32 = 42;
14866        ˇ
14867        fn main() {
14868            println!("hello");
14869
14870            println!("world");
14871        }
14872      "#
14873        .unindent(),
14874    );
14875
14876    cx.update_editor(|editor, window, cx| {
14877        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14878        editor.delete_line(&DeleteLine, window, cx);
14879    });
14880    executor.run_until_parked();
14881    cx.assert_state_with_diff(
14882        r#"
14883        ˇ
14884        fn main() {
14885            println!("hello");
14886
14887            println!("world");
14888        }
14889      "#
14890        .unindent(),
14891    );
14892}
14893
14894#[gpui::test]
14895async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14896    init_test(cx, |_| {});
14897
14898    let mut cx = EditorTestContext::new(cx).await;
14899    cx.set_head_text(indoc! { "
14900        one
14901        two
14902        three
14903        four
14904        five
14905        "
14906    });
14907    cx.set_state(indoc! { "
14908        one
14909        ˇthree
14910        five
14911    "});
14912    cx.run_until_parked();
14913    cx.update_editor(|editor, window, cx| {
14914        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14915    });
14916    cx.assert_state_with_diff(
14917        indoc! { "
14918        one
14919      - two
14920        ˇthree
14921      - four
14922        five
14923    "}
14924        .to_string(),
14925    );
14926    cx.update_editor(|editor, window, cx| {
14927        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14928    });
14929
14930    cx.assert_state_with_diff(
14931        indoc! { "
14932        one
14933        ˇthree
14934        five
14935    "}
14936        .to_string(),
14937    );
14938
14939    cx.set_state(indoc! { "
14940        one
14941        ˇTWO
14942        three
14943        four
14944        five
14945    "});
14946    cx.run_until_parked();
14947    cx.update_editor(|editor, window, cx| {
14948        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14949    });
14950
14951    cx.assert_state_with_diff(
14952        indoc! { "
14953            one
14954          - two
14955          + ˇTWO
14956            three
14957            four
14958            five
14959        "}
14960        .to_string(),
14961    );
14962    cx.update_editor(|editor, window, cx| {
14963        editor.move_up(&Default::default(), window, cx);
14964        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14965    });
14966    cx.assert_state_with_diff(
14967        indoc! { "
14968            one
14969            ˇTWO
14970            three
14971            four
14972            five
14973        "}
14974        .to_string(),
14975    );
14976}
14977
14978#[gpui::test]
14979async fn test_edits_around_expanded_deletion_hunks(
14980    executor: BackgroundExecutor,
14981    cx: &mut TestAppContext,
14982) {
14983    init_test(cx, |_| {});
14984
14985    let mut cx = EditorTestContext::new(cx).await;
14986
14987    let diff_base = r#"
14988        use some::mod1;
14989        use some::mod2;
14990
14991        const A: u32 = 42;
14992        const B: u32 = 42;
14993        const C: u32 = 42;
14994
14995
14996        fn main() {
14997            println!("hello");
14998
14999            println!("world");
15000        }
15001    "#
15002    .unindent();
15003    executor.run_until_parked();
15004    cx.set_state(
15005        &r#"
15006        use some::mod1;
15007        use some::mod2;
15008
15009        ˇconst B: u32 = 42;
15010        const C: u32 = 42;
15011
15012
15013        fn main() {
15014            println!("hello");
15015
15016            println!("world");
15017        }
15018        "#
15019        .unindent(),
15020    );
15021
15022    cx.set_head_text(&diff_base);
15023    executor.run_until_parked();
15024
15025    cx.update_editor(|editor, window, cx| {
15026        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15027    });
15028    executor.run_until_parked();
15029
15030    cx.assert_state_with_diff(
15031        r#"
15032        use some::mod1;
15033        use some::mod2;
15034
15035      - const A: u32 = 42;
15036        ˇconst B: u32 = 42;
15037        const C: u32 = 42;
15038
15039
15040        fn main() {
15041            println!("hello");
15042
15043            println!("world");
15044        }
15045      "#
15046        .unindent(),
15047    );
15048
15049    cx.update_editor(|editor, window, cx| {
15050        editor.delete_line(&DeleteLine, window, cx);
15051    });
15052    executor.run_until_parked();
15053    cx.assert_state_with_diff(
15054        r#"
15055        use some::mod1;
15056        use some::mod2;
15057
15058      - const A: u32 = 42;
15059      - const B: u32 = 42;
15060        ˇconst C: u32 = 42;
15061
15062
15063        fn main() {
15064            println!("hello");
15065
15066            println!("world");
15067        }
15068      "#
15069        .unindent(),
15070    );
15071
15072    cx.update_editor(|editor, window, cx| {
15073        editor.delete_line(&DeleteLine, window, cx);
15074    });
15075    executor.run_until_parked();
15076    cx.assert_state_with_diff(
15077        r#"
15078        use some::mod1;
15079        use some::mod2;
15080
15081      - const A: u32 = 42;
15082      - const B: u32 = 42;
15083      - const C: u32 = 42;
15084        ˇ
15085
15086        fn main() {
15087            println!("hello");
15088
15089            println!("world");
15090        }
15091      "#
15092        .unindent(),
15093    );
15094
15095    cx.update_editor(|editor, window, cx| {
15096        editor.handle_input("replacement", window, cx);
15097    });
15098    executor.run_until_parked();
15099    cx.assert_state_with_diff(
15100        r#"
15101        use some::mod1;
15102        use some::mod2;
15103
15104      - const A: u32 = 42;
15105      - const B: u32 = 42;
15106      - const C: u32 = 42;
15107      -
15108      + replacementˇ
15109
15110        fn main() {
15111            println!("hello");
15112
15113            println!("world");
15114        }
15115      "#
15116        .unindent(),
15117    );
15118}
15119
15120#[gpui::test]
15121async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15122    init_test(cx, |_| {});
15123
15124    let mut cx = EditorTestContext::new(cx).await;
15125
15126    let base_text = r#"
15127        one
15128        two
15129        three
15130        four
15131        five
15132    "#
15133    .unindent();
15134    executor.run_until_parked();
15135    cx.set_state(
15136        &r#"
15137        one
15138        two
15139        fˇour
15140        five
15141        "#
15142        .unindent(),
15143    );
15144
15145    cx.set_head_text(&base_text);
15146    executor.run_until_parked();
15147
15148    cx.update_editor(|editor, window, cx| {
15149        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15150    });
15151    executor.run_until_parked();
15152
15153    cx.assert_state_with_diff(
15154        r#"
15155          one
15156          two
15157        - three
15158          fˇour
15159          five
15160        "#
15161        .unindent(),
15162    );
15163
15164    cx.update_editor(|editor, window, cx| {
15165        editor.backspace(&Backspace, window, cx);
15166        editor.backspace(&Backspace, window, cx);
15167    });
15168    executor.run_until_parked();
15169    cx.assert_state_with_diff(
15170        r#"
15171          one
15172          two
15173        - threeˇ
15174        - four
15175        + our
15176          five
15177        "#
15178        .unindent(),
15179    );
15180}
15181
15182#[gpui::test]
15183async fn test_edit_after_expanded_modification_hunk(
15184    executor: BackgroundExecutor,
15185    cx: &mut TestAppContext,
15186) {
15187    init_test(cx, |_| {});
15188
15189    let mut cx = EditorTestContext::new(cx).await;
15190
15191    let diff_base = r#"
15192        use some::mod1;
15193        use some::mod2;
15194
15195        const A: u32 = 42;
15196        const B: u32 = 42;
15197        const C: u32 = 42;
15198        const D: u32 = 42;
15199
15200
15201        fn main() {
15202            println!("hello");
15203
15204            println!("world");
15205        }"#
15206    .unindent();
15207
15208    cx.set_state(
15209        &r#"
15210        use some::mod1;
15211        use some::mod2;
15212
15213        const A: u32 = 42;
15214        const B: u32 = 42;
15215        const C: u32 = 43ˇ
15216        const D: u32 = 42;
15217
15218
15219        fn main() {
15220            println!("hello");
15221
15222            println!("world");
15223        }"#
15224        .unindent(),
15225    );
15226
15227    cx.set_head_text(&diff_base);
15228    executor.run_until_parked();
15229    cx.update_editor(|editor, window, cx| {
15230        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15231    });
15232    executor.run_until_parked();
15233
15234    cx.assert_state_with_diff(
15235        r#"
15236        use some::mod1;
15237        use some::mod2;
15238
15239        const A: u32 = 42;
15240        const B: u32 = 42;
15241      - const C: u32 = 42;
15242      + const C: u32 = 43ˇ
15243        const D: u32 = 42;
15244
15245
15246        fn main() {
15247            println!("hello");
15248
15249            println!("world");
15250        }"#
15251        .unindent(),
15252    );
15253
15254    cx.update_editor(|editor, window, cx| {
15255        editor.handle_input("\nnew_line\n", window, cx);
15256    });
15257    executor.run_until_parked();
15258
15259    cx.assert_state_with_diff(
15260        r#"
15261        use some::mod1;
15262        use some::mod2;
15263
15264        const A: u32 = 42;
15265        const B: u32 = 42;
15266      - const C: u32 = 42;
15267      + const C: u32 = 43
15268      + new_line
15269      + ˇ
15270        const D: u32 = 42;
15271
15272
15273        fn main() {
15274            println!("hello");
15275
15276            println!("world");
15277        }"#
15278        .unindent(),
15279    );
15280}
15281
15282#[gpui::test]
15283async fn test_stage_and_unstage_added_file_hunk(
15284    executor: BackgroundExecutor,
15285    cx: &mut TestAppContext,
15286) {
15287    init_test(cx, |_| {});
15288
15289    let mut cx = EditorTestContext::new(cx).await;
15290    cx.update_editor(|editor, _, cx| {
15291        editor.set_expand_all_diff_hunks(cx);
15292    });
15293
15294    let working_copy = r#"
15295            ˇfn main() {
15296                println!("hello, world!");
15297            }
15298        "#
15299    .unindent();
15300
15301    cx.set_state(&working_copy);
15302    executor.run_until_parked();
15303
15304    cx.assert_state_with_diff(
15305        r#"
15306            + ˇfn main() {
15307            +     println!("hello, world!");
15308            + }
15309        "#
15310        .unindent(),
15311    );
15312    cx.assert_index_text(None);
15313
15314    cx.update_editor(|editor, window, cx| {
15315        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15316    });
15317    executor.run_until_parked();
15318    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15319    cx.assert_state_with_diff(
15320        r#"
15321            + ˇfn main() {
15322            +     println!("hello, world!");
15323            + }
15324        "#
15325        .unindent(),
15326    );
15327
15328    cx.update_editor(|editor, window, cx| {
15329        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15330    });
15331    executor.run_until_parked();
15332    cx.assert_index_text(None);
15333}
15334
15335async fn setup_indent_guides_editor(
15336    text: &str,
15337    cx: &mut TestAppContext,
15338) -> (BufferId, EditorTestContext) {
15339    init_test(cx, |_| {});
15340
15341    let mut cx = EditorTestContext::new(cx).await;
15342
15343    let buffer_id = cx.update_editor(|editor, window, cx| {
15344        editor.set_text(text, window, cx);
15345        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15346
15347        buffer_ids[0]
15348    });
15349
15350    (buffer_id, cx)
15351}
15352
15353fn assert_indent_guides(
15354    range: Range<u32>,
15355    expected: Vec<IndentGuide>,
15356    active_indices: Option<Vec<usize>>,
15357    cx: &mut EditorTestContext,
15358) {
15359    let indent_guides = cx.update_editor(|editor, window, cx| {
15360        let snapshot = editor.snapshot(window, cx).display_snapshot;
15361        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15362            editor,
15363            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15364            true,
15365            &snapshot,
15366            cx,
15367        );
15368
15369        indent_guides.sort_by(|a, b| {
15370            a.depth.cmp(&b.depth).then(
15371                a.start_row
15372                    .cmp(&b.start_row)
15373                    .then(a.end_row.cmp(&b.end_row)),
15374            )
15375        });
15376        indent_guides
15377    });
15378
15379    if let Some(expected) = active_indices {
15380        let active_indices = cx.update_editor(|editor, window, cx| {
15381            let snapshot = editor.snapshot(window, cx).display_snapshot;
15382            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15383        });
15384
15385        assert_eq!(
15386            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15387            expected,
15388            "Active indent guide indices do not match"
15389        );
15390    }
15391
15392    assert_eq!(indent_guides, expected, "Indent guides do not match");
15393}
15394
15395fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15396    IndentGuide {
15397        buffer_id,
15398        start_row: MultiBufferRow(start_row),
15399        end_row: MultiBufferRow(end_row),
15400        depth,
15401        tab_size: 4,
15402        settings: IndentGuideSettings {
15403            enabled: true,
15404            line_width: 1,
15405            active_line_width: 1,
15406            ..Default::default()
15407        },
15408    }
15409}
15410
15411#[gpui::test]
15412async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15413    let (buffer_id, mut cx) = setup_indent_guides_editor(
15414        &"
15415    fn main() {
15416        let a = 1;
15417    }"
15418        .unindent(),
15419        cx,
15420    )
15421    .await;
15422
15423    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15424}
15425
15426#[gpui::test]
15427async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15428    let (buffer_id, mut cx) = setup_indent_guides_editor(
15429        &"
15430    fn main() {
15431        let a = 1;
15432        let b = 2;
15433    }"
15434        .unindent(),
15435        cx,
15436    )
15437    .await;
15438
15439    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15440}
15441
15442#[gpui::test]
15443async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15444    let (buffer_id, mut cx) = setup_indent_guides_editor(
15445        &"
15446    fn main() {
15447        let a = 1;
15448        if a == 3 {
15449            let b = 2;
15450        } else {
15451            let c = 3;
15452        }
15453    }"
15454        .unindent(),
15455        cx,
15456    )
15457    .await;
15458
15459    assert_indent_guides(
15460        0..8,
15461        vec![
15462            indent_guide(buffer_id, 1, 6, 0),
15463            indent_guide(buffer_id, 3, 3, 1),
15464            indent_guide(buffer_id, 5, 5, 1),
15465        ],
15466        None,
15467        &mut cx,
15468    );
15469}
15470
15471#[gpui::test]
15472async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15473    let (buffer_id, mut cx) = setup_indent_guides_editor(
15474        &"
15475    fn main() {
15476        let a = 1;
15477            let b = 2;
15478        let c = 3;
15479    }"
15480        .unindent(),
15481        cx,
15482    )
15483    .await;
15484
15485    assert_indent_guides(
15486        0..5,
15487        vec![
15488            indent_guide(buffer_id, 1, 3, 0),
15489            indent_guide(buffer_id, 2, 2, 1),
15490        ],
15491        None,
15492        &mut cx,
15493    );
15494}
15495
15496#[gpui::test]
15497async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15498    let (buffer_id, mut cx) = setup_indent_guides_editor(
15499        &"
15500        fn main() {
15501            let a = 1;
15502
15503            let c = 3;
15504        }"
15505        .unindent(),
15506        cx,
15507    )
15508    .await;
15509
15510    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15511}
15512
15513#[gpui::test]
15514async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15515    let (buffer_id, mut cx) = setup_indent_guides_editor(
15516        &"
15517        fn main() {
15518            let a = 1;
15519
15520            let c = 3;
15521
15522            if a == 3 {
15523                let b = 2;
15524            } else {
15525                let c = 3;
15526            }
15527        }"
15528        .unindent(),
15529        cx,
15530    )
15531    .await;
15532
15533    assert_indent_guides(
15534        0..11,
15535        vec![
15536            indent_guide(buffer_id, 1, 9, 0),
15537            indent_guide(buffer_id, 6, 6, 1),
15538            indent_guide(buffer_id, 8, 8, 1),
15539        ],
15540        None,
15541        &mut cx,
15542    );
15543}
15544
15545#[gpui::test]
15546async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15547    let (buffer_id, mut cx) = setup_indent_guides_editor(
15548        &"
15549        fn main() {
15550            let a = 1;
15551
15552            let c = 3;
15553
15554            if a == 3 {
15555                let b = 2;
15556            } else {
15557                let c = 3;
15558            }
15559        }"
15560        .unindent(),
15561        cx,
15562    )
15563    .await;
15564
15565    assert_indent_guides(
15566        1..11,
15567        vec![
15568            indent_guide(buffer_id, 1, 9, 0),
15569            indent_guide(buffer_id, 6, 6, 1),
15570            indent_guide(buffer_id, 8, 8, 1),
15571        ],
15572        None,
15573        &mut cx,
15574    );
15575}
15576
15577#[gpui::test]
15578async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15579    let (buffer_id, mut cx) = setup_indent_guides_editor(
15580        &"
15581        fn main() {
15582            let a = 1;
15583
15584            let c = 3;
15585
15586            if a == 3 {
15587                let b = 2;
15588            } else {
15589                let c = 3;
15590            }
15591        }"
15592        .unindent(),
15593        cx,
15594    )
15595    .await;
15596
15597    assert_indent_guides(
15598        1..10,
15599        vec![
15600            indent_guide(buffer_id, 1, 9, 0),
15601            indent_guide(buffer_id, 6, 6, 1),
15602            indent_guide(buffer_id, 8, 8, 1),
15603        ],
15604        None,
15605        &mut cx,
15606    );
15607}
15608
15609#[gpui::test]
15610async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15611    let (buffer_id, mut cx) = setup_indent_guides_editor(
15612        &"
15613        block1
15614            block2
15615                block3
15616                    block4
15617            block2
15618        block1
15619        block1"
15620            .unindent(),
15621        cx,
15622    )
15623    .await;
15624
15625    assert_indent_guides(
15626        1..10,
15627        vec![
15628            indent_guide(buffer_id, 1, 4, 0),
15629            indent_guide(buffer_id, 2, 3, 1),
15630            indent_guide(buffer_id, 3, 3, 2),
15631        ],
15632        None,
15633        &mut cx,
15634    );
15635}
15636
15637#[gpui::test]
15638async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15639    let (buffer_id, mut cx) = setup_indent_guides_editor(
15640        &"
15641        block1
15642            block2
15643                block3
15644
15645        block1
15646        block1"
15647            .unindent(),
15648        cx,
15649    )
15650    .await;
15651
15652    assert_indent_guides(
15653        0..6,
15654        vec![
15655            indent_guide(buffer_id, 1, 2, 0),
15656            indent_guide(buffer_id, 2, 2, 1),
15657        ],
15658        None,
15659        &mut cx,
15660    );
15661}
15662
15663#[gpui::test]
15664async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15665    let (buffer_id, mut cx) = setup_indent_guides_editor(
15666        &"
15667        block1
15668
15669
15670
15671            block2
15672        "
15673        .unindent(),
15674        cx,
15675    )
15676    .await;
15677
15678    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15679}
15680
15681#[gpui::test]
15682async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15683    let (buffer_id, mut cx) = setup_indent_guides_editor(
15684        &"
15685        def a:
15686        \tb = 3
15687        \tif True:
15688        \t\tc = 4
15689        \t\td = 5
15690        \tprint(b)
15691        "
15692        .unindent(),
15693        cx,
15694    )
15695    .await;
15696
15697    assert_indent_guides(
15698        0..6,
15699        vec![
15700            indent_guide(buffer_id, 1, 6, 0),
15701            indent_guide(buffer_id, 3, 4, 1),
15702        ],
15703        None,
15704        &mut cx,
15705    );
15706}
15707
15708#[gpui::test]
15709async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15710    let (buffer_id, mut cx) = setup_indent_guides_editor(
15711        &"
15712    fn main() {
15713        let a = 1;
15714    }"
15715        .unindent(),
15716        cx,
15717    )
15718    .await;
15719
15720    cx.update_editor(|editor, window, cx| {
15721        editor.change_selections(None, window, cx, |s| {
15722            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15723        });
15724    });
15725
15726    assert_indent_guides(
15727        0..3,
15728        vec![indent_guide(buffer_id, 1, 1, 0)],
15729        Some(vec![0]),
15730        &mut cx,
15731    );
15732}
15733
15734#[gpui::test]
15735async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15736    let (buffer_id, mut cx) = setup_indent_guides_editor(
15737        &"
15738    fn main() {
15739        if 1 == 2 {
15740            let a = 1;
15741        }
15742    }"
15743        .unindent(),
15744        cx,
15745    )
15746    .await;
15747
15748    cx.update_editor(|editor, window, cx| {
15749        editor.change_selections(None, window, cx, |s| {
15750            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15751        });
15752    });
15753
15754    assert_indent_guides(
15755        0..4,
15756        vec![
15757            indent_guide(buffer_id, 1, 3, 0),
15758            indent_guide(buffer_id, 2, 2, 1),
15759        ],
15760        Some(vec![1]),
15761        &mut cx,
15762    );
15763
15764    cx.update_editor(|editor, window, cx| {
15765        editor.change_selections(None, window, cx, |s| {
15766            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15767        });
15768    });
15769
15770    assert_indent_guides(
15771        0..4,
15772        vec![
15773            indent_guide(buffer_id, 1, 3, 0),
15774            indent_guide(buffer_id, 2, 2, 1),
15775        ],
15776        Some(vec![1]),
15777        &mut cx,
15778    );
15779
15780    cx.update_editor(|editor, window, cx| {
15781        editor.change_selections(None, window, cx, |s| {
15782            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15783        });
15784    });
15785
15786    assert_indent_guides(
15787        0..4,
15788        vec![
15789            indent_guide(buffer_id, 1, 3, 0),
15790            indent_guide(buffer_id, 2, 2, 1),
15791        ],
15792        Some(vec![0]),
15793        &mut cx,
15794    );
15795}
15796
15797#[gpui::test]
15798async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15799    let (buffer_id, mut cx) = setup_indent_guides_editor(
15800        &"
15801    fn main() {
15802        let a = 1;
15803
15804        let b = 2;
15805    }"
15806        .unindent(),
15807        cx,
15808    )
15809    .await;
15810
15811    cx.update_editor(|editor, window, cx| {
15812        editor.change_selections(None, window, cx, |s| {
15813            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15814        });
15815    });
15816
15817    assert_indent_guides(
15818        0..5,
15819        vec![indent_guide(buffer_id, 1, 3, 0)],
15820        Some(vec![0]),
15821        &mut cx,
15822    );
15823}
15824
15825#[gpui::test]
15826async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15827    let (buffer_id, mut cx) = setup_indent_guides_editor(
15828        &"
15829    def m:
15830        a = 1
15831        pass"
15832            .unindent(),
15833        cx,
15834    )
15835    .await;
15836
15837    cx.update_editor(|editor, window, cx| {
15838        editor.change_selections(None, window, cx, |s| {
15839            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15840        });
15841    });
15842
15843    assert_indent_guides(
15844        0..3,
15845        vec![indent_guide(buffer_id, 1, 2, 0)],
15846        Some(vec![0]),
15847        &mut cx,
15848    );
15849}
15850
15851#[gpui::test]
15852async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15853    init_test(cx, |_| {});
15854    let mut cx = EditorTestContext::new(cx).await;
15855    let text = indoc! {
15856        "
15857        impl A {
15858            fn b() {
15859                0;
15860                3;
15861                5;
15862                6;
15863                7;
15864            }
15865        }
15866        "
15867    };
15868    let base_text = indoc! {
15869        "
15870        impl A {
15871            fn b() {
15872                0;
15873                1;
15874                2;
15875                3;
15876                4;
15877            }
15878            fn c() {
15879                5;
15880                6;
15881                7;
15882            }
15883        }
15884        "
15885    };
15886
15887    cx.update_editor(|editor, window, cx| {
15888        editor.set_text(text, window, cx);
15889
15890        editor.buffer().update(cx, |multibuffer, cx| {
15891            let buffer = multibuffer.as_singleton().unwrap();
15892            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15893
15894            multibuffer.set_all_diff_hunks_expanded(cx);
15895            multibuffer.add_diff(diff, cx);
15896
15897            buffer.read(cx).remote_id()
15898        })
15899    });
15900    cx.run_until_parked();
15901
15902    cx.assert_state_with_diff(
15903        indoc! { "
15904          impl A {
15905              fn b() {
15906                  0;
15907        -         1;
15908        -         2;
15909                  3;
15910        -         4;
15911        -     }
15912        -     fn c() {
15913                  5;
15914                  6;
15915                  7;
15916              }
15917          }
15918          ˇ"
15919        }
15920        .to_string(),
15921    );
15922
15923    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15924        editor
15925            .snapshot(window, cx)
15926            .buffer_snapshot
15927            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15928            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15929            .collect::<Vec<_>>()
15930    });
15931    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15932    assert_eq!(
15933        actual_guides,
15934        vec![
15935            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15936            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15937            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15938        ]
15939    );
15940}
15941
15942#[gpui::test]
15943async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15944    init_test(cx, |_| {});
15945    let mut cx = EditorTestContext::new(cx).await;
15946
15947    let diff_base = r#"
15948        a
15949        b
15950        c
15951        "#
15952    .unindent();
15953
15954    cx.set_state(
15955        &r#"
15956        ˇA
15957        b
15958        C
15959        "#
15960        .unindent(),
15961    );
15962    cx.set_head_text(&diff_base);
15963    cx.update_editor(|editor, window, cx| {
15964        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15965    });
15966    executor.run_until_parked();
15967
15968    let both_hunks_expanded = r#"
15969        - a
15970        + ˇA
15971          b
15972        - c
15973        + C
15974        "#
15975    .unindent();
15976
15977    cx.assert_state_with_diff(both_hunks_expanded.clone());
15978
15979    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15980        let snapshot = editor.snapshot(window, cx);
15981        let hunks = editor
15982            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15983            .collect::<Vec<_>>();
15984        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15985        let buffer_id = hunks[0].buffer_id;
15986        hunks
15987            .into_iter()
15988            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15989            .collect::<Vec<_>>()
15990    });
15991    assert_eq!(hunk_ranges.len(), 2);
15992
15993    cx.update_editor(|editor, _, cx| {
15994        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15995    });
15996    executor.run_until_parked();
15997
15998    let second_hunk_expanded = r#"
15999          ˇA
16000          b
16001        - c
16002        + C
16003        "#
16004    .unindent();
16005
16006    cx.assert_state_with_diff(second_hunk_expanded);
16007
16008    cx.update_editor(|editor, _, cx| {
16009        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16010    });
16011    executor.run_until_parked();
16012
16013    cx.assert_state_with_diff(both_hunks_expanded.clone());
16014
16015    cx.update_editor(|editor, _, cx| {
16016        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16017    });
16018    executor.run_until_parked();
16019
16020    let first_hunk_expanded = r#"
16021        - a
16022        + ˇA
16023          b
16024          C
16025        "#
16026    .unindent();
16027
16028    cx.assert_state_with_diff(first_hunk_expanded);
16029
16030    cx.update_editor(|editor, _, cx| {
16031        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16032    });
16033    executor.run_until_parked();
16034
16035    cx.assert_state_with_diff(both_hunks_expanded);
16036
16037    cx.set_state(
16038        &r#"
16039        ˇA
16040        b
16041        "#
16042        .unindent(),
16043    );
16044    cx.run_until_parked();
16045
16046    // TODO this cursor position seems bad
16047    cx.assert_state_with_diff(
16048        r#"
16049        - ˇa
16050        + A
16051          b
16052        "#
16053        .unindent(),
16054    );
16055
16056    cx.update_editor(|editor, window, cx| {
16057        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16058    });
16059
16060    cx.assert_state_with_diff(
16061        r#"
16062            - ˇa
16063            + A
16064              b
16065            - c
16066            "#
16067        .unindent(),
16068    );
16069
16070    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16071        let snapshot = editor.snapshot(window, cx);
16072        let hunks = editor
16073            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16074            .collect::<Vec<_>>();
16075        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16076        let buffer_id = hunks[0].buffer_id;
16077        hunks
16078            .into_iter()
16079            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16080            .collect::<Vec<_>>()
16081    });
16082    assert_eq!(hunk_ranges.len(), 2);
16083
16084    cx.update_editor(|editor, _, cx| {
16085        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16086    });
16087    executor.run_until_parked();
16088
16089    cx.assert_state_with_diff(
16090        r#"
16091        - ˇa
16092        + A
16093          b
16094        "#
16095        .unindent(),
16096    );
16097}
16098
16099#[gpui::test]
16100async fn test_toggle_deletion_hunk_at_start_of_file(
16101    executor: BackgroundExecutor,
16102    cx: &mut TestAppContext,
16103) {
16104    init_test(cx, |_| {});
16105    let mut cx = EditorTestContext::new(cx).await;
16106
16107    let diff_base = r#"
16108        a
16109        b
16110        c
16111        "#
16112    .unindent();
16113
16114    cx.set_state(
16115        &r#"
16116        ˇb
16117        c
16118        "#
16119        .unindent(),
16120    );
16121    cx.set_head_text(&diff_base);
16122    cx.update_editor(|editor, window, cx| {
16123        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16124    });
16125    executor.run_until_parked();
16126
16127    let hunk_expanded = r#"
16128        - a
16129          ˇb
16130          c
16131        "#
16132    .unindent();
16133
16134    cx.assert_state_with_diff(hunk_expanded.clone());
16135
16136    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16137        let snapshot = editor.snapshot(window, cx);
16138        let hunks = editor
16139            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16140            .collect::<Vec<_>>();
16141        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16142        let buffer_id = hunks[0].buffer_id;
16143        hunks
16144            .into_iter()
16145            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16146            .collect::<Vec<_>>()
16147    });
16148    assert_eq!(hunk_ranges.len(), 1);
16149
16150    cx.update_editor(|editor, _, cx| {
16151        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16152    });
16153    executor.run_until_parked();
16154
16155    let hunk_collapsed = r#"
16156          ˇb
16157          c
16158        "#
16159    .unindent();
16160
16161    cx.assert_state_with_diff(hunk_collapsed);
16162
16163    cx.update_editor(|editor, _, cx| {
16164        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16165    });
16166    executor.run_until_parked();
16167
16168    cx.assert_state_with_diff(hunk_expanded.clone());
16169}
16170
16171#[gpui::test]
16172async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16173    init_test(cx, |_| {});
16174
16175    let fs = FakeFs::new(cx.executor());
16176    fs.insert_tree(
16177        path!("/test"),
16178        json!({
16179            ".git": {},
16180            "file-1": "ONE\n",
16181            "file-2": "TWO\n",
16182            "file-3": "THREE\n",
16183        }),
16184    )
16185    .await;
16186
16187    fs.set_head_for_repo(
16188        path!("/test/.git").as_ref(),
16189        &[
16190            ("file-1".into(), "one\n".into()),
16191            ("file-2".into(), "two\n".into()),
16192            ("file-3".into(), "three\n".into()),
16193        ],
16194    );
16195
16196    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16197    let mut buffers = vec![];
16198    for i in 1..=3 {
16199        let buffer = project
16200            .update(cx, |project, cx| {
16201                let path = format!(path!("/test/file-{}"), i);
16202                project.open_local_buffer(path, cx)
16203            })
16204            .await
16205            .unwrap();
16206        buffers.push(buffer);
16207    }
16208
16209    let multibuffer = cx.new(|cx| {
16210        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16211        multibuffer.set_all_diff_hunks_expanded(cx);
16212        for buffer in &buffers {
16213            let snapshot = buffer.read(cx).snapshot();
16214            multibuffer.set_excerpts_for_path(
16215                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16216                buffer.clone(),
16217                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16218                DEFAULT_MULTIBUFFER_CONTEXT,
16219                cx,
16220            );
16221        }
16222        multibuffer
16223    });
16224
16225    let editor = cx.add_window(|window, cx| {
16226        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16227    });
16228    cx.run_until_parked();
16229
16230    let snapshot = editor
16231        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16232        .unwrap();
16233    let hunks = snapshot
16234        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16235        .map(|hunk| match hunk {
16236            DisplayDiffHunk::Unfolded {
16237                display_row_range, ..
16238            } => display_row_range,
16239            DisplayDiffHunk::Folded { .. } => unreachable!(),
16240        })
16241        .collect::<Vec<_>>();
16242    assert_eq!(
16243        hunks,
16244        [
16245            DisplayRow(2)..DisplayRow(4),
16246            DisplayRow(7)..DisplayRow(9),
16247            DisplayRow(12)..DisplayRow(14),
16248        ]
16249    );
16250}
16251
16252#[gpui::test]
16253async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16254    init_test(cx, |_| {});
16255
16256    let mut cx = EditorTestContext::new(cx).await;
16257    cx.set_head_text(indoc! { "
16258        one
16259        two
16260        three
16261        four
16262        five
16263        "
16264    });
16265    cx.set_index_text(indoc! { "
16266        one
16267        two
16268        three
16269        four
16270        five
16271        "
16272    });
16273    cx.set_state(indoc! {"
16274        one
16275        TWO
16276        ˇTHREE
16277        FOUR
16278        five
16279    "});
16280    cx.run_until_parked();
16281    cx.update_editor(|editor, window, cx| {
16282        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16283    });
16284    cx.run_until_parked();
16285    cx.assert_index_text(Some(indoc! {"
16286        one
16287        TWO
16288        THREE
16289        FOUR
16290        five
16291    "}));
16292    cx.set_state(indoc! { "
16293        one
16294        TWO
16295        ˇTHREE-HUNDRED
16296        FOUR
16297        five
16298    "});
16299    cx.run_until_parked();
16300    cx.update_editor(|editor, window, cx| {
16301        let snapshot = editor.snapshot(window, cx);
16302        let hunks = editor
16303            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16304            .collect::<Vec<_>>();
16305        assert_eq!(hunks.len(), 1);
16306        assert_eq!(
16307            hunks[0].status(),
16308            DiffHunkStatus {
16309                kind: DiffHunkStatusKind::Modified,
16310                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16311            }
16312        );
16313
16314        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16315    });
16316    cx.run_until_parked();
16317    cx.assert_index_text(Some(indoc! {"
16318        one
16319        TWO
16320        THREE-HUNDRED
16321        FOUR
16322        five
16323    "}));
16324}
16325
16326#[gpui::test]
16327fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16328    init_test(cx, |_| {});
16329
16330    let editor = cx.add_window(|window, cx| {
16331        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16332        build_editor(buffer, window, cx)
16333    });
16334
16335    let render_args = Arc::new(Mutex::new(None));
16336    let snapshot = editor
16337        .update(cx, |editor, window, cx| {
16338            let snapshot = editor.buffer().read(cx).snapshot(cx);
16339            let range =
16340                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16341
16342            struct RenderArgs {
16343                row: MultiBufferRow,
16344                folded: bool,
16345                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16346            }
16347
16348            let crease = Crease::inline(
16349                range,
16350                FoldPlaceholder::test(),
16351                {
16352                    let toggle_callback = render_args.clone();
16353                    move |row, folded, callback, _window, _cx| {
16354                        *toggle_callback.lock() = Some(RenderArgs {
16355                            row,
16356                            folded,
16357                            callback,
16358                        });
16359                        div()
16360                    }
16361                },
16362                |_row, _folded, _window, _cx| div(),
16363            );
16364
16365            editor.insert_creases(Some(crease), cx);
16366            let snapshot = editor.snapshot(window, cx);
16367            let _div = snapshot.render_crease_toggle(
16368                MultiBufferRow(1),
16369                false,
16370                cx.entity().clone(),
16371                window,
16372                cx,
16373            );
16374            snapshot
16375        })
16376        .unwrap();
16377
16378    let render_args = render_args.lock().take().unwrap();
16379    assert_eq!(render_args.row, MultiBufferRow(1));
16380    assert!(!render_args.folded);
16381    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16382
16383    cx.update_window(*editor, |_, window, cx| {
16384        (render_args.callback)(true, window, cx)
16385    })
16386    .unwrap();
16387    let snapshot = editor
16388        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16389        .unwrap();
16390    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16391
16392    cx.update_window(*editor, |_, window, cx| {
16393        (render_args.callback)(false, window, cx)
16394    })
16395    .unwrap();
16396    let snapshot = editor
16397        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16398        .unwrap();
16399    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16400}
16401
16402#[gpui::test]
16403async fn test_input_text(cx: &mut TestAppContext) {
16404    init_test(cx, |_| {});
16405    let mut cx = EditorTestContext::new(cx).await;
16406
16407    cx.set_state(
16408        &r#"ˇone
16409        two
16410
16411        three
16412        fourˇ
16413        five
16414
16415        siˇx"#
16416            .unindent(),
16417    );
16418
16419    cx.dispatch_action(HandleInput(String::new()));
16420    cx.assert_editor_state(
16421        &r#"ˇone
16422        two
16423
16424        three
16425        fourˇ
16426        five
16427
16428        siˇx"#
16429            .unindent(),
16430    );
16431
16432    cx.dispatch_action(HandleInput("AAAA".to_string()));
16433    cx.assert_editor_state(
16434        &r#"AAAAˇone
16435        two
16436
16437        three
16438        fourAAAAˇ
16439        five
16440
16441        siAAAAˇx"#
16442            .unindent(),
16443    );
16444}
16445
16446#[gpui::test]
16447async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16448    init_test(cx, |_| {});
16449
16450    let mut cx = EditorTestContext::new(cx).await;
16451    cx.set_state(
16452        r#"let foo = 1;
16453let foo = 2;
16454let foo = 3;
16455let fooˇ = 4;
16456let foo = 5;
16457let foo = 6;
16458let foo = 7;
16459let foo = 8;
16460let foo = 9;
16461let foo = 10;
16462let foo = 11;
16463let foo = 12;
16464let foo = 13;
16465let foo = 14;
16466let foo = 15;"#,
16467    );
16468
16469    cx.update_editor(|e, window, cx| {
16470        assert_eq!(
16471            e.next_scroll_position,
16472            NextScrollCursorCenterTopBottom::Center,
16473            "Default next scroll direction is center",
16474        );
16475
16476        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16477        assert_eq!(
16478            e.next_scroll_position,
16479            NextScrollCursorCenterTopBottom::Top,
16480            "After center, next scroll direction should be top",
16481        );
16482
16483        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16484        assert_eq!(
16485            e.next_scroll_position,
16486            NextScrollCursorCenterTopBottom::Bottom,
16487            "After top, next scroll direction should be bottom",
16488        );
16489
16490        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16491        assert_eq!(
16492            e.next_scroll_position,
16493            NextScrollCursorCenterTopBottom::Center,
16494            "After bottom, scrolling should start over",
16495        );
16496
16497        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16498        assert_eq!(
16499            e.next_scroll_position,
16500            NextScrollCursorCenterTopBottom::Top,
16501            "Scrolling continues if retriggered fast enough"
16502        );
16503    });
16504
16505    cx.executor()
16506        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16507    cx.executor().run_until_parked();
16508    cx.update_editor(|e, _, _| {
16509        assert_eq!(
16510            e.next_scroll_position,
16511            NextScrollCursorCenterTopBottom::Center,
16512            "If scrolling is not triggered fast enough, it should reset"
16513        );
16514    });
16515}
16516
16517#[gpui::test]
16518async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16519    init_test(cx, |_| {});
16520    let mut cx = EditorLspTestContext::new_rust(
16521        lsp::ServerCapabilities {
16522            definition_provider: Some(lsp::OneOf::Left(true)),
16523            references_provider: Some(lsp::OneOf::Left(true)),
16524            ..lsp::ServerCapabilities::default()
16525        },
16526        cx,
16527    )
16528    .await;
16529
16530    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16531        let go_to_definition = cx
16532            .lsp
16533            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16534                move |params, _| async move {
16535                    if empty_go_to_definition {
16536                        Ok(None)
16537                    } else {
16538                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16539                            uri: params.text_document_position_params.text_document.uri,
16540                            range: lsp::Range::new(
16541                                lsp::Position::new(4, 3),
16542                                lsp::Position::new(4, 6),
16543                            ),
16544                        })))
16545                    }
16546                },
16547            );
16548        let references = cx
16549            .lsp
16550            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16551                Ok(Some(vec![lsp::Location {
16552                    uri: params.text_document_position.text_document.uri,
16553                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16554                }]))
16555            });
16556        (go_to_definition, references)
16557    };
16558
16559    cx.set_state(
16560        &r#"fn one() {
16561            let mut a = ˇtwo();
16562        }
16563
16564        fn two() {}"#
16565            .unindent(),
16566    );
16567    set_up_lsp_handlers(false, &mut cx);
16568    let navigated = cx
16569        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16570        .await
16571        .expect("Failed to navigate to definition");
16572    assert_eq!(
16573        navigated,
16574        Navigated::Yes,
16575        "Should have navigated to definition from the GetDefinition response"
16576    );
16577    cx.assert_editor_state(
16578        &r#"fn one() {
16579            let mut a = two();
16580        }
16581
16582        fn «twoˇ»() {}"#
16583            .unindent(),
16584    );
16585
16586    let editors = cx.update_workspace(|workspace, _, cx| {
16587        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16588    });
16589    cx.update_editor(|_, _, test_editor_cx| {
16590        assert_eq!(
16591            editors.len(),
16592            1,
16593            "Initially, only one, test, editor should be open in the workspace"
16594        );
16595        assert_eq!(
16596            test_editor_cx.entity(),
16597            editors.last().expect("Asserted len is 1").clone()
16598        );
16599    });
16600
16601    set_up_lsp_handlers(true, &mut cx);
16602    let navigated = cx
16603        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16604        .await
16605        .expect("Failed to navigate to lookup references");
16606    assert_eq!(
16607        navigated,
16608        Navigated::Yes,
16609        "Should have navigated to references as a fallback after empty GoToDefinition response"
16610    );
16611    // We should not change the selections in the existing file,
16612    // if opening another milti buffer with the references
16613    cx.assert_editor_state(
16614        &r#"fn one() {
16615            let mut a = two();
16616        }
16617
16618        fn «twoˇ»() {}"#
16619            .unindent(),
16620    );
16621    let editors = cx.update_workspace(|workspace, _, cx| {
16622        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16623    });
16624    cx.update_editor(|_, _, test_editor_cx| {
16625        assert_eq!(
16626            editors.len(),
16627            2,
16628            "After falling back to references search, we open a new editor with the results"
16629        );
16630        let references_fallback_text = editors
16631            .into_iter()
16632            .find(|new_editor| *new_editor != test_editor_cx.entity())
16633            .expect("Should have one non-test editor now")
16634            .read(test_editor_cx)
16635            .text(test_editor_cx);
16636        assert_eq!(
16637            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16638            "Should use the range from the references response and not the GoToDefinition one"
16639        );
16640    });
16641}
16642
16643#[gpui::test]
16644async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16645    init_test(cx, |_| {});
16646    cx.update(|cx| {
16647        let mut editor_settings = EditorSettings::get_global(cx).clone();
16648        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16649        EditorSettings::override_global(editor_settings, cx);
16650    });
16651    let mut cx = EditorLspTestContext::new_rust(
16652        lsp::ServerCapabilities {
16653            definition_provider: Some(lsp::OneOf::Left(true)),
16654            references_provider: Some(lsp::OneOf::Left(true)),
16655            ..lsp::ServerCapabilities::default()
16656        },
16657        cx,
16658    )
16659    .await;
16660    let original_state = r#"fn one() {
16661        let mut a = ˇtwo();
16662    }
16663
16664    fn two() {}"#
16665        .unindent();
16666    cx.set_state(&original_state);
16667
16668    let mut go_to_definition = cx
16669        .lsp
16670        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16671            move |_, _| async move { Ok(None) },
16672        );
16673    let _references = cx
16674        .lsp
16675        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16676            panic!("Should not call for references with no go to definition fallback")
16677        });
16678
16679    let navigated = cx
16680        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16681        .await
16682        .expect("Failed to navigate to lookup references");
16683    go_to_definition
16684        .next()
16685        .await
16686        .expect("Should have called the go_to_definition handler");
16687
16688    assert_eq!(
16689        navigated,
16690        Navigated::No,
16691        "Should have navigated to references as a fallback after empty GoToDefinition response"
16692    );
16693    cx.assert_editor_state(&original_state);
16694    let editors = cx.update_workspace(|workspace, _, cx| {
16695        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16696    });
16697    cx.update_editor(|_, _, _| {
16698        assert_eq!(
16699            editors.len(),
16700            1,
16701            "After unsuccessful fallback, no other editor should have been opened"
16702        );
16703    });
16704}
16705
16706#[gpui::test]
16707async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16708    init_test(cx, |_| {});
16709
16710    let language = Arc::new(Language::new(
16711        LanguageConfig::default(),
16712        Some(tree_sitter_rust::LANGUAGE.into()),
16713    ));
16714
16715    let text = r#"
16716        #[cfg(test)]
16717        mod tests() {
16718            #[test]
16719            fn runnable_1() {
16720                let a = 1;
16721            }
16722
16723            #[test]
16724            fn runnable_2() {
16725                let a = 1;
16726                let b = 2;
16727            }
16728        }
16729    "#
16730    .unindent();
16731
16732    let fs = FakeFs::new(cx.executor());
16733    fs.insert_file("/file.rs", Default::default()).await;
16734
16735    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16736    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16737    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16738    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16739    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16740
16741    let editor = cx.new_window_entity(|window, cx| {
16742        Editor::new(
16743            EditorMode::Full,
16744            multi_buffer,
16745            Some(project.clone()),
16746            window,
16747            cx,
16748        )
16749    });
16750
16751    editor.update_in(cx, |editor, window, cx| {
16752        let snapshot = editor.buffer().read(cx).snapshot(cx);
16753        editor.tasks.insert(
16754            (buffer.read(cx).remote_id(), 3),
16755            RunnableTasks {
16756                templates: vec![],
16757                offset: snapshot.anchor_before(43),
16758                column: 0,
16759                extra_variables: HashMap::default(),
16760                context_range: BufferOffset(43)..BufferOffset(85),
16761            },
16762        );
16763        editor.tasks.insert(
16764            (buffer.read(cx).remote_id(), 8),
16765            RunnableTasks {
16766                templates: vec![],
16767                offset: snapshot.anchor_before(86),
16768                column: 0,
16769                extra_variables: HashMap::default(),
16770                context_range: BufferOffset(86)..BufferOffset(191),
16771            },
16772        );
16773
16774        // Test finding task when cursor is inside function body
16775        editor.change_selections(None, window, cx, |s| {
16776            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16777        });
16778        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16779        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16780
16781        // Test finding task when cursor is on function name
16782        editor.change_selections(None, window, cx, |s| {
16783            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16784        });
16785        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16786        assert_eq!(row, 8, "Should find task when cursor is on function name");
16787    });
16788}
16789
16790#[gpui::test]
16791async fn test_folding_buffers(cx: &mut TestAppContext) {
16792    init_test(cx, |_| {});
16793
16794    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16795    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16796    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16797
16798    let fs = FakeFs::new(cx.executor());
16799    fs.insert_tree(
16800        path!("/a"),
16801        json!({
16802            "first.rs": sample_text_1,
16803            "second.rs": sample_text_2,
16804            "third.rs": sample_text_3,
16805        }),
16806    )
16807    .await;
16808    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16809    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16810    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16811    let worktree = project.update(cx, |project, cx| {
16812        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16813        assert_eq!(worktrees.len(), 1);
16814        worktrees.pop().unwrap()
16815    });
16816    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16817
16818    let buffer_1 = project
16819        .update(cx, |project, cx| {
16820            project.open_buffer((worktree_id, "first.rs"), cx)
16821        })
16822        .await
16823        .unwrap();
16824    let buffer_2 = project
16825        .update(cx, |project, cx| {
16826            project.open_buffer((worktree_id, "second.rs"), cx)
16827        })
16828        .await
16829        .unwrap();
16830    let buffer_3 = project
16831        .update(cx, |project, cx| {
16832            project.open_buffer((worktree_id, "third.rs"), cx)
16833        })
16834        .await
16835        .unwrap();
16836
16837    let multi_buffer = cx.new(|cx| {
16838        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16839        multi_buffer.push_excerpts(
16840            buffer_1.clone(),
16841            [
16842                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16843                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16844                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16845            ],
16846            cx,
16847        );
16848        multi_buffer.push_excerpts(
16849            buffer_2.clone(),
16850            [
16851                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16852                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16853                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16854            ],
16855            cx,
16856        );
16857        multi_buffer.push_excerpts(
16858            buffer_3.clone(),
16859            [
16860                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16861                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16862                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16863            ],
16864            cx,
16865        );
16866        multi_buffer
16867    });
16868    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16869        Editor::new(
16870            EditorMode::Full,
16871            multi_buffer.clone(),
16872            Some(project.clone()),
16873            window,
16874            cx,
16875        )
16876    });
16877
16878    assert_eq!(
16879        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16880        "\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",
16881    );
16882
16883    multi_buffer_editor.update(cx, |editor, cx| {
16884        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16885    });
16886    assert_eq!(
16887        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16888        "\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",
16889        "After folding the first buffer, its text should not be displayed"
16890    );
16891
16892    multi_buffer_editor.update(cx, |editor, cx| {
16893        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16894    });
16895    assert_eq!(
16896        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16897        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16898        "After folding the second buffer, its text should not be displayed"
16899    );
16900
16901    multi_buffer_editor.update(cx, |editor, cx| {
16902        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16903    });
16904    assert_eq!(
16905        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16906        "\n\n\n\n\n",
16907        "After folding the third buffer, its text should not be displayed"
16908    );
16909
16910    // Emulate selection inside the fold logic, that should work
16911    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16912        editor
16913            .snapshot(window, cx)
16914            .next_line_boundary(Point::new(0, 4));
16915    });
16916
16917    multi_buffer_editor.update(cx, |editor, cx| {
16918        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16919    });
16920    assert_eq!(
16921        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16922        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16923        "After unfolding the second buffer, its text should be displayed"
16924    );
16925
16926    // Typing inside of buffer 1 causes that buffer to be unfolded.
16927    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16928        assert_eq!(
16929            multi_buffer
16930                .read(cx)
16931                .snapshot(cx)
16932                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16933                .collect::<String>(),
16934            "bbbb"
16935        );
16936        editor.change_selections(None, window, cx, |selections| {
16937            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16938        });
16939        editor.handle_input("B", window, cx);
16940    });
16941
16942    assert_eq!(
16943        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16944        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16945        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16946    );
16947
16948    multi_buffer_editor.update(cx, |editor, cx| {
16949        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16950    });
16951    assert_eq!(
16952        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16953        "\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",
16954        "After unfolding the all buffers, all original text should be displayed"
16955    );
16956}
16957
16958#[gpui::test]
16959async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16960    init_test(cx, |_| {});
16961
16962    let sample_text_1 = "1111\n2222\n3333".to_string();
16963    let sample_text_2 = "4444\n5555\n6666".to_string();
16964    let sample_text_3 = "7777\n8888\n9999".to_string();
16965
16966    let fs = FakeFs::new(cx.executor());
16967    fs.insert_tree(
16968        path!("/a"),
16969        json!({
16970            "first.rs": sample_text_1,
16971            "second.rs": sample_text_2,
16972            "third.rs": sample_text_3,
16973        }),
16974    )
16975    .await;
16976    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16977    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16978    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16979    let worktree = project.update(cx, |project, cx| {
16980        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16981        assert_eq!(worktrees.len(), 1);
16982        worktrees.pop().unwrap()
16983    });
16984    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16985
16986    let buffer_1 = project
16987        .update(cx, |project, cx| {
16988            project.open_buffer((worktree_id, "first.rs"), cx)
16989        })
16990        .await
16991        .unwrap();
16992    let buffer_2 = project
16993        .update(cx, |project, cx| {
16994            project.open_buffer((worktree_id, "second.rs"), cx)
16995        })
16996        .await
16997        .unwrap();
16998    let buffer_3 = project
16999        .update(cx, |project, cx| {
17000            project.open_buffer((worktree_id, "third.rs"), cx)
17001        })
17002        .await
17003        .unwrap();
17004
17005    let multi_buffer = cx.new(|cx| {
17006        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17007        multi_buffer.push_excerpts(
17008            buffer_1.clone(),
17009            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17010            cx,
17011        );
17012        multi_buffer.push_excerpts(
17013            buffer_2.clone(),
17014            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17015            cx,
17016        );
17017        multi_buffer.push_excerpts(
17018            buffer_3.clone(),
17019            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17020            cx,
17021        );
17022        multi_buffer
17023    });
17024
17025    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17026        Editor::new(
17027            EditorMode::Full,
17028            multi_buffer,
17029            Some(project.clone()),
17030            window,
17031            cx,
17032        )
17033    });
17034
17035    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17036    assert_eq!(
17037        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17038        full_text,
17039    );
17040
17041    multi_buffer_editor.update(cx, |editor, cx| {
17042        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17043    });
17044    assert_eq!(
17045        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17046        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17047        "After folding the first buffer, its text should not be displayed"
17048    );
17049
17050    multi_buffer_editor.update(cx, |editor, cx| {
17051        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17052    });
17053
17054    assert_eq!(
17055        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17056        "\n\n\n\n\n\n7777\n8888\n9999",
17057        "After folding the second buffer, its text should not be displayed"
17058    );
17059
17060    multi_buffer_editor.update(cx, |editor, cx| {
17061        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17062    });
17063    assert_eq!(
17064        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17065        "\n\n\n\n\n",
17066        "After folding the third buffer, its text should not be displayed"
17067    );
17068
17069    multi_buffer_editor.update(cx, |editor, cx| {
17070        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17071    });
17072    assert_eq!(
17073        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17074        "\n\n\n\n4444\n5555\n6666\n\n",
17075        "After unfolding the second buffer, its text should be displayed"
17076    );
17077
17078    multi_buffer_editor.update(cx, |editor, cx| {
17079        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17080    });
17081    assert_eq!(
17082        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17083        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17084        "After unfolding the first buffer, its text should be displayed"
17085    );
17086
17087    multi_buffer_editor.update(cx, |editor, cx| {
17088        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17089    });
17090    assert_eq!(
17091        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17092        full_text,
17093        "After unfolding all buffers, all original text should be displayed"
17094    );
17095}
17096
17097#[gpui::test]
17098async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17099    init_test(cx, |_| {});
17100
17101    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17102
17103    let fs = FakeFs::new(cx.executor());
17104    fs.insert_tree(
17105        path!("/a"),
17106        json!({
17107            "main.rs": sample_text,
17108        }),
17109    )
17110    .await;
17111    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17112    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17113    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17114    let worktree = project.update(cx, |project, cx| {
17115        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17116        assert_eq!(worktrees.len(), 1);
17117        worktrees.pop().unwrap()
17118    });
17119    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17120
17121    let buffer_1 = project
17122        .update(cx, |project, cx| {
17123            project.open_buffer((worktree_id, "main.rs"), cx)
17124        })
17125        .await
17126        .unwrap();
17127
17128    let multi_buffer = cx.new(|cx| {
17129        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17130        multi_buffer.push_excerpts(
17131            buffer_1.clone(),
17132            [ExcerptRange::new(
17133                Point::new(0, 0)
17134                    ..Point::new(
17135                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17136                        0,
17137                    ),
17138            )],
17139            cx,
17140        );
17141        multi_buffer
17142    });
17143    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17144        Editor::new(
17145            EditorMode::Full,
17146            multi_buffer,
17147            Some(project.clone()),
17148            window,
17149            cx,
17150        )
17151    });
17152
17153    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17154    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17155        enum TestHighlight {}
17156        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17157        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17158        editor.highlight_text::<TestHighlight>(
17159            vec![highlight_range.clone()],
17160            HighlightStyle::color(Hsla::green()),
17161            cx,
17162        );
17163        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17164    });
17165
17166    let full_text = format!("\n\n{sample_text}");
17167    assert_eq!(
17168        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17169        full_text,
17170    );
17171}
17172
17173#[gpui::test]
17174async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17175    init_test(cx, |_| {});
17176    cx.update(|cx| {
17177        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17178            "keymaps/default-linux.json",
17179            cx,
17180        )
17181        .unwrap();
17182        cx.bind_keys(default_key_bindings);
17183    });
17184
17185    let (editor, cx) = cx.add_window_view(|window, cx| {
17186        let multi_buffer = MultiBuffer::build_multi(
17187            [
17188                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17189                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17190                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17191                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17192            ],
17193            cx,
17194        );
17195        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17196
17197        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17198        // fold all but the second buffer, so that we test navigating between two
17199        // adjacent folded buffers, as well as folded buffers at the start and
17200        // end the multibuffer
17201        editor.fold_buffer(buffer_ids[0], cx);
17202        editor.fold_buffer(buffer_ids[2], cx);
17203        editor.fold_buffer(buffer_ids[3], cx);
17204
17205        editor
17206    });
17207    cx.simulate_resize(size(px(1000.), px(1000.)));
17208
17209    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17210    cx.assert_excerpts_with_selections(indoc! {"
17211        [EXCERPT]
17212        ˇ[FOLDED]
17213        [EXCERPT]
17214        a1
17215        b1
17216        [EXCERPT]
17217        [FOLDED]
17218        [EXCERPT]
17219        [FOLDED]
17220        "
17221    });
17222    cx.simulate_keystroke("down");
17223    cx.assert_excerpts_with_selections(indoc! {"
17224        [EXCERPT]
17225        [FOLDED]
17226        [EXCERPT]
17227        ˇa1
17228        b1
17229        [EXCERPT]
17230        [FOLDED]
17231        [EXCERPT]
17232        [FOLDED]
17233        "
17234    });
17235    cx.simulate_keystroke("down");
17236    cx.assert_excerpts_with_selections(indoc! {"
17237        [EXCERPT]
17238        [FOLDED]
17239        [EXCERPT]
17240        a1
17241        ˇb1
17242        [EXCERPT]
17243        [FOLDED]
17244        [EXCERPT]
17245        [FOLDED]
17246        "
17247    });
17248    cx.simulate_keystroke("down");
17249    cx.assert_excerpts_with_selections(indoc! {"
17250        [EXCERPT]
17251        [FOLDED]
17252        [EXCERPT]
17253        a1
17254        b1
17255        ˇ[EXCERPT]
17256        [FOLDED]
17257        [EXCERPT]
17258        [FOLDED]
17259        "
17260    });
17261    cx.simulate_keystroke("down");
17262    cx.assert_excerpts_with_selections(indoc! {"
17263        [EXCERPT]
17264        [FOLDED]
17265        [EXCERPT]
17266        a1
17267        b1
17268        [EXCERPT]
17269        ˇ[FOLDED]
17270        [EXCERPT]
17271        [FOLDED]
17272        "
17273    });
17274    for _ in 0..5 {
17275        cx.simulate_keystroke("down");
17276        cx.assert_excerpts_with_selections(indoc! {"
17277            [EXCERPT]
17278            [FOLDED]
17279            [EXCERPT]
17280            a1
17281            b1
17282            [EXCERPT]
17283            [FOLDED]
17284            [EXCERPT]
17285            ˇ[FOLDED]
17286            "
17287        });
17288    }
17289
17290    cx.simulate_keystroke("up");
17291    cx.assert_excerpts_with_selections(indoc! {"
17292        [EXCERPT]
17293        [FOLDED]
17294        [EXCERPT]
17295        a1
17296        b1
17297        [EXCERPT]
17298        ˇ[FOLDED]
17299        [EXCERPT]
17300        [FOLDED]
17301        "
17302    });
17303    cx.simulate_keystroke("up");
17304    cx.assert_excerpts_with_selections(indoc! {"
17305        [EXCERPT]
17306        [FOLDED]
17307        [EXCERPT]
17308        a1
17309        b1
17310        ˇ[EXCERPT]
17311        [FOLDED]
17312        [EXCERPT]
17313        [FOLDED]
17314        "
17315    });
17316    cx.simulate_keystroke("up");
17317    cx.assert_excerpts_with_selections(indoc! {"
17318        [EXCERPT]
17319        [FOLDED]
17320        [EXCERPT]
17321        a1
17322        ˇb1
17323        [EXCERPT]
17324        [FOLDED]
17325        [EXCERPT]
17326        [FOLDED]
17327        "
17328    });
17329    cx.simulate_keystroke("up");
17330    cx.assert_excerpts_with_selections(indoc! {"
17331        [EXCERPT]
17332        [FOLDED]
17333        [EXCERPT]
17334        ˇa1
17335        b1
17336        [EXCERPT]
17337        [FOLDED]
17338        [EXCERPT]
17339        [FOLDED]
17340        "
17341    });
17342    for _ in 0..5 {
17343        cx.simulate_keystroke("up");
17344        cx.assert_excerpts_with_selections(indoc! {"
17345            [EXCERPT]
17346            ˇ[FOLDED]
17347            [EXCERPT]
17348            a1
17349            b1
17350            [EXCERPT]
17351            [FOLDED]
17352            [EXCERPT]
17353            [FOLDED]
17354            "
17355        });
17356    }
17357}
17358
17359#[gpui::test]
17360async fn test_inline_completion_text(cx: &mut TestAppContext) {
17361    init_test(cx, |_| {});
17362
17363    // Simple insertion
17364    assert_highlighted_edits(
17365        "Hello, world!",
17366        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17367        true,
17368        cx,
17369        |highlighted_edits, cx| {
17370            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17371            assert_eq!(highlighted_edits.highlights.len(), 1);
17372            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17373            assert_eq!(
17374                highlighted_edits.highlights[0].1.background_color,
17375                Some(cx.theme().status().created_background)
17376            );
17377        },
17378    )
17379    .await;
17380
17381    // Replacement
17382    assert_highlighted_edits(
17383        "This is a test.",
17384        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17385        false,
17386        cx,
17387        |highlighted_edits, cx| {
17388            assert_eq!(highlighted_edits.text, "That is a test.");
17389            assert_eq!(highlighted_edits.highlights.len(), 1);
17390            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17391            assert_eq!(
17392                highlighted_edits.highlights[0].1.background_color,
17393                Some(cx.theme().status().created_background)
17394            );
17395        },
17396    )
17397    .await;
17398
17399    // Multiple edits
17400    assert_highlighted_edits(
17401        "Hello, world!",
17402        vec![
17403            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17404            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17405        ],
17406        false,
17407        cx,
17408        |highlighted_edits, cx| {
17409            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17410            assert_eq!(highlighted_edits.highlights.len(), 2);
17411            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17412            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17413            assert_eq!(
17414                highlighted_edits.highlights[0].1.background_color,
17415                Some(cx.theme().status().created_background)
17416            );
17417            assert_eq!(
17418                highlighted_edits.highlights[1].1.background_color,
17419                Some(cx.theme().status().created_background)
17420            );
17421        },
17422    )
17423    .await;
17424
17425    // Multiple lines with edits
17426    assert_highlighted_edits(
17427        "First line\nSecond line\nThird line\nFourth line",
17428        vec![
17429            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17430            (
17431                Point::new(2, 0)..Point::new(2, 10),
17432                "New third line".to_string(),
17433            ),
17434            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17435        ],
17436        false,
17437        cx,
17438        |highlighted_edits, cx| {
17439            assert_eq!(
17440                highlighted_edits.text,
17441                "Second modified\nNew third line\nFourth updated line"
17442            );
17443            assert_eq!(highlighted_edits.highlights.len(), 3);
17444            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17445            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17446            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17447            for highlight in &highlighted_edits.highlights {
17448                assert_eq!(
17449                    highlight.1.background_color,
17450                    Some(cx.theme().status().created_background)
17451                );
17452            }
17453        },
17454    )
17455    .await;
17456}
17457
17458#[gpui::test]
17459async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17460    init_test(cx, |_| {});
17461
17462    // Deletion
17463    assert_highlighted_edits(
17464        "Hello, world!",
17465        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17466        true,
17467        cx,
17468        |highlighted_edits, cx| {
17469            assert_eq!(highlighted_edits.text, "Hello, world!");
17470            assert_eq!(highlighted_edits.highlights.len(), 1);
17471            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17472            assert_eq!(
17473                highlighted_edits.highlights[0].1.background_color,
17474                Some(cx.theme().status().deleted_background)
17475            );
17476        },
17477    )
17478    .await;
17479
17480    // Insertion
17481    assert_highlighted_edits(
17482        "Hello, world!",
17483        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17484        true,
17485        cx,
17486        |highlighted_edits, cx| {
17487            assert_eq!(highlighted_edits.highlights.len(), 1);
17488            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17489            assert_eq!(
17490                highlighted_edits.highlights[0].1.background_color,
17491                Some(cx.theme().status().created_background)
17492            );
17493        },
17494    )
17495    .await;
17496}
17497
17498async fn assert_highlighted_edits(
17499    text: &str,
17500    edits: Vec<(Range<Point>, String)>,
17501    include_deletions: bool,
17502    cx: &mut TestAppContext,
17503    assertion_fn: impl Fn(HighlightedText, &App),
17504) {
17505    let window = cx.add_window(|window, cx| {
17506        let buffer = MultiBuffer::build_simple(text, cx);
17507        Editor::new(EditorMode::Full, buffer, None, window, cx)
17508    });
17509    let cx = &mut VisualTestContext::from_window(*window, cx);
17510
17511    let (buffer, snapshot) = window
17512        .update(cx, |editor, _window, cx| {
17513            (
17514                editor.buffer().clone(),
17515                editor.buffer().read(cx).snapshot(cx),
17516            )
17517        })
17518        .unwrap();
17519
17520    let edits = edits
17521        .into_iter()
17522        .map(|(range, edit)| {
17523            (
17524                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17525                edit,
17526            )
17527        })
17528        .collect::<Vec<_>>();
17529
17530    let text_anchor_edits = edits
17531        .clone()
17532        .into_iter()
17533        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17534        .collect::<Vec<_>>();
17535
17536    let edit_preview = window
17537        .update(cx, |_, _window, cx| {
17538            buffer
17539                .read(cx)
17540                .as_singleton()
17541                .unwrap()
17542                .read(cx)
17543                .preview_edits(text_anchor_edits.into(), cx)
17544        })
17545        .unwrap()
17546        .await;
17547
17548    cx.update(|_window, cx| {
17549        let highlighted_edits = inline_completion_edit_text(
17550            &snapshot.as_singleton().unwrap().2,
17551            &edits,
17552            &edit_preview,
17553            include_deletions,
17554            cx,
17555        );
17556        assertion_fn(highlighted_edits, cx)
17557    });
17558}
17559
17560#[track_caller]
17561fn assert_breakpoint(
17562    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17563    path: &Arc<Path>,
17564    expected: Vec<(u32, Breakpoint)>,
17565) {
17566    if expected.len() == 0usize {
17567        assert!(!breakpoints.contains_key(path), "{}", path.display());
17568    } else {
17569        let mut breakpoint = breakpoints
17570            .get(path)
17571            .unwrap()
17572            .into_iter()
17573            .map(|breakpoint| {
17574                (
17575                    breakpoint.row,
17576                    Breakpoint {
17577                        message: breakpoint.message.clone(),
17578                        state: breakpoint.state,
17579                        condition: breakpoint.condition.clone(),
17580                        hit_condition: breakpoint.hit_condition.clone(),
17581                    },
17582                )
17583            })
17584            .collect::<Vec<_>>();
17585
17586        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17587
17588        assert_eq!(expected, breakpoint);
17589    }
17590}
17591
17592fn add_log_breakpoint_at_cursor(
17593    editor: &mut Editor,
17594    log_message: &str,
17595    window: &mut Window,
17596    cx: &mut Context<Editor>,
17597) {
17598    let (anchor, bp) = editor
17599        .breakpoint_at_cursor_head(window, cx)
17600        .unwrap_or_else(|| {
17601            let cursor_position: Point = editor.selections.newest(cx).head();
17602
17603            let breakpoint_position = editor
17604                .snapshot(window, cx)
17605                .display_snapshot
17606                .buffer_snapshot
17607                .anchor_before(Point::new(cursor_position.row, 0));
17608
17609            (breakpoint_position, Breakpoint::new_log(&log_message))
17610        });
17611
17612    editor.edit_breakpoint_at_anchor(
17613        anchor,
17614        bp,
17615        BreakpointEditAction::EditLogMessage(log_message.into()),
17616        cx,
17617    );
17618}
17619
17620#[gpui::test]
17621async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17622    init_test(cx, |_| {});
17623
17624    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17625    let fs = FakeFs::new(cx.executor());
17626    fs.insert_tree(
17627        path!("/a"),
17628        json!({
17629            "main.rs": sample_text,
17630        }),
17631    )
17632    .await;
17633    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17634    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17635    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17636
17637    let fs = FakeFs::new(cx.executor());
17638    fs.insert_tree(
17639        path!("/a"),
17640        json!({
17641            "main.rs": sample_text,
17642        }),
17643    )
17644    .await;
17645    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17646    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17647    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17648    let worktree_id = workspace
17649        .update(cx, |workspace, _window, cx| {
17650            workspace.project().update(cx, |project, cx| {
17651                project.worktrees(cx).next().unwrap().read(cx).id()
17652            })
17653        })
17654        .unwrap();
17655
17656    let buffer = project
17657        .update(cx, |project, cx| {
17658            project.open_buffer((worktree_id, "main.rs"), cx)
17659        })
17660        .await
17661        .unwrap();
17662
17663    let (editor, cx) = cx.add_window_view(|window, cx| {
17664        Editor::new(
17665            EditorMode::Full,
17666            MultiBuffer::build_from_buffer(buffer, cx),
17667            Some(project.clone()),
17668            window,
17669            cx,
17670        )
17671    });
17672
17673    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17674    let abs_path = project.read_with(cx, |project, cx| {
17675        project
17676            .absolute_path(&project_path, cx)
17677            .map(|path_buf| Arc::from(path_buf.to_owned()))
17678            .unwrap()
17679    });
17680
17681    // assert we can add breakpoint on the first line
17682    editor.update_in(cx, |editor, window, cx| {
17683        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17684        editor.move_to_end(&MoveToEnd, window, cx);
17685        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17686    });
17687
17688    let breakpoints = editor.update(cx, |editor, cx| {
17689        editor
17690            .breakpoint_store()
17691            .as_ref()
17692            .unwrap()
17693            .read(cx)
17694            .all_breakpoints(cx)
17695            .clone()
17696    });
17697
17698    assert_eq!(1, breakpoints.len());
17699    assert_breakpoint(
17700        &breakpoints,
17701        &abs_path,
17702        vec![
17703            (0, Breakpoint::new_standard()),
17704            (3, Breakpoint::new_standard()),
17705        ],
17706    );
17707
17708    editor.update_in(cx, |editor, window, cx| {
17709        editor.move_to_beginning(&MoveToBeginning, window, cx);
17710        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17711    });
17712
17713    let breakpoints = editor.update(cx, |editor, cx| {
17714        editor
17715            .breakpoint_store()
17716            .as_ref()
17717            .unwrap()
17718            .read(cx)
17719            .all_breakpoints(cx)
17720            .clone()
17721    });
17722
17723    assert_eq!(1, breakpoints.len());
17724    assert_breakpoint(
17725        &breakpoints,
17726        &abs_path,
17727        vec![(3, Breakpoint::new_standard())],
17728    );
17729
17730    editor.update_in(cx, |editor, window, cx| {
17731        editor.move_to_end(&MoveToEnd, window, cx);
17732        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17733    });
17734
17735    let breakpoints = editor.update(cx, |editor, cx| {
17736        editor
17737            .breakpoint_store()
17738            .as_ref()
17739            .unwrap()
17740            .read(cx)
17741            .all_breakpoints(cx)
17742            .clone()
17743    });
17744
17745    assert_eq!(0, breakpoints.len());
17746    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17747}
17748
17749#[gpui::test]
17750async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17751    init_test(cx, |_| {});
17752
17753    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17754
17755    let fs = FakeFs::new(cx.executor());
17756    fs.insert_tree(
17757        path!("/a"),
17758        json!({
17759            "main.rs": sample_text,
17760        }),
17761    )
17762    .await;
17763    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17764    let (workspace, cx) =
17765        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17766
17767    let worktree_id = workspace.update(cx, |workspace, cx| {
17768        workspace.project().update(cx, |project, cx| {
17769            project.worktrees(cx).next().unwrap().read(cx).id()
17770        })
17771    });
17772
17773    let buffer = project
17774        .update(cx, |project, cx| {
17775            project.open_buffer((worktree_id, "main.rs"), cx)
17776        })
17777        .await
17778        .unwrap();
17779
17780    let (editor, cx) = cx.add_window_view(|window, cx| {
17781        Editor::new(
17782            EditorMode::Full,
17783            MultiBuffer::build_from_buffer(buffer, cx),
17784            Some(project.clone()),
17785            window,
17786            cx,
17787        )
17788    });
17789
17790    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17791    let abs_path = project.read_with(cx, |project, cx| {
17792        project
17793            .absolute_path(&project_path, cx)
17794            .map(|path_buf| Arc::from(path_buf.to_owned()))
17795            .unwrap()
17796    });
17797
17798    editor.update_in(cx, |editor, window, cx| {
17799        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17800    });
17801
17802    let breakpoints = editor.update(cx, |editor, cx| {
17803        editor
17804            .breakpoint_store()
17805            .as_ref()
17806            .unwrap()
17807            .read(cx)
17808            .all_breakpoints(cx)
17809            .clone()
17810    });
17811
17812    assert_breakpoint(
17813        &breakpoints,
17814        &abs_path,
17815        vec![(0, Breakpoint::new_log("hello world"))],
17816    );
17817
17818    // Removing a log message from a log breakpoint should remove it
17819    editor.update_in(cx, |editor, window, cx| {
17820        add_log_breakpoint_at_cursor(editor, "", window, cx);
17821    });
17822
17823    let breakpoints = editor.update(cx, |editor, cx| {
17824        editor
17825            .breakpoint_store()
17826            .as_ref()
17827            .unwrap()
17828            .read(cx)
17829            .all_breakpoints(cx)
17830            .clone()
17831    });
17832
17833    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17834
17835    editor.update_in(cx, |editor, window, cx| {
17836        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17837        editor.move_to_end(&MoveToEnd, window, cx);
17838        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17839        // Not adding a log message to a standard breakpoint shouldn't remove it
17840        add_log_breakpoint_at_cursor(editor, "", window, cx);
17841    });
17842
17843    let breakpoints = editor.update(cx, |editor, cx| {
17844        editor
17845            .breakpoint_store()
17846            .as_ref()
17847            .unwrap()
17848            .read(cx)
17849            .all_breakpoints(cx)
17850            .clone()
17851    });
17852
17853    assert_breakpoint(
17854        &breakpoints,
17855        &abs_path,
17856        vec![
17857            (0, Breakpoint::new_standard()),
17858            (3, Breakpoint::new_standard()),
17859        ],
17860    );
17861
17862    editor.update_in(cx, |editor, window, cx| {
17863        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17864    });
17865
17866    let breakpoints = editor.update(cx, |editor, cx| {
17867        editor
17868            .breakpoint_store()
17869            .as_ref()
17870            .unwrap()
17871            .read(cx)
17872            .all_breakpoints(cx)
17873            .clone()
17874    });
17875
17876    assert_breakpoint(
17877        &breakpoints,
17878        &abs_path,
17879        vec![
17880            (0, Breakpoint::new_standard()),
17881            (3, Breakpoint::new_log("hello world")),
17882        ],
17883    );
17884
17885    editor.update_in(cx, |editor, window, cx| {
17886        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17887    });
17888
17889    let breakpoints = editor.update(cx, |editor, cx| {
17890        editor
17891            .breakpoint_store()
17892            .as_ref()
17893            .unwrap()
17894            .read(cx)
17895            .all_breakpoints(cx)
17896            .clone()
17897    });
17898
17899    assert_breakpoint(
17900        &breakpoints,
17901        &abs_path,
17902        vec![
17903            (0, Breakpoint::new_standard()),
17904            (3, Breakpoint::new_log("hello Earth!!")),
17905        ],
17906    );
17907}
17908
17909/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17910/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17911/// or when breakpoints were placed out of order. This tests for a regression too
17912#[gpui::test]
17913async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17914    init_test(cx, |_| {});
17915
17916    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17917    let fs = FakeFs::new(cx.executor());
17918    fs.insert_tree(
17919        path!("/a"),
17920        json!({
17921            "main.rs": sample_text,
17922        }),
17923    )
17924    .await;
17925    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17926    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17927    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17928
17929    let fs = FakeFs::new(cx.executor());
17930    fs.insert_tree(
17931        path!("/a"),
17932        json!({
17933            "main.rs": sample_text,
17934        }),
17935    )
17936    .await;
17937    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17938    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17939    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17940    let worktree_id = workspace
17941        .update(cx, |workspace, _window, cx| {
17942            workspace.project().update(cx, |project, cx| {
17943                project.worktrees(cx).next().unwrap().read(cx).id()
17944            })
17945        })
17946        .unwrap();
17947
17948    let buffer = project
17949        .update(cx, |project, cx| {
17950            project.open_buffer((worktree_id, "main.rs"), cx)
17951        })
17952        .await
17953        .unwrap();
17954
17955    let (editor, cx) = cx.add_window_view(|window, cx| {
17956        Editor::new(
17957            EditorMode::Full,
17958            MultiBuffer::build_from_buffer(buffer, cx),
17959            Some(project.clone()),
17960            window,
17961            cx,
17962        )
17963    });
17964
17965    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17966    let abs_path = project.read_with(cx, |project, cx| {
17967        project
17968            .absolute_path(&project_path, cx)
17969            .map(|path_buf| Arc::from(path_buf.to_owned()))
17970            .unwrap()
17971    });
17972
17973    // assert we can add breakpoint on the first line
17974    editor.update_in(cx, |editor, window, cx| {
17975        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17976        editor.move_to_end(&MoveToEnd, window, cx);
17977        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17978        editor.move_up(&MoveUp, window, cx);
17979        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17980    });
17981
17982    let breakpoints = editor.update(cx, |editor, cx| {
17983        editor
17984            .breakpoint_store()
17985            .as_ref()
17986            .unwrap()
17987            .read(cx)
17988            .all_breakpoints(cx)
17989            .clone()
17990    });
17991
17992    assert_eq!(1, breakpoints.len());
17993    assert_breakpoint(
17994        &breakpoints,
17995        &abs_path,
17996        vec![
17997            (0, Breakpoint::new_standard()),
17998            (2, Breakpoint::new_standard()),
17999            (3, Breakpoint::new_standard()),
18000        ],
18001    );
18002
18003    editor.update_in(cx, |editor, window, cx| {
18004        editor.move_to_beginning(&MoveToBeginning, window, cx);
18005        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18006        editor.move_to_end(&MoveToEnd, window, cx);
18007        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18008        // Disabling a breakpoint that doesn't exist should do nothing
18009        editor.move_up(&MoveUp, window, cx);
18010        editor.move_up(&MoveUp, window, cx);
18011        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18012    });
18013
18014    let breakpoints = editor.update(cx, |editor, cx| {
18015        editor
18016            .breakpoint_store()
18017            .as_ref()
18018            .unwrap()
18019            .read(cx)
18020            .all_breakpoints(cx)
18021            .clone()
18022    });
18023
18024    let disable_breakpoint = {
18025        let mut bp = Breakpoint::new_standard();
18026        bp.state = BreakpointState::Disabled;
18027        bp
18028    };
18029
18030    assert_eq!(1, breakpoints.len());
18031    assert_breakpoint(
18032        &breakpoints,
18033        &abs_path,
18034        vec![
18035            (0, disable_breakpoint.clone()),
18036            (2, Breakpoint::new_standard()),
18037            (3, disable_breakpoint.clone()),
18038        ],
18039    );
18040
18041    editor.update_in(cx, |editor, window, cx| {
18042        editor.move_to_beginning(&MoveToBeginning, window, cx);
18043        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18044        editor.move_to_end(&MoveToEnd, window, cx);
18045        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18046        editor.move_up(&MoveUp, window, cx);
18047        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18048    });
18049
18050    let breakpoints = editor.update(cx, |editor, cx| {
18051        editor
18052            .breakpoint_store()
18053            .as_ref()
18054            .unwrap()
18055            .read(cx)
18056            .all_breakpoints(cx)
18057            .clone()
18058    });
18059
18060    assert_eq!(1, breakpoints.len());
18061    assert_breakpoint(
18062        &breakpoints,
18063        &abs_path,
18064        vec![
18065            (0, Breakpoint::new_standard()),
18066            (2, disable_breakpoint),
18067            (3, Breakpoint::new_standard()),
18068        ],
18069    );
18070}
18071
18072#[gpui::test]
18073async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18074    init_test(cx, |_| {});
18075    let capabilities = lsp::ServerCapabilities {
18076        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18077            prepare_provider: Some(true),
18078            work_done_progress_options: Default::default(),
18079        })),
18080        ..Default::default()
18081    };
18082    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18083
18084    cx.set_state(indoc! {"
18085        struct Fˇoo {}
18086    "});
18087
18088    cx.update_editor(|editor, _, cx| {
18089        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18090        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18091        editor.highlight_background::<DocumentHighlightRead>(
18092            &[highlight_range],
18093            |c| c.editor_document_highlight_read_background,
18094            cx,
18095        );
18096    });
18097
18098    let mut prepare_rename_handler = cx
18099        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18100            move |_, _, _| async move {
18101                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18102                    start: lsp::Position {
18103                        line: 0,
18104                        character: 7,
18105                    },
18106                    end: lsp::Position {
18107                        line: 0,
18108                        character: 10,
18109                    },
18110                })))
18111            },
18112        );
18113    let prepare_rename_task = cx
18114        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18115        .expect("Prepare rename was not started");
18116    prepare_rename_handler.next().await.unwrap();
18117    prepare_rename_task.await.expect("Prepare rename failed");
18118
18119    let mut rename_handler =
18120        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18121            let edit = lsp::TextEdit {
18122                range: lsp::Range {
18123                    start: lsp::Position {
18124                        line: 0,
18125                        character: 7,
18126                    },
18127                    end: lsp::Position {
18128                        line: 0,
18129                        character: 10,
18130                    },
18131                },
18132                new_text: "FooRenamed".to_string(),
18133            };
18134            Ok(Some(lsp::WorkspaceEdit::new(
18135                // Specify the same edit twice
18136                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18137            )))
18138        });
18139    let rename_task = cx
18140        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18141        .expect("Confirm rename was not started");
18142    rename_handler.next().await.unwrap();
18143    rename_task.await.expect("Confirm rename failed");
18144    cx.run_until_parked();
18145
18146    // Despite two edits, only one is actually applied as those are identical
18147    cx.assert_editor_state(indoc! {"
18148        struct FooRenamedˇ {}
18149    "});
18150}
18151
18152#[gpui::test]
18153async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18154    init_test(cx, |_| {});
18155    // These capabilities indicate that the server does not support prepare rename.
18156    let capabilities = lsp::ServerCapabilities {
18157        rename_provider: Some(lsp::OneOf::Left(true)),
18158        ..Default::default()
18159    };
18160    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18161
18162    cx.set_state(indoc! {"
18163        struct Fˇoo {}
18164    "});
18165
18166    cx.update_editor(|editor, _window, cx| {
18167        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18168        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18169        editor.highlight_background::<DocumentHighlightRead>(
18170            &[highlight_range],
18171            |c| c.editor_document_highlight_read_background,
18172            cx,
18173        );
18174    });
18175
18176    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18177        .expect("Prepare rename was not started")
18178        .await
18179        .expect("Prepare rename failed");
18180
18181    let mut rename_handler =
18182        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18183            let edit = lsp::TextEdit {
18184                range: lsp::Range {
18185                    start: lsp::Position {
18186                        line: 0,
18187                        character: 7,
18188                    },
18189                    end: lsp::Position {
18190                        line: 0,
18191                        character: 10,
18192                    },
18193                },
18194                new_text: "FooRenamed".to_string(),
18195            };
18196            Ok(Some(lsp::WorkspaceEdit::new(
18197                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18198            )))
18199        });
18200    let rename_task = cx
18201        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18202        .expect("Confirm rename was not started");
18203    rename_handler.next().await.unwrap();
18204    rename_task.await.expect("Confirm rename failed");
18205    cx.run_until_parked();
18206
18207    // Correct range is renamed, as `surrounding_word` is used to find it.
18208    cx.assert_editor_state(indoc! {"
18209        struct FooRenamedˇ {}
18210    "});
18211}
18212
18213#[gpui::test]
18214async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18215    init_test(cx, |_| {});
18216    let mut cx = EditorTestContext::new(cx).await;
18217
18218    let language = Arc::new(
18219        Language::new(
18220            LanguageConfig::default(),
18221            Some(tree_sitter_html::LANGUAGE.into()),
18222        )
18223        .with_brackets_query(
18224            r#"
18225            ("<" @open "/>" @close)
18226            ("</" @open ">" @close)
18227            ("<" @open ">" @close)
18228            ("\"" @open "\"" @close)
18229            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18230        "#,
18231        )
18232        .unwrap(),
18233    );
18234    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18235
18236    cx.set_state(indoc! {"
18237        <span>ˇ</span>
18238    "});
18239    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18240    cx.assert_editor_state(indoc! {"
18241        <span>
18242        ˇ
18243        </span>
18244    "});
18245
18246    cx.set_state(indoc! {"
18247        <span><span></span>ˇ</span>
18248    "});
18249    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18250    cx.assert_editor_state(indoc! {"
18251        <span><span></span>
18252        ˇ</span>
18253    "});
18254
18255    cx.set_state(indoc! {"
18256        <span>ˇ
18257        </span>
18258    "});
18259    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18260    cx.assert_editor_state(indoc! {"
18261        <span>
18262        ˇ
18263        </span>
18264    "});
18265}
18266
18267#[gpui::test(iterations = 10)]
18268async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18269    init_test(cx, |_| {});
18270
18271    let fs = FakeFs::new(cx.executor());
18272    fs.insert_tree(
18273        path!("/dir"),
18274        json!({
18275            "a.ts": "a",
18276        }),
18277    )
18278    .await;
18279
18280    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18281    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18282    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18283
18284    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18285    language_registry.add(Arc::new(Language::new(
18286        LanguageConfig {
18287            name: "TypeScript".into(),
18288            matcher: LanguageMatcher {
18289                path_suffixes: vec!["ts".to_string()],
18290                ..Default::default()
18291            },
18292            ..Default::default()
18293        },
18294        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18295    )));
18296    let mut fake_language_servers = language_registry.register_fake_lsp(
18297        "TypeScript",
18298        FakeLspAdapter {
18299            capabilities: lsp::ServerCapabilities {
18300                code_lens_provider: Some(lsp::CodeLensOptions {
18301                    resolve_provider: Some(true),
18302                }),
18303                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18304                    commands: vec!["_the/command".to_string()],
18305                    ..lsp::ExecuteCommandOptions::default()
18306                }),
18307                ..lsp::ServerCapabilities::default()
18308            },
18309            ..FakeLspAdapter::default()
18310        },
18311    );
18312
18313    let (buffer, _handle) = project
18314        .update(cx, |p, cx| {
18315            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18316        })
18317        .await
18318        .unwrap();
18319    cx.executor().run_until_parked();
18320
18321    let fake_server = fake_language_servers.next().await.unwrap();
18322
18323    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18324    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18325    drop(buffer_snapshot);
18326    let actions = cx
18327        .update_window(*workspace, |_, window, cx| {
18328            project.code_actions(&buffer, anchor..anchor, window, cx)
18329        })
18330        .unwrap();
18331
18332    fake_server
18333        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18334            Ok(Some(vec![
18335                lsp::CodeLens {
18336                    range: lsp::Range::default(),
18337                    command: Some(lsp::Command {
18338                        title: "Code lens command".to_owned(),
18339                        command: "_the/command".to_owned(),
18340                        arguments: None,
18341                    }),
18342                    data: None,
18343                },
18344                lsp::CodeLens {
18345                    range: lsp::Range::default(),
18346                    command: Some(lsp::Command {
18347                        title: "Command not in capabilities".to_owned(),
18348                        command: "not in capabilities".to_owned(),
18349                        arguments: None,
18350                    }),
18351                    data: None,
18352                },
18353                lsp::CodeLens {
18354                    range: lsp::Range {
18355                        start: lsp::Position {
18356                            line: 1,
18357                            character: 1,
18358                        },
18359                        end: lsp::Position {
18360                            line: 1,
18361                            character: 1,
18362                        },
18363                    },
18364                    command: Some(lsp::Command {
18365                        title: "Command not in range".to_owned(),
18366                        command: "_the/command".to_owned(),
18367                        arguments: None,
18368                    }),
18369                    data: None,
18370                },
18371            ]))
18372        })
18373        .next()
18374        .await;
18375
18376    let actions = actions.await.unwrap();
18377    assert_eq!(
18378        actions.len(),
18379        1,
18380        "Should have only one valid action for the 0..0 range"
18381    );
18382    let action = actions[0].clone();
18383    let apply = project.update(cx, |project, cx| {
18384        project.apply_code_action(buffer.clone(), action, true, cx)
18385    });
18386
18387    // Resolving the code action does not populate its edits. In absence of
18388    // edits, we must execute the given command.
18389    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18390        |mut lens, _| async move {
18391            let lens_command = lens.command.as_mut().expect("should have a command");
18392            assert_eq!(lens_command.title, "Code lens command");
18393            lens_command.arguments = Some(vec![json!("the-argument")]);
18394            Ok(lens)
18395        },
18396    );
18397
18398    // While executing the command, the language server sends the editor
18399    // a `workspaceEdit` request.
18400    fake_server
18401        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18402            let fake = fake_server.clone();
18403            move |params, _| {
18404                assert_eq!(params.command, "_the/command");
18405                let fake = fake.clone();
18406                async move {
18407                    fake.server
18408                        .request::<lsp::request::ApplyWorkspaceEdit>(
18409                            lsp::ApplyWorkspaceEditParams {
18410                                label: None,
18411                                edit: lsp::WorkspaceEdit {
18412                                    changes: Some(
18413                                        [(
18414                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18415                                            vec![lsp::TextEdit {
18416                                                range: lsp::Range::new(
18417                                                    lsp::Position::new(0, 0),
18418                                                    lsp::Position::new(0, 0),
18419                                                ),
18420                                                new_text: "X".into(),
18421                                            }],
18422                                        )]
18423                                        .into_iter()
18424                                        .collect(),
18425                                    ),
18426                                    ..Default::default()
18427                                },
18428                            },
18429                        )
18430                        .await
18431                        .unwrap();
18432                    Ok(Some(json!(null)))
18433                }
18434            }
18435        })
18436        .next()
18437        .await;
18438
18439    // Applying the code lens command returns a project transaction containing the edits
18440    // sent by the language server in its `workspaceEdit` request.
18441    let transaction = apply.await.unwrap();
18442    assert!(transaction.0.contains_key(&buffer));
18443    buffer.update(cx, |buffer, cx| {
18444        assert_eq!(buffer.text(), "Xa");
18445        buffer.undo(cx);
18446        assert_eq!(buffer.text(), "a");
18447    });
18448}
18449
18450#[gpui::test]
18451async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18452    init_test(cx, |_| {});
18453
18454    let fs = FakeFs::new(cx.executor());
18455    let main_text = r#"fn main() {
18456println!("1");
18457println!("2");
18458println!("3");
18459println!("4");
18460println!("5");
18461}"#;
18462    let lib_text = "mod foo {}";
18463    fs.insert_tree(
18464        path!("/a"),
18465        json!({
18466            "lib.rs": lib_text,
18467            "main.rs": main_text,
18468        }),
18469    )
18470    .await;
18471
18472    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18473    let (workspace, cx) =
18474        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18475    let worktree_id = workspace.update(cx, |workspace, cx| {
18476        workspace.project().update(cx, |project, cx| {
18477            project.worktrees(cx).next().unwrap().read(cx).id()
18478        })
18479    });
18480
18481    let expected_ranges = vec![
18482        Point::new(0, 0)..Point::new(0, 0),
18483        Point::new(1, 0)..Point::new(1, 1),
18484        Point::new(2, 0)..Point::new(2, 2),
18485        Point::new(3, 0)..Point::new(3, 3),
18486    ];
18487
18488    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18489    let editor_1 = workspace
18490        .update_in(cx, |workspace, window, cx| {
18491            workspace.open_path(
18492                (worktree_id, "main.rs"),
18493                Some(pane_1.downgrade()),
18494                true,
18495                window,
18496                cx,
18497            )
18498        })
18499        .unwrap()
18500        .await
18501        .downcast::<Editor>()
18502        .unwrap();
18503    pane_1.update(cx, |pane, cx| {
18504        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18505        open_editor.update(cx, |editor, cx| {
18506            assert_eq!(
18507                editor.display_text(cx),
18508                main_text,
18509                "Original main.rs text on initial open",
18510            );
18511            assert_eq!(
18512                editor
18513                    .selections
18514                    .all::<Point>(cx)
18515                    .into_iter()
18516                    .map(|s| s.range())
18517                    .collect::<Vec<_>>(),
18518                vec![Point::zero()..Point::zero()],
18519                "Default selections on initial open",
18520            );
18521        })
18522    });
18523    editor_1.update_in(cx, |editor, window, cx| {
18524        editor.change_selections(None, window, cx, |s| {
18525            s.select_ranges(expected_ranges.clone());
18526        });
18527    });
18528
18529    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18530        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18531    });
18532    let editor_2 = workspace
18533        .update_in(cx, |workspace, window, cx| {
18534            workspace.open_path(
18535                (worktree_id, "main.rs"),
18536                Some(pane_2.downgrade()),
18537                true,
18538                window,
18539                cx,
18540            )
18541        })
18542        .unwrap()
18543        .await
18544        .downcast::<Editor>()
18545        .unwrap();
18546    pane_2.update(cx, |pane, cx| {
18547        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18548        open_editor.update(cx, |editor, cx| {
18549            assert_eq!(
18550                editor.display_text(cx),
18551                main_text,
18552                "Original main.rs text on initial open in another panel",
18553            );
18554            assert_eq!(
18555                editor
18556                    .selections
18557                    .all::<Point>(cx)
18558                    .into_iter()
18559                    .map(|s| s.range())
18560                    .collect::<Vec<_>>(),
18561                vec![Point::zero()..Point::zero()],
18562                "Default selections on initial open in another panel",
18563            );
18564        })
18565    });
18566
18567    editor_2.update_in(cx, |editor, window, cx| {
18568        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18569    });
18570
18571    let _other_editor_1 = workspace
18572        .update_in(cx, |workspace, window, cx| {
18573            workspace.open_path(
18574                (worktree_id, "lib.rs"),
18575                Some(pane_1.downgrade()),
18576                true,
18577                window,
18578                cx,
18579            )
18580        })
18581        .unwrap()
18582        .await
18583        .downcast::<Editor>()
18584        .unwrap();
18585    pane_1
18586        .update_in(cx, |pane, window, cx| {
18587            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18588                .unwrap()
18589        })
18590        .await
18591        .unwrap();
18592    drop(editor_1);
18593    pane_1.update(cx, |pane, cx| {
18594        pane.active_item()
18595            .unwrap()
18596            .downcast::<Editor>()
18597            .unwrap()
18598            .update(cx, |editor, cx| {
18599                assert_eq!(
18600                    editor.display_text(cx),
18601                    lib_text,
18602                    "Other file should be open and active",
18603                );
18604            });
18605        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18606    });
18607
18608    let _other_editor_2 = workspace
18609        .update_in(cx, |workspace, window, cx| {
18610            workspace.open_path(
18611                (worktree_id, "lib.rs"),
18612                Some(pane_2.downgrade()),
18613                true,
18614                window,
18615                cx,
18616            )
18617        })
18618        .unwrap()
18619        .await
18620        .downcast::<Editor>()
18621        .unwrap();
18622    pane_2
18623        .update_in(cx, |pane, window, cx| {
18624            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18625                .unwrap()
18626        })
18627        .await
18628        .unwrap();
18629    drop(editor_2);
18630    pane_2.update(cx, |pane, cx| {
18631        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18632        open_editor.update(cx, |editor, cx| {
18633            assert_eq!(
18634                editor.display_text(cx),
18635                lib_text,
18636                "Other file should be open and active in another panel too",
18637            );
18638        });
18639        assert_eq!(
18640            pane.items().count(),
18641            1,
18642            "No other editors should be open in another pane",
18643        );
18644    });
18645
18646    let _editor_1_reopened = workspace
18647        .update_in(cx, |workspace, window, cx| {
18648            workspace.open_path(
18649                (worktree_id, "main.rs"),
18650                Some(pane_1.downgrade()),
18651                true,
18652                window,
18653                cx,
18654            )
18655        })
18656        .unwrap()
18657        .await
18658        .downcast::<Editor>()
18659        .unwrap();
18660    let _editor_2_reopened = workspace
18661        .update_in(cx, |workspace, window, cx| {
18662            workspace.open_path(
18663                (worktree_id, "main.rs"),
18664                Some(pane_2.downgrade()),
18665                true,
18666                window,
18667                cx,
18668            )
18669        })
18670        .unwrap()
18671        .await
18672        .downcast::<Editor>()
18673        .unwrap();
18674    pane_1.update(cx, |pane, cx| {
18675        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18676        open_editor.update(cx, |editor, cx| {
18677            assert_eq!(
18678                editor.display_text(cx),
18679                main_text,
18680                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18681            );
18682            assert_eq!(
18683                editor
18684                    .selections
18685                    .all::<Point>(cx)
18686                    .into_iter()
18687                    .map(|s| s.range())
18688                    .collect::<Vec<_>>(),
18689                expected_ranges,
18690                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18691            );
18692        })
18693    });
18694    pane_2.update(cx, |pane, cx| {
18695        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18696        open_editor.update(cx, |editor, cx| {
18697            assert_eq!(
18698                editor.display_text(cx),
18699                r#"fn main() {
18700⋯rintln!("1");
18701⋯intln!("2");
18702⋯ntln!("3");
18703println!("4");
18704println!("5");
18705}"#,
18706                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18707            );
18708            assert_eq!(
18709                editor
18710                    .selections
18711                    .all::<Point>(cx)
18712                    .into_iter()
18713                    .map(|s| s.range())
18714                    .collect::<Vec<_>>(),
18715                vec![Point::zero()..Point::zero()],
18716                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18717            );
18718        })
18719    });
18720}
18721
18722#[gpui::test]
18723async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18724    init_test(cx, |_| {});
18725
18726    let fs = FakeFs::new(cx.executor());
18727    let main_text = r#"fn main() {
18728println!("1");
18729println!("2");
18730println!("3");
18731println!("4");
18732println!("5");
18733}"#;
18734    let lib_text = "mod foo {}";
18735    fs.insert_tree(
18736        path!("/a"),
18737        json!({
18738            "lib.rs": lib_text,
18739            "main.rs": main_text,
18740        }),
18741    )
18742    .await;
18743
18744    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18745    let (workspace, cx) =
18746        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18747    let worktree_id = workspace.update(cx, |workspace, cx| {
18748        workspace.project().update(cx, |project, cx| {
18749            project.worktrees(cx).next().unwrap().read(cx).id()
18750        })
18751    });
18752
18753    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18754    let editor = workspace
18755        .update_in(cx, |workspace, window, cx| {
18756            workspace.open_path(
18757                (worktree_id, "main.rs"),
18758                Some(pane.downgrade()),
18759                true,
18760                window,
18761                cx,
18762            )
18763        })
18764        .unwrap()
18765        .await
18766        .downcast::<Editor>()
18767        .unwrap();
18768    pane.update(cx, |pane, cx| {
18769        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18770        open_editor.update(cx, |editor, cx| {
18771            assert_eq!(
18772                editor.display_text(cx),
18773                main_text,
18774                "Original main.rs text on initial open",
18775            );
18776        })
18777    });
18778    editor.update_in(cx, |editor, window, cx| {
18779        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18780    });
18781
18782    cx.update_global(|store: &mut SettingsStore, cx| {
18783        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18784            s.restore_on_file_reopen = Some(false);
18785        });
18786    });
18787    editor.update_in(cx, |editor, window, cx| {
18788        editor.fold_ranges(
18789            vec![
18790                Point::new(1, 0)..Point::new(1, 1),
18791                Point::new(2, 0)..Point::new(2, 2),
18792                Point::new(3, 0)..Point::new(3, 3),
18793            ],
18794            false,
18795            window,
18796            cx,
18797        );
18798    });
18799    pane.update_in(cx, |pane, window, cx| {
18800        pane.close_all_items(&CloseAllItems::default(), window, cx)
18801            .unwrap()
18802    })
18803    .await
18804    .unwrap();
18805    pane.update(cx, |pane, _| {
18806        assert!(pane.active_item().is_none());
18807    });
18808    cx.update_global(|store: &mut SettingsStore, cx| {
18809        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18810            s.restore_on_file_reopen = Some(true);
18811        });
18812    });
18813
18814    let _editor_reopened = workspace
18815        .update_in(cx, |workspace, window, cx| {
18816            workspace.open_path(
18817                (worktree_id, "main.rs"),
18818                Some(pane.downgrade()),
18819                true,
18820                window,
18821                cx,
18822            )
18823        })
18824        .unwrap()
18825        .await
18826        .downcast::<Editor>()
18827        .unwrap();
18828    pane.update(cx, |pane, cx| {
18829        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18830        open_editor.update(cx, |editor, cx| {
18831            assert_eq!(
18832                editor.display_text(cx),
18833                main_text,
18834                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18835            );
18836        })
18837    });
18838}
18839
18840fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18841    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18842    point..point
18843}
18844
18845fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18846    let (text, ranges) = marked_text_ranges(marked_text, true);
18847    assert_eq!(editor.text(cx), text);
18848    assert_eq!(
18849        editor.selections.ranges(cx),
18850        ranges,
18851        "Assert selections are {}",
18852        marked_text
18853    );
18854}
18855
18856pub fn handle_signature_help_request(
18857    cx: &mut EditorLspTestContext,
18858    mocked_response: lsp::SignatureHelp,
18859) -> impl Future<Output = ()> + use<> {
18860    let mut request =
18861        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18862            let mocked_response = mocked_response.clone();
18863            async move { Ok(Some(mocked_response)) }
18864        });
18865
18866    async move {
18867        request.next().await;
18868    }
18869}
18870
18871/// Handle completion request passing a marked string specifying where the completion
18872/// should be triggered from using '|' character, what range should be replaced, and what completions
18873/// should be returned using '<' and '>' to delimit the range.
18874///
18875/// Also see `handle_completion_request_with_insert_and_replace`.
18876#[track_caller]
18877pub fn handle_completion_request(
18878    cx: &mut EditorLspTestContext,
18879    marked_string: &str,
18880    completions: Vec<&'static str>,
18881    counter: Arc<AtomicUsize>,
18882) -> impl Future<Output = ()> {
18883    let complete_from_marker: TextRangeMarker = '|'.into();
18884    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18885    let (_, mut marked_ranges) = marked_text_ranges_by(
18886        marked_string,
18887        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18888    );
18889
18890    let complete_from_position =
18891        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18892    let replace_range =
18893        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18894
18895    let mut request =
18896        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18897            let completions = completions.clone();
18898            counter.fetch_add(1, atomic::Ordering::Release);
18899            async move {
18900                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18901                assert_eq!(
18902                    params.text_document_position.position,
18903                    complete_from_position
18904                );
18905                Ok(Some(lsp::CompletionResponse::Array(
18906                    completions
18907                        .iter()
18908                        .map(|completion_text| lsp::CompletionItem {
18909                            label: completion_text.to_string(),
18910                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18911                                range: replace_range,
18912                                new_text: completion_text.to_string(),
18913                            })),
18914                            ..Default::default()
18915                        })
18916                        .collect(),
18917                )))
18918            }
18919        });
18920
18921    async move {
18922        request.next().await;
18923    }
18924}
18925
18926/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18927/// given instead, which also contains an `insert` range.
18928///
18929/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18930/// that is, `replace_range.start..cursor_pos`.
18931pub fn handle_completion_request_with_insert_and_replace(
18932    cx: &mut EditorLspTestContext,
18933    marked_string: &str,
18934    completions: Vec<&'static str>,
18935    counter: Arc<AtomicUsize>,
18936) -> impl Future<Output = ()> {
18937    let complete_from_marker: TextRangeMarker = '|'.into();
18938    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18939    let (_, mut marked_ranges) = marked_text_ranges_by(
18940        marked_string,
18941        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18942    );
18943
18944    let complete_from_position =
18945        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18946    let replace_range =
18947        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18948
18949    let mut request =
18950        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18951            let completions = completions.clone();
18952            counter.fetch_add(1, atomic::Ordering::Release);
18953            async move {
18954                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18955                assert_eq!(
18956                    params.text_document_position.position, complete_from_position,
18957                    "marker `|` position doesn't match",
18958                );
18959                Ok(Some(lsp::CompletionResponse::Array(
18960                    completions
18961                        .iter()
18962                        .map(|completion_text| lsp::CompletionItem {
18963                            label: completion_text.to_string(),
18964                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18965                                lsp::InsertReplaceEdit {
18966                                    insert: lsp::Range {
18967                                        start: replace_range.start,
18968                                        end: complete_from_position,
18969                                    },
18970                                    replace: replace_range,
18971                                    new_text: completion_text.to_string(),
18972                                },
18973                            )),
18974                            ..Default::default()
18975                        })
18976                        .collect(),
18977                )))
18978            }
18979        });
18980
18981    async move {
18982        request.next().await;
18983    }
18984}
18985
18986fn handle_resolve_completion_request(
18987    cx: &mut EditorLspTestContext,
18988    edits: Option<Vec<(&'static str, &'static str)>>,
18989) -> impl Future<Output = ()> {
18990    let edits = edits.map(|edits| {
18991        edits
18992            .iter()
18993            .map(|(marked_string, new_text)| {
18994                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18995                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18996                lsp::TextEdit::new(replace_range, new_text.to_string())
18997            })
18998            .collect::<Vec<_>>()
18999    });
19000
19001    let mut request =
19002        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19003            let edits = edits.clone();
19004            async move {
19005                Ok(lsp::CompletionItem {
19006                    additional_text_edits: edits,
19007                    ..Default::default()
19008                })
19009            }
19010        });
19011
19012    async move {
19013        request.next().await;
19014    }
19015}
19016
19017pub(crate) fn update_test_language_settings(
19018    cx: &mut TestAppContext,
19019    f: impl Fn(&mut AllLanguageSettingsContent),
19020) {
19021    cx.update(|cx| {
19022        SettingsStore::update_global(cx, |store, cx| {
19023            store.update_user_settings::<AllLanguageSettings>(cx, f);
19024        });
19025    });
19026}
19027
19028pub(crate) fn update_test_project_settings(
19029    cx: &mut TestAppContext,
19030    f: impl Fn(&mut ProjectSettings),
19031) {
19032    cx.update(|cx| {
19033        SettingsStore::update_global(cx, |store, cx| {
19034            store.update_user_settings::<ProjectSettings>(cx, f);
19035        });
19036    });
19037}
19038
19039pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19040    cx.update(|cx| {
19041        assets::Assets.load_test_fonts(cx);
19042        let store = SettingsStore::test(cx);
19043        cx.set_global(store);
19044        theme::init(theme::LoadThemes::JustBase, cx);
19045        release_channel::init(SemanticVersion::default(), cx);
19046        client::init_settings(cx);
19047        language::init(cx);
19048        Project::init_settings(cx);
19049        workspace::init_settings(cx);
19050        crate::init(cx);
19051    });
19052
19053    update_test_language_settings(cx, f);
19054}
19055
19056#[track_caller]
19057fn assert_hunk_revert(
19058    not_reverted_text_with_selections: &str,
19059    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19060    expected_reverted_text_with_selections: &str,
19061    base_text: &str,
19062    cx: &mut EditorLspTestContext,
19063) {
19064    cx.set_state(not_reverted_text_with_selections);
19065    cx.set_head_text(base_text);
19066    cx.executor().run_until_parked();
19067
19068    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19069        let snapshot = editor.snapshot(window, cx);
19070        let reverted_hunk_statuses = snapshot
19071            .buffer_snapshot
19072            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19073            .map(|hunk| hunk.status().kind)
19074            .collect::<Vec<_>>();
19075
19076        editor.git_restore(&Default::default(), window, cx);
19077        reverted_hunk_statuses
19078    });
19079    cx.executor().run_until_parked();
19080    cx.assert_editor_state(expected_reverted_text_with_selections);
19081    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19082}