editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor,
    6        editor_lsp_test_context::{git_commit_lang, EditorLspTestContext},
    7        editor_test_context::EditorTestContext,
    8        select_ranges,
    9    },
   10    JoinLines,
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions,
   17};
   18use indoc::indoc;
   19use language::{
   20    language_settings::{
   21        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   22        LanguageSettingsContent, PrettierSettings,
   23    },
   24    BracketPairConfig,
   25    Capability::ReadWrite,
   26    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   27    Override, Point,
   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    debugger::breakpoint_store::{BreakpointKind, SerializedBreakpoint},
   36    project_settings::{LspSettings, ProjectSettings},
   37    FakeFs,
   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::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   51    uri,
   52};
   53use workspace::{
   54    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   55    NavigationEntry, ViewId,
   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 {
 3169                context: Point::new(0, 0)..Point::new(2, 0),
 3170                primary: None,
 3171            }],
 3172            cx,
 3173        );
 3174        multibuffer.push_excerpts(
 3175            rust_buffer.clone(),
 3176            [ExcerptRange {
 3177                context: Point::new(0, 0)..Point::new(1, 0),
 3178                primary: None,
 3179            }],
 3180            cx,
 3181        );
 3182        multibuffer
 3183    });
 3184
 3185    cx.add_window(|window, cx| {
 3186        let mut editor = build_editor(multibuffer, window, cx);
 3187
 3188        assert_eq!(
 3189            editor.text(cx),
 3190            indoc! {"
 3191                a = 1
 3192                b = 2
 3193
 3194                const c: usize = 3;
 3195            "}
 3196        );
 3197
 3198        select_ranges(
 3199            &mut editor,
 3200            indoc! {"
 3201                «aˇ» = 1
 3202                b = 2
 3203
 3204                «const c:ˇ» usize = 3;
 3205            "},
 3206            window,
 3207            cx,
 3208        );
 3209
 3210        editor.tab(&Tab, window, cx);
 3211        assert_text_with_selections(
 3212            &mut editor,
 3213            indoc! {"
 3214                  «aˇ» = 1
 3215                b = 2
 3216
 3217                    «const c:ˇ» usize = 3;
 3218            "},
 3219            cx,
 3220        );
 3221        editor.backtab(&Backtab, window, cx);
 3222        assert_text_with_selections(
 3223            &mut editor,
 3224            indoc! {"
 3225                «aˇ» = 1
 3226                b = 2
 3227
 3228                «const c:ˇ» usize = 3;
 3229            "},
 3230            cx,
 3231        );
 3232
 3233        editor
 3234    });
 3235}
 3236
 3237#[gpui::test]
 3238async fn test_backspace(cx: &mut TestAppContext) {
 3239    init_test(cx, |_| {});
 3240
 3241    let mut cx = EditorTestContext::new(cx).await;
 3242
 3243    // Basic backspace
 3244    cx.set_state(indoc! {"
 3245        onˇe two three
 3246        fou«rˇ» five six
 3247        seven «ˇeight nine
 3248        »ten
 3249    "});
 3250    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        oˇe two three
 3253        fouˇ five six
 3254        seven ˇten
 3255    "});
 3256
 3257    // Test backspace inside and around indents
 3258    cx.set_state(indoc! {"
 3259        zero
 3260            ˇone
 3261                ˇtwo
 3262            ˇ ˇ ˇ  three
 3263        ˇ  ˇ  four
 3264    "});
 3265    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3266    cx.assert_editor_state(indoc! {"
 3267        zero
 3268        ˇone
 3269            ˇtwo
 3270        ˇ  threeˇ  four
 3271    "});
 3272
 3273    // Test backspace with line_mode set to true
 3274    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3275    cx.set_state(indoc! {"
 3276        The ˇquick ˇbrown
 3277        fox jumps over
 3278        the lazy dog
 3279        ˇThe qu«ick bˇ»rown"});
 3280    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3281    cx.assert_editor_state(indoc! {"
 3282        ˇfox jumps over
 3283        the lazy dogˇ"});
 3284}
 3285
 3286#[gpui::test]
 3287async fn test_delete(cx: &mut TestAppContext) {
 3288    init_test(cx, |_| {});
 3289
 3290    let mut cx = EditorTestContext::new(cx).await;
 3291    cx.set_state(indoc! {"
 3292        onˇe two three
 3293        fou«rˇ» five six
 3294        seven «ˇeight nine
 3295        »ten
 3296    "});
 3297    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3298    cx.assert_editor_state(indoc! {"
 3299        onˇ two three
 3300        fouˇ five six
 3301        seven ˇten
 3302    "});
 3303
 3304    // Test backspace with line_mode set to true
 3305    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3306    cx.set_state(indoc! {"
 3307        The ˇquick ˇbrown
 3308        fox «ˇjum»ps over
 3309        the lazy dog
 3310        ˇThe qu«ick bˇ»rown"});
 3311    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3312    cx.assert_editor_state("ˇthe lazy dogˇ");
 3313}
 3314
 3315#[gpui::test]
 3316fn test_delete_line(cx: &mut TestAppContext) {
 3317    init_test(cx, |_| {});
 3318
 3319    let editor = cx.add_window(|window, cx| {
 3320        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3321        build_editor(buffer, window, cx)
 3322    });
 3323    _ = editor.update(cx, |editor, window, cx| {
 3324        editor.change_selections(None, window, cx, |s| {
 3325            s.select_display_ranges([
 3326                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3327                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3328                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3329            ])
 3330        });
 3331        editor.delete_line(&DeleteLine, window, cx);
 3332        assert_eq!(editor.display_text(cx), "ghi");
 3333        assert_eq!(
 3334            editor.selections.display_ranges(cx),
 3335            vec![
 3336                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3337                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3338            ]
 3339        );
 3340    });
 3341
 3342    let editor = cx.add_window(|window, cx| {
 3343        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3344        build_editor(buffer, window, cx)
 3345    });
 3346    _ = editor.update(cx, |editor, window, cx| {
 3347        editor.change_selections(None, window, cx, |s| {
 3348            s.select_display_ranges([
 3349                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3350            ])
 3351        });
 3352        editor.delete_line(&DeleteLine, window, cx);
 3353        assert_eq!(editor.display_text(cx), "ghi\n");
 3354        assert_eq!(
 3355            editor.selections.display_ranges(cx),
 3356            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3357        );
 3358    });
 3359}
 3360
 3361#[gpui::test]
 3362fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3363    init_test(cx, |_| {});
 3364
 3365    cx.add_window(|window, cx| {
 3366        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3367        let mut editor = build_editor(buffer.clone(), window, cx);
 3368        let buffer = buffer.read(cx).as_singleton().unwrap();
 3369
 3370        assert_eq!(
 3371            editor.selections.ranges::<Point>(cx),
 3372            &[Point::new(0, 0)..Point::new(0, 0)]
 3373        );
 3374
 3375        // When on single line, replace newline at end by space
 3376        editor.join_lines(&JoinLines, window, cx);
 3377        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3378        assert_eq!(
 3379            editor.selections.ranges::<Point>(cx),
 3380            &[Point::new(0, 3)..Point::new(0, 3)]
 3381        );
 3382
 3383        // When multiple lines are selected, remove newlines that are spanned by the selection
 3384        editor.change_selections(None, window, cx, |s| {
 3385            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3386        });
 3387        editor.join_lines(&JoinLines, window, cx);
 3388        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3389        assert_eq!(
 3390            editor.selections.ranges::<Point>(cx),
 3391            &[Point::new(0, 11)..Point::new(0, 11)]
 3392        );
 3393
 3394        // Undo should be transactional
 3395        editor.undo(&Undo, window, cx);
 3396        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3397        assert_eq!(
 3398            editor.selections.ranges::<Point>(cx),
 3399            &[Point::new(0, 5)..Point::new(2, 2)]
 3400        );
 3401
 3402        // When joining an empty line don't insert a space
 3403        editor.change_selections(None, window, cx, |s| {
 3404            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3405        });
 3406        editor.join_lines(&JoinLines, window, cx);
 3407        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3408        assert_eq!(
 3409            editor.selections.ranges::<Point>(cx),
 3410            [Point::new(2, 3)..Point::new(2, 3)]
 3411        );
 3412
 3413        // We can remove trailing newlines
 3414        editor.join_lines(&JoinLines, window, cx);
 3415        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3416        assert_eq!(
 3417            editor.selections.ranges::<Point>(cx),
 3418            [Point::new(2, 3)..Point::new(2, 3)]
 3419        );
 3420
 3421        // We don't blow up on the last line
 3422        editor.join_lines(&JoinLines, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3424        assert_eq!(
 3425            editor.selections.ranges::<Point>(cx),
 3426            [Point::new(2, 3)..Point::new(2, 3)]
 3427        );
 3428
 3429        // reset to test indentation
 3430        editor.buffer.update(cx, |buffer, cx| {
 3431            buffer.edit(
 3432                [
 3433                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3434                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3435                ],
 3436                None,
 3437                cx,
 3438            )
 3439        });
 3440
 3441        // We remove any leading spaces
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3443        editor.change_selections(None, window, cx, |s| {
 3444            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3445        });
 3446        editor.join_lines(&JoinLines, window, cx);
 3447        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3448
 3449        // We don't insert a space for a line containing only spaces
 3450        editor.join_lines(&JoinLines, window, cx);
 3451        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3452
 3453        // We ignore any leading tabs
 3454        editor.join_lines(&JoinLines, window, cx);
 3455        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3456
 3457        editor
 3458    });
 3459}
 3460
 3461#[gpui::test]
 3462fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3463    init_test(cx, |_| {});
 3464
 3465    cx.add_window(|window, cx| {
 3466        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3467        let mut editor = build_editor(buffer.clone(), window, cx);
 3468        let buffer = buffer.read(cx).as_singleton().unwrap();
 3469
 3470        editor.change_selections(None, window, cx, |s| {
 3471            s.select_ranges([
 3472                Point::new(0, 2)..Point::new(1, 1),
 3473                Point::new(1, 2)..Point::new(1, 2),
 3474                Point::new(3, 1)..Point::new(3, 2),
 3475            ])
 3476        });
 3477
 3478        editor.join_lines(&JoinLines, window, cx);
 3479        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3480
 3481        assert_eq!(
 3482            editor.selections.ranges::<Point>(cx),
 3483            [
 3484                Point::new(0, 7)..Point::new(0, 7),
 3485                Point::new(1, 3)..Point::new(1, 3)
 3486            ]
 3487        );
 3488        editor
 3489    });
 3490}
 3491
 3492#[gpui::test]
 3493async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3494    init_test(cx, |_| {});
 3495
 3496    let mut cx = EditorTestContext::new(cx).await;
 3497
 3498    let diff_base = r#"
 3499        Line 0
 3500        Line 1
 3501        Line 2
 3502        Line 3
 3503        "#
 3504    .unindent();
 3505
 3506    cx.set_state(
 3507        &r#"
 3508        ˇLine 0
 3509        Line 1
 3510        Line 2
 3511        Line 3
 3512        "#
 3513        .unindent(),
 3514    );
 3515
 3516    cx.set_head_text(&diff_base);
 3517    executor.run_until_parked();
 3518
 3519    // Join lines
 3520    cx.update_editor(|editor, window, cx| {
 3521        editor.join_lines(&JoinLines, window, cx);
 3522    });
 3523    executor.run_until_parked();
 3524
 3525    cx.assert_editor_state(
 3526        &r#"
 3527        Line 0ˇ Line 1
 3528        Line 2
 3529        Line 3
 3530        "#
 3531        .unindent(),
 3532    );
 3533    // Join again
 3534    cx.update_editor(|editor, window, cx| {
 3535        editor.join_lines(&JoinLines, window, cx);
 3536    });
 3537    executor.run_until_parked();
 3538
 3539    cx.assert_editor_state(
 3540        &r#"
 3541        Line 0 Line 1ˇ Line 2
 3542        Line 3
 3543        "#
 3544        .unindent(),
 3545    );
 3546}
 3547
 3548#[gpui::test]
 3549async fn test_custom_newlines_cause_no_false_positive_diffs(
 3550    executor: BackgroundExecutor,
 3551    cx: &mut TestAppContext,
 3552) {
 3553    init_test(cx, |_| {});
 3554    let mut cx = EditorTestContext::new(cx).await;
 3555    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3556    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3557    executor.run_until_parked();
 3558
 3559    cx.update_editor(|editor, window, cx| {
 3560        let snapshot = editor.snapshot(window, cx);
 3561        assert_eq!(
 3562            snapshot
 3563                .buffer_snapshot
 3564                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3565                .collect::<Vec<_>>(),
 3566            Vec::new(),
 3567            "Should not have any diffs for files with custom newlines"
 3568        );
 3569    });
 3570}
 3571
 3572#[gpui::test]
 3573async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3574    init_test(cx, |_| {});
 3575
 3576    let mut cx = EditorTestContext::new(cx).await;
 3577
 3578    // Test sort_lines_case_insensitive()
 3579    cx.set_state(indoc! {"
 3580        «z
 3581        y
 3582        x
 3583        Z
 3584        Y
 3585        Xˇ»
 3586    "});
 3587    cx.update_editor(|e, window, cx| {
 3588        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3589    });
 3590    cx.assert_editor_state(indoc! {"
 3591        «x
 3592        X
 3593        y
 3594        Y
 3595        z
 3596        Zˇ»
 3597    "});
 3598
 3599    // Test reverse_lines()
 3600    cx.set_state(indoc! {"
 3601        «5
 3602        4
 3603        3
 3604        2
 3605        1ˇ»
 3606    "});
 3607    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3608    cx.assert_editor_state(indoc! {"
 3609        «1
 3610        2
 3611        3
 3612        4
 3613        5ˇ»
 3614    "});
 3615
 3616    // Skip testing shuffle_line()
 3617
 3618    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3619    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3620
 3621    // Don't manipulate when cursor is on single line, but expand the selection
 3622    cx.set_state(indoc! {"
 3623        ddˇdd
 3624        ccc
 3625        bb
 3626        a
 3627    "});
 3628    cx.update_editor(|e, window, cx| {
 3629        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3630    });
 3631    cx.assert_editor_state(indoc! {"
 3632        «ddddˇ»
 3633        ccc
 3634        bb
 3635        a
 3636    "});
 3637
 3638    // Basic manipulate case
 3639    // Start selection moves to column 0
 3640    // End of selection shrinks to fit shorter line
 3641    cx.set_state(indoc! {"
 3642        dd«d
 3643        ccc
 3644        bb
 3645        aaaaaˇ»
 3646    "});
 3647    cx.update_editor(|e, window, cx| {
 3648        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3649    });
 3650    cx.assert_editor_state(indoc! {"
 3651        «aaaaa
 3652        bb
 3653        ccc
 3654        dddˇ»
 3655    "});
 3656
 3657    // Manipulate case with newlines
 3658    cx.set_state(indoc! {"
 3659        dd«d
 3660        ccc
 3661
 3662        bb
 3663        aaaaa
 3664
 3665        ˇ»
 3666    "});
 3667    cx.update_editor(|e, window, cx| {
 3668        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3669    });
 3670    cx.assert_editor_state(indoc! {"
 3671        «
 3672
 3673        aaaaa
 3674        bb
 3675        ccc
 3676        dddˇ»
 3677
 3678    "});
 3679
 3680    // Adding new line
 3681    cx.set_state(indoc! {"
 3682        aa«a
 3683        bbˇ»b
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3687    });
 3688    cx.assert_editor_state(indoc! {"
 3689        «aaa
 3690        bbb
 3691        added_lineˇ»
 3692    "});
 3693
 3694    // Removing line
 3695    cx.set_state(indoc! {"
 3696        aa«a
 3697        bbbˇ»
 3698    "});
 3699    cx.update_editor(|e, window, cx| {
 3700        e.manipulate_lines(window, cx, |lines| {
 3701            lines.pop();
 3702        })
 3703    });
 3704    cx.assert_editor_state(indoc! {"
 3705        «aaaˇ»
 3706    "});
 3707
 3708    // Removing all lines
 3709    cx.set_state(indoc! {"
 3710        aa«a
 3711        bbbˇ»
 3712    "});
 3713    cx.update_editor(|e, window, cx| {
 3714        e.manipulate_lines(window, cx, |lines| {
 3715            lines.drain(..);
 3716        })
 3717    });
 3718    cx.assert_editor_state(indoc! {"
 3719        ˇ
 3720    "});
 3721}
 3722
 3723#[gpui::test]
 3724async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3725    init_test(cx, |_| {});
 3726
 3727    let mut cx = EditorTestContext::new(cx).await;
 3728
 3729    // Consider continuous selection as single selection
 3730    cx.set_state(indoc! {"
 3731        Aaa«aa
 3732        cˇ»c«c
 3733        bb
 3734        aaaˇ»aa
 3735    "});
 3736    cx.update_editor(|e, window, cx| {
 3737        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3738    });
 3739    cx.assert_editor_state(indoc! {"
 3740        «Aaaaa
 3741        ccc
 3742        bb
 3743        aaaaaˇ»
 3744    "});
 3745
 3746    cx.set_state(indoc! {"
 3747        Aaa«aa
 3748        cˇ»c«c
 3749        bb
 3750        aaaˇ»aa
 3751    "});
 3752    cx.update_editor(|e, window, cx| {
 3753        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3754    });
 3755    cx.assert_editor_state(indoc! {"
 3756        «Aaaaa
 3757        ccc
 3758        bbˇ»
 3759    "});
 3760
 3761    // Consider non continuous selection as distinct dedup operations
 3762    cx.set_state(indoc! {"
 3763        «aaaaa
 3764        bb
 3765        aaaaa
 3766        aaaaaˇ»
 3767
 3768        aaa«aaˇ»
 3769    "});
 3770    cx.update_editor(|e, window, cx| {
 3771        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3772    });
 3773    cx.assert_editor_state(indoc! {"
 3774        «aaaaa
 3775        bbˇ»
 3776
 3777        «aaaaaˇ»
 3778    "});
 3779}
 3780
 3781#[gpui::test]
 3782async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3783    init_test(cx, |_| {});
 3784
 3785    let mut cx = EditorTestContext::new(cx).await;
 3786
 3787    cx.set_state(indoc! {"
 3788        «Aaa
 3789        aAa
 3790        Aaaˇ»
 3791    "});
 3792    cx.update_editor(|e, window, cx| {
 3793        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3794    });
 3795    cx.assert_editor_state(indoc! {"
 3796        «Aaa
 3797        aAaˇ»
 3798    "});
 3799
 3800    cx.set_state(indoc! {"
 3801        «Aaa
 3802        aAa
 3803        aaAˇ»
 3804    "});
 3805    cx.update_editor(|e, window, cx| {
 3806        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3807    });
 3808    cx.assert_editor_state(indoc! {"
 3809        «Aaaˇ»
 3810    "});
 3811}
 3812
 3813#[gpui::test]
 3814async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3815    init_test(cx, |_| {});
 3816
 3817    let mut cx = EditorTestContext::new(cx).await;
 3818
 3819    // Manipulate with multiple selections on a single line
 3820    cx.set_state(indoc! {"
 3821        dd«dd
 3822        cˇ»c«c
 3823        bb
 3824        aaaˇ»aa
 3825    "});
 3826    cx.update_editor(|e, window, cx| {
 3827        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3828    });
 3829    cx.assert_editor_state(indoc! {"
 3830        «aaaaa
 3831        bb
 3832        ccc
 3833        ddddˇ»
 3834    "});
 3835
 3836    // Manipulate with multiple disjoin selections
 3837    cx.set_state(indoc! {"
 3838 3839        4
 3840        3
 3841        2
 3842        1ˇ»
 3843
 3844        dd«dd
 3845        ccc
 3846        bb
 3847        aaaˇ»aa
 3848    "});
 3849    cx.update_editor(|e, window, cx| {
 3850        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3851    });
 3852    cx.assert_editor_state(indoc! {"
 3853        «1
 3854        2
 3855        3
 3856        4
 3857        5ˇ»
 3858
 3859        «aaaaa
 3860        bb
 3861        ccc
 3862        ddddˇ»
 3863    "});
 3864
 3865    // Adding lines on each selection
 3866    cx.set_state(indoc! {"
 3867 3868        1ˇ»
 3869
 3870        bb«bb
 3871        aaaˇ»aa
 3872    "});
 3873    cx.update_editor(|e, window, cx| {
 3874        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3875    });
 3876    cx.assert_editor_state(indoc! {"
 3877        «2
 3878        1
 3879        added lineˇ»
 3880
 3881        «bbbb
 3882        aaaaa
 3883        added lineˇ»
 3884    "});
 3885
 3886    // Removing lines on each selection
 3887    cx.set_state(indoc! {"
 3888 3889        1ˇ»
 3890
 3891        bb«bb
 3892        aaaˇ»aa
 3893    "});
 3894    cx.update_editor(|e, window, cx| {
 3895        e.manipulate_lines(window, cx, |lines| {
 3896            lines.pop();
 3897        })
 3898    });
 3899    cx.assert_editor_state(indoc! {"
 3900        «2ˇ»
 3901
 3902        «bbbbˇ»
 3903    "});
 3904}
 3905
 3906#[gpui::test]
 3907async fn test_manipulate_text(cx: &mut TestAppContext) {
 3908    init_test(cx, |_| {});
 3909
 3910    let mut cx = EditorTestContext::new(cx).await;
 3911
 3912    // Test convert_to_upper_case()
 3913    cx.set_state(indoc! {"
 3914        «hello worldˇ»
 3915    "});
 3916    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3917    cx.assert_editor_state(indoc! {"
 3918        «HELLO WORLDˇ»
 3919    "});
 3920
 3921    // Test convert_to_lower_case()
 3922    cx.set_state(indoc! {"
 3923        «HELLO WORLDˇ»
 3924    "});
 3925    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3926    cx.assert_editor_state(indoc! {"
 3927        «hello worldˇ»
 3928    "});
 3929
 3930    // Test multiple line, single selection case
 3931    cx.set_state(indoc! {"
 3932        «The quick brown
 3933        fox jumps over
 3934        the lazy dogˇ»
 3935    "});
 3936    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3937    cx.assert_editor_state(indoc! {"
 3938        «The Quick Brown
 3939        Fox Jumps Over
 3940        The Lazy Dogˇ»
 3941    "});
 3942
 3943    // Test multiple line, single selection case
 3944    cx.set_state(indoc! {"
 3945        «The quick brown
 3946        fox jumps over
 3947        the lazy dogˇ»
 3948    "});
 3949    cx.update_editor(|e, window, cx| {
 3950        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3951    });
 3952    cx.assert_editor_state(indoc! {"
 3953        «TheQuickBrown
 3954        FoxJumpsOver
 3955        TheLazyDogˇ»
 3956    "});
 3957
 3958    // From here on out, test more complex cases of manipulate_text()
 3959
 3960    // Test no selection case - should affect words cursors are in
 3961    // Cursor at beginning, middle, and end of word
 3962    cx.set_state(indoc! {"
 3963        ˇhello big beauˇtiful worldˇ
 3964    "});
 3965    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3966    cx.assert_editor_state(indoc! {"
 3967        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3968    "});
 3969
 3970    // Test multiple selections on a single line and across multiple lines
 3971    cx.set_state(indoc! {"
 3972        «Theˇ» quick «brown
 3973        foxˇ» jumps «overˇ»
 3974        the «lazyˇ» dog
 3975    "});
 3976    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3977    cx.assert_editor_state(indoc! {"
 3978        «THEˇ» quick «BROWN
 3979        FOXˇ» jumps «OVERˇ»
 3980        the «LAZYˇ» dog
 3981    "});
 3982
 3983    // Test case where text length grows
 3984    cx.set_state(indoc! {"
 3985        «tschüߡ»
 3986    "});
 3987    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3988    cx.assert_editor_state(indoc! {"
 3989        «TSCHÜSSˇ»
 3990    "});
 3991
 3992    // Test to make sure we don't crash when text shrinks
 3993    cx.set_state(indoc! {"
 3994        aaa_bbbˇ
 3995    "});
 3996    cx.update_editor(|e, window, cx| {
 3997        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3998    });
 3999    cx.assert_editor_state(indoc! {"
 4000        «aaaBbbˇ»
 4001    "});
 4002
 4003    // Test to make sure we all aware of the fact that each word can grow and shrink
 4004    // Final selections should be aware of this fact
 4005    cx.set_state(indoc! {"
 4006        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4007    "});
 4008    cx.update_editor(|e, window, cx| {
 4009        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4010    });
 4011    cx.assert_editor_state(indoc! {"
 4012        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4013    "});
 4014
 4015    cx.set_state(indoc! {"
 4016        «hElLo, WoRld!ˇ»
 4017    "});
 4018    cx.update_editor(|e, window, cx| {
 4019        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4020    });
 4021    cx.assert_editor_state(indoc! {"
 4022        «HeLlO, wOrLD!ˇ»
 4023    "});
 4024}
 4025
 4026#[gpui::test]
 4027fn test_duplicate_line(cx: &mut TestAppContext) {
 4028    init_test(cx, |_| {});
 4029
 4030    let editor = cx.add_window(|window, cx| {
 4031        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4032        build_editor(buffer, window, cx)
 4033    });
 4034    _ = editor.update(cx, |editor, window, cx| {
 4035        editor.change_selections(None, window, cx, |s| {
 4036            s.select_display_ranges([
 4037                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4038                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4039                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4040                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4041            ])
 4042        });
 4043        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4044        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4045        assert_eq!(
 4046            editor.selections.display_ranges(cx),
 4047            vec![
 4048                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4049                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4050                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4051                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4052            ]
 4053        );
 4054    });
 4055
 4056    let editor = cx.add_window(|window, cx| {
 4057        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4058        build_editor(buffer, window, cx)
 4059    });
 4060    _ = editor.update(cx, |editor, window, cx| {
 4061        editor.change_selections(None, window, cx, |s| {
 4062            s.select_display_ranges([
 4063                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4064                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4065            ])
 4066        });
 4067        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4068        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4069        assert_eq!(
 4070            editor.selections.display_ranges(cx),
 4071            vec![
 4072                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4073                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4074            ]
 4075        );
 4076    });
 4077
 4078    // With `move_upwards` the selections stay in place, except for
 4079    // the lines inserted above them
 4080    let editor = cx.add_window(|window, cx| {
 4081        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4082        build_editor(buffer, window, cx)
 4083    });
 4084    _ = editor.update(cx, |editor, window, cx| {
 4085        editor.change_selections(None, window, cx, |s| {
 4086            s.select_display_ranges([
 4087                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4088                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4089                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4090                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4091            ])
 4092        });
 4093        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4094        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4095        assert_eq!(
 4096            editor.selections.display_ranges(cx),
 4097            vec![
 4098                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4099                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4100                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4101                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4102            ]
 4103        );
 4104    });
 4105
 4106    let editor = cx.add_window(|window, cx| {
 4107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4108        build_editor(buffer, window, cx)
 4109    });
 4110    _ = editor.update(cx, |editor, window, cx| {
 4111        editor.change_selections(None, window, cx, |s| {
 4112            s.select_display_ranges([
 4113                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4114                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4115            ])
 4116        });
 4117        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4118        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4119        assert_eq!(
 4120            editor.selections.display_ranges(cx),
 4121            vec![
 4122                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4123                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4124            ]
 4125        );
 4126    });
 4127
 4128    let editor = cx.add_window(|window, cx| {
 4129        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4130        build_editor(buffer, window, cx)
 4131    });
 4132    _ = editor.update(cx, |editor, window, cx| {
 4133        editor.change_selections(None, window, cx, |s| {
 4134            s.select_display_ranges([
 4135                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4136                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4137            ])
 4138        });
 4139        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4140        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4141        assert_eq!(
 4142            editor.selections.display_ranges(cx),
 4143            vec![
 4144                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4145                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4146            ]
 4147        );
 4148    });
 4149}
 4150
 4151#[gpui::test]
 4152fn test_move_line_up_down(cx: &mut TestAppContext) {
 4153    init_test(cx, |_| {});
 4154
 4155    let editor = cx.add_window(|window, cx| {
 4156        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4157        build_editor(buffer, window, cx)
 4158    });
 4159    _ = editor.update(cx, |editor, window, cx| {
 4160        editor.fold_creases(
 4161            vec![
 4162                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4163                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4164                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4165            ],
 4166            true,
 4167            window,
 4168            cx,
 4169        );
 4170        editor.change_selections(None, window, cx, |s| {
 4171            s.select_display_ranges([
 4172                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4173                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4174                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4175                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4176            ])
 4177        });
 4178        assert_eq!(
 4179            editor.display_text(cx),
 4180            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4181        );
 4182
 4183        editor.move_line_up(&MoveLineUp, window, cx);
 4184        assert_eq!(
 4185            editor.display_text(cx),
 4186            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4187        );
 4188        assert_eq!(
 4189            editor.selections.display_ranges(cx),
 4190            vec![
 4191                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4192                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4193                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4194                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4195            ]
 4196        );
 4197    });
 4198
 4199    _ = editor.update(cx, |editor, window, cx| {
 4200        editor.move_line_down(&MoveLineDown, window, cx);
 4201        assert_eq!(
 4202            editor.display_text(cx),
 4203            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4204        );
 4205        assert_eq!(
 4206            editor.selections.display_ranges(cx),
 4207            vec![
 4208                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4209                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4210                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4211                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4212            ]
 4213        );
 4214    });
 4215
 4216    _ = editor.update(cx, |editor, window, cx| {
 4217        editor.move_line_down(&MoveLineDown, window, cx);
 4218        assert_eq!(
 4219            editor.display_text(cx),
 4220            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4221        );
 4222        assert_eq!(
 4223            editor.selections.display_ranges(cx),
 4224            vec![
 4225                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4226                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4227                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4228                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4229            ]
 4230        );
 4231    });
 4232
 4233    _ = editor.update(cx, |editor, window, cx| {
 4234        editor.move_line_up(&MoveLineUp, window, cx);
 4235        assert_eq!(
 4236            editor.display_text(cx),
 4237            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4238        );
 4239        assert_eq!(
 4240            editor.selections.display_ranges(cx),
 4241            vec![
 4242                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4243                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4244                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4245                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4246            ]
 4247        );
 4248    });
 4249}
 4250
 4251#[gpui::test]
 4252fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4253    init_test(cx, |_| {});
 4254
 4255    let editor = cx.add_window(|window, cx| {
 4256        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4257        build_editor(buffer, window, cx)
 4258    });
 4259    _ = editor.update(cx, |editor, window, cx| {
 4260        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4261        editor.insert_blocks(
 4262            [BlockProperties {
 4263                style: BlockStyle::Fixed,
 4264                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4265                height: 1,
 4266                render: Arc::new(|_| div().into_any()),
 4267                priority: 0,
 4268            }],
 4269            Some(Autoscroll::fit()),
 4270            cx,
 4271        );
 4272        editor.change_selections(None, window, cx, |s| {
 4273            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4274        });
 4275        editor.move_line_down(&MoveLineDown, window, cx);
 4276    });
 4277}
 4278
 4279#[gpui::test]
 4280async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4281    init_test(cx, |_| {});
 4282
 4283    let mut cx = EditorTestContext::new(cx).await;
 4284    cx.set_state(
 4285        &"
 4286            ˇzero
 4287            one
 4288            two
 4289            three
 4290            four
 4291            five
 4292        "
 4293        .unindent(),
 4294    );
 4295
 4296    // Create a four-line block that replaces three lines of text.
 4297    cx.update_editor(|editor, window, cx| {
 4298        let snapshot = editor.snapshot(window, cx);
 4299        let snapshot = &snapshot.buffer_snapshot;
 4300        let placement = BlockPlacement::Replace(
 4301            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4302        );
 4303        editor.insert_blocks(
 4304            [BlockProperties {
 4305                placement,
 4306                height: 4,
 4307                style: BlockStyle::Sticky,
 4308                render: Arc::new(|_| gpui::div().into_any_element()),
 4309                priority: 0,
 4310            }],
 4311            None,
 4312            cx,
 4313        );
 4314    });
 4315
 4316    // Move down so that the cursor touches the block.
 4317    cx.update_editor(|editor, window, cx| {
 4318        editor.move_down(&Default::default(), window, cx);
 4319    });
 4320    cx.assert_editor_state(
 4321        &"
 4322            zero
 4323            «one
 4324            two
 4325            threeˇ»
 4326            four
 4327            five
 4328        "
 4329        .unindent(),
 4330    );
 4331
 4332    // Move down past the block.
 4333    cx.update_editor(|editor, window, cx| {
 4334        editor.move_down(&Default::default(), window, cx);
 4335    });
 4336    cx.assert_editor_state(
 4337        &"
 4338            zero
 4339            one
 4340            two
 4341            three
 4342            ˇfour
 4343            five
 4344        "
 4345        .unindent(),
 4346    );
 4347}
 4348
 4349#[gpui::test]
 4350fn test_transpose(cx: &mut TestAppContext) {
 4351    init_test(cx, |_| {});
 4352
 4353    _ = cx.add_window(|window, cx| {
 4354        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4355        editor.set_style(EditorStyle::default(), window, cx);
 4356        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4357        editor.transpose(&Default::default(), window, cx);
 4358        assert_eq!(editor.text(cx), "bac");
 4359        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4360
 4361        editor.transpose(&Default::default(), window, cx);
 4362        assert_eq!(editor.text(cx), "bca");
 4363        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4364
 4365        editor.transpose(&Default::default(), window, cx);
 4366        assert_eq!(editor.text(cx), "bac");
 4367        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4368
 4369        editor
 4370    });
 4371
 4372    _ = cx.add_window(|window, cx| {
 4373        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4374        editor.set_style(EditorStyle::default(), window, cx);
 4375        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4376        editor.transpose(&Default::default(), window, cx);
 4377        assert_eq!(editor.text(cx), "acb\nde");
 4378        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4379
 4380        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4381        editor.transpose(&Default::default(), window, cx);
 4382        assert_eq!(editor.text(cx), "acbd\ne");
 4383        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4384
 4385        editor.transpose(&Default::default(), window, cx);
 4386        assert_eq!(editor.text(cx), "acbde\n");
 4387        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4388
 4389        editor.transpose(&Default::default(), window, cx);
 4390        assert_eq!(editor.text(cx), "acbd\ne");
 4391        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4392
 4393        editor
 4394    });
 4395
 4396    _ = cx.add_window(|window, cx| {
 4397        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4398        editor.set_style(EditorStyle::default(), window, cx);
 4399        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4400        editor.transpose(&Default::default(), window, cx);
 4401        assert_eq!(editor.text(cx), "bacd\ne");
 4402        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4403
 4404        editor.transpose(&Default::default(), window, cx);
 4405        assert_eq!(editor.text(cx), "bcade\n");
 4406        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4407
 4408        editor.transpose(&Default::default(), window, cx);
 4409        assert_eq!(editor.text(cx), "bcda\ne");
 4410        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4411
 4412        editor.transpose(&Default::default(), window, cx);
 4413        assert_eq!(editor.text(cx), "bcade\n");
 4414        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4415
 4416        editor.transpose(&Default::default(), window, cx);
 4417        assert_eq!(editor.text(cx), "bcaed\n");
 4418        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4419
 4420        editor
 4421    });
 4422
 4423    _ = cx.add_window(|window, cx| {
 4424        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4425        editor.set_style(EditorStyle::default(), window, cx);
 4426        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4427        editor.transpose(&Default::default(), window, cx);
 4428        assert_eq!(editor.text(cx), "🏀🍐✋");
 4429        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4430
 4431        editor.transpose(&Default::default(), window, cx);
 4432        assert_eq!(editor.text(cx), "🏀✋🍐");
 4433        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4434
 4435        editor.transpose(&Default::default(), window, cx);
 4436        assert_eq!(editor.text(cx), "🏀🍐✋");
 4437        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4438
 4439        editor
 4440    });
 4441}
 4442
 4443#[gpui::test]
 4444async fn test_rewrap(cx: &mut TestAppContext) {
 4445    init_test(cx, |settings| {
 4446        settings.languages.extend([
 4447            (
 4448                "Markdown".into(),
 4449                LanguageSettingsContent {
 4450                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4451                    ..Default::default()
 4452                },
 4453            ),
 4454            (
 4455                "Plain Text".into(),
 4456                LanguageSettingsContent {
 4457                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4458                    ..Default::default()
 4459                },
 4460            ),
 4461        ])
 4462    });
 4463
 4464    let mut cx = EditorTestContext::new(cx).await;
 4465
 4466    let language_with_c_comments = Arc::new(Language::new(
 4467        LanguageConfig {
 4468            line_comments: vec!["// ".into()],
 4469            ..LanguageConfig::default()
 4470        },
 4471        None,
 4472    ));
 4473    let language_with_pound_comments = Arc::new(Language::new(
 4474        LanguageConfig {
 4475            line_comments: vec!["# ".into()],
 4476            ..LanguageConfig::default()
 4477        },
 4478        None,
 4479    ));
 4480    let markdown_language = Arc::new(Language::new(
 4481        LanguageConfig {
 4482            name: "Markdown".into(),
 4483            ..LanguageConfig::default()
 4484        },
 4485        None,
 4486    ));
 4487    let language_with_doc_comments = Arc::new(Language::new(
 4488        LanguageConfig {
 4489            line_comments: vec!["// ".into(), "/// ".into()],
 4490            ..LanguageConfig::default()
 4491        },
 4492        Some(tree_sitter_rust::LANGUAGE.into()),
 4493    ));
 4494
 4495    let plaintext_language = Arc::new(Language::new(
 4496        LanguageConfig {
 4497            name: "Plain Text".into(),
 4498            ..LanguageConfig::default()
 4499        },
 4500        None,
 4501    ));
 4502
 4503    assert_rewrap(
 4504        indoc! {"
 4505            // ˇ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.
 4506        "},
 4507        indoc! {"
 4508            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4509            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4510            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4511            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4512            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4513            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4514            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4515            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4516            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4517            // porttitor id. Aliquam id accumsan eros.
 4518        "},
 4519        language_with_c_comments.clone(),
 4520        &mut cx,
 4521    );
 4522
 4523    // Test that rewrapping works inside of a selection
 4524    assert_rewrap(
 4525        indoc! {"
 4526            «// 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.ˇ»
 4527        "},
 4528        indoc! {"
 4529            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4530            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4531            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4532            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4533            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4534            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4535            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4536            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4537            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4538            // porttitor id. Aliquam id accumsan eros.ˇ»
 4539        "},
 4540        language_with_c_comments.clone(),
 4541        &mut cx,
 4542    );
 4543
 4544    // Test that cursors that expand to the same region are collapsed.
 4545    assert_rewrap(
 4546        indoc! {"
 4547            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4548            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4549            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4550            // ˇ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.
 4551        "},
 4552        indoc! {"
 4553            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4554            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4555            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4556            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4557            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4558            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4559            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4560            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4561            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4562            // porttitor id. Aliquam id accumsan eros.
 4563        "},
 4564        language_with_c_comments.clone(),
 4565        &mut cx,
 4566    );
 4567
 4568    // Test that non-contiguous selections are treated separately.
 4569    assert_rewrap(
 4570        indoc! {"
 4571            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4572            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4573            //
 4574            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4575            // ˇ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.
 4576        "},
 4577        indoc! {"
 4578            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4579            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4580            // auctor, eu lacinia sapien scelerisque.
 4581            //
 4582            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4583            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4584            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4585            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4586            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4587            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4588            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4589        "},
 4590        language_with_c_comments.clone(),
 4591        &mut cx,
 4592    );
 4593
 4594    // Test that different comment prefixes are supported.
 4595    assert_rewrap(
 4596        indoc! {"
 4597            # ˇ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.
 4598        "},
 4599        indoc! {"
 4600            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4601            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4602            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4603            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4604            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4605            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4606            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4607            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4608            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4609            # accumsan eros.
 4610        "},
 4611        language_with_pound_comments.clone(),
 4612        &mut cx,
 4613    );
 4614
 4615    // Test that rewrapping is ignored outside of comments in most languages.
 4616    assert_rewrap(
 4617        indoc! {"
 4618            /// Adds two numbers.
 4619            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4620            fn add(a: u32, b: u32) -> u32 {
 4621                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ˇ
 4622            }
 4623        "},
 4624        indoc! {"
 4625            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4626            /// 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        language_with_doc_comments.clone(),
 4632        &mut cx,
 4633    );
 4634
 4635    // Test that rewrapping works in Markdown and Plain Text languages.
 4636    assert_rewrap(
 4637        indoc! {"
 4638            # Hello
 4639
 4640            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.
 4641        "},
 4642        indoc! {"
 4643            # Hello
 4644
 4645            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4646            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4647            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4648            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4649            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4650            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4651            Integer sit amet scelerisque nisi.
 4652        "},
 4653        markdown_language,
 4654        &mut cx,
 4655    );
 4656
 4657    assert_rewrap(
 4658        indoc! {"
 4659            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.
 4660        "},
 4661        indoc! {"
 4662            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4663            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4664            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4665            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4666            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4667            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4668            Integer sit amet scelerisque nisi.
 4669        "},
 4670        plaintext_language,
 4671        &mut cx,
 4672    );
 4673
 4674    // Test rewrapping unaligned comments in a selection.
 4675    assert_rewrap(
 4676        indoc! {"
 4677            fn foo() {
 4678                if true {
 4679            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4680            // Praesent semper egestas tellus id dignissim.ˇ»
 4681                    do_something();
 4682                } else {
 4683                    //
 4684                }
 4685            }
 4686        "},
 4687        indoc! {"
 4688            fn foo() {
 4689                if true {
 4690            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4691                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4692                    // egestas tellus id dignissim.ˇ»
 4693                    do_something();
 4694                } else {
 4695                    //
 4696                }
 4697            }
 4698        "},
 4699        language_with_doc_comments.clone(),
 4700        &mut cx,
 4701    );
 4702
 4703    assert_rewrap(
 4704        indoc! {"
 4705            fn foo() {
 4706                if true {
 4707            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4708            // Praesent semper egestas tellus id dignissim.»
 4709                    do_something();
 4710                } else {
 4711                    //
 4712                }
 4713
 4714            }
 4715        "},
 4716        indoc! {"
 4717            fn foo() {
 4718                if true {
 4719            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4720                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4721                    // egestas tellus id dignissim.»
 4722                    do_something();
 4723                } else {
 4724                    //
 4725                }
 4726
 4727            }
 4728        "},
 4729        language_with_doc_comments.clone(),
 4730        &mut cx,
 4731    );
 4732
 4733    #[track_caller]
 4734    fn assert_rewrap(
 4735        unwrapped_text: &str,
 4736        wrapped_text: &str,
 4737        language: Arc<Language>,
 4738        cx: &mut EditorTestContext,
 4739    ) {
 4740        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4741        cx.set_state(unwrapped_text);
 4742        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4743        cx.assert_editor_state(wrapped_text);
 4744    }
 4745}
 4746
 4747#[gpui::test]
 4748async fn test_hard_wrap(cx: &mut TestAppContext) {
 4749    init_test(cx, |_| {});
 4750    let mut cx = EditorTestContext::new(cx).await;
 4751
 4752    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4753    cx.update_editor(|editor, _, cx| {
 4754        editor.set_hard_wrap(Some(14), cx);
 4755    });
 4756
 4757    cx.set_state(indoc!(
 4758        "
 4759        one two three ˇ
 4760        "
 4761    ));
 4762    cx.simulate_input("four");
 4763    cx.run_until_parked();
 4764
 4765    cx.assert_editor_state(indoc!(
 4766        "
 4767        one two three
 4768        fourˇ
 4769        "
 4770    ));
 4771
 4772    cx.update_editor(|editor, window, cx| {
 4773        editor.newline(&Default::default(), window, cx);
 4774    });
 4775    cx.run_until_parked();
 4776    cx.assert_editor_state(indoc!(
 4777        "
 4778        one two three
 4779        four
 4780        ˇ
 4781        "
 4782    ));
 4783
 4784    cx.simulate_input("five");
 4785    cx.run_until_parked();
 4786    cx.assert_editor_state(indoc!(
 4787        "
 4788        one two three
 4789        four
 4790        fiveˇ
 4791        "
 4792    ));
 4793
 4794    cx.update_editor(|editor, window, cx| {
 4795        editor.newline(&Default::default(), window, cx);
 4796    });
 4797    cx.run_until_parked();
 4798    cx.simulate_input("# ");
 4799    cx.run_until_parked();
 4800    cx.assert_editor_state(indoc!(
 4801        "
 4802        one two three
 4803        four
 4804        five
 4805        # ˇ
 4806        "
 4807    ));
 4808
 4809    cx.update_editor(|editor, window, cx| {
 4810        editor.newline(&Default::default(), window, cx);
 4811    });
 4812    cx.run_until_parked();
 4813    cx.assert_editor_state(indoc!(
 4814        "
 4815        one two three
 4816        four
 4817        five
 4818        #\x20
 4819 4820        "
 4821    ));
 4822
 4823    cx.simulate_input(" 6");
 4824    cx.run_until_parked();
 4825    cx.assert_editor_state(indoc!(
 4826        "
 4827        one two three
 4828        four
 4829        five
 4830        #
 4831        # 6ˇ
 4832        "
 4833    ));
 4834}
 4835
 4836#[gpui::test]
 4837async fn test_clipboard(cx: &mut TestAppContext) {
 4838    init_test(cx, |_| {});
 4839
 4840    let mut cx = EditorTestContext::new(cx).await;
 4841
 4842    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4843    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4844    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4845
 4846    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4847    cx.set_state("two ˇfour ˇsix ˇ");
 4848    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4849    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4850
 4851    // Paste again but with only two cursors. Since the number of cursors doesn't
 4852    // match the number of slices in the clipboard, the entire clipboard text
 4853    // is pasted at each cursor.
 4854    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4855    cx.update_editor(|e, window, cx| {
 4856        e.handle_input("( ", window, cx);
 4857        e.paste(&Paste, window, cx);
 4858        e.handle_input(") ", window, cx);
 4859    });
 4860    cx.assert_editor_state(
 4861        &([
 4862            "( one✅ ",
 4863            "three ",
 4864            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4865            "three ",
 4866            "five ) ˇ",
 4867        ]
 4868        .join("\n")),
 4869    );
 4870
 4871    // Cut with three selections, one of which is full-line.
 4872    cx.set_state(indoc! {"
 4873        1«2ˇ»3
 4874        4ˇ567
 4875        «8ˇ»9"});
 4876    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4877    cx.assert_editor_state(indoc! {"
 4878        1ˇ3
 4879        ˇ9"});
 4880
 4881    // Paste with three selections, noticing how the copied selection that was full-line
 4882    // gets inserted before the second cursor.
 4883    cx.set_state(indoc! {"
 4884        1ˇ3
 4885 4886        «oˇ»ne"});
 4887    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4888    cx.assert_editor_state(indoc! {"
 4889        12ˇ3
 4890        4567
 4891 4892        8ˇne"});
 4893
 4894    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4895    cx.set_state(indoc! {"
 4896        The quick brown
 4897        fox juˇmps over
 4898        the lazy dog"});
 4899    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4900    assert_eq!(
 4901        cx.read_from_clipboard()
 4902            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4903        Some("fox jumps over\n".to_string())
 4904    );
 4905
 4906    // Paste with three selections, noticing how the copied full-line selection is inserted
 4907    // before the empty selections but replaces the selection that is non-empty.
 4908    cx.set_state(indoc! {"
 4909        Tˇhe quick brown
 4910        «foˇ»x jumps over
 4911        tˇhe lazy dog"});
 4912    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4913    cx.assert_editor_state(indoc! {"
 4914        fox jumps over
 4915        Tˇhe quick brown
 4916        fox jumps over
 4917        ˇx jumps over
 4918        fox jumps over
 4919        tˇhe lazy dog"});
 4920}
 4921
 4922#[gpui::test]
 4923async fn test_copy_trim(cx: &mut TestAppContext) {
 4924    init_test(cx, |_| {});
 4925
 4926    let mut cx = EditorTestContext::new(cx).await;
 4927    cx.set_state(
 4928        r#"            «for selection in selections.iter() {
 4929            let mut start = selection.start;
 4930            let mut end = selection.end;
 4931            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 4932            if is_entire_line {
 4933                start = Point::new(start.row, 0);ˇ»
 4934                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4935            }
 4936        "#,
 4937    );
 4938    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4939    assert_eq!(
 4940        cx.read_from_clipboard()
 4941            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4942        Some(
 4943            "for selection in selections.iter() {
 4944            let mut start = selection.start;
 4945            let mut end = selection.end;
 4946            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 4947            if is_entire_line {
 4948                start = Point::new(start.row, 0);"
 4949                .to_string()
 4950        ),
 4951        "Regular copying preserves all indentation selected",
 4952    );
 4953    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4954    assert_eq!(
 4955        cx.read_from_clipboard()
 4956            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4957        Some(
 4958            "for selection in selections.iter() {
 4959let mut start = selection.start;
 4960let mut end = selection.end;
 4961let is_entire_line = selection.is_empty() || self.selections.line_mode;
 4962if is_entire_line {
 4963    start = Point::new(start.row, 0);"
 4964                .to_string()
 4965        ),
 4966        "Copying with stripping should strip all leading whitespaces"
 4967    );
 4968
 4969    cx.set_state(
 4970        r#"       «     for selection in selections.iter() {
 4971            let mut start = selection.start;
 4972            let mut end = selection.end;
 4973            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 4974            if is_entire_line {
 4975                start = Point::new(start.row, 0);ˇ»
 4976                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4977            }
 4978        "#,
 4979    );
 4980    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4981    assert_eq!(
 4982        cx.read_from_clipboard()
 4983            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4984        Some(
 4985            "     for selection in selections.iter() {
 4986            let mut start = selection.start;
 4987            let mut end = selection.end;
 4988            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 4989            if is_entire_line {
 4990                start = Point::new(start.row, 0);"
 4991                .to_string()
 4992        ),
 4993        "Regular copying preserves all indentation selected",
 4994    );
 4995    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4996    assert_eq!(
 4997        cx.read_from_clipboard()
 4998            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4999        Some(
 5000            "for selection in selections.iter() {
 5001let mut start = selection.start;
 5002let mut end = selection.end;
 5003let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5004if is_entire_line {
 5005    start = Point::new(start.row, 0);"
 5006                .to_string()
 5007        ),
 5008        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5009    );
 5010
 5011    cx.set_state(
 5012        r#"       «ˇ     for selection in selections.iter() {
 5013            let mut start = selection.start;
 5014            let mut end = selection.end;
 5015            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5016            if is_entire_line {
 5017                start = Point::new(start.row, 0);»
 5018                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5019            }
 5020        "#,
 5021    );
 5022    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5023    assert_eq!(
 5024        cx.read_from_clipboard()
 5025            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5026        Some(
 5027            "     for selection in selections.iter() {
 5028            let mut start = selection.start;
 5029            let mut end = selection.end;
 5030            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5031            if is_entire_line {
 5032                start = Point::new(start.row, 0);"
 5033                .to_string()
 5034        ),
 5035        "Regular copying for reverse selection works the same",
 5036    );
 5037    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5038    assert_eq!(
 5039        cx.read_from_clipboard()
 5040            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5041        Some(
 5042            "for selection in selections.iter() {
 5043let mut start = selection.start;
 5044let mut end = selection.end;
 5045let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5046if is_entire_line {
 5047    start = Point::new(start.row, 0);"
 5048                .to_string()
 5049        ),
 5050        "Copying with stripping for reverse selection works the same"
 5051    );
 5052
 5053    cx.set_state(
 5054        r#"            for selection «in selections.iter() {
 5055            let mut start = selection.start;
 5056            let mut end = selection.end;
 5057            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5058            if is_entire_line {
 5059                start = Point::new(start.row, 0);ˇ»
 5060                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5061            }
 5062        "#,
 5063    );
 5064    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5065    assert_eq!(
 5066        cx.read_from_clipboard()
 5067            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5068        Some(
 5069            "in selections.iter() {
 5070            let mut start = selection.start;
 5071            let mut end = selection.end;
 5072            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5073            if is_entire_line {
 5074                start = Point::new(start.row, 0);"
 5075                .to_string()
 5076        ),
 5077        "When selecting past the indent, the copying works as usual",
 5078    );
 5079    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5080    assert_eq!(
 5081        cx.read_from_clipboard()
 5082            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5083        Some(
 5084            "in selections.iter() {
 5085            let mut start = selection.start;
 5086            let mut end = selection.end;
 5087            let is_entire_line = selection.is_empty() || self.selections.line_mode;
 5088            if is_entire_line {
 5089                start = Point::new(start.row, 0);"
 5090                .to_string()
 5091        ),
 5092        "When selecting past the indent, nothing is trimmed"
 5093    );
 5094}
 5095
 5096#[gpui::test]
 5097async fn test_paste_multiline(cx: &mut TestAppContext) {
 5098    init_test(cx, |_| {});
 5099
 5100    let mut cx = EditorTestContext::new(cx).await;
 5101    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5102
 5103    // Cut an indented block, without the leading whitespace.
 5104    cx.set_state(indoc! {"
 5105        const a: B = (
 5106            c(),
 5107            «d(
 5108                e,
 5109                f
 5110            )ˇ»
 5111        );
 5112    "});
 5113    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5114    cx.assert_editor_state(indoc! {"
 5115        const a: B = (
 5116            c(),
 5117            ˇ
 5118        );
 5119    "});
 5120
 5121    // Paste it at the same position.
 5122    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5123    cx.assert_editor_state(indoc! {"
 5124        const a: B = (
 5125            c(),
 5126            d(
 5127                e,
 5128                f
 5129 5130        );
 5131    "});
 5132
 5133    // Paste it at a line with a lower indent level.
 5134    cx.set_state(indoc! {"
 5135        ˇ
 5136        const a: B = (
 5137            c(),
 5138        );
 5139    "});
 5140    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5141    cx.assert_editor_state(indoc! {"
 5142        d(
 5143            e,
 5144            f
 5145 5146        const a: B = (
 5147            c(),
 5148        );
 5149    "});
 5150
 5151    // Cut an indented block, with the leading whitespace.
 5152    cx.set_state(indoc! {"
 5153        const a: B = (
 5154            c(),
 5155        «    d(
 5156                e,
 5157                f
 5158            )
 5159        ˇ»);
 5160    "});
 5161    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5162    cx.assert_editor_state(indoc! {"
 5163        const a: B = (
 5164            c(),
 5165        ˇ);
 5166    "});
 5167
 5168    // Paste it at the same position.
 5169    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5170    cx.assert_editor_state(indoc! {"
 5171        const a: B = (
 5172            c(),
 5173            d(
 5174                e,
 5175                f
 5176            )
 5177        ˇ);
 5178    "});
 5179
 5180    // Paste it at a line with a higher indent level.
 5181    cx.set_state(indoc! {"
 5182        const a: B = (
 5183            c(),
 5184            d(
 5185                e,
 5186 5187            )
 5188        );
 5189    "});
 5190    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5191    cx.assert_editor_state(indoc! {"
 5192        const a: B = (
 5193            c(),
 5194            d(
 5195                e,
 5196                f    d(
 5197                    e,
 5198                    f
 5199                )
 5200        ˇ
 5201            )
 5202        );
 5203    "});
 5204
 5205    // Copy an indented block, starting mid-line
 5206    cx.set_state(indoc! {"
 5207        const a: B = (
 5208            c(),
 5209            somethin«g(
 5210                e,
 5211                f
 5212            )ˇ»
 5213        );
 5214    "});
 5215    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5216
 5217    // Paste it on a line with a lower indent level
 5218    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5219    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5220    cx.assert_editor_state(indoc! {"
 5221        const a: B = (
 5222            c(),
 5223            something(
 5224                e,
 5225                f
 5226            )
 5227        );
 5228        g(
 5229            e,
 5230            f
 5231"});
 5232}
 5233
 5234#[gpui::test]
 5235async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5236    init_test(cx, |_| {});
 5237
 5238    cx.write_to_clipboard(ClipboardItem::new_string(
 5239        "    d(\n        e\n    );\n".into(),
 5240    ));
 5241
 5242    let mut cx = EditorTestContext::new(cx).await;
 5243    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5244
 5245    cx.set_state(indoc! {"
 5246        fn a() {
 5247            b();
 5248            if c() {
 5249                ˇ
 5250            }
 5251        }
 5252    "});
 5253
 5254    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5255    cx.assert_editor_state(indoc! {"
 5256        fn a() {
 5257            b();
 5258            if c() {
 5259                d(
 5260                    e
 5261                );
 5262        ˇ
 5263            }
 5264        }
 5265    "});
 5266
 5267    cx.set_state(indoc! {"
 5268        fn a() {
 5269            b();
 5270            ˇ
 5271        }
 5272    "});
 5273
 5274    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5275    cx.assert_editor_state(indoc! {"
 5276        fn a() {
 5277            b();
 5278            d(
 5279                e
 5280            );
 5281        ˇ
 5282        }
 5283    "});
 5284}
 5285
 5286#[gpui::test]
 5287fn test_select_all(cx: &mut TestAppContext) {
 5288    init_test(cx, |_| {});
 5289
 5290    let editor = cx.add_window(|window, cx| {
 5291        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5292        build_editor(buffer, window, cx)
 5293    });
 5294    _ = editor.update(cx, |editor, window, cx| {
 5295        editor.select_all(&SelectAll, window, cx);
 5296        assert_eq!(
 5297            editor.selections.display_ranges(cx),
 5298            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5299        );
 5300    });
 5301}
 5302
 5303#[gpui::test]
 5304fn test_select_line(cx: &mut TestAppContext) {
 5305    init_test(cx, |_| {});
 5306
 5307    let editor = cx.add_window(|window, cx| {
 5308        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5309        build_editor(buffer, window, cx)
 5310    });
 5311    _ = editor.update(cx, |editor, window, cx| {
 5312        editor.change_selections(None, window, cx, |s| {
 5313            s.select_display_ranges([
 5314                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5315                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5316                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5317                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5318            ])
 5319        });
 5320        editor.select_line(&SelectLine, window, cx);
 5321        assert_eq!(
 5322            editor.selections.display_ranges(cx),
 5323            vec![
 5324                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5325                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5326            ]
 5327        );
 5328    });
 5329
 5330    _ = editor.update(cx, |editor, window, cx| {
 5331        editor.select_line(&SelectLine, window, cx);
 5332        assert_eq!(
 5333            editor.selections.display_ranges(cx),
 5334            vec![
 5335                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5336                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5337            ]
 5338        );
 5339    });
 5340
 5341    _ = editor.update(cx, |editor, window, cx| {
 5342        editor.select_line(&SelectLine, window, cx);
 5343        assert_eq!(
 5344            editor.selections.display_ranges(cx),
 5345            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5346        );
 5347    });
 5348}
 5349
 5350#[gpui::test]
 5351async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5352    init_test(cx, |_| {});
 5353    let mut cx = EditorTestContext::new(cx).await;
 5354
 5355    #[track_caller]
 5356    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5357        cx.set_state(initial_state);
 5358        cx.update_editor(|e, window, cx| {
 5359            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5360        });
 5361        cx.assert_editor_state(expected_state);
 5362    }
 5363
 5364    // Selection starts and ends at the middle of lines, left-to-right
 5365    test(
 5366        &mut cx,
 5367        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5368        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5369    );
 5370    // Same thing, right-to-left
 5371    test(
 5372        &mut cx,
 5373        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5374        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5375    );
 5376
 5377    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5378    test(
 5379        &mut cx,
 5380        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5381        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5382    );
 5383    // Same thing, right-to-left
 5384    test(
 5385        &mut cx,
 5386        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5387        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5388    );
 5389
 5390    // Whole buffer, left-to-right, last line ends with newline
 5391    test(
 5392        &mut cx,
 5393        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5394        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5395    );
 5396    // Same thing, right-to-left
 5397    test(
 5398        &mut cx,
 5399        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5400        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5401    );
 5402
 5403    // Starts at the end of a line, ends at the start of another
 5404    test(
 5405        &mut cx,
 5406        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5407        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5408    );
 5409}
 5410
 5411#[gpui::test]
 5412async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5413    init_test(cx, |_| {});
 5414
 5415    let editor = cx.add_window(|window, cx| {
 5416        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5417        build_editor(buffer, window, cx)
 5418    });
 5419
 5420    // setup
 5421    _ = editor.update(cx, |editor, window, cx| {
 5422        editor.fold_creases(
 5423            vec![
 5424                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5425                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5426                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5427            ],
 5428            true,
 5429            window,
 5430            cx,
 5431        );
 5432        assert_eq!(
 5433            editor.display_text(cx),
 5434            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5435        );
 5436    });
 5437
 5438    _ = editor.update(cx, |editor, window, cx| {
 5439        editor.change_selections(None, window, cx, |s| {
 5440            s.select_display_ranges([
 5441                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5442                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5443                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5444                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5445            ])
 5446        });
 5447        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5448        assert_eq!(
 5449            editor.display_text(cx),
 5450            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5451        );
 5452    });
 5453    EditorTestContext::for_editor(editor, cx)
 5454        .await
 5455        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5456
 5457    _ = editor.update(cx, |editor, window, cx| {
 5458        editor.change_selections(None, window, cx, |s| {
 5459            s.select_display_ranges([
 5460                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5461            ])
 5462        });
 5463        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5464        assert_eq!(
 5465            editor.display_text(cx),
 5466            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5467        );
 5468        assert_eq!(
 5469            editor.selections.display_ranges(cx),
 5470            [
 5471                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5472                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5473                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5474                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5475                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5476                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5477                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5478            ]
 5479        );
 5480    });
 5481    EditorTestContext::for_editor(editor, cx)
 5482        .await
 5483        .assert_editor_state(
 5484            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5485        );
 5486}
 5487
 5488#[gpui::test]
 5489async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5490    init_test(cx, |_| {});
 5491
 5492    let mut cx = EditorTestContext::new(cx).await;
 5493
 5494    cx.set_state(indoc!(
 5495        r#"abc
 5496           defˇghi
 5497
 5498           jk
 5499           nlmo
 5500           "#
 5501    ));
 5502
 5503    cx.update_editor(|editor, window, cx| {
 5504        editor.add_selection_above(&Default::default(), window, cx);
 5505    });
 5506
 5507    cx.assert_editor_state(indoc!(
 5508        r#"abcˇ
 5509           defˇghi
 5510
 5511           jk
 5512           nlmo
 5513           "#
 5514    ));
 5515
 5516    cx.update_editor(|editor, window, cx| {
 5517        editor.add_selection_above(&Default::default(), window, cx);
 5518    });
 5519
 5520    cx.assert_editor_state(indoc!(
 5521        r#"abcˇ
 5522            defˇghi
 5523
 5524            jk
 5525            nlmo
 5526            "#
 5527    ));
 5528
 5529    cx.update_editor(|editor, window, cx| {
 5530        editor.add_selection_below(&Default::default(), window, cx);
 5531    });
 5532
 5533    cx.assert_editor_state(indoc!(
 5534        r#"abc
 5535           defˇghi
 5536
 5537           jk
 5538           nlmo
 5539           "#
 5540    ));
 5541
 5542    cx.update_editor(|editor, window, cx| {
 5543        editor.undo_selection(&Default::default(), window, cx);
 5544    });
 5545
 5546    cx.assert_editor_state(indoc!(
 5547        r#"abcˇ
 5548           defˇghi
 5549
 5550           jk
 5551           nlmo
 5552           "#
 5553    ));
 5554
 5555    cx.update_editor(|editor, window, cx| {
 5556        editor.redo_selection(&Default::default(), window, cx);
 5557    });
 5558
 5559    cx.assert_editor_state(indoc!(
 5560        r#"abc
 5561           defˇghi
 5562
 5563           jk
 5564           nlmo
 5565           "#
 5566    ));
 5567
 5568    cx.update_editor(|editor, window, cx| {
 5569        editor.add_selection_below(&Default::default(), window, cx);
 5570    });
 5571
 5572    cx.assert_editor_state(indoc!(
 5573        r#"abc
 5574           defˇghi
 5575
 5576           jk
 5577           nlmˇo
 5578           "#
 5579    ));
 5580
 5581    cx.update_editor(|editor, window, cx| {
 5582        editor.add_selection_below(&Default::default(), window, cx);
 5583    });
 5584
 5585    cx.assert_editor_state(indoc!(
 5586        r#"abc
 5587           defˇghi
 5588
 5589           jk
 5590           nlmˇo
 5591           "#
 5592    ));
 5593
 5594    // change selections
 5595    cx.set_state(indoc!(
 5596        r#"abc
 5597           def«ˇg»hi
 5598
 5599           jk
 5600           nlmo
 5601           "#
 5602    ));
 5603
 5604    cx.update_editor(|editor, window, cx| {
 5605        editor.add_selection_below(&Default::default(), window, cx);
 5606    });
 5607
 5608    cx.assert_editor_state(indoc!(
 5609        r#"abc
 5610           def«ˇg»hi
 5611
 5612           jk
 5613           nlm«ˇo»
 5614           "#
 5615    ));
 5616
 5617    cx.update_editor(|editor, window, cx| {
 5618        editor.add_selection_below(&Default::default(), window, cx);
 5619    });
 5620
 5621    cx.assert_editor_state(indoc!(
 5622        r#"abc
 5623           def«ˇg»hi
 5624
 5625           jk
 5626           nlm«ˇo»
 5627           "#
 5628    ));
 5629
 5630    cx.update_editor(|editor, window, cx| {
 5631        editor.add_selection_above(&Default::default(), window, cx);
 5632    });
 5633
 5634    cx.assert_editor_state(indoc!(
 5635        r#"abc
 5636           def«ˇg»hi
 5637
 5638           jk
 5639           nlmo
 5640           "#
 5641    ));
 5642
 5643    cx.update_editor(|editor, window, cx| {
 5644        editor.add_selection_above(&Default::default(), window, cx);
 5645    });
 5646
 5647    cx.assert_editor_state(indoc!(
 5648        r#"abc
 5649           def«ˇg»hi
 5650
 5651           jk
 5652           nlmo
 5653           "#
 5654    ));
 5655
 5656    // Change selections again
 5657    cx.set_state(indoc!(
 5658        r#"a«bc
 5659           defgˇ»hi
 5660
 5661           jk
 5662           nlmo
 5663           "#
 5664    ));
 5665
 5666    cx.update_editor(|editor, window, cx| {
 5667        editor.add_selection_below(&Default::default(), window, cx);
 5668    });
 5669
 5670    cx.assert_editor_state(indoc!(
 5671        r#"a«bcˇ»
 5672           d«efgˇ»hi
 5673
 5674           j«kˇ»
 5675           nlmo
 5676           "#
 5677    ));
 5678
 5679    cx.update_editor(|editor, window, cx| {
 5680        editor.add_selection_below(&Default::default(), window, cx);
 5681    });
 5682    cx.assert_editor_state(indoc!(
 5683        r#"a«bcˇ»
 5684           d«efgˇ»hi
 5685
 5686           j«kˇ»
 5687           n«lmoˇ»
 5688           "#
 5689    ));
 5690    cx.update_editor(|editor, window, cx| {
 5691        editor.add_selection_above(&Default::default(), window, cx);
 5692    });
 5693
 5694    cx.assert_editor_state(indoc!(
 5695        r#"a«bcˇ»
 5696           d«efgˇ»hi
 5697
 5698           j«kˇ»
 5699           nlmo
 5700           "#
 5701    ));
 5702
 5703    // Change selections again
 5704    cx.set_state(indoc!(
 5705        r#"abc
 5706           d«ˇefghi
 5707
 5708           jk
 5709           nlm»o
 5710           "#
 5711    ));
 5712
 5713    cx.update_editor(|editor, window, cx| {
 5714        editor.add_selection_above(&Default::default(), window, cx);
 5715    });
 5716
 5717    cx.assert_editor_state(indoc!(
 5718        r#"a«ˇbc»
 5719           d«ˇef»ghi
 5720
 5721           j«ˇk»
 5722           n«ˇlm»o
 5723           "#
 5724    ));
 5725
 5726    cx.update_editor(|editor, window, cx| {
 5727        editor.add_selection_below(&Default::default(), window, cx);
 5728    });
 5729
 5730    cx.assert_editor_state(indoc!(
 5731        r#"abc
 5732           d«ˇef»ghi
 5733
 5734           j«ˇk»
 5735           n«ˇlm»o
 5736           "#
 5737    ));
 5738}
 5739
 5740#[gpui::test]
 5741async fn test_select_next(cx: &mut TestAppContext) {
 5742    init_test(cx, |_| {});
 5743
 5744    let mut cx = EditorTestContext::new(cx).await;
 5745    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5746
 5747    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5748        .unwrap();
 5749    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5750
 5751    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5752        .unwrap();
 5753    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5754
 5755    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5756    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5757
 5758    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5759    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5760
 5761    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5762        .unwrap();
 5763    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5764
 5765    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5766        .unwrap();
 5767    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5768}
 5769
 5770#[gpui::test]
 5771async fn test_select_all_matches(cx: &mut TestAppContext) {
 5772    init_test(cx, |_| {});
 5773
 5774    let mut cx = EditorTestContext::new(cx).await;
 5775
 5776    // Test caret-only selections
 5777    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5778    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5779        .unwrap();
 5780    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5781
 5782    // Test left-to-right selections
 5783    cx.set_state("abc\n«abcˇ»\nabc");
 5784    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5785        .unwrap();
 5786    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5787
 5788    // Test right-to-left selections
 5789    cx.set_state("abc\n«ˇabc»\nabc");
 5790    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5791        .unwrap();
 5792    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5793
 5794    // Test selecting whitespace with caret selection
 5795    cx.set_state("abc\nˇ   abc\nabc");
 5796    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5797        .unwrap();
 5798    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5799
 5800    // Test selecting whitespace with left-to-right selection
 5801    cx.set_state("abc\n«ˇ  »abc\nabc");
 5802    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5803        .unwrap();
 5804    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5805
 5806    // Test no matches with right-to-left selection
 5807    cx.set_state("abc\n«  ˇ»abc\nabc");
 5808    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5809        .unwrap();
 5810    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5811}
 5812
 5813#[gpui::test]
 5814async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5815    init_test(cx, |_| {});
 5816
 5817    let mut cx = EditorTestContext::new(cx).await;
 5818    cx.set_state(
 5819        r#"let foo = 2;
 5820lˇet foo = 2;
 5821let fooˇ = 2;
 5822let foo = 2;
 5823let foo = ˇ2;"#,
 5824    );
 5825
 5826    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5827        .unwrap();
 5828    cx.assert_editor_state(
 5829        r#"let foo = 2;
 5830«letˇ» foo = 2;
 5831let «fooˇ» = 2;
 5832let foo = 2;
 5833let foo = «2ˇ»;"#,
 5834    );
 5835
 5836    // noop for multiple selections with different contents
 5837    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5838        .unwrap();
 5839    cx.assert_editor_state(
 5840        r#"let foo = 2;
 5841«letˇ» foo = 2;
 5842let «fooˇ» = 2;
 5843let foo = 2;
 5844let foo = «2ˇ»;"#,
 5845    );
 5846}
 5847
 5848#[gpui::test]
 5849async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5850    init_test(cx, |_| {});
 5851
 5852    let mut cx =
 5853        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5854
 5855    cx.assert_editor_state(indoc! {"
 5856        ˇbbb
 5857        ccc
 5858
 5859        bbb
 5860        ccc
 5861        "});
 5862    cx.dispatch_action(SelectPrevious::default());
 5863    cx.assert_editor_state(indoc! {"
 5864                «bbbˇ»
 5865                ccc
 5866
 5867                bbb
 5868                ccc
 5869                "});
 5870    cx.dispatch_action(SelectPrevious::default());
 5871    cx.assert_editor_state(indoc! {"
 5872                «bbbˇ»
 5873                ccc
 5874
 5875                «bbbˇ»
 5876                ccc
 5877                "});
 5878}
 5879
 5880#[gpui::test]
 5881async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5882    init_test(cx, |_| {});
 5883
 5884    let mut cx = EditorTestContext::new(cx).await;
 5885    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5886
 5887    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5888        .unwrap();
 5889    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5890
 5891    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5892        .unwrap();
 5893    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5894
 5895    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5896    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5897
 5898    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5899    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5900
 5901    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5902        .unwrap();
 5903    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5904
 5905    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5906        .unwrap();
 5907    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5908
 5909    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5910        .unwrap();
 5911    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5912}
 5913
 5914#[gpui::test]
 5915async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5916    init_test(cx, |_| {});
 5917
 5918    let mut cx = EditorTestContext::new(cx).await;
 5919    cx.set_state("");
 5920
 5921    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5922        .unwrap();
 5923    cx.assert_editor_state("«aˇ»");
 5924    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5925        .unwrap();
 5926    cx.assert_editor_state("«aˇ»");
 5927}
 5928
 5929#[gpui::test]
 5930async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5931    init_test(cx, |_| {});
 5932
 5933    let mut cx = EditorTestContext::new(cx).await;
 5934    cx.set_state(
 5935        r#"let foo = 2;
 5936lˇet foo = 2;
 5937let fooˇ = 2;
 5938let foo = 2;
 5939let foo = ˇ2;"#,
 5940    );
 5941
 5942    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5943        .unwrap();
 5944    cx.assert_editor_state(
 5945        r#"let foo = 2;
 5946«letˇ» foo = 2;
 5947let «fooˇ» = 2;
 5948let foo = 2;
 5949let foo = «2ˇ»;"#,
 5950    );
 5951
 5952    // noop for multiple selections with different contents
 5953    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5954        .unwrap();
 5955    cx.assert_editor_state(
 5956        r#"let foo = 2;
 5957«letˇ» foo = 2;
 5958let «fooˇ» = 2;
 5959let foo = 2;
 5960let foo = «2ˇ»;"#,
 5961    );
 5962}
 5963
 5964#[gpui::test]
 5965async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5966    init_test(cx, |_| {});
 5967
 5968    let mut cx = EditorTestContext::new(cx).await;
 5969    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5970
 5971    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5972        .unwrap();
 5973    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5974
 5975    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5976        .unwrap();
 5977    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5978
 5979    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5980    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5981
 5982    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5983    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5984
 5985    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5986        .unwrap();
 5987    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5988
 5989    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5990        .unwrap();
 5991    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5992}
 5993
 5994#[gpui::test]
 5995async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5996    init_test(cx, |_| {});
 5997
 5998    let language = Arc::new(Language::new(
 5999        LanguageConfig::default(),
 6000        Some(tree_sitter_rust::LANGUAGE.into()),
 6001    ));
 6002
 6003    let text = r#"
 6004        use mod1::mod2::{mod3, mod4};
 6005
 6006        fn fn_1(param1: bool, param2: &str) {
 6007            let var1 = "text";
 6008        }
 6009    "#
 6010    .unindent();
 6011
 6012    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6013    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6014    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6015
 6016    editor
 6017        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6018        .await;
 6019
 6020    editor.update_in(cx, |editor, window, cx| {
 6021        editor.change_selections(None, window, cx, |s| {
 6022            s.select_display_ranges([
 6023                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6024                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6025                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6026            ]);
 6027        });
 6028        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6029    });
 6030    editor.update(cx, |editor, cx| {
 6031        assert_text_with_selections(
 6032            editor,
 6033            indoc! {r#"
 6034                use mod1::mod2::{mod3, «mod4ˇ»};
 6035
 6036                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6037                    let var1 = "«ˇtext»";
 6038                }
 6039            "#},
 6040            cx,
 6041        );
 6042    });
 6043
 6044    editor.update_in(cx, |editor, window, cx| {
 6045        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6046    });
 6047    editor.update(cx, |editor, cx| {
 6048        assert_text_with_selections(
 6049            editor,
 6050            indoc! {r#"
 6051                use mod1::mod2::«{mod3, mod4}ˇ»;
 6052
 6053                «ˇfn fn_1(param1: bool, param2: &str) {
 6054                    let var1 = "text";
 6055 6056            "#},
 6057            cx,
 6058        );
 6059    });
 6060
 6061    editor.update_in(cx, |editor, window, cx| {
 6062        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6063    });
 6064    assert_eq!(
 6065        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6066        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6067    );
 6068
 6069    // Trying to expand the selected syntax node one more time has no effect.
 6070    editor.update_in(cx, |editor, window, cx| {
 6071        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6072    });
 6073    assert_eq!(
 6074        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6075        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6076    );
 6077
 6078    editor.update_in(cx, |editor, window, cx| {
 6079        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6080    });
 6081    editor.update(cx, |editor, cx| {
 6082        assert_text_with_selections(
 6083            editor,
 6084            indoc! {r#"
 6085                use mod1::mod2::«{mod3, mod4}ˇ»;
 6086
 6087                «ˇfn fn_1(param1: bool, param2: &str) {
 6088                    let var1 = "text";
 6089 6090            "#},
 6091            cx,
 6092        );
 6093    });
 6094
 6095    editor.update_in(cx, |editor, window, cx| {
 6096        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6097    });
 6098    editor.update(cx, |editor, cx| {
 6099        assert_text_with_selections(
 6100            editor,
 6101            indoc! {r#"
 6102                use mod1::mod2::{mod3, «mod4ˇ»};
 6103
 6104                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6105                    let var1 = "«ˇtext»";
 6106                }
 6107            "#},
 6108            cx,
 6109        );
 6110    });
 6111
 6112    editor.update_in(cx, |editor, window, cx| {
 6113        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6114    });
 6115    editor.update(cx, |editor, cx| {
 6116        assert_text_with_selections(
 6117            editor,
 6118            indoc! {r#"
 6119                use mod1::mod2::{mod3, mo«ˇ»d4};
 6120
 6121                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6122                    let var1 = "te«ˇ»xt";
 6123                }
 6124            "#},
 6125            cx,
 6126        );
 6127    });
 6128
 6129    // Trying to shrink the selected syntax node one more time has no effect.
 6130    editor.update_in(cx, |editor, window, cx| {
 6131        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6132    });
 6133    editor.update_in(cx, |editor, _, cx| {
 6134        assert_text_with_selections(
 6135            editor,
 6136            indoc! {r#"
 6137                use mod1::mod2::{mod3, mo«ˇ»d4};
 6138
 6139                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6140                    let var1 = "te«ˇ»xt";
 6141                }
 6142            "#},
 6143            cx,
 6144        );
 6145    });
 6146
 6147    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6148    // a fold.
 6149    editor.update_in(cx, |editor, window, cx| {
 6150        editor.fold_creases(
 6151            vec![
 6152                Crease::simple(
 6153                    Point::new(0, 21)..Point::new(0, 24),
 6154                    FoldPlaceholder::test(),
 6155                ),
 6156                Crease::simple(
 6157                    Point::new(3, 20)..Point::new(3, 22),
 6158                    FoldPlaceholder::test(),
 6159                ),
 6160            ],
 6161            true,
 6162            window,
 6163            cx,
 6164        );
 6165        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6166    });
 6167    editor.update(cx, |editor, cx| {
 6168        assert_text_with_selections(
 6169            editor,
 6170            indoc! {r#"
 6171                use mod1::mod2::«{mod3, mod4}ˇ»;
 6172
 6173                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6174                    «ˇlet var1 = "text";»
 6175                }
 6176            "#},
 6177            cx,
 6178        );
 6179    });
 6180}
 6181
 6182#[gpui::test]
 6183async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6184    init_test(cx, |_| {});
 6185
 6186    let base_text = r#"
 6187        impl A {
 6188            // this is an uncommitted comment
 6189
 6190            fn b() {
 6191                c();
 6192            }
 6193
 6194            // this is another uncommitted comment
 6195
 6196            fn d() {
 6197                // e
 6198                // f
 6199            }
 6200        }
 6201
 6202        fn g() {
 6203            // h
 6204        }
 6205    "#
 6206    .unindent();
 6207
 6208    let text = r#"
 6209        ˇimpl A {
 6210
 6211            fn b() {
 6212                c();
 6213            }
 6214
 6215            fn d() {
 6216                // e
 6217                // f
 6218            }
 6219        }
 6220
 6221        fn g() {
 6222            // h
 6223        }
 6224    "#
 6225    .unindent();
 6226
 6227    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6228    cx.set_state(&text);
 6229    cx.set_head_text(&base_text);
 6230    cx.update_editor(|editor, window, cx| {
 6231        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6232    });
 6233
 6234    cx.assert_state_with_diff(
 6235        "
 6236        ˇimpl A {
 6237      -     // this is an uncommitted comment
 6238
 6239            fn b() {
 6240                c();
 6241            }
 6242
 6243      -     // this is another uncommitted comment
 6244      -
 6245            fn d() {
 6246                // e
 6247                // f
 6248            }
 6249        }
 6250
 6251        fn g() {
 6252            // h
 6253        }
 6254    "
 6255        .unindent(),
 6256    );
 6257
 6258    let expected_display_text = "
 6259        impl A {
 6260            // this is an uncommitted comment
 6261
 6262            fn b() {
 6263 6264            }
 6265
 6266            // this is another uncommitted comment
 6267
 6268            fn d() {
 6269 6270            }
 6271        }
 6272
 6273        fn g() {
 6274 6275        }
 6276        "
 6277    .unindent();
 6278
 6279    cx.update_editor(|editor, window, cx| {
 6280        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6281        assert_eq!(editor.display_text(cx), expected_display_text);
 6282    });
 6283}
 6284
 6285#[gpui::test]
 6286async fn test_autoindent(cx: &mut TestAppContext) {
 6287    init_test(cx, |_| {});
 6288
 6289    let language = Arc::new(
 6290        Language::new(
 6291            LanguageConfig {
 6292                brackets: BracketPairConfig {
 6293                    pairs: vec![
 6294                        BracketPair {
 6295                            start: "{".to_string(),
 6296                            end: "}".to_string(),
 6297                            close: false,
 6298                            surround: false,
 6299                            newline: true,
 6300                        },
 6301                        BracketPair {
 6302                            start: "(".to_string(),
 6303                            end: ")".to_string(),
 6304                            close: false,
 6305                            surround: false,
 6306                            newline: true,
 6307                        },
 6308                    ],
 6309                    ..Default::default()
 6310                },
 6311                ..Default::default()
 6312            },
 6313            Some(tree_sitter_rust::LANGUAGE.into()),
 6314        )
 6315        .with_indents_query(
 6316            r#"
 6317                (_ "(" ")" @end) @indent
 6318                (_ "{" "}" @end) @indent
 6319            "#,
 6320        )
 6321        .unwrap(),
 6322    );
 6323
 6324    let text = "fn a() {}";
 6325
 6326    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6327    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6328    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6329    editor
 6330        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6331        .await;
 6332
 6333    editor.update_in(cx, |editor, window, cx| {
 6334        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6335        editor.newline(&Newline, window, cx);
 6336        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6337        assert_eq!(
 6338            editor.selections.ranges(cx),
 6339            &[
 6340                Point::new(1, 4)..Point::new(1, 4),
 6341                Point::new(3, 4)..Point::new(3, 4),
 6342                Point::new(5, 0)..Point::new(5, 0)
 6343            ]
 6344        );
 6345    });
 6346}
 6347
 6348#[gpui::test]
 6349async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6350    init_test(cx, |_| {});
 6351
 6352    {
 6353        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6354        cx.set_state(indoc! {"
 6355            impl A {
 6356
 6357                fn b() {}
 6358
 6359            «fn c() {
 6360
 6361            }ˇ»
 6362            }
 6363        "});
 6364
 6365        cx.update_editor(|editor, window, cx| {
 6366            editor.autoindent(&Default::default(), window, cx);
 6367        });
 6368
 6369        cx.assert_editor_state(indoc! {"
 6370            impl A {
 6371
 6372                fn b() {}
 6373
 6374                «fn c() {
 6375
 6376                }ˇ»
 6377            }
 6378        "});
 6379    }
 6380
 6381    {
 6382        let mut cx = EditorTestContext::new_multibuffer(
 6383            cx,
 6384            [indoc! { "
 6385                impl A {
 6386                «
 6387                // a
 6388                fn b(){}
 6389                »
 6390                «
 6391                    }
 6392                    fn c(){}
 6393                »
 6394            "}],
 6395        );
 6396
 6397        let buffer = cx.update_editor(|editor, _, cx| {
 6398            let buffer = editor.buffer().update(cx, |buffer, _| {
 6399                buffer.all_buffers().iter().next().unwrap().clone()
 6400            });
 6401            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6402            buffer
 6403        });
 6404
 6405        cx.run_until_parked();
 6406        cx.update_editor(|editor, window, cx| {
 6407            editor.select_all(&Default::default(), window, cx);
 6408            editor.autoindent(&Default::default(), window, cx)
 6409        });
 6410        cx.run_until_parked();
 6411
 6412        cx.update(|_, cx| {
 6413            pretty_assertions::assert_eq!(
 6414                buffer.read(cx).text(),
 6415                indoc! { "
 6416                    impl A {
 6417
 6418                        // a
 6419                        fn b(){}
 6420
 6421
 6422                    }
 6423                    fn c(){}
 6424
 6425                " }
 6426            )
 6427        });
 6428    }
 6429}
 6430
 6431#[gpui::test]
 6432async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6433    init_test(cx, |_| {});
 6434
 6435    let mut cx = EditorTestContext::new(cx).await;
 6436
 6437    let language = Arc::new(Language::new(
 6438        LanguageConfig {
 6439            brackets: BracketPairConfig {
 6440                pairs: vec![
 6441                    BracketPair {
 6442                        start: "{".to_string(),
 6443                        end: "}".to_string(),
 6444                        close: true,
 6445                        surround: true,
 6446                        newline: true,
 6447                    },
 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: false,
 6466                        surround: false,
 6467                        newline: true,
 6468                    },
 6469                    BracketPair {
 6470                        start: "\"".to_string(),
 6471                        end: "\"".to_string(),
 6472                        close: true,
 6473                        surround: true,
 6474                        newline: false,
 6475                    },
 6476                    BracketPair {
 6477                        start: "<".to_string(),
 6478                        end: ">".to_string(),
 6479                        close: false,
 6480                        surround: true,
 6481                        newline: true,
 6482                    },
 6483                ],
 6484                ..Default::default()
 6485            },
 6486            autoclose_before: "})]".to_string(),
 6487            ..Default::default()
 6488        },
 6489        Some(tree_sitter_rust::LANGUAGE.into()),
 6490    ));
 6491
 6492    cx.language_registry().add(language.clone());
 6493    cx.update_buffer(|buffer, cx| {
 6494        buffer.set_language(Some(language), cx);
 6495    });
 6496
 6497    cx.set_state(
 6498        &r#"
 6499            🏀ˇ
 6500            εˇ
 6501            ❤️ˇ
 6502        "#
 6503        .unindent(),
 6504    );
 6505
 6506    // autoclose multiple nested brackets at multiple cursors
 6507    cx.update_editor(|editor, window, cx| {
 6508        editor.handle_input("{", window, cx);
 6509        editor.handle_input("{", window, cx);
 6510        editor.handle_input("{", window, cx);
 6511    });
 6512    cx.assert_editor_state(
 6513        &"
 6514            🏀{{{ˇ}}}
 6515            ε{{{ˇ}}}
 6516            ❤️{{{ˇ}}}
 6517        "
 6518        .unindent(),
 6519    );
 6520
 6521    // insert a different closing bracket
 6522    cx.update_editor(|editor, window, cx| {
 6523        editor.handle_input(")", window, cx);
 6524    });
 6525    cx.assert_editor_state(
 6526        &"
 6527            🏀{{{)ˇ}}}
 6528            ε{{{)ˇ}}}
 6529            ❤️{{{)ˇ}}}
 6530        "
 6531        .unindent(),
 6532    );
 6533
 6534    // skip over the auto-closed brackets when typing a closing bracket
 6535    cx.update_editor(|editor, window, cx| {
 6536        editor.move_right(&MoveRight, window, cx);
 6537        editor.handle_input("}", window, cx);
 6538        editor.handle_input("}", window, cx);
 6539        editor.handle_input("}", window, cx);
 6540    });
 6541    cx.assert_editor_state(
 6542        &"
 6543            🏀{{{)}}}}ˇ
 6544            ε{{{)}}}}ˇ
 6545            ❤️{{{)}}}}ˇ
 6546        "
 6547        .unindent(),
 6548    );
 6549
 6550    // autoclose multi-character pairs
 6551    cx.set_state(
 6552        &"
 6553            ˇ
 6554            ˇ
 6555        "
 6556        .unindent(),
 6557    );
 6558    cx.update_editor(|editor, window, cx| {
 6559        editor.handle_input("/", window, cx);
 6560        editor.handle_input("*", window, cx);
 6561    });
 6562    cx.assert_editor_state(
 6563        &"
 6564            /*ˇ */
 6565            /*ˇ */
 6566        "
 6567        .unindent(),
 6568    );
 6569
 6570    // one cursor autocloses a multi-character pair, one cursor
 6571    // does not autoclose.
 6572    cx.set_state(
 6573        &"
 6574 6575            ˇ
 6576        "
 6577        .unindent(),
 6578    );
 6579    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6580    cx.assert_editor_state(
 6581        &"
 6582            /*ˇ */
 6583 6584        "
 6585        .unindent(),
 6586    );
 6587
 6588    // Don't autoclose if the next character isn't whitespace and isn't
 6589    // listed in the language's "autoclose_before" section.
 6590    cx.set_state("ˇa b");
 6591    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6592    cx.assert_editor_state("{ˇa b");
 6593
 6594    // Don't autoclose if `close` is false for the bracket pair
 6595    cx.set_state("ˇ");
 6596    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6597    cx.assert_editor_state("");
 6598
 6599    // Surround with brackets if text is selected
 6600    cx.set_state("«aˇ» b");
 6601    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6602    cx.assert_editor_state("{«aˇ»} b");
 6603
 6604    // Autoclose when not immediately after a word character
 6605    cx.set_state("a ˇ");
 6606    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6607    cx.assert_editor_state("a \"ˇ\"");
 6608
 6609    // Autoclose pair where the start and end characters are the same
 6610    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6611    cx.assert_editor_state("a \"\"ˇ");
 6612
 6613    // Don't autoclose when immediately after a word character
 6614    cx.set_state("");
 6615    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6616    cx.assert_editor_state("a\"ˇ");
 6617
 6618    // Do autoclose when after a non-word character
 6619    cx.set_state("");
 6620    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6621    cx.assert_editor_state("{\"ˇ\"");
 6622
 6623    // Non identical pairs autoclose regardless of preceding character
 6624    cx.set_state("");
 6625    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6626    cx.assert_editor_state("a{ˇ}");
 6627
 6628    // Don't autoclose pair if autoclose is disabled
 6629    cx.set_state("ˇ");
 6630    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6631    cx.assert_editor_state("");
 6632
 6633    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6634    cx.set_state("«aˇ» b");
 6635    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6636    cx.assert_editor_state("<«aˇ»> b");
 6637}
 6638
 6639#[gpui::test]
 6640async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6641    init_test(cx, |settings| {
 6642        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6643    });
 6644
 6645    let mut cx = EditorTestContext::new(cx).await;
 6646
 6647    let language = Arc::new(Language::new(
 6648        LanguageConfig {
 6649            brackets: BracketPairConfig {
 6650                pairs: vec![
 6651                    BracketPair {
 6652                        start: "{".to_string(),
 6653                        end: "}".to_string(),
 6654                        close: true,
 6655                        surround: true,
 6656                        newline: true,
 6657                    },
 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: false,
 6669                        surround: false,
 6670                        newline: true,
 6671                    },
 6672                ],
 6673                ..Default::default()
 6674            },
 6675            autoclose_before: "})]".to_string(),
 6676            ..Default::default()
 6677        },
 6678        Some(tree_sitter_rust::LANGUAGE.into()),
 6679    ));
 6680
 6681    cx.language_registry().add(language.clone());
 6682    cx.update_buffer(|buffer, cx| {
 6683        buffer.set_language(Some(language), cx);
 6684    });
 6685
 6686    cx.set_state(
 6687        &"
 6688            ˇ
 6689            ˇ
 6690            ˇ
 6691        "
 6692        .unindent(),
 6693    );
 6694
 6695    // ensure only matching closing brackets are skipped over
 6696    cx.update_editor(|editor, window, cx| {
 6697        editor.handle_input("}", window, cx);
 6698        editor.move_left(&MoveLeft, window, cx);
 6699        editor.handle_input(")", window, cx);
 6700        editor.move_left(&MoveLeft, window, cx);
 6701    });
 6702    cx.assert_editor_state(
 6703        &"
 6704            ˇ)}
 6705            ˇ)}
 6706            ˇ)}
 6707        "
 6708        .unindent(),
 6709    );
 6710
 6711    // skip-over closing brackets at multiple cursors
 6712    cx.update_editor(|editor, window, cx| {
 6713        editor.handle_input(")", window, cx);
 6714        editor.handle_input("}", window, cx);
 6715    });
 6716    cx.assert_editor_state(
 6717        &"
 6718            )}ˇ
 6719            )}ˇ
 6720            )}ˇ
 6721        "
 6722        .unindent(),
 6723    );
 6724
 6725    // ignore non-close brackets
 6726    cx.update_editor(|editor, window, cx| {
 6727        editor.handle_input("]", window, cx);
 6728        editor.move_left(&MoveLeft, window, cx);
 6729        editor.handle_input("]", window, cx);
 6730    });
 6731    cx.assert_editor_state(
 6732        &"
 6733            )}]ˇ]
 6734            )}]ˇ]
 6735            )}]ˇ]
 6736        "
 6737        .unindent(),
 6738    );
 6739}
 6740
 6741#[gpui::test]
 6742async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6743    init_test(cx, |_| {});
 6744
 6745    let mut cx = EditorTestContext::new(cx).await;
 6746
 6747    let html_language = Arc::new(
 6748        Language::new(
 6749            LanguageConfig {
 6750                name: "HTML".into(),
 6751                brackets: BracketPairConfig {
 6752                    pairs: vec![
 6753                        BracketPair {
 6754                            start: "<".into(),
 6755                            end: ">".into(),
 6756                            close: true,
 6757                            ..Default::default()
 6758                        },
 6759                        BracketPair {
 6760                            start: "{".into(),
 6761                            end: "}".into(),
 6762                            close: true,
 6763                            ..Default::default()
 6764                        },
 6765                        BracketPair {
 6766                            start: "(".into(),
 6767                            end: ")".into(),
 6768                            close: true,
 6769                            ..Default::default()
 6770                        },
 6771                    ],
 6772                    ..Default::default()
 6773                },
 6774                autoclose_before: "})]>".into(),
 6775                ..Default::default()
 6776            },
 6777            Some(tree_sitter_html::LANGUAGE.into()),
 6778        )
 6779        .with_injection_query(
 6780            r#"
 6781            (script_element
 6782                (raw_text) @injection.content
 6783                (#set! injection.language "javascript"))
 6784            "#,
 6785        )
 6786        .unwrap(),
 6787    );
 6788
 6789    let javascript_language = Arc::new(Language::new(
 6790        LanguageConfig {
 6791            name: "JavaScript".into(),
 6792            brackets: BracketPairConfig {
 6793                pairs: vec![
 6794                    BracketPair {
 6795                        start: "/*".into(),
 6796                        end: " */".into(),
 6797                        close: true,
 6798                        ..Default::default()
 6799                    },
 6800                    BracketPair {
 6801                        start: "{".into(),
 6802                        end: "}".into(),
 6803                        close: true,
 6804                        ..Default::default()
 6805                    },
 6806                    BracketPair {
 6807                        start: "(".into(),
 6808                        end: ")".into(),
 6809                        close: true,
 6810                        ..Default::default()
 6811                    },
 6812                ],
 6813                ..Default::default()
 6814            },
 6815            autoclose_before: "})]>".into(),
 6816            ..Default::default()
 6817        },
 6818        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6819    ));
 6820
 6821    cx.language_registry().add(html_language.clone());
 6822    cx.language_registry().add(javascript_language.clone());
 6823
 6824    cx.update_buffer(|buffer, cx| {
 6825        buffer.set_language(Some(html_language), cx);
 6826    });
 6827
 6828    cx.set_state(
 6829        &r#"
 6830            <body>ˇ
 6831                <script>
 6832                    var x = 1;ˇ
 6833                </script>
 6834            </body>ˇ
 6835        "#
 6836        .unindent(),
 6837    );
 6838
 6839    // Precondition: different languages are active at different locations.
 6840    cx.update_editor(|editor, window, cx| {
 6841        let snapshot = editor.snapshot(window, cx);
 6842        let cursors = editor.selections.ranges::<usize>(cx);
 6843        let languages = cursors
 6844            .iter()
 6845            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6846            .collect::<Vec<_>>();
 6847        assert_eq!(
 6848            languages,
 6849            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6850        );
 6851    });
 6852
 6853    // Angle brackets autoclose in HTML, but not JavaScript.
 6854    cx.update_editor(|editor, window, cx| {
 6855        editor.handle_input("<", window, cx);
 6856        editor.handle_input("a", window, cx);
 6857    });
 6858    cx.assert_editor_state(
 6859        &r#"
 6860            <body><aˇ>
 6861                <script>
 6862                    var x = 1;<aˇ
 6863                </script>
 6864            </body><aˇ>
 6865        "#
 6866        .unindent(),
 6867    );
 6868
 6869    // Curly braces and parens autoclose in both HTML and JavaScript.
 6870    cx.update_editor(|editor, window, cx| {
 6871        editor.handle_input(" b=", window, cx);
 6872        editor.handle_input("{", window, cx);
 6873        editor.handle_input("c", window, cx);
 6874        editor.handle_input("(", window, cx);
 6875    });
 6876    cx.assert_editor_state(
 6877        &r#"
 6878            <body><a b={c(ˇ)}>
 6879                <script>
 6880                    var x = 1;<a b={c(ˇ)}
 6881                </script>
 6882            </body><a b={c(ˇ)}>
 6883        "#
 6884        .unindent(),
 6885    );
 6886
 6887    // Brackets that were already autoclosed are skipped.
 6888    cx.update_editor(|editor, window, cx| {
 6889        editor.handle_input(")", window, cx);
 6890        editor.handle_input("d", window, cx);
 6891        editor.handle_input("}", window, cx);
 6892    });
 6893    cx.assert_editor_state(
 6894        &r#"
 6895            <body><a b={c()d}ˇ>
 6896                <script>
 6897                    var x = 1;<a b={c()d}ˇ
 6898                </script>
 6899            </body><a b={c()d}ˇ>
 6900        "#
 6901        .unindent(),
 6902    );
 6903    cx.update_editor(|editor, window, cx| {
 6904        editor.handle_input(">", window, cx);
 6905    });
 6906    cx.assert_editor_state(
 6907        &r#"
 6908            <body><a b={c()d}>ˇ
 6909                <script>
 6910                    var x = 1;<a b={c()d}>ˇ
 6911                </script>
 6912            </body><a b={c()d}>ˇ
 6913        "#
 6914        .unindent(),
 6915    );
 6916
 6917    // Reset
 6918    cx.set_state(
 6919        &r#"
 6920            <body>ˇ
 6921                <script>
 6922                    var x = 1;ˇ
 6923                </script>
 6924            </body>ˇ
 6925        "#
 6926        .unindent(),
 6927    );
 6928
 6929    cx.update_editor(|editor, window, cx| {
 6930        editor.handle_input("<", window, cx);
 6931    });
 6932    cx.assert_editor_state(
 6933        &r#"
 6934            <body><ˇ>
 6935                <script>
 6936                    var x = 1;<ˇ
 6937                </script>
 6938            </body><ˇ>
 6939        "#
 6940        .unindent(),
 6941    );
 6942
 6943    // When backspacing, the closing angle brackets are removed.
 6944    cx.update_editor(|editor, window, cx| {
 6945        editor.backspace(&Backspace, window, cx);
 6946    });
 6947    cx.assert_editor_state(
 6948        &r#"
 6949            <body>ˇ
 6950                <script>
 6951                    var x = 1;ˇ
 6952                </script>
 6953            </body>ˇ
 6954        "#
 6955        .unindent(),
 6956    );
 6957
 6958    // Block comments autoclose in JavaScript, but not HTML.
 6959    cx.update_editor(|editor, window, cx| {
 6960        editor.handle_input("/", window, cx);
 6961        editor.handle_input("*", window, cx);
 6962    });
 6963    cx.assert_editor_state(
 6964        &r#"
 6965            <body>/*ˇ
 6966                <script>
 6967                    var x = 1;/*ˇ */
 6968                </script>
 6969            </body>/*ˇ
 6970        "#
 6971        .unindent(),
 6972    );
 6973}
 6974
 6975#[gpui::test]
 6976async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6977    init_test(cx, |_| {});
 6978
 6979    let mut cx = EditorTestContext::new(cx).await;
 6980
 6981    let rust_language = Arc::new(
 6982        Language::new(
 6983            LanguageConfig {
 6984                name: "Rust".into(),
 6985                brackets: serde_json::from_value(json!([
 6986                    { "start": "{", "end": "}", "close": true, "newline": true },
 6987                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6988                ]))
 6989                .unwrap(),
 6990                autoclose_before: "})]>".into(),
 6991                ..Default::default()
 6992            },
 6993            Some(tree_sitter_rust::LANGUAGE.into()),
 6994        )
 6995        .with_override_query("(string_literal) @string")
 6996        .unwrap(),
 6997    );
 6998
 6999    cx.language_registry().add(rust_language.clone());
 7000    cx.update_buffer(|buffer, cx| {
 7001        buffer.set_language(Some(rust_language), cx);
 7002    });
 7003
 7004    cx.set_state(
 7005        &r#"
 7006            let x = ˇ
 7007        "#
 7008        .unindent(),
 7009    );
 7010
 7011    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7012    cx.update_editor(|editor, window, cx| {
 7013        editor.handle_input("\"", window, cx);
 7014    });
 7015    cx.assert_editor_state(
 7016        &r#"
 7017            let x = "ˇ"
 7018        "#
 7019        .unindent(),
 7020    );
 7021
 7022    // Inserting another quotation mark. The cursor moves across the existing
 7023    // automatically-inserted quotation mark.
 7024    cx.update_editor(|editor, window, cx| {
 7025        editor.handle_input("\"", window, cx);
 7026    });
 7027    cx.assert_editor_state(
 7028        &r#"
 7029            let x = ""ˇ
 7030        "#
 7031        .unindent(),
 7032    );
 7033
 7034    // Reset
 7035    cx.set_state(
 7036        &r#"
 7037            let x = ˇ
 7038        "#
 7039        .unindent(),
 7040    );
 7041
 7042    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7043    cx.update_editor(|editor, window, cx| {
 7044        editor.handle_input("\"", window, cx);
 7045        editor.handle_input(" ", window, cx);
 7046        editor.move_left(&Default::default(), window, cx);
 7047        editor.handle_input("\\", window, cx);
 7048        editor.handle_input("\"", window, cx);
 7049    });
 7050    cx.assert_editor_state(
 7051        &r#"
 7052            let x = "\"ˇ "
 7053        "#
 7054        .unindent(),
 7055    );
 7056
 7057    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7058    // mark. Nothing is inserted.
 7059    cx.update_editor(|editor, window, cx| {
 7060        editor.move_right(&Default::default(), window, cx);
 7061        editor.handle_input("\"", window, cx);
 7062    });
 7063    cx.assert_editor_state(
 7064        &r#"
 7065            let x = "\" "ˇ
 7066        "#
 7067        .unindent(),
 7068    );
 7069}
 7070
 7071#[gpui::test]
 7072async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7073    init_test(cx, |_| {});
 7074
 7075    let language = Arc::new(Language::new(
 7076        LanguageConfig {
 7077            brackets: BracketPairConfig {
 7078                pairs: vec![
 7079                    BracketPair {
 7080                        start: "{".to_string(),
 7081                        end: "}".to_string(),
 7082                        close: true,
 7083                        surround: true,
 7084                        newline: true,
 7085                    },
 7086                    BracketPair {
 7087                        start: "/* ".to_string(),
 7088                        end: "*/".to_string(),
 7089                        close: true,
 7090                        surround: true,
 7091                        ..Default::default()
 7092                    },
 7093                ],
 7094                ..Default::default()
 7095            },
 7096            ..Default::default()
 7097        },
 7098        Some(tree_sitter_rust::LANGUAGE.into()),
 7099    ));
 7100
 7101    let text = r#"
 7102        a
 7103        b
 7104        c
 7105    "#
 7106    .unindent();
 7107
 7108    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7109    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7110    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7111    editor
 7112        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7113        .await;
 7114
 7115    editor.update_in(cx, |editor, window, cx| {
 7116        editor.change_selections(None, window, cx, |s| {
 7117            s.select_display_ranges([
 7118                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7119                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7120                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7121            ])
 7122        });
 7123
 7124        editor.handle_input("{", window, cx);
 7125        editor.handle_input("{", window, cx);
 7126        editor.handle_input("{", window, cx);
 7127        assert_eq!(
 7128            editor.text(cx),
 7129            "
 7130                {{{a}}}
 7131                {{{b}}}
 7132                {{{c}}}
 7133            "
 7134            .unindent()
 7135        );
 7136        assert_eq!(
 7137            editor.selections.display_ranges(cx),
 7138            [
 7139                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7140                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7141                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7142            ]
 7143        );
 7144
 7145        editor.undo(&Undo, window, cx);
 7146        editor.undo(&Undo, window, cx);
 7147        editor.undo(&Undo, window, cx);
 7148        assert_eq!(
 7149            editor.text(cx),
 7150            "
 7151                a
 7152                b
 7153                c
 7154            "
 7155            .unindent()
 7156        );
 7157        assert_eq!(
 7158            editor.selections.display_ranges(cx),
 7159            [
 7160                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7161                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7162                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7163            ]
 7164        );
 7165
 7166        // Ensure inserting the first character of a multi-byte bracket pair
 7167        // doesn't surround the selections with the bracket.
 7168        editor.handle_input("/", window, cx);
 7169        assert_eq!(
 7170            editor.text(cx),
 7171            "
 7172                /
 7173                /
 7174                /
 7175            "
 7176            .unindent()
 7177        );
 7178        assert_eq!(
 7179            editor.selections.display_ranges(cx),
 7180            [
 7181                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7182                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7183                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7184            ]
 7185        );
 7186
 7187        editor.undo(&Undo, window, cx);
 7188        assert_eq!(
 7189            editor.text(cx),
 7190            "
 7191                a
 7192                b
 7193                c
 7194            "
 7195            .unindent()
 7196        );
 7197        assert_eq!(
 7198            editor.selections.display_ranges(cx),
 7199            [
 7200                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7201                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7202                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7203            ]
 7204        );
 7205
 7206        // Ensure inserting the last character of a multi-byte bracket pair
 7207        // doesn't surround the selections with the bracket.
 7208        editor.handle_input("*", window, cx);
 7209        assert_eq!(
 7210            editor.text(cx),
 7211            "
 7212                *
 7213                *
 7214                *
 7215            "
 7216            .unindent()
 7217        );
 7218        assert_eq!(
 7219            editor.selections.display_ranges(cx),
 7220            [
 7221                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7222                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7223                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7224            ]
 7225        );
 7226    });
 7227}
 7228
 7229#[gpui::test]
 7230async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7231    init_test(cx, |_| {});
 7232
 7233    let language = Arc::new(Language::new(
 7234        LanguageConfig {
 7235            brackets: BracketPairConfig {
 7236                pairs: vec![BracketPair {
 7237                    start: "{".to_string(),
 7238                    end: "}".to_string(),
 7239                    close: true,
 7240                    surround: true,
 7241                    newline: true,
 7242                }],
 7243                ..Default::default()
 7244            },
 7245            autoclose_before: "}".to_string(),
 7246            ..Default::default()
 7247        },
 7248        Some(tree_sitter_rust::LANGUAGE.into()),
 7249    ));
 7250
 7251    let text = r#"
 7252        a
 7253        b
 7254        c
 7255    "#
 7256    .unindent();
 7257
 7258    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7259    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7260    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7261    editor
 7262        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7263        .await;
 7264
 7265    editor.update_in(cx, |editor, window, cx| {
 7266        editor.change_selections(None, window, cx, |s| {
 7267            s.select_ranges([
 7268                Point::new(0, 1)..Point::new(0, 1),
 7269                Point::new(1, 1)..Point::new(1, 1),
 7270                Point::new(2, 1)..Point::new(2, 1),
 7271            ])
 7272        });
 7273
 7274        editor.handle_input("{", window, cx);
 7275        editor.handle_input("{", window, cx);
 7276        editor.handle_input("_", window, cx);
 7277        assert_eq!(
 7278            editor.text(cx),
 7279            "
 7280                a{{_}}
 7281                b{{_}}
 7282                c{{_}}
 7283            "
 7284            .unindent()
 7285        );
 7286        assert_eq!(
 7287            editor.selections.ranges::<Point>(cx),
 7288            [
 7289                Point::new(0, 4)..Point::new(0, 4),
 7290                Point::new(1, 4)..Point::new(1, 4),
 7291                Point::new(2, 4)..Point::new(2, 4)
 7292            ]
 7293        );
 7294
 7295        editor.backspace(&Default::default(), window, cx);
 7296        editor.backspace(&Default::default(), window, cx);
 7297        assert_eq!(
 7298            editor.text(cx),
 7299            "
 7300                a{}
 7301                b{}
 7302                c{}
 7303            "
 7304            .unindent()
 7305        );
 7306        assert_eq!(
 7307            editor.selections.ranges::<Point>(cx),
 7308            [
 7309                Point::new(0, 2)..Point::new(0, 2),
 7310                Point::new(1, 2)..Point::new(1, 2),
 7311                Point::new(2, 2)..Point::new(2, 2)
 7312            ]
 7313        );
 7314
 7315        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7316        assert_eq!(
 7317            editor.text(cx),
 7318            "
 7319                a
 7320                b
 7321                c
 7322            "
 7323            .unindent()
 7324        );
 7325        assert_eq!(
 7326            editor.selections.ranges::<Point>(cx),
 7327            [
 7328                Point::new(0, 1)..Point::new(0, 1),
 7329                Point::new(1, 1)..Point::new(1, 1),
 7330                Point::new(2, 1)..Point::new(2, 1)
 7331            ]
 7332        );
 7333    });
 7334}
 7335
 7336#[gpui::test]
 7337async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7338    init_test(cx, |settings| {
 7339        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7340    });
 7341
 7342    let mut cx = EditorTestContext::new(cx).await;
 7343
 7344    let language = Arc::new(Language::new(
 7345        LanguageConfig {
 7346            brackets: BracketPairConfig {
 7347                pairs: vec![
 7348                    BracketPair {
 7349                        start: "{".to_string(),
 7350                        end: "}".to_string(),
 7351                        close: true,
 7352                        surround: true,
 7353                        newline: true,
 7354                    },
 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: false,
 7366                        surround: true,
 7367                        newline: true,
 7368                    },
 7369                ],
 7370                ..Default::default()
 7371            },
 7372            autoclose_before: "})]".to_string(),
 7373            ..Default::default()
 7374        },
 7375        Some(tree_sitter_rust::LANGUAGE.into()),
 7376    ));
 7377
 7378    cx.language_registry().add(language.clone());
 7379    cx.update_buffer(|buffer, cx| {
 7380        buffer.set_language(Some(language), cx);
 7381    });
 7382
 7383    cx.set_state(
 7384        &"
 7385            {(ˇ)}
 7386            [[ˇ]]
 7387            {(ˇ)}
 7388        "
 7389        .unindent(),
 7390    );
 7391
 7392    cx.update_editor(|editor, window, cx| {
 7393        editor.backspace(&Default::default(), window, cx);
 7394        editor.backspace(&Default::default(), window, cx);
 7395    });
 7396
 7397    cx.assert_editor_state(
 7398        &"
 7399            ˇ
 7400            ˇ]]
 7401            ˇ
 7402        "
 7403        .unindent(),
 7404    );
 7405
 7406    cx.update_editor(|editor, window, cx| {
 7407        editor.handle_input("{", window, cx);
 7408        editor.handle_input("{", window, cx);
 7409        editor.move_right(&MoveRight, window, cx);
 7410        editor.move_right(&MoveRight, window, cx);
 7411        editor.move_left(&MoveLeft, window, cx);
 7412        editor.move_left(&MoveLeft, window, cx);
 7413        editor.backspace(&Default::default(), window, cx);
 7414    });
 7415
 7416    cx.assert_editor_state(
 7417        &"
 7418            {ˇ}
 7419            {ˇ}]]
 7420            {ˇ}
 7421        "
 7422        .unindent(),
 7423    );
 7424
 7425    cx.update_editor(|editor, window, cx| {
 7426        editor.backspace(&Default::default(), window, cx);
 7427    });
 7428
 7429    cx.assert_editor_state(
 7430        &"
 7431            ˇ
 7432            ˇ]]
 7433            ˇ
 7434        "
 7435        .unindent(),
 7436    );
 7437}
 7438
 7439#[gpui::test]
 7440async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7441    init_test(cx, |_| {});
 7442
 7443    let language = Arc::new(Language::new(
 7444        LanguageConfig::default(),
 7445        Some(tree_sitter_rust::LANGUAGE.into()),
 7446    ));
 7447
 7448    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7449    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7450    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7451    editor
 7452        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7453        .await;
 7454
 7455    editor.update_in(cx, |editor, window, cx| {
 7456        editor.set_auto_replace_emoji_shortcode(true);
 7457
 7458        editor.handle_input("Hello ", window, cx);
 7459        editor.handle_input(":wave", window, cx);
 7460        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7461
 7462        editor.handle_input(":", window, cx);
 7463        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7464
 7465        editor.handle_input(" :smile", window, cx);
 7466        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7467
 7468        editor.handle_input(":", window, cx);
 7469        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7470
 7471        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7472        editor.handle_input(":wave", window, cx);
 7473        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7474
 7475        editor.handle_input(":", window, cx);
 7476        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7477
 7478        editor.handle_input(":1", window, cx);
 7479        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7480
 7481        editor.handle_input(":", window, cx);
 7482        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7483
 7484        // Ensure shortcode does not get replaced when it is part of a word
 7485        editor.handle_input(" Test:wave", window, cx);
 7486        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7487
 7488        editor.handle_input(":", window, cx);
 7489        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7490
 7491        editor.set_auto_replace_emoji_shortcode(false);
 7492
 7493        // Ensure shortcode does not get replaced when auto replace is off
 7494        editor.handle_input(" :wave", window, cx);
 7495        assert_eq!(
 7496            editor.text(cx),
 7497            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7498        );
 7499
 7500        editor.handle_input(":", window, cx);
 7501        assert_eq!(
 7502            editor.text(cx),
 7503            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7504        );
 7505    });
 7506}
 7507
 7508#[gpui::test]
 7509async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7510    init_test(cx, |_| {});
 7511
 7512    let (text, insertion_ranges) = marked_text_ranges(
 7513        indoc! {"
 7514            ˇ
 7515        "},
 7516        false,
 7517    );
 7518
 7519    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7520    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7521
 7522    _ = editor.update_in(cx, |editor, window, cx| {
 7523        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7524
 7525        editor
 7526            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7527            .unwrap();
 7528
 7529        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7530            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7531            assert_eq!(editor.text(cx), expected_text);
 7532            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7533        }
 7534
 7535        assert(
 7536            editor,
 7537            cx,
 7538            indoc! {"
 7539            type «» =•
 7540            "},
 7541        );
 7542
 7543        assert!(editor.context_menu_visible(), "There should be a matches");
 7544    });
 7545}
 7546
 7547#[gpui::test]
 7548async fn test_snippets(cx: &mut TestAppContext) {
 7549    init_test(cx, |_| {});
 7550
 7551    let (text, insertion_ranges) = marked_text_ranges(
 7552        indoc! {"
 7553            a.ˇ b
 7554            a.ˇ b
 7555            a.ˇ b
 7556        "},
 7557        false,
 7558    );
 7559
 7560    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7561    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7562
 7563    editor.update_in(cx, |editor, window, cx| {
 7564        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7565
 7566        editor
 7567            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7568            .unwrap();
 7569
 7570        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7571            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7572            assert_eq!(editor.text(cx), expected_text);
 7573            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7574        }
 7575
 7576        assert(
 7577            editor,
 7578            cx,
 7579            indoc! {"
 7580                a.f(«one», two, «three») b
 7581                a.f(«one», two, «three») b
 7582                a.f(«one», two, «three») b
 7583            "},
 7584        );
 7585
 7586        // Can't move earlier than the first tab stop
 7587        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7588        assert(
 7589            editor,
 7590            cx,
 7591            indoc! {"
 7592                a.f(«one», two, «three») b
 7593                a.f(«one», two, «three») b
 7594                a.f(«one», two, «three») b
 7595            "},
 7596        );
 7597
 7598        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7599        assert(
 7600            editor,
 7601            cx,
 7602            indoc! {"
 7603                a.f(one, «two», three) b
 7604                a.f(one, «two», three) b
 7605                a.f(one, «two», three) b
 7606            "},
 7607        );
 7608
 7609        editor.move_to_prev_snippet_tabstop(window, cx);
 7610        assert(
 7611            editor,
 7612            cx,
 7613            indoc! {"
 7614                a.f(«one», two, «three») b
 7615                a.f(«one», two, «three») b
 7616                a.f(«one», two, «three») b
 7617            "},
 7618        );
 7619
 7620        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7621        assert(
 7622            editor,
 7623            cx,
 7624            indoc! {"
 7625                a.f(one, «two», three) b
 7626                a.f(one, «two», three) b
 7627                a.f(one, «two», three) b
 7628            "},
 7629        );
 7630        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7631        assert(
 7632            editor,
 7633            cx,
 7634            indoc! {"
 7635                a.f(one, two, three)ˇ b
 7636                a.f(one, two, three)ˇ b
 7637                a.f(one, two, three)ˇ b
 7638            "},
 7639        );
 7640
 7641        // As soon as the last tab stop is reached, snippet state is gone
 7642        editor.move_to_prev_snippet_tabstop(window, cx);
 7643        assert(
 7644            editor,
 7645            cx,
 7646            indoc! {"
 7647                a.f(one, two, three)ˇ b
 7648                a.f(one, two, three)ˇ b
 7649                a.f(one, two, three)ˇ b
 7650            "},
 7651        );
 7652    });
 7653}
 7654
 7655#[gpui::test]
 7656async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7657    init_test(cx, |_| {});
 7658
 7659    let fs = FakeFs::new(cx.executor());
 7660    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7661
 7662    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7663
 7664    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7665    language_registry.add(rust_lang());
 7666    let mut fake_servers = language_registry.register_fake_lsp(
 7667        "Rust",
 7668        FakeLspAdapter {
 7669            capabilities: lsp::ServerCapabilities {
 7670                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7671                ..Default::default()
 7672            },
 7673            ..Default::default()
 7674        },
 7675    );
 7676
 7677    let buffer = project
 7678        .update(cx, |project, cx| {
 7679            project.open_local_buffer(path!("/file.rs"), cx)
 7680        })
 7681        .await
 7682        .unwrap();
 7683
 7684    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7685    let (editor, cx) = cx.add_window_view(|window, cx| {
 7686        build_editor_with_project(project.clone(), buffer, window, cx)
 7687    });
 7688    editor.update_in(cx, |editor, window, cx| {
 7689        editor.set_text("one\ntwo\nthree\n", window, cx)
 7690    });
 7691    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7692
 7693    cx.executor().start_waiting();
 7694    let fake_server = fake_servers.next().await.unwrap();
 7695
 7696    let save = editor
 7697        .update_in(cx, |editor, window, cx| {
 7698            editor.save(true, project.clone(), window, cx)
 7699        })
 7700        .unwrap();
 7701    fake_server
 7702        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7703            assert_eq!(
 7704                params.text_document.uri,
 7705                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7706            );
 7707            assert_eq!(params.options.tab_size, 4);
 7708            Ok(Some(vec![lsp::TextEdit::new(
 7709                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7710                ", ".to_string(),
 7711            )]))
 7712        })
 7713        .next()
 7714        .await;
 7715    cx.executor().start_waiting();
 7716    save.await;
 7717
 7718    assert_eq!(
 7719        editor.update(cx, |editor, cx| editor.text(cx)),
 7720        "one, two\nthree\n"
 7721    );
 7722    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7723
 7724    editor.update_in(cx, |editor, window, cx| {
 7725        editor.set_text("one\ntwo\nthree\n", window, cx)
 7726    });
 7727    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7728
 7729    // Ensure we can still save even if formatting hangs.
 7730    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7731        move |params, _| async move {
 7732            assert_eq!(
 7733                params.text_document.uri,
 7734                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7735            );
 7736            futures::future::pending::<()>().await;
 7737            unreachable!()
 7738        },
 7739    );
 7740    let save = editor
 7741        .update_in(cx, |editor, window, cx| {
 7742            editor.save(true, project.clone(), window, cx)
 7743        })
 7744        .unwrap();
 7745    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7746    cx.executor().start_waiting();
 7747    save.await;
 7748    assert_eq!(
 7749        editor.update(cx, |editor, cx| editor.text(cx)),
 7750        "one\ntwo\nthree\n"
 7751    );
 7752    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7753
 7754    // For non-dirty buffer, no formatting request should be sent
 7755    let save = editor
 7756        .update_in(cx, |editor, window, cx| {
 7757            editor.save(true, project.clone(), window, cx)
 7758        })
 7759        .unwrap();
 7760    let _pending_format_request = fake_server
 7761        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7762            panic!("Should not be invoked on non-dirty buffer");
 7763        })
 7764        .next();
 7765    cx.executor().start_waiting();
 7766    save.await;
 7767
 7768    // Set rust language override and assert overridden tabsize is sent to language server
 7769    update_test_language_settings(cx, |settings| {
 7770        settings.languages.insert(
 7771            "Rust".into(),
 7772            LanguageSettingsContent {
 7773                tab_size: NonZeroU32::new(8),
 7774                ..Default::default()
 7775            },
 7776        );
 7777    });
 7778
 7779    editor.update_in(cx, |editor, window, cx| {
 7780        editor.set_text("somehting_new\n", window, cx)
 7781    });
 7782    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7783    let save = editor
 7784        .update_in(cx, |editor, window, cx| {
 7785            editor.save(true, project.clone(), window, cx)
 7786        })
 7787        .unwrap();
 7788    fake_server
 7789        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7790            assert_eq!(
 7791                params.text_document.uri,
 7792                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7793            );
 7794            assert_eq!(params.options.tab_size, 8);
 7795            Ok(Some(vec![]))
 7796        })
 7797        .next()
 7798        .await;
 7799    cx.executor().start_waiting();
 7800    save.await;
 7801}
 7802
 7803#[gpui::test]
 7804async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7805    init_test(cx, |_| {});
 7806
 7807    let cols = 4;
 7808    let rows = 10;
 7809    let sample_text_1 = sample_text(rows, cols, 'a');
 7810    assert_eq!(
 7811        sample_text_1,
 7812        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7813    );
 7814    let sample_text_2 = sample_text(rows, cols, 'l');
 7815    assert_eq!(
 7816        sample_text_2,
 7817        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7818    );
 7819    let sample_text_3 = sample_text(rows, cols, 'v');
 7820    assert_eq!(
 7821        sample_text_3,
 7822        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7823    );
 7824
 7825    let fs = FakeFs::new(cx.executor());
 7826    fs.insert_tree(
 7827        path!("/a"),
 7828        json!({
 7829            "main.rs": sample_text_1,
 7830            "other.rs": sample_text_2,
 7831            "lib.rs": sample_text_3,
 7832        }),
 7833    )
 7834    .await;
 7835
 7836    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7837    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7838    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7839
 7840    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7841    language_registry.add(rust_lang());
 7842    let mut fake_servers = language_registry.register_fake_lsp(
 7843        "Rust",
 7844        FakeLspAdapter {
 7845            capabilities: lsp::ServerCapabilities {
 7846                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7847                ..Default::default()
 7848            },
 7849            ..Default::default()
 7850        },
 7851    );
 7852
 7853    let worktree = project.update(cx, |project, cx| {
 7854        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7855        assert_eq!(worktrees.len(), 1);
 7856        worktrees.pop().unwrap()
 7857    });
 7858    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7859
 7860    let buffer_1 = project
 7861        .update(cx, |project, cx| {
 7862            project.open_buffer((worktree_id, "main.rs"), cx)
 7863        })
 7864        .await
 7865        .unwrap();
 7866    let buffer_2 = project
 7867        .update(cx, |project, cx| {
 7868            project.open_buffer((worktree_id, "other.rs"), cx)
 7869        })
 7870        .await
 7871        .unwrap();
 7872    let buffer_3 = project
 7873        .update(cx, |project, cx| {
 7874            project.open_buffer((worktree_id, "lib.rs"), cx)
 7875        })
 7876        .await
 7877        .unwrap();
 7878
 7879    let multi_buffer = cx.new(|cx| {
 7880        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7881        multi_buffer.push_excerpts(
 7882            buffer_1.clone(),
 7883            [
 7884                ExcerptRange {
 7885                    context: Point::new(0, 0)..Point::new(3, 0),
 7886                    primary: None,
 7887                },
 7888                ExcerptRange {
 7889                    context: Point::new(5, 0)..Point::new(7, 0),
 7890                    primary: None,
 7891                },
 7892                ExcerptRange {
 7893                    context: Point::new(9, 0)..Point::new(10, 4),
 7894                    primary: None,
 7895                },
 7896            ],
 7897            cx,
 7898        );
 7899        multi_buffer.push_excerpts(
 7900            buffer_2.clone(),
 7901            [
 7902                ExcerptRange {
 7903                    context: Point::new(0, 0)..Point::new(3, 0),
 7904                    primary: None,
 7905                },
 7906                ExcerptRange {
 7907                    context: Point::new(5, 0)..Point::new(7, 0),
 7908                    primary: None,
 7909                },
 7910                ExcerptRange {
 7911                    context: Point::new(9, 0)..Point::new(10, 4),
 7912                    primary: None,
 7913                },
 7914            ],
 7915            cx,
 7916        );
 7917        multi_buffer.push_excerpts(
 7918            buffer_3.clone(),
 7919            [
 7920                ExcerptRange {
 7921                    context: Point::new(0, 0)..Point::new(3, 0),
 7922                    primary: None,
 7923                },
 7924                ExcerptRange {
 7925                    context: Point::new(5, 0)..Point::new(7, 0),
 7926                    primary: None,
 7927                },
 7928                ExcerptRange {
 7929                    context: Point::new(9, 0)..Point::new(10, 4),
 7930                    primary: None,
 7931                },
 7932            ],
 7933            cx,
 7934        );
 7935        multi_buffer
 7936    });
 7937    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7938        Editor::new(
 7939            EditorMode::Full,
 7940            multi_buffer,
 7941            Some(project.clone()),
 7942            window,
 7943            cx,
 7944        )
 7945    });
 7946
 7947    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7948        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7949            s.select_ranges(Some(1..2))
 7950        });
 7951        editor.insert("|one|two|three|", window, cx);
 7952    });
 7953    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7954    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7955        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7956            s.select_ranges(Some(60..70))
 7957        });
 7958        editor.insert("|four|five|six|", window, cx);
 7959    });
 7960    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7961
 7962    // First two buffers should be edited, but not the third one.
 7963    assert_eq!(
 7964        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7965        "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}",
 7966    );
 7967    buffer_1.update(cx, |buffer, _| {
 7968        assert!(buffer.is_dirty());
 7969        assert_eq!(
 7970            buffer.text(),
 7971            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7972        )
 7973    });
 7974    buffer_2.update(cx, |buffer, _| {
 7975        assert!(buffer.is_dirty());
 7976        assert_eq!(
 7977            buffer.text(),
 7978            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7979        )
 7980    });
 7981    buffer_3.update(cx, |buffer, _| {
 7982        assert!(!buffer.is_dirty());
 7983        assert_eq!(buffer.text(), sample_text_3,)
 7984    });
 7985    cx.executor().run_until_parked();
 7986
 7987    cx.executor().start_waiting();
 7988    let save = multi_buffer_editor
 7989        .update_in(cx, |editor, window, cx| {
 7990            editor.save(true, project.clone(), window, cx)
 7991        })
 7992        .unwrap();
 7993
 7994    let fake_server = fake_servers.next().await.unwrap();
 7995    fake_server
 7996        .server
 7997        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7998            Ok(Some(vec![lsp::TextEdit::new(
 7999                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8000                format!("[{} formatted]", params.text_document.uri),
 8001            )]))
 8002        })
 8003        .detach();
 8004    save.await;
 8005
 8006    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8007    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8008    assert_eq!(
 8009        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8010        uri!("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}"),
 8011    );
 8012    buffer_1.update(cx, |buffer, _| {
 8013        assert!(!buffer.is_dirty());
 8014        assert_eq!(
 8015            buffer.text(),
 8016            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8017        )
 8018    });
 8019    buffer_2.update(cx, |buffer, _| {
 8020        assert!(!buffer.is_dirty());
 8021        assert_eq!(
 8022            buffer.text(),
 8023            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8024        )
 8025    });
 8026    buffer_3.update(cx, |buffer, _| {
 8027        assert!(!buffer.is_dirty());
 8028        assert_eq!(buffer.text(), sample_text_3,)
 8029    });
 8030}
 8031
 8032#[gpui::test]
 8033async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8034    init_test(cx, |_| {});
 8035
 8036    let fs = FakeFs::new(cx.executor());
 8037    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8038
 8039    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8040
 8041    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8042    language_registry.add(rust_lang());
 8043    let mut fake_servers = language_registry.register_fake_lsp(
 8044        "Rust",
 8045        FakeLspAdapter {
 8046            capabilities: lsp::ServerCapabilities {
 8047                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8048                ..Default::default()
 8049            },
 8050            ..Default::default()
 8051        },
 8052    );
 8053
 8054    let buffer = project
 8055        .update(cx, |project, cx| {
 8056            project.open_local_buffer(path!("/file.rs"), cx)
 8057        })
 8058        .await
 8059        .unwrap();
 8060
 8061    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8062    let (editor, cx) = cx.add_window_view(|window, cx| {
 8063        build_editor_with_project(project.clone(), buffer, window, cx)
 8064    });
 8065    editor.update_in(cx, |editor, window, cx| {
 8066        editor.set_text("one\ntwo\nthree\n", window, cx)
 8067    });
 8068    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8069
 8070    cx.executor().start_waiting();
 8071    let fake_server = fake_servers.next().await.unwrap();
 8072
 8073    let save = editor
 8074        .update_in(cx, |editor, window, cx| {
 8075            editor.save(true, project.clone(), window, cx)
 8076        })
 8077        .unwrap();
 8078    fake_server
 8079        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8080            assert_eq!(
 8081                params.text_document.uri,
 8082                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8083            );
 8084            assert_eq!(params.options.tab_size, 4);
 8085            Ok(Some(vec![lsp::TextEdit::new(
 8086                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8087                ", ".to_string(),
 8088            )]))
 8089        })
 8090        .next()
 8091        .await;
 8092    cx.executor().start_waiting();
 8093    save.await;
 8094    assert_eq!(
 8095        editor.update(cx, |editor, cx| editor.text(cx)),
 8096        "one, two\nthree\n"
 8097    );
 8098    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8099
 8100    editor.update_in(cx, |editor, window, cx| {
 8101        editor.set_text("one\ntwo\nthree\n", window, cx)
 8102    });
 8103    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8104
 8105    // Ensure we can still save even if formatting hangs.
 8106    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8107        move |params, _| async move {
 8108            assert_eq!(
 8109                params.text_document.uri,
 8110                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8111            );
 8112            futures::future::pending::<()>().await;
 8113            unreachable!()
 8114        },
 8115    );
 8116    let save = editor
 8117        .update_in(cx, |editor, window, cx| {
 8118            editor.save(true, project.clone(), window, cx)
 8119        })
 8120        .unwrap();
 8121    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8122    cx.executor().start_waiting();
 8123    save.await;
 8124    assert_eq!(
 8125        editor.update(cx, |editor, cx| editor.text(cx)),
 8126        "one\ntwo\nthree\n"
 8127    );
 8128    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8129
 8130    // For non-dirty buffer, no formatting request should be sent
 8131    let save = editor
 8132        .update_in(cx, |editor, window, cx| {
 8133            editor.save(true, project.clone(), window, cx)
 8134        })
 8135        .unwrap();
 8136    let _pending_format_request = fake_server
 8137        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8138            panic!("Should not be invoked on non-dirty buffer");
 8139        })
 8140        .next();
 8141    cx.executor().start_waiting();
 8142    save.await;
 8143
 8144    // Set Rust language override and assert overridden tabsize is sent to language server
 8145    update_test_language_settings(cx, |settings| {
 8146        settings.languages.insert(
 8147            "Rust".into(),
 8148            LanguageSettingsContent {
 8149                tab_size: NonZeroU32::new(8),
 8150                ..Default::default()
 8151            },
 8152        );
 8153    });
 8154
 8155    editor.update_in(cx, |editor, window, cx| {
 8156        editor.set_text("somehting_new\n", window, cx)
 8157    });
 8158    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8159    let save = editor
 8160        .update_in(cx, |editor, window, cx| {
 8161            editor.save(true, project.clone(), window, cx)
 8162        })
 8163        .unwrap();
 8164    fake_server
 8165        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8166            assert_eq!(
 8167                params.text_document.uri,
 8168                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8169            );
 8170            assert_eq!(params.options.tab_size, 8);
 8171            Ok(Some(vec![]))
 8172        })
 8173        .next()
 8174        .await;
 8175    cx.executor().start_waiting();
 8176    save.await;
 8177}
 8178
 8179#[gpui::test]
 8180async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8181    init_test(cx, |settings| {
 8182        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8183            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8184        ))
 8185    });
 8186
 8187    let fs = FakeFs::new(cx.executor());
 8188    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8189
 8190    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8191
 8192    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8193    language_registry.add(Arc::new(Language::new(
 8194        LanguageConfig {
 8195            name: "Rust".into(),
 8196            matcher: LanguageMatcher {
 8197                path_suffixes: vec!["rs".to_string()],
 8198                ..Default::default()
 8199            },
 8200            ..LanguageConfig::default()
 8201        },
 8202        Some(tree_sitter_rust::LANGUAGE.into()),
 8203    )));
 8204    update_test_language_settings(cx, |settings| {
 8205        // Enable Prettier formatting for the same buffer, and ensure
 8206        // LSP is called instead of Prettier.
 8207        settings.defaults.prettier = Some(PrettierSettings {
 8208            allowed: true,
 8209            ..PrettierSettings::default()
 8210        });
 8211    });
 8212    let mut fake_servers = language_registry.register_fake_lsp(
 8213        "Rust",
 8214        FakeLspAdapter {
 8215            capabilities: lsp::ServerCapabilities {
 8216                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8217                ..Default::default()
 8218            },
 8219            ..Default::default()
 8220        },
 8221    );
 8222
 8223    let buffer = project
 8224        .update(cx, |project, cx| {
 8225            project.open_local_buffer(path!("/file.rs"), cx)
 8226        })
 8227        .await
 8228        .unwrap();
 8229
 8230    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8231    let (editor, cx) = cx.add_window_view(|window, cx| {
 8232        build_editor_with_project(project.clone(), buffer, window, cx)
 8233    });
 8234    editor.update_in(cx, |editor, window, cx| {
 8235        editor.set_text("one\ntwo\nthree\n", window, cx)
 8236    });
 8237
 8238    cx.executor().start_waiting();
 8239    let fake_server = fake_servers.next().await.unwrap();
 8240
 8241    let format = editor
 8242        .update_in(cx, |editor, window, cx| {
 8243            editor.perform_format(
 8244                project.clone(),
 8245                FormatTrigger::Manual,
 8246                FormatTarget::Buffers,
 8247                window,
 8248                cx,
 8249            )
 8250        })
 8251        .unwrap();
 8252    fake_server
 8253        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8254            assert_eq!(
 8255                params.text_document.uri,
 8256                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8257            );
 8258            assert_eq!(params.options.tab_size, 4);
 8259            Ok(Some(vec![lsp::TextEdit::new(
 8260                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8261                ", ".to_string(),
 8262            )]))
 8263        })
 8264        .next()
 8265        .await;
 8266    cx.executor().start_waiting();
 8267    format.await;
 8268    assert_eq!(
 8269        editor.update(cx, |editor, cx| editor.text(cx)),
 8270        "one, two\nthree\n"
 8271    );
 8272
 8273    editor.update_in(cx, |editor, window, cx| {
 8274        editor.set_text("one\ntwo\nthree\n", window, cx)
 8275    });
 8276    // Ensure we don't lock if formatting hangs.
 8277    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8278        move |params, _| async move {
 8279            assert_eq!(
 8280                params.text_document.uri,
 8281                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8282            );
 8283            futures::future::pending::<()>().await;
 8284            unreachable!()
 8285        },
 8286    );
 8287    let format = editor
 8288        .update_in(cx, |editor, window, cx| {
 8289            editor.perform_format(
 8290                project,
 8291                FormatTrigger::Manual,
 8292                FormatTarget::Buffers,
 8293                window,
 8294                cx,
 8295            )
 8296        })
 8297        .unwrap();
 8298    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8299    cx.executor().start_waiting();
 8300    format.await;
 8301    assert_eq!(
 8302        editor.update(cx, |editor, cx| editor.text(cx)),
 8303        "one\ntwo\nthree\n"
 8304    );
 8305}
 8306
 8307#[gpui::test]
 8308async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8309    init_test(cx, |settings| {
 8310        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8311            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8312        ))
 8313    });
 8314
 8315    let fs = FakeFs::new(cx.executor());
 8316    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8317
 8318    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8319
 8320    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8321    language_registry.add(Arc::new(Language::new(
 8322        LanguageConfig {
 8323            name: "TypeScript".into(),
 8324            matcher: LanguageMatcher {
 8325                path_suffixes: vec!["ts".to_string()],
 8326                ..Default::default()
 8327            },
 8328            ..LanguageConfig::default()
 8329        },
 8330        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8331    )));
 8332    update_test_language_settings(cx, |settings| {
 8333        settings.defaults.prettier = Some(PrettierSettings {
 8334            allowed: true,
 8335            ..PrettierSettings::default()
 8336        });
 8337    });
 8338    let mut fake_servers = language_registry.register_fake_lsp(
 8339        "TypeScript",
 8340        FakeLspAdapter {
 8341            capabilities: lsp::ServerCapabilities {
 8342                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8343                ..Default::default()
 8344            },
 8345            ..Default::default()
 8346        },
 8347    );
 8348
 8349    let buffer = project
 8350        .update(cx, |project, cx| {
 8351            project.open_local_buffer(path!("/file.ts"), cx)
 8352        })
 8353        .await
 8354        .unwrap();
 8355
 8356    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8357    let (editor, cx) = cx.add_window_view(|window, cx| {
 8358        build_editor_with_project(project.clone(), buffer, window, cx)
 8359    });
 8360    editor.update_in(cx, |editor, window, cx| {
 8361        editor.set_text(
 8362            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8363            window,
 8364            cx,
 8365        )
 8366    });
 8367
 8368    cx.executor().start_waiting();
 8369    let fake_server = fake_servers.next().await.unwrap();
 8370
 8371    let format = editor
 8372        .update_in(cx, |editor, window, cx| {
 8373            editor.perform_code_action_kind(
 8374                project.clone(),
 8375                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8376                window,
 8377                cx,
 8378            )
 8379        })
 8380        .unwrap();
 8381    fake_server
 8382        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8383            assert_eq!(
 8384                params.text_document.uri,
 8385                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8386            );
 8387            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8388                lsp::CodeAction {
 8389                    title: "Organize Imports".to_string(),
 8390                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8391                    edit: Some(lsp::WorkspaceEdit {
 8392                        changes: Some(
 8393                            [(
 8394                                params.text_document.uri.clone(),
 8395                                vec![lsp::TextEdit::new(
 8396                                    lsp::Range::new(
 8397                                        lsp::Position::new(1, 0),
 8398                                        lsp::Position::new(2, 0),
 8399                                    ),
 8400                                    "".to_string(),
 8401                                )],
 8402                            )]
 8403                            .into_iter()
 8404                            .collect(),
 8405                        ),
 8406                        ..Default::default()
 8407                    }),
 8408                    ..Default::default()
 8409                },
 8410            )]))
 8411        })
 8412        .next()
 8413        .await;
 8414    cx.executor().start_waiting();
 8415    format.await;
 8416    assert_eq!(
 8417        editor.update(cx, |editor, cx| editor.text(cx)),
 8418        "import { a } from 'module';\n\nconst x = a;\n"
 8419    );
 8420
 8421    editor.update_in(cx, |editor, window, cx| {
 8422        editor.set_text(
 8423            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8424            window,
 8425            cx,
 8426        )
 8427    });
 8428    // Ensure we don't lock if code action hangs.
 8429    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8430        move |params, _| async move {
 8431            assert_eq!(
 8432                params.text_document.uri,
 8433                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8434            );
 8435            futures::future::pending::<()>().await;
 8436            unreachable!()
 8437        },
 8438    );
 8439    let format = editor
 8440        .update_in(cx, |editor, window, cx| {
 8441            editor.perform_code_action_kind(
 8442                project,
 8443                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8444                window,
 8445                cx,
 8446            )
 8447        })
 8448        .unwrap();
 8449    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8450    cx.executor().start_waiting();
 8451    format.await;
 8452    assert_eq!(
 8453        editor.update(cx, |editor, cx| editor.text(cx)),
 8454        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8455    );
 8456}
 8457
 8458#[gpui::test]
 8459async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8460    init_test(cx, |_| {});
 8461
 8462    let mut cx = EditorLspTestContext::new_rust(
 8463        lsp::ServerCapabilities {
 8464            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8465            ..Default::default()
 8466        },
 8467        cx,
 8468    )
 8469    .await;
 8470
 8471    cx.set_state(indoc! {"
 8472        one.twoˇ
 8473    "});
 8474
 8475    // The format request takes a long time. When it completes, it inserts
 8476    // a newline and an indent before the `.`
 8477    cx.lsp
 8478        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8479            let executor = cx.background_executor().clone();
 8480            async move {
 8481                executor.timer(Duration::from_millis(100)).await;
 8482                Ok(Some(vec![lsp::TextEdit {
 8483                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8484                    new_text: "\n    ".into(),
 8485                }]))
 8486            }
 8487        });
 8488
 8489    // Submit a format request.
 8490    let format_1 = cx
 8491        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8492        .unwrap();
 8493    cx.executor().run_until_parked();
 8494
 8495    // Submit a second format request.
 8496    let format_2 = cx
 8497        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8498        .unwrap();
 8499    cx.executor().run_until_parked();
 8500
 8501    // Wait for both format requests to complete
 8502    cx.executor().advance_clock(Duration::from_millis(200));
 8503    cx.executor().start_waiting();
 8504    format_1.await.unwrap();
 8505    cx.executor().start_waiting();
 8506    format_2.await.unwrap();
 8507
 8508    // The formatting edits only happens once.
 8509    cx.assert_editor_state(indoc! {"
 8510        one
 8511            .twoˇ
 8512    "});
 8513}
 8514
 8515#[gpui::test]
 8516async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8517    init_test(cx, |settings| {
 8518        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8519    });
 8520
 8521    let mut cx = EditorLspTestContext::new_rust(
 8522        lsp::ServerCapabilities {
 8523            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8524            ..Default::default()
 8525        },
 8526        cx,
 8527    )
 8528    .await;
 8529
 8530    // Set up a buffer white some trailing whitespace and no trailing newline.
 8531    cx.set_state(
 8532        &[
 8533            "one ",   //
 8534            "twoˇ",   //
 8535            "three ", //
 8536            "four",   //
 8537        ]
 8538        .join("\n"),
 8539    );
 8540
 8541    // Submit a format request.
 8542    let format = cx
 8543        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8544        .unwrap();
 8545
 8546    // Record which buffer changes have been sent to the language server
 8547    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8548    cx.lsp
 8549        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8550            let buffer_changes = buffer_changes.clone();
 8551            move |params, _| {
 8552                buffer_changes.lock().extend(
 8553                    params
 8554                        .content_changes
 8555                        .into_iter()
 8556                        .map(|e| (e.range.unwrap(), e.text)),
 8557                );
 8558            }
 8559        });
 8560
 8561    // Handle formatting requests to the language server.
 8562    cx.lsp
 8563        .set_request_handler::<lsp::request::Formatting, _, _>({
 8564            let buffer_changes = buffer_changes.clone();
 8565            move |_, _| {
 8566                // When formatting is requested, trailing whitespace has already been stripped,
 8567                // and the trailing newline has already been added.
 8568                assert_eq!(
 8569                    &buffer_changes.lock()[1..],
 8570                    &[
 8571                        (
 8572                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8573                            "".into()
 8574                        ),
 8575                        (
 8576                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8577                            "".into()
 8578                        ),
 8579                        (
 8580                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8581                            "\n".into()
 8582                        ),
 8583                    ]
 8584                );
 8585
 8586                // Insert blank lines between each line of the buffer.
 8587                async move {
 8588                    Ok(Some(vec![
 8589                        lsp::TextEdit {
 8590                            range: lsp::Range::new(
 8591                                lsp::Position::new(1, 0),
 8592                                lsp::Position::new(1, 0),
 8593                            ),
 8594                            new_text: "\n".into(),
 8595                        },
 8596                        lsp::TextEdit {
 8597                            range: lsp::Range::new(
 8598                                lsp::Position::new(2, 0),
 8599                                lsp::Position::new(2, 0),
 8600                            ),
 8601                            new_text: "\n".into(),
 8602                        },
 8603                    ]))
 8604                }
 8605            }
 8606        });
 8607
 8608    // After formatting the buffer, the trailing whitespace is stripped,
 8609    // a newline is appended, and the edits provided by the language server
 8610    // have been applied.
 8611    format.await.unwrap();
 8612    cx.assert_editor_state(
 8613        &[
 8614            "one",   //
 8615            "",      //
 8616            "twoˇ",  //
 8617            "",      //
 8618            "three", //
 8619            "four",  //
 8620            "",      //
 8621        ]
 8622        .join("\n"),
 8623    );
 8624
 8625    // Undoing the formatting undoes the trailing whitespace removal, the
 8626    // trailing newline, and the LSP edits.
 8627    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8628    cx.assert_editor_state(
 8629        &[
 8630            "one ",   //
 8631            "twoˇ",   //
 8632            "three ", //
 8633            "four",   //
 8634        ]
 8635        .join("\n"),
 8636    );
 8637}
 8638
 8639#[gpui::test]
 8640async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8641    cx: &mut TestAppContext,
 8642) {
 8643    init_test(cx, |_| {});
 8644
 8645    cx.update(|cx| {
 8646        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8647            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8648                settings.auto_signature_help = Some(true);
 8649            });
 8650        });
 8651    });
 8652
 8653    let mut cx = EditorLspTestContext::new_rust(
 8654        lsp::ServerCapabilities {
 8655            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8656                ..Default::default()
 8657            }),
 8658            ..Default::default()
 8659        },
 8660        cx,
 8661    )
 8662    .await;
 8663
 8664    let language = Language::new(
 8665        LanguageConfig {
 8666            name: "Rust".into(),
 8667            brackets: BracketPairConfig {
 8668                pairs: vec![
 8669                    BracketPair {
 8670                        start: "{".to_string(),
 8671                        end: "}".to_string(),
 8672                        close: true,
 8673                        surround: true,
 8674                        newline: true,
 8675                    },
 8676                    BracketPair {
 8677                        start: "(".to_string(),
 8678                        end: ")".to_string(),
 8679                        close: true,
 8680                        surround: true,
 8681                        newline: true,
 8682                    },
 8683                    BracketPair {
 8684                        start: "/*".to_string(),
 8685                        end: " */".to_string(),
 8686                        close: true,
 8687                        surround: true,
 8688                        newline: true,
 8689                    },
 8690                    BracketPair {
 8691                        start: "[".to_string(),
 8692                        end: "]".to_string(),
 8693                        close: false,
 8694                        surround: false,
 8695                        newline: true,
 8696                    },
 8697                    BracketPair {
 8698                        start: "\"".to_string(),
 8699                        end: "\"".to_string(),
 8700                        close: true,
 8701                        surround: true,
 8702                        newline: false,
 8703                    },
 8704                    BracketPair {
 8705                        start: "<".to_string(),
 8706                        end: ">".to_string(),
 8707                        close: false,
 8708                        surround: true,
 8709                        newline: true,
 8710                    },
 8711                ],
 8712                ..Default::default()
 8713            },
 8714            autoclose_before: "})]".to_string(),
 8715            ..Default::default()
 8716        },
 8717        Some(tree_sitter_rust::LANGUAGE.into()),
 8718    );
 8719    let language = Arc::new(language);
 8720
 8721    cx.language_registry().add(language.clone());
 8722    cx.update_buffer(|buffer, cx| {
 8723        buffer.set_language(Some(language), cx);
 8724    });
 8725
 8726    cx.set_state(
 8727        &r#"
 8728            fn main() {
 8729                sampleˇ
 8730            }
 8731        "#
 8732        .unindent(),
 8733    );
 8734
 8735    cx.update_editor(|editor, window, cx| {
 8736        editor.handle_input("(", window, cx);
 8737    });
 8738    cx.assert_editor_state(
 8739        &"
 8740            fn main() {
 8741                sample(ˇ)
 8742            }
 8743        "
 8744        .unindent(),
 8745    );
 8746
 8747    let mocked_response = lsp::SignatureHelp {
 8748        signatures: vec![lsp::SignatureInformation {
 8749            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8750            documentation: None,
 8751            parameters: Some(vec![
 8752                lsp::ParameterInformation {
 8753                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8754                    documentation: None,
 8755                },
 8756                lsp::ParameterInformation {
 8757                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8758                    documentation: None,
 8759                },
 8760            ]),
 8761            active_parameter: None,
 8762        }],
 8763        active_signature: Some(0),
 8764        active_parameter: Some(0),
 8765    };
 8766    handle_signature_help_request(&mut cx, mocked_response).await;
 8767
 8768    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8769        .await;
 8770
 8771    cx.editor(|editor, _, _| {
 8772        let signature_help_state = editor.signature_help_state.popover().cloned();
 8773        assert_eq!(
 8774            signature_help_state.unwrap().label,
 8775            "param1: u8, param2: u8"
 8776        );
 8777    });
 8778}
 8779
 8780#[gpui::test]
 8781async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8782    init_test(cx, |_| {});
 8783
 8784    cx.update(|cx| {
 8785        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8786            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8787                settings.auto_signature_help = Some(false);
 8788                settings.show_signature_help_after_edits = Some(false);
 8789            });
 8790        });
 8791    });
 8792
 8793    let mut cx = EditorLspTestContext::new_rust(
 8794        lsp::ServerCapabilities {
 8795            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8796                ..Default::default()
 8797            }),
 8798            ..Default::default()
 8799        },
 8800        cx,
 8801    )
 8802    .await;
 8803
 8804    let language = Language::new(
 8805        LanguageConfig {
 8806            name: "Rust".into(),
 8807            brackets: BracketPairConfig {
 8808                pairs: vec![
 8809                    BracketPair {
 8810                        start: "{".to_string(),
 8811                        end: "}".to_string(),
 8812                        close: true,
 8813                        surround: true,
 8814                        newline: true,
 8815                    },
 8816                    BracketPair {
 8817                        start: "(".to_string(),
 8818                        end: ")".to_string(),
 8819                        close: true,
 8820                        surround: true,
 8821                        newline: true,
 8822                    },
 8823                    BracketPair {
 8824                        start: "/*".to_string(),
 8825                        end: " */".to_string(),
 8826                        close: true,
 8827                        surround: true,
 8828                        newline: true,
 8829                    },
 8830                    BracketPair {
 8831                        start: "[".to_string(),
 8832                        end: "]".to_string(),
 8833                        close: false,
 8834                        surround: false,
 8835                        newline: true,
 8836                    },
 8837                    BracketPair {
 8838                        start: "\"".to_string(),
 8839                        end: "\"".to_string(),
 8840                        close: true,
 8841                        surround: true,
 8842                        newline: false,
 8843                    },
 8844                    BracketPair {
 8845                        start: "<".to_string(),
 8846                        end: ">".to_string(),
 8847                        close: false,
 8848                        surround: true,
 8849                        newline: true,
 8850                    },
 8851                ],
 8852                ..Default::default()
 8853            },
 8854            autoclose_before: "})]".to_string(),
 8855            ..Default::default()
 8856        },
 8857        Some(tree_sitter_rust::LANGUAGE.into()),
 8858    );
 8859    let language = Arc::new(language);
 8860
 8861    cx.language_registry().add(language.clone());
 8862    cx.update_buffer(|buffer, cx| {
 8863        buffer.set_language(Some(language), cx);
 8864    });
 8865
 8866    // Ensure that signature_help is not called when no signature help is enabled.
 8867    cx.set_state(
 8868        &r#"
 8869            fn main() {
 8870                sampleˇ
 8871            }
 8872        "#
 8873        .unindent(),
 8874    );
 8875    cx.update_editor(|editor, window, cx| {
 8876        editor.handle_input("(", window, cx);
 8877    });
 8878    cx.assert_editor_state(
 8879        &"
 8880            fn main() {
 8881                sample(ˇ)
 8882            }
 8883        "
 8884        .unindent(),
 8885    );
 8886    cx.editor(|editor, _, _| {
 8887        assert!(editor.signature_help_state.task().is_none());
 8888    });
 8889
 8890    let mocked_response = lsp::SignatureHelp {
 8891        signatures: vec![lsp::SignatureInformation {
 8892            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8893            documentation: None,
 8894            parameters: Some(vec![
 8895                lsp::ParameterInformation {
 8896                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8897                    documentation: None,
 8898                },
 8899                lsp::ParameterInformation {
 8900                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8901                    documentation: None,
 8902                },
 8903            ]),
 8904            active_parameter: None,
 8905        }],
 8906        active_signature: Some(0),
 8907        active_parameter: Some(0),
 8908    };
 8909
 8910    // Ensure that signature_help is called when enabled afte edits
 8911    cx.update(|_, cx| {
 8912        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8913            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8914                settings.auto_signature_help = Some(false);
 8915                settings.show_signature_help_after_edits = Some(true);
 8916            });
 8917        });
 8918    });
 8919    cx.set_state(
 8920        &r#"
 8921            fn main() {
 8922                sampleˇ
 8923            }
 8924        "#
 8925        .unindent(),
 8926    );
 8927    cx.update_editor(|editor, window, cx| {
 8928        editor.handle_input("(", window, cx);
 8929    });
 8930    cx.assert_editor_state(
 8931        &"
 8932            fn main() {
 8933                sample(ˇ)
 8934            }
 8935        "
 8936        .unindent(),
 8937    );
 8938    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8939    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8940        .await;
 8941    cx.update_editor(|editor, _, _| {
 8942        let signature_help_state = editor.signature_help_state.popover().cloned();
 8943        assert!(signature_help_state.is_some());
 8944        assert_eq!(
 8945            signature_help_state.unwrap().label,
 8946            "param1: u8, param2: u8"
 8947        );
 8948        editor.signature_help_state = SignatureHelpState::default();
 8949    });
 8950
 8951    // Ensure that signature_help is called when auto signature help override is enabled
 8952    cx.update(|_, cx| {
 8953        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8954            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8955                settings.auto_signature_help = Some(true);
 8956                settings.show_signature_help_after_edits = Some(false);
 8957            });
 8958        });
 8959    });
 8960    cx.set_state(
 8961        &r#"
 8962            fn main() {
 8963                sampleˇ
 8964            }
 8965        "#
 8966        .unindent(),
 8967    );
 8968    cx.update_editor(|editor, window, cx| {
 8969        editor.handle_input("(", window, cx);
 8970    });
 8971    cx.assert_editor_state(
 8972        &"
 8973            fn main() {
 8974                sample(ˇ)
 8975            }
 8976        "
 8977        .unindent(),
 8978    );
 8979    handle_signature_help_request(&mut cx, mocked_response).await;
 8980    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8981        .await;
 8982    cx.editor(|editor, _, _| {
 8983        let signature_help_state = editor.signature_help_state.popover().cloned();
 8984        assert!(signature_help_state.is_some());
 8985        assert_eq!(
 8986            signature_help_state.unwrap().label,
 8987            "param1: u8, param2: u8"
 8988        );
 8989    });
 8990}
 8991
 8992#[gpui::test]
 8993async fn test_signature_help(cx: &mut TestAppContext) {
 8994    init_test(cx, |_| {});
 8995    cx.update(|cx| {
 8996        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8997            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8998                settings.auto_signature_help = Some(true);
 8999            });
 9000        });
 9001    });
 9002
 9003    let mut cx = EditorLspTestContext::new_rust(
 9004        lsp::ServerCapabilities {
 9005            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9006                ..Default::default()
 9007            }),
 9008            ..Default::default()
 9009        },
 9010        cx,
 9011    )
 9012    .await;
 9013
 9014    // A test that directly calls `show_signature_help`
 9015    cx.update_editor(|editor, window, cx| {
 9016        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9017    });
 9018
 9019    let mocked_response = lsp::SignatureHelp {
 9020        signatures: vec![lsp::SignatureInformation {
 9021            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9022            documentation: None,
 9023            parameters: Some(vec![
 9024                lsp::ParameterInformation {
 9025                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9026                    documentation: None,
 9027                },
 9028                lsp::ParameterInformation {
 9029                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9030                    documentation: None,
 9031                },
 9032            ]),
 9033            active_parameter: None,
 9034        }],
 9035        active_signature: Some(0),
 9036        active_parameter: Some(0),
 9037    };
 9038    handle_signature_help_request(&mut cx, mocked_response).await;
 9039
 9040    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9041        .await;
 9042
 9043    cx.editor(|editor, _, _| {
 9044        let signature_help_state = editor.signature_help_state.popover().cloned();
 9045        assert!(signature_help_state.is_some());
 9046        assert_eq!(
 9047            signature_help_state.unwrap().label,
 9048            "param1: u8, param2: u8"
 9049        );
 9050    });
 9051
 9052    // When exiting outside from inside the brackets, `signature_help` is closed.
 9053    cx.set_state(indoc! {"
 9054        fn main() {
 9055            sample(ˇ);
 9056        }
 9057
 9058        fn sample(param1: u8, param2: u8) {}
 9059    "});
 9060
 9061    cx.update_editor(|editor, window, cx| {
 9062        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9063    });
 9064
 9065    let mocked_response = lsp::SignatureHelp {
 9066        signatures: Vec::new(),
 9067        active_signature: None,
 9068        active_parameter: None,
 9069    };
 9070    handle_signature_help_request(&mut cx, mocked_response).await;
 9071
 9072    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9073        .await;
 9074
 9075    cx.editor(|editor, _, _| {
 9076        assert!(!editor.signature_help_state.is_shown());
 9077    });
 9078
 9079    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9080    cx.set_state(indoc! {"
 9081        fn main() {
 9082            sample(ˇ);
 9083        }
 9084
 9085        fn sample(param1: u8, param2: u8) {}
 9086    "});
 9087
 9088    let mocked_response = lsp::SignatureHelp {
 9089        signatures: vec![lsp::SignatureInformation {
 9090            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9091            documentation: None,
 9092            parameters: Some(vec![
 9093                lsp::ParameterInformation {
 9094                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9095                    documentation: None,
 9096                },
 9097                lsp::ParameterInformation {
 9098                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9099                    documentation: None,
 9100                },
 9101            ]),
 9102            active_parameter: None,
 9103        }],
 9104        active_signature: Some(0),
 9105        active_parameter: Some(0),
 9106    };
 9107    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9108    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9109        .await;
 9110    cx.editor(|editor, _, _| {
 9111        assert!(editor.signature_help_state.is_shown());
 9112    });
 9113
 9114    // Restore the popover with more parameter input
 9115    cx.set_state(indoc! {"
 9116        fn main() {
 9117            sample(param1, param2ˇ);
 9118        }
 9119
 9120        fn sample(param1: u8, param2: u8) {}
 9121    "});
 9122
 9123    let mocked_response = lsp::SignatureHelp {
 9124        signatures: vec![lsp::SignatureInformation {
 9125            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9126            documentation: None,
 9127            parameters: Some(vec![
 9128                lsp::ParameterInformation {
 9129                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9130                    documentation: None,
 9131                },
 9132                lsp::ParameterInformation {
 9133                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9134                    documentation: None,
 9135                },
 9136            ]),
 9137            active_parameter: None,
 9138        }],
 9139        active_signature: Some(0),
 9140        active_parameter: Some(1),
 9141    };
 9142    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9143    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9144        .await;
 9145
 9146    // When selecting a range, the popover is gone.
 9147    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9148    cx.update_editor(|editor, window, cx| {
 9149        editor.change_selections(None, window, cx, |s| {
 9150            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9151        })
 9152    });
 9153    cx.assert_editor_state(indoc! {"
 9154        fn main() {
 9155            sample(param1, «ˇparam2»);
 9156        }
 9157
 9158        fn sample(param1: u8, param2: u8) {}
 9159    "});
 9160    cx.editor(|editor, _, _| {
 9161        assert!(!editor.signature_help_state.is_shown());
 9162    });
 9163
 9164    // When unselecting again, the popover is back if within the brackets.
 9165    cx.update_editor(|editor, window, cx| {
 9166        editor.change_selections(None, window, cx, |s| {
 9167            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9168        })
 9169    });
 9170    cx.assert_editor_state(indoc! {"
 9171        fn main() {
 9172            sample(param1, ˇparam2);
 9173        }
 9174
 9175        fn sample(param1: u8, param2: u8) {}
 9176    "});
 9177    handle_signature_help_request(&mut cx, mocked_response).await;
 9178    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9179        .await;
 9180    cx.editor(|editor, _, _| {
 9181        assert!(editor.signature_help_state.is_shown());
 9182    });
 9183
 9184    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9185    cx.update_editor(|editor, window, cx| {
 9186        editor.change_selections(None, window, cx, |s| {
 9187            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9188            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9189        })
 9190    });
 9191    cx.assert_editor_state(indoc! {"
 9192        fn main() {
 9193            sample(param1, ˇparam2);
 9194        }
 9195
 9196        fn sample(param1: u8, param2: u8) {}
 9197    "});
 9198
 9199    let mocked_response = lsp::SignatureHelp {
 9200        signatures: vec![lsp::SignatureInformation {
 9201            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9202            documentation: None,
 9203            parameters: Some(vec![
 9204                lsp::ParameterInformation {
 9205                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9206                    documentation: None,
 9207                },
 9208                lsp::ParameterInformation {
 9209                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9210                    documentation: None,
 9211                },
 9212            ]),
 9213            active_parameter: None,
 9214        }],
 9215        active_signature: Some(0),
 9216        active_parameter: Some(1),
 9217    };
 9218    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9219    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9220        .await;
 9221    cx.update_editor(|editor, _, cx| {
 9222        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9223    });
 9224    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9225        .await;
 9226    cx.update_editor(|editor, window, cx| {
 9227        editor.change_selections(None, window, cx, |s| {
 9228            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9229        })
 9230    });
 9231    cx.assert_editor_state(indoc! {"
 9232        fn main() {
 9233            sample(param1, «ˇparam2»);
 9234        }
 9235
 9236        fn sample(param1: u8, param2: u8) {}
 9237    "});
 9238    cx.update_editor(|editor, window, cx| {
 9239        editor.change_selections(None, window, cx, |s| {
 9240            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9241        })
 9242    });
 9243    cx.assert_editor_state(indoc! {"
 9244        fn main() {
 9245            sample(param1, ˇparam2);
 9246        }
 9247
 9248        fn sample(param1: u8, param2: u8) {}
 9249    "});
 9250    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9251        .await;
 9252}
 9253
 9254#[gpui::test]
 9255async fn test_completion(cx: &mut TestAppContext) {
 9256    init_test(cx, |_| {});
 9257
 9258    let mut cx = EditorLspTestContext::new_rust(
 9259        lsp::ServerCapabilities {
 9260            completion_provider: Some(lsp::CompletionOptions {
 9261                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9262                resolve_provider: Some(true),
 9263                ..Default::default()
 9264            }),
 9265            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9266            ..Default::default()
 9267        },
 9268        cx,
 9269    )
 9270    .await;
 9271    let counter = Arc::new(AtomicUsize::new(0));
 9272
 9273    cx.set_state(indoc! {"
 9274        oneˇ
 9275        two
 9276        three
 9277    "});
 9278    cx.simulate_keystroke(".");
 9279    handle_completion_request(
 9280        &mut cx,
 9281        indoc! {"
 9282            one.|<>
 9283            two
 9284            three
 9285        "},
 9286        vec!["first_completion", "second_completion"],
 9287        counter.clone(),
 9288    )
 9289    .await;
 9290    cx.condition(|editor, _| editor.context_menu_visible())
 9291        .await;
 9292    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9293
 9294    let _handler = handle_signature_help_request(
 9295        &mut cx,
 9296        lsp::SignatureHelp {
 9297            signatures: vec![lsp::SignatureInformation {
 9298                label: "test signature".to_string(),
 9299                documentation: None,
 9300                parameters: Some(vec![lsp::ParameterInformation {
 9301                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9302                    documentation: None,
 9303                }]),
 9304                active_parameter: None,
 9305            }],
 9306            active_signature: None,
 9307            active_parameter: None,
 9308        },
 9309    );
 9310    cx.update_editor(|editor, window, cx| {
 9311        assert!(
 9312            !editor.signature_help_state.is_shown(),
 9313            "No signature help was called for"
 9314        );
 9315        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9316    });
 9317    cx.run_until_parked();
 9318    cx.update_editor(|editor, _, _| {
 9319        assert!(
 9320            !editor.signature_help_state.is_shown(),
 9321            "No signature help should be shown when completions menu is open"
 9322        );
 9323    });
 9324
 9325    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9326        editor.context_menu_next(&Default::default(), window, cx);
 9327        editor
 9328            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9329            .unwrap()
 9330    });
 9331    cx.assert_editor_state(indoc! {"
 9332        one.second_completionˇ
 9333        two
 9334        three
 9335    "});
 9336
 9337    handle_resolve_completion_request(
 9338        &mut cx,
 9339        Some(vec![
 9340            (
 9341                //This overlaps with the primary completion edit which is
 9342                //misbehavior from the LSP spec, test that we filter it out
 9343                indoc! {"
 9344                    one.second_ˇcompletion
 9345                    two
 9346                    threeˇ
 9347                "},
 9348                "overlapping additional edit",
 9349            ),
 9350            (
 9351                indoc! {"
 9352                    one.second_completion
 9353                    two
 9354                    threeˇ
 9355                "},
 9356                "\nadditional edit",
 9357            ),
 9358        ]),
 9359    )
 9360    .await;
 9361    apply_additional_edits.await.unwrap();
 9362    cx.assert_editor_state(indoc! {"
 9363        one.second_completionˇ
 9364        two
 9365        three
 9366        additional edit
 9367    "});
 9368
 9369    cx.set_state(indoc! {"
 9370        one.second_completion
 9371        twoˇ
 9372        threeˇ
 9373        additional edit
 9374    "});
 9375    cx.simulate_keystroke(" ");
 9376    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9377    cx.simulate_keystroke("s");
 9378    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9379
 9380    cx.assert_editor_state(indoc! {"
 9381        one.second_completion
 9382        two sˇ
 9383        three sˇ
 9384        additional edit
 9385    "});
 9386    handle_completion_request(
 9387        &mut cx,
 9388        indoc! {"
 9389            one.second_completion
 9390            two s
 9391            three <s|>
 9392            additional edit
 9393        "},
 9394        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9395        counter.clone(),
 9396    )
 9397    .await;
 9398    cx.condition(|editor, _| editor.context_menu_visible())
 9399        .await;
 9400    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9401
 9402    cx.simulate_keystroke("i");
 9403
 9404    handle_completion_request(
 9405        &mut cx,
 9406        indoc! {"
 9407            one.second_completion
 9408            two si
 9409            three <si|>
 9410            additional edit
 9411        "},
 9412        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9413        counter.clone(),
 9414    )
 9415    .await;
 9416    cx.condition(|editor, _| editor.context_menu_visible())
 9417        .await;
 9418    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9419
 9420    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9421        editor
 9422            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9423            .unwrap()
 9424    });
 9425    cx.assert_editor_state(indoc! {"
 9426        one.second_completion
 9427        two sixth_completionˇ
 9428        three sixth_completionˇ
 9429        additional edit
 9430    "});
 9431
 9432    apply_additional_edits.await.unwrap();
 9433
 9434    update_test_language_settings(&mut cx, |settings| {
 9435        settings.defaults.show_completions_on_input = Some(false);
 9436    });
 9437    cx.set_state("editorˇ");
 9438    cx.simulate_keystroke(".");
 9439    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9440    cx.simulate_keystrokes("c l o");
 9441    cx.assert_editor_state("editor.cloˇ");
 9442    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9443    cx.update_editor(|editor, window, cx| {
 9444        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9445    });
 9446    handle_completion_request(
 9447        &mut cx,
 9448        "editor.<clo|>",
 9449        vec!["close", "clobber"],
 9450        counter.clone(),
 9451    )
 9452    .await;
 9453    cx.condition(|editor, _| editor.context_menu_visible())
 9454        .await;
 9455    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9456
 9457    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9458        editor
 9459            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9460            .unwrap()
 9461    });
 9462    cx.assert_editor_state("editor.closeˇ");
 9463    handle_resolve_completion_request(&mut cx, None).await;
 9464    apply_additional_edits.await.unwrap();
 9465}
 9466
 9467#[gpui::test]
 9468async fn test_word_completion(cx: &mut TestAppContext) {
 9469    let lsp_fetch_timeout_ms = 10;
 9470    init_test(cx, |language_settings| {
 9471        language_settings.defaults.completions = Some(CompletionSettings {
 9472            words: WordsCompletionMode::Fallback,
 9473            lsp: true,
 9474            lsp_fetch_timeout_ms: 10,
 9475        });
 9476    });
 9477
 9478    let mut cx = EditorLspTestContext::new_rust(
 9479        lsp::ServerCapabilities {
 9480            completion_provider: Some(lsp::CompletionOptions {
 9481                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9482                ..lsp::CompletionOptions::default()
 9483            }),
 9484            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9485            ..lsp::ServerCapabilities::default()
 9486        },
 9487        cx,
 9488    )
 9489    .await;
 9490
 9491    let throttle_completions = Arc::new(AtomicBool::new(false));
 9492
 9493    let lsp_throttle_completions = throttle_completions.clone();
 9494    let _completion_requests_handler =
 9495        cx.lsp
 9496            .server
 9497            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9498                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9499                let cx = cx.clone();
 9500                async move {
 9501                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9502                        cx.background_executor()
 9503                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9504                            .await;
 9505                    }
 9506                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9507                        lsp::CompletionItem {
 9508                            label: "first".into(),
 9509                            ..lsp::CompletionItem::default()
 9510                        },
 9511                        lsp::CompletionItem {
 9512                            label: "last".into(),
 9513                            ..lsp::CompletionItem::default()
 9514                        },
 9515                    ])))
 9516                }
 9517            });
 9518
 9519    cx.set_state(indoc! {"
 9520        oneˇ
 9521        two
 9522        three
 9523    "});
 9524    cx.simulate_keystroke(".");
 9525    cx.executor().run_until_parked();
 9526    cx.condition(|editor, _| editor.context_menu_visible())
 9527        .await;
 9528    cx.update_editor(|editor, window, cx| {
 9529        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9530        {
 9531            assert_eq!(
 9532                completion_menu_entries(&menu),
 9533                &["first", "last"],
 9534                "When LSP server is fast to reply, no fallback word completions are used"
 9535            );
 9536        } else {
 9537            panic!("expected completion menu to be open");
 9538        }
 9539        editor.cancel(&Cancel, window, cx);
 9540    });
 9541    cx.executor().run_until_parked();
 9542    cx.condition(|editor, _| !editor.context_menu_visible())
 9543        .await;
 9544
 9545    throttle_completions.store(true, atomic::Ordering::Release);
 9546    cx.simulate_keystroke(".");
 9547    cx.executor()
 9548        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9549    cx.executor().run_until_parked();
 9550    cx.condition(|editor, _| editor.context_menu_visible())
 9551        .await;
 9552    cx.update_editor(|editor, _, _| {
 9553        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9554        {
 9555            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9556                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9557        } else {
 9558            panic!("expected completion menu to be open");
 9559        }
 9560    });
 9561}
 9562
 9563#[gpui::test]
 9564async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9565    init_test(cx, |language_settings| {
 9566        language_settings.defaults.completions = Some(CompletionSettings {
 9567            words: WordsCompletionMode::Enabled,
 9568            lsp: true,
 9569            lsp_fetch_timeout_ms: 0,
 9570        });
 9571    });
 9572
 9573    let mut cx = EditorLspTestContext::new_rust(
 9574        lsp::ServerCapabilities {
 9575            completion_provider: Some(lsp::CompletionOptions {
 9576                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9577                ..lsp::CompletionOptions::default()
 9578            }),
 9579            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9580            ..lsp::ServerCapabilities::default()
 9581        },
 9582        cx,
 9583    )
 9584    .await;
 9585
 9586    let _completion_requests_handler =
 9587        cx.lsp
 9588            .server
 9589            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9590                Ok(Some(lsp::CompletionResponse::Array(vec![
 9591                    lsp::CompletionItem {
 9592                        label: "first".into(),
 9593                        ..lsp::CompletionItem::default()
 9594                    },
 9595                    lsp::CompletionItem {
 9596                        label: "last".into(),
 9597                        ..lsp::CompletionItem::default()
 9598                    },
 9599                ])))
 9600            });
 9601
 9602    cx.set_state(indoc! {"ˇ
 9603        first
 9604        last
 9605        second
 9606    "});
 9607    cx.simulate_keystroke(".");
 9608    cx.executor().run_until_parked();
 9609    cx.condition(|editor, _| editor.context_menu_visible())
 9610        .await;
 9611    cx.update_editor(|editor, _, _| {
 9612        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9613        {
 9614            assert_eq!(
 9615                completion_menu_entries(&menu),
 9616                &["first", "last", "second"],
 9617                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9618            );
 9619        } else {
 9620            panic!("expected completion menu to be open");
 9621        }
 9622    });
 9623}
 9624
 9625#[gpui::test]
 9626async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9627    init_test(cx, |language_settings| {
 9628        language_settings.defaults.completions = Some(CompletionSettings {
 9629            words: WordsCompletionMode::Disabled,
 9630            lsp: true,
 9631            lsp_fetch_timeout_ms: 0,
 9632        });
 9633    });
 9634
 9635    let mut cx = EditorLspTestContext::new_rust(
 9636        lsp::ServerCapabilities {
 9637            completion_provider: Some(lsp::CompletionOptions {
 9638                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9639                ..lsp::CompletionOptions::default()
 9640            }),
 9641            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9642            ..lsp::ServerCapabilities::default()
 9643        },
 9644        cx,
 9645    )
 9646    .await;
 9647
 9648    let _completion_requests_handler =
 9649        cx.lsp
 9650            .server
 9651            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9652                panic!("LSP completions should not be queried when dealing with word completions")
 9653            });
 9654
 9655    cx.set_state(indoc! {"ˇ
 9656        first
 9657        last
 9658        second
 9659    "});
 9660    cx.update_editor(|editor, window, cx| {
 9661        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9662    });
 9663    cx.executor().run_until_parked();
 9664    cx.condition(|editor, _| editor.context_menu_visible())
 9665        .await;
 9666    cx.update_editor(|editor, _, _| {
 9667        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9668        {
 9669            assert_eq!(
 9670                completion_menu_entries(&menu),
 9671                &["first", "last", "second"],
 9672                "`ShowWordCompletions` action should show word completions"
 9673            );
 9674        } else {
 9675            panic!("expected completion menu to be open");
 9676        }
 9677    });
 9678
 9679    cx.simulate_keystroke("l");
 9680    cx.executor().run_until_parked();
 9681    cx.condition(|editor, _| editor.context_menu_visible())
 9682        .await;
 9683    cx.update_editor(|editor, _, _| {
 9684        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9685        {
 9686            assert_eq!(
 9687                completion_menu_entries(&menu),
 9688                &["last"],
 9689                "After showing word completions, further editing should filter them and not query the LSP"
 9690            );
 9691        } else {
 9692            panic!("expected completion menu to be open");
 9693        }
 9694    });
 9695}
 9696
 9697#[gpui::test]
 9698async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9699    init_test(cx, |language_settings| {
 9700        language_settings.defaults.completions = Some(CompletionSettings {
 9701            words: WordsCompletionMode::Fallback,
 9702            lsp: false,
 9703            lsp_fetch_timeout_ms: 0,
 9704        });
 9705    });
 9706
 9707    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9708
 9709    cx.set_state(indoc! {"ˇ
 9710        0_usize
 9711        let
 9712        33
 9713        4.5f32
 9714    "});
 9715    cx.update_editor(|editor, window, cx| {
 9716        editor.show_completions(&ShowCompletions::default(), window, cx);
 9717    });
 9718    cx.executor().run_until_parked();
 9719    cx.condition(|editor, _| editor.context_menu_visible())
 9720        .await;
 9721    cx.update_editor(|editor, window, cx| {
 9722        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9723        {
 9724            assert_eq!(
 9725                completion_menu_entries(&menu),
 9726                &["let"],
 9727                "With no digits in the completion query, no digits should be in the word completions"
 9728            );
 9729        } else {
 9730            panic!("expected completion menu to be open");
 9731        }
 9732        editor.cancel(&Cancel, window, cx);
 9733    });
 9734
 9735    cx.set_state(indoc! {" 9736        0_usize
 9737        let
 9738        3
 9739        33.35f32
 9740    "});
 9741    cx.update_editor(|editor, window, cx| {
 9742        editor.show_completions(&ShowCompletions::default(), window, cx);
 9743    });
 9744    cx.executor().run_until_parked();
 9745    cx.condition(|editor, _| editor.context_menu_visible())
 9746        .await;
 9747    cx.update_editor(|editor, _, _| {
 9748        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9749        {
 9750            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9751                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9752        } else {
 9753            panic!("expected completion menu to be open");
 9754        }
 9755    });
 9756}
 9757
 9758fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9759    let position = || lsp::Position {
 9760        line: params.text_document_position.position.line,
 9761        character: params.text_document_position.position.character,
 9762    };
 9763    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9764        range: lsp::Range {
 9765            start: position(),
 9766            end: position(),
 9767        },
 9768        new_text: text.to_string(),
 9769    }))
 9770}
 9771
 9772#[gpui::test]
 9773async fn test_multiline_completion(cx: &mut TestAppContext) {
 9774    init_test(cx, |_| {});
 9775
 9776    let fs = FakeFs::new(cx.executor());
 9777    fs.insert_tree(
 9778        path!("/a"),
 9779        json!({
 9780            "main.ts": "a",
 9781        }),
 9782    )
 9783    .await;
 9784
 9785    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9786    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9787    let typescript_language = Arc::new(Language::new(
 9788        LanguageConfig {
 9789            name: "TypeScript".into(),
 9790            matcher: LanguageMatcher {
 9791                path_suffixes: vec!["ts".to_string()],
 9792                ..LanguageMatcher::default()
 9793            },
 9794            line_comments: vec!["// ".into()],
 9795            ..LanguageConfig::default()
 9796        },
 9797        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9798    ));
 9799    language_registry.add(typescript_language.clone());
 9800    let mut fake_servers = language_registry.register_fake_lsp(
 9801        "TypeScript",
 9802        FakeLspAdapter {
 9803            capabilities: lsp::ServerCapabilities {
 9804                completion_provider: Some(lsp::CompletionOptions {
 9805                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9806                    ..lsp::CompletionOptions::default()
 9807                }),
 9808                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9809                ..lsp::ServerCapabilities::default()
 9810            },
 9811            // Emulate vtsls label generation
 9812            label_for_completion: Some(Box::new(|item, _| {
 9813                let text = if let Some(description) = item
 9814                    .label_details
 9815                    .as_ref()
 9816                    .and_then(|label_details| label_details.description.as_ref())
 9817                {
 9818                    format!("{} {}", item.label, description)
 9819                } else if let Some(detail) = &item.detail {
 9820                    format!("{} {}", item.label, detail)
 9821                } else {
 9822                    item.label.clone()
 9823                };
 9824                let len = text.len();
 9825                Some(language::CodeLabel {
 9826                    text,
 9827                    runs: Vec::new(),
 9828                    filter_range: 0..len,
 9829                })
 9830            })),
 9831            ..FakeLspAdapter::default()
 9832        },
 9833    );
 9834    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9835    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9836    let worktree_id = workspace
 9837        .update(cx, |workspace, _window, cx| {
 9838            workspace.project().update(cx, |project, cx| {
 9839                project.worktrees(cx).next().unwrap().read(cx).id()
 9840            })
 9841        })
 9842        .unwrap();
 9843    let _buffer = project
 9844        .update(cx, |project, cx| {
 9845            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9846        })
 9847        .await
 9848        .unwrap();
 9849    let editor = workspace
 9850        .update(cx, |workspace, window, cx| {
 9851            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9852        })
 9853        .unwrap()
 9854        .await
 9855        .unwrap()
 9856        .downcast::<Editor>()
 9857        .unwrap();
 9858    let fake_server = fake_servers.next().await.unwrap();
 9859
 9860    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9861    let multiline_label_2 = "a\nb\nc\n";
 9862    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9863    let multiline_description = "d\ne\nf\n";
 9864    let multiline_detail_2 = "g\nh\ni\n";
 9865
 9866    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
 9867        move |params, _| async move {
 9868            Ok(Some(lsp::CompletionResponse::Array(vec![
 9869                lsp::CompletionItem {
 9870                    label: multiline_label.to_string(),
 9871                    text_edit: gen_text_edit(&params, "new_text_1"),
 9872                    ..lsp::CompletionItem::default()
 9873                },
 9874                lsp::CompletionItem {
 9875                    label: "single line label 1".to_string(),
 9876                    detail: Some(multiline_detail.to_string()),
 9877                    text_edit: gen_text_edit(&params, "new_text_2"),
 9878                    ..lsp::CompletionItem::default()
 9879                },
 9880                lsp::CompletionItem {
 9881                    label: "single line label 2".to_string(),
 9882                    label_details: Some(lsp::CompletionItemLabelDetails {
 9883                        description: Some(multiline_description.to_string()),
 9884                        detail: None,
 9885                    }),
 9886                    text_edit: gen_text_edit(&params, "new_text_2"),
 9887                    ..lsp::CompletionItem::default()
 9888                },
 9889                lsp::CompletionItem {
 9890                    label: multiline_label_2.to_string(),
 9891                    detail: Some(multiline_detail_2.to_string()),
 9892                    text_edit: gen_text_edit(&params, "new_text_3"),
 9893                    ..lsp::CompletionItem::default()
 9894                },
 9895                lsp::CompletionItem {
 9896                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9897                    detail: Some(
 9898                        "Details with many     spaces and \t but without newlines".to_string(),
 9899                    ),
 9900                    text_edit: gen_text_edit(&params, "new_text_4"),
 9901                    ..lsp::CompletionItem::default()
 9902                },
 9903            ])))
 9904        },
 9905    );
 9906
 9907    editor.update_in(cx, |editor, window, cx| {
 9908        cx.focus_self(window);
 9909        editor.move_to_end(&MoveToEnd, window, cx);
 9910        editor.handle_input(".", window, cx);
 9911    });
 9912    cx.run_until_parked();
 9913    completion_handle.next().await.unwrap();
 9914
 9915    editor.update(cx, |editor, _| {
 9916        assert!(editor.context_menu_visible());
 9917        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9918        {
 9919            let completion_labels = menu
 9920                .completions
 9921                .borrow()
 9922                .iter()
 9923                .map(|c| c.label.text.clone())
 9924                .collect::<Vec<_>>();
 9925            assert_eq!(
 9926                completion_labels,
 9927                &[
 9928                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9929                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9930                    "single line label 2 d e f ",
 9931                    "a b c g h i ",
 9932                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9933                ],
 9934                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9935            );
 9936
 9937            for completion in menu
 9938                .completions
 9939                .borrow()
 9940                .iter() {
 9941                    assert_eq!(
 9942                        completion.label.filter_range,
 9943                        0..completion.label.text.len(),
 9944                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9945                    );
 9946                }
 9947        } else {
 9948            panic!("expected completion menu to be open");
 9949        }
 9950    });
 9951}
 9952
 9953#[gpui::test]
 9954async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9955    init_test(cx, |_| {});
 9956    let mut cx = EditorLspTestContext::new_rust(
 9957        lsp::ServerCapabilities {
 9958            completion_provider: Some(lsp::CompletionOptions {
 9959                trigger_characters: Some(vec![".".to_string()]),
 9960                ..Default::default()
 9961            }),
 9962            ..Default::default()
 9963        },
 9964        cx,
 9965    )
 9966    .await;
 9967    cx.lsp
 9968        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 9969            Ok(Some(lsp::CompletionResponse::Array(vec![
 9970                lsp::CompletionItem {
 9971                    label: "first".into(),
 9972                    ..Default::default()
 9973                },
 9974                lsp::CompletionItem {
 9975                    label: "last".into(),
 9976                    ..Default::default()
 9977                },
 9978            ])))
 9979        });
 9980    cx.set_state("variableˇ");
 9981    cx.simulate_keystroke(".");
 9982    cx.executor().run_until_parked();
 9983
 9984    cx.update_editor(|editor, _, _| {
 9985        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9986        {
 9987            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9988        } else {
 9989            panic!("expected completion menu to be open");
 9990        }
 9991    });
 9992
 9993    cx.update_editor(|editor, window, cx| {
 9994        editor.move_page_down(&MovePageDown::default(), window, cx);
 9995        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9996        {
 9997            assert!(
 9998                menu.selected_item == 1,
 9999                "expected PageDown to select the last item from the context menu"
10000            );
10001        } else {
10002            panic!("expected completion menu to stay open after PageDown");
10003        }
10004    });
10005
10006    cx.update_editor(|editor, window, cx| {
10007        editor.move_page_up(&MovePageUp::default(), window, cx);
10008        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10009        {
10010            assert!(
10011                menu.selected_item == 0,
10012                "expected PageUp to select the first item from the context menu"
10013            );
10014        } else {
10015            panic!("expected completion menu to stay open after PageUp");
10016        }
10017    });
10018}
10019
10020#[gpui::test]
10021async fn test_completion_sort(cx: &mut TestAppContext) {
10022    init_test(cx, |_| {});
10023    let mut cx = EditorLspTestContext::new_rust(
10024        lsp::ServerCapabilities {
10025            completion_provider: Some(lsp::CompletionOptions {
10026                trigger_characters: Some(vec![".".to_string()]),
10027                ..Default::default()
10028            }),
10029            ..Default::default()
10030        },
10031        cx,
10032    )
10033    .await;
10034    cx.lsp
10035        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10036            Ok(Some(lsp::CompletionResponse::Array(vec![
10037                lsp::CompletionItem {
10038                    label: "Range".into(),
10039                    sort_text: Some("a".into()),
10040                    ..Default::default()
10041                },
10042                lsp::CompletionItem {
10043                    label: "r".into(),
10044                    sort_text: Some("b".into()),
10045                    ..Default::default()
10046                },
10047                lsp::CompletionItem {
10048                    label: "ret".into(),
10049                    sort_text: Some("c".into()),
10050                    ..Default::default()
10051                },
10052                lsp::CompletionItem {
10053                    label: "return".into(),
10054                    sort_text: Some("d".into()),
10055                    ..Default::default()
10056                },
10057                lsp::CompletionItem {
10058                    label: "slice".into(),
10059                    sort_text: Some("d".into()),
10060                    ..Default::default()
10061                },
10062            ])))
10063        });
10064    cx.set_state("");
10065    cx.executor().run_until_parked();
10066    cx.update_editor(|editor, window, cx| {
10067        editor.show_completions(
10068            &ShowCompletions {
10069                trigger: Some("r".into()),
10070            },
10071            window,
10072            cx,
10073        );
10074    });
10075    cx.executor().run_until_parked();
10076
10077    cx.update_editor(|editor, _, _| {
10078        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10079        {
10080            assert_eq!(
10081                completion_menu_entries(&menu),
10082                &["r", "ret", "Range", "return"]
10083            );
10084        } else {
10085            panic!("expected completion menu to be open");
10086        }
10087    });
10088}
10089
10090#[gpui::test]
10091async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10092    init_test(cx, |_| {});
10093
10094    let mut cx = EditorLspTestContext::new_rust(
10095        lsp::ServerCapabilities {
10096            completion_provider: Some(lsp::CompletionOptions {
10097                trigger_characters: Some(vec![".".to_string()]),
10098                resolve_provider: Some(true),
10099                ..Default::default()
10100            }),
10101            ..Default::default()
10102        },
10103        cx,
10104    )
10105    .await;
10106
10107    cx.set_state("fn main() { let a = 2ˇ; }");
10108    cx.simulate_keystroke(".");
10109    let completion_item = lsp::CompletionItem {
10110        label: "Some".into(),
10111        kind: Some(lsp::CompletionItemKind::SNIPPET),
10112        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10113        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10114            kind: lsp::MarkupKind::Markdown,
10115            value: "```rust\nSome(2)\n```".to_string(),
10116        })),
10117        deprecated: Some(false),
10118        sort_text: Some("Some".to_string()),
10119        filter_text: Some("Some".to_string()),
10120        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10121        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10122            range: lsp::Range {
10123                start: lsp::Position {
10124                    line: 0,
10125                    character: 22,
10126                },
10127                end: lsp::Position {
10128                    line: 0,
10129                    character: 22,
10130                },
10131            },
10132            new_text: "Some(2)".to_string(),
10133        })),
10134        additional_text_edits: Some(vec![lsp::TextEdit {
10135            range: lsp::Range {
10136                start: lsp::Position {
10137                    line: 0,
10138                    character: 20,
10139                },
10140                end: lsp::Position {
10141                    line: 0,
10142                    character: 22,
10143                },
10144            },
10145            new_text: "".to_string(),
10146        }]),
10147        ..Default::default()
10148    };
10149
10150    let closure_completion_item = completion_item.clone();
10151    let counter = Arc::new(AtomicUsize::new(0));
10152    let counter_clone = counter.clone();
10153    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10154        let task_completion_item = closure_completion_item.clone();
10155        counter_clone.fetch_add(1, atomic::Ordering::Release);
10156        async move {
10157            Ok(Some(lsp::CompletionResponse::Array(vec![
10158                task_completion_item,
10159            ])))
10160        }
10161    });
10162
10163    cx.condition(|editor, _| editor.context_menu_visible())
10164        .await;
10165    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10166    assert!(request.next().await.is_some());
10167    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10168
10169    cx.simulate_keystrokes("S o m");
10170    cx.condition(|editor, _| editor.context_menu_visible())
10171        .await;
10172    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10173    assert!(request.next().await.is_some());
10174    assert!(request.next().await.is_some());
10175    assert!(request.next().await.is_some());
10176    request.close();
10177    assert!(request.next().await.is_none());
10178    assert_eq!(
10179        counter.load(atomic::Ordering::Acquire),
10180        4,
10181        "With the completions menu open, only one LSP request should happen per input"
10182    );
10183}
10184
10185#[gpui::test]
10186async fn test_toggle_comment(cx: &mut TestAppContext) {
10187    init_test(cx, |_| {});
10188    let mut cx = EditorTestContext::new(cx).await;
10189    let language = Arc::new(Language::new(
10190        LanguageConfig {
10191            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10192            ..Default::default()
10193        },
10194        Some(tree_sitter_rust::LANGUAGE.into()),
10195    ));
10196    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10197
10198    // If multiple selections intersect a line, the line is only toggled once.
10199    cx.set_state(indoc! {"
10200        fn a() {
10201            «//b();
10202            ˇ»// «c();
10203            //ˇ»  d();
10204        }
10205    "});
10206
10207    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10208
10209    cx.assert_editor_state(indoc! {"
10210        fn a() {
10211            «b();
10212            c();
10213            ˇ» d();
10214        }
10215    "});
10216
10217    // The comment prefix is inserted at the same column for every line in a
10218    // selection.
10219    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10220
10221    cx.assert_editor_state(indoc! {"
10222        fn a() {
10223            // «b();
10224            // c();
10225            ˇ»//  d();
10226        }
10227    "});
10228
10229    // If a selection ends at the beginning of a line, that line is not toggled.
10230    cx.set_selections_state(indoc! {"
10231        fn a() {
10232            // b();
10233            «// c();
10234        ˇ»    //  d();
10235        }
10236    "});
10237
10238    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10239
10240    cx.assert_editor_state(indoc! {"
10241        fn a() {
10242            // b();
10243            «c();
10244        ˇ»    //  d();
10245        }
10246    "});
10247
10248    // If a selection span a single line and is empty, the line is toggled.
10249    cx.set_state(indoc! {"
10250        fn a() {
10251            a();
10252            b();
10253        ˇ
10254        }
10255    "});
10256
10257    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10258
10259    cx.assert_editor_state(indoc! {"
10260        fn a() {
10261            a();
10262            b();
10263        //•ˇ
10264        }
10265    "});
10266
10267    // If a selection span multiple lines, empty lines are not toggled.
10268    cx.set_state(indoc! {"
10269        fn a() {
10270            «a();
10271
10272            c();ˇ»
10273        }
10274    "});
10275
10276    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10277
10278    cx.assert_editor_state(indoc! {"
10279        fn a() {
10280            // «a();
10281
10282            // c();ˇ»
10283        }
10284    "});
10285
10286    // If a selection includes multiple comment prefixes, all lines are uncommented.
10287    cx.set_state(indoc! {"
10288        fn a() {
10289            «// a();
10290            /// b();
10291            //! c();ˇ»
10292        }
10293    "});
10294
10295    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10296
10297    cx.assert_editor_state(indoc! {"
10298        fn a() {
10299            «a();
10300            b();
10301            c();ˇ»
10302        }
10303    "});
10304}
10305
10306#[gpui::test]
10307async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10308    init_test(cx, |_| {});
10309    let mut cx = EditorTestContext::new(cx).await;
10310    let language = Arc::new(Language::new(
10311        LanguageConfig {
10312            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10313            ..Default::default()
10314        },
10315        Some(tree_sitter_rust::LANGUAGE.into()),
10316    ));
10317    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10318
10319    let toggle_comments = &ToggleComments {
10320        advance_downwards: false,
10321        ignore_indent: true,
10322    };
10323
10324    // If multiple selections intersect a line, the line is only toggled once.
10325    cx.set_state(indoc! {"
10326        fn a() {
10327        //    «b();
10328        //    c();
10329        //    ˇ» d();
10330        }
10331    "});
10332
10333    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10334
10335    cx.assert_editor_state(indoc! {"
10336        fn a() {
10337            «b();
10338            c();
10339            ˇ» d();
10340        }
10341    "});
10342
10343    // The comment prefix is inserted at the beginning of each line
10344    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10345
10346    cx.assert_editor_state(indoc! {"
10347        fn a() {
10348        //    «b();
10349        //    c();
10350        //    ˇ» d();
10351        }
10352    "});
10353
10354    // If a selection ends at the beginning of a line, that line is not toggled.
10355    cx.set_selections_state(indoc! {"
10356        fn a() {
10357        //    b();
10358        //    «c();
10359        ˇ»//     d();
10360        }
10361    "});
10362
10363    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10364
10365    cx.assert_editor_state(indoc! {"
10366        fn a() {
10367        //    b();
10368            «c();
10369        ˇ»//     d();
10370        }
10371    "});
10372
10373    // If a selection span a single line and is empty, the line is toggled.
10374    cx.set_state(indoc! {"
10375        fn a() {
10376            a();
10377            b();
10378        ˇ
10379        }
10380    "});
10381
10382    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10383
10384    cx.assert_editor_state(indoc! {"
10385        fn a() {
10386            a();
10387            b();
10388        //ˇ
10389        }
10390    "});
10391
10392    // If a selection span multiple lines, empty lines are not toggled.
10393    cx.set_state(indoc! {"
10394        fn a() {
10395            «a();
10396
10397            c();ˇ»
10398        }
10399    "});
10400
10401    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10402
10403    cx.assert_editor_state(indoc! {"
10404        fn a() {
10405        //    «a();
10406
10407        //    c();ˇ»
10408        }
10409    "});
10410
10411    // If a selection includes multiple comment prefixes, all lines are uncommented.
10412    cx.set_state(indoc! {"
10413        fn a() {
10414        //    «a();
10415        ///    b();
10416        //!    c();ˇ»
10417        }
10418    "});
10419
10420    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10421
10422    cx.assert_editor_state(indoc! {"
10423        fn a() {
10424            «a();
10425            b();
10426            c();ˇ»
10427        }
10428    "});
10429}
10430
10431#[gpui::test]
10432async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10433    init_test(cx, |_| {});
10434
10435    let language = Arc::new(Language::new(
10436        LanguageConfig {
10437            line_comments: vec!["// ".into()],
10438            ..Default::default()
10439        },
10440        Some(tree_sitter_rust::LANGUAGE.into()),
10441    ));
10442
10443    let mut cx = EditorTestContext::new(cx).await;
10444
10445    cx.language_registry().add(language.clone());
10446    cx.update_buffer(|buffer, cx| {
10447        buffer.set_language(Some(language), cx);
10448    });
10449
10450    let toggle_comments = &ToggleComments {
10451        advance_downwards: true,
10452        ignore_indent: false,
10453    };
10454
10455    // Single cursor on one line -> advance
10456    // Cursor moves horizontally 3 characters as well on non-blank line
10457    cx.set_state(indoc!(
10458        "fn a() {
10459             ˇdog();
10460             cat();
10461        }"
10462    ));
10463    cx.update_editor(|editor, window, cx| {
10464        editor.toggle_comments(toggle_comments, window, cx);
10465    });
10466    cx.assert_editor_state(indoc!(
10467        "fn a() {
10468             // dog();
10469             catˇ();
10470        }"
10471    ));
10472
10473    // Single selection on one line -> don't advance
10474    cx.set_state(indoc!(
10475        "fn a() {
10476             «dog()ˇ»;
10477             cat();
10478        }"
10479    ));
10480    cx.update_editor(|editor, window, cx| {
10481        editor.toggle_comments(toggle_comments, window, cx);
10482    });
10483    cx.assert_editor_state(indoc!(
10484        "fn a() {
10485             // «dog()ˇ»;
10486             cat();
10487        }"
10488    ));
10489
10490    // Multiple cursors on one line -> advance
10491    cx.set_state(indoc!(
10492        "fn a() {
10493             ˇdˇog();
10494             cat();
10495        }"
10496    ));
10497    cx.update_editor(|editor, window, cx| {
10498        editor.toggle_comments(toggle_comments, window, cx);
10499    });
10500    cx.assert_editor_state(indoc!(
10501        "fn a() {
10502             // dog();
10503             catˇ(ˇ);
10504        }"
10505    ));
10506
10507    // Multiple cursors on one line, with selection -> don't advance
10508    cx.set_state(indoc!(
10509        "fn a() {
10510             ˇdˇog«()ˇ»;
10511             cat();
10512        }"
10513    ));
10514    cx.update_editor(|editor, window, cx| {
10515        editor.toggle_comments(toggle_comments, window, cx);
10516    });
10517    cx.assert_editor_state(indoc!(
10518        "fn a() {
10519             // ˇdˇog«()ˇ»;
10520             cat();
10521        }"
10522    ));
10523
10524    // Single cursor on one line -> advance
10525    // Cursor moves to column 0 on blank line
10526    cx.set_state(indoc!(
10527        "fn a() {
10528             ˇdog();
10529
10530             cat();
10531        }"
10532    ));
10533    cx.update_editor(|editor, window, cx| {
10534        editor.toggle_comments(toggle_comments, window, cx);
10535    });
10536    cx.assert_editor_state(indoc!(
10537        "fn a() {
10538             // dog();
10539        ˇ
10540             cat();
10541        }"
10542    ));
10543
10544    // Single cursor on one line -> advance
10545    // Cursor starts and ends at column 0
10546    cx.set_state(indoc!(
10547        "fn a() {
10548         ˇ    dog();
10549             cat();
10550        }"
10551    ));
10552    cx.update_editor(|editor, window, cx| {
10553        editor.toggle_comments(toggle_comments, window, cx);
10554    });
10555    cx.assert_editor_state(indoc!(
10556        "fn a() {
10557             // dog();
10558         ˇ    cat();
10559        }"
10560    ));
10561}
10562
10563#[gpui::test]
10564async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10565    init_test(cx, |_| {});
10566
10567    let mut cx = EditorTestContext::new(cx).await;
10568
10569    let html_language = Arc::new(
10570        Language::new(
10571            LanguageConfig {
10572                name: "HTML".into(),
10573                block_comment: Some(("<!-- ".into(), " -->".into())),
10574                ..Default::default()
10575            },
10576            Some(tree_sitter_html::LANGUAGE.into()),
10577        )
10578        .with_injection_query(
10579            r#"
10580            (script_element
10581                (raw_text) @injection.content
10582                (#set! injection.language "javascript"))
10583            "#,
10584        )
10585        .unwrap(),
10586    );
10587
10588    let javascript_language = Arc::new(Language::new(
10589        LanguageConfig {
10590            name: "JavaScript".into(),
10591            line_comments: vec!["// ".into()],
10592            ..Default::default()
10593        },
10594        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10595    ));
10596
10597    cx.language_registry().add(html_language.clone());
10598    cx.language_registry().add(javascript_language.clone());
10599    cx.update_buffer(|buffer, cx| {
10600        buffer.set_language(Some(html_language), cx);
10601    });
10602
10603    // Toggle comments for empty selections
10604    cx.set_state(
10605        &r#"
10606            <p>A</p>ˇ
10607            <p>B</p>ˇ
10608            <p>C</p>ˇ
10609        "#
10610        .unindent(),
10611    );
10612    cx.update_editor(|editor, window, cx| {
10613        editor.toggle_comments(&ToggleComments::default(), window, cx)
10614    });
10615    cx.assert_editor_state(
10616        &r#"
10617            <!-- <p>A</p>ˇ -->
10618            <!-- <p>B</p>ˇ -->
10619            <!-- <p>C</p>ˇ -->
10620        "#
10621        .unindent(),
10622    );
10623    cx.update_editor(|editor, window, cx| {
10624        editor.toggle_comments(&ToggleComments::default(), window, cx)
10625    });
10626    cx.assert_editor_state(
10627        &r#"
10628            <p>A</p>ˇ
10629            <p>B</p>ˇ
10630            <p>C</p>ˇ
10631        "#
10632        .unindent(),
10633    );
10634
10635    // Toggle comments for mixture of empty and non-empty selections, where
10636    // multiple selections occupy a given line.
10637    cx.set_state(
10638        &r#"
10639            <p>A«</p>
10640            <p>ˇ»B</p>ˇ
10641            <p>C«</p>
10642            <p>ˇ»D</p>ˇ
10643        "#
10644        .unindent(),
10645    );
10646
10647    cx.update_editor(|editor, window, cx| {
10648        editor.toggle_comments(&ToggleComments::default(), window, cx)
10649    });
10650    cx.assert_editor_state(
10651        &r#"
10652            <!-- <p>A«</p>
10653            <p>ˇ»B</p>ˇ -->
10654            <!-- <p>C«</p>
10655            <p>ˇ»D</p>ˇ -->
10656        "#
10657        .unindent(),
10658    );
10659    cx.update_editor(|editor, window, cx| {
10660        editor.toggle_comments(&ToggleComments::default(), window, cx)
10661    });
10662    cx.assert_editor_state(
10663        &r#"
10664            <p>A«</p>
10665            <p>ˇ»B</p>ˇ
10666            <p>C«</p>
10667            <p>ˇ»D</p>ˇ
10668        "#
10669        .unindent(),
10670    );
10671
10672    // Toggle comments when different languages are active for different
10673    // selections.
10674    cx.set_state(
10675        &r#"
10676            ˇ<script>
10677                ˇvar x = new Y();
10678            ˇ</script>
10679        "#
10680        .unindent(),
10681    );
10682    cx.executor().run_until_parked();
10683    cx.update_editor(|editor, window, cx| {
10684        editor.toggle_comments(&ToggleComments::default(), window, cx)
10685    });
10686    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10687    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10688    cx.assert_editor_state(
10689        &r#"
10690            <!-- ˇ<script> -->
10691                // ˇvar x = new Y();
10692            <!-- ˇ</script> -->
10693        "#
10694        .unindent(),
10695    );
10696}
10697
10698#[gpui::test]
10699fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10700    init_test(cx, |_| {});
10701
10702    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10703    let multibuffer = cx.new(|cx| {
10704        let mut multibuffer = MultiBuffer::new(ReadWrite);
10705        multibuffer.push_excerpts(
10706            buffer.clone(),
10707            [
10708                ExcerptRange {
10709                    context: Point::new(0, 0)..Point::new(0, 4),
10710                    primary: None,
10711                },
10712                ExcerptRange {
10713                    context: Point::new(1, 0)..Point::new(1, 4),
10714                    primary: None,
10715                },
10716            ],
10717            cx,
10718        );
10719        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10720        multibuffer
10721    });
10722
10723    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10724    editor.update_in(cx, |editor, window, cx| {
10725        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10726        editor.change_selections(None, window, cx, |s| {
10727            s.select_ranges([
10728                Point::new(0, 0)..Point::new(0, 0),
10729                Point::new(1, 0)..Point::new(1, 0),
10730            ])
10731        });
10732
10733        editor.handle_input("X", window, cx);
10734        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10735        assert_eq!(
10736            editor.selections.ranges(cx),
10737            [
10738                Point::new(0, 1)..Point::new(0, 1),
10739                Point::new(1, 1)..Point::new(1, 1),
10740            ]
10741        );
10742
10743        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10744        editor.change_selections(None, window, cx, |s| {
10745            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10746        });
10747        editor.backspace(&Default::default(), window, cx);
10748        assert_eq!(editor.text(cx), "Xa\nbbb");
10749        assert_eq!(
10750            editor.selections.ranges(cx),
10751            [Point::new(1, 0)..Point::new(1, 0)]
10752        );
10753
10754        editor.change_selections(None, window, cx, |s| {
10755            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10756        });
10757        editor.backspace(&Default::default(), window, cx);
10758        assert_eq!(editor.text(cx), "X\nbb");
10759        assert_eq!(
10760            editor.selections.ranges(cx),
10761            [Point::new(0, 1)..Point::new(0, 1)]
10762        );
10763    });
10764}
10765
10766#[gpui::test]
10767fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10768    init_test(cx, |_| {});
10769
10770    let markers = vec![('[', ']').into(), ('(', ')').into()];
10771    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10772        indoc! {"
10773            [aaaa
10774            (bbbb]
10775            cccc)",
10776        },
10777        markers.clone(),
10778    );
10779    let excerpt_ranges = markers.into_iter().map(|marker| {
10780        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10781        ExcerptRange {
10782            context,
10783            primary: None,
10784        }
10785    });
10786    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10787    let multibuffer = cx.new(|cx| {
10788        let mut multibuffer = MultiBuffer::new(ReadWrite);
10789        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10790        multibuffer
10791    });
10792
10793    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10794    editor.update_in(cx, |editor, window, cx| {
10795        let (expected_text, selection_ranges) = marked_text_ranges(
10796            indoc! {"
10797                aaaa
10798                bˇbbb
10799                bˇbbˇb
10800                cccc"
10801            },
10802            true,
10803        );
10804        assert_eq!(editor.text(cx), expected_text);
10805        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10806
10807        editor.handle_input("X", window, cx);
10808
10809        let (expected_text, expected_selections) = marked_text_ranges(
10810            indoc! {"
10811                aaaa
10812                bXˇbbXb
10813                bXˇbbXˇb
10814                cccc"
10815            },
10816            false,
10817        );
10818        assert_eq!(editor.text(cx), expected_text);
10819        assert_eq!(editor.selections.ranges(cx), expected_selections);
10820
10821        editor.newline(&Newline, window, cx);
10822        let (expected_text, expected_selections) = marked_text_ranges(
10823            indoc! {"
10824                aaaa
10825                bX
10826                ˇbbX
10827                b
10828                bX
10829                ˇbbX
10830                ˇb
10831                cccc"
10832            },
10833            false,
10834        );
10835        assert_eq!(editor.text(cx), expected_text);
10836        assert_eq!(editor.selections.ranges(cx), expected_selections);
10837    });
10838}
10839
10840#[gpui::test]
10841fn test_refresh_selections(cx: &mut TestAppContext) {
10842    init_test(cx, |_| {});
10843
10844    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10845    let mut excerpt1_id = None;
10846    let multibuffer = cx.new(|cx| {
10847        let mut multibuffer = MultiBuffer::new(ReadWrite);
10848        excerpt1_id = multibuffer
10849            .push_excerpts(
10850                buffer.clone(),
10851                [
10852                    ExcerptRange {
10853                        context: Point::new(0, 0)..Point::new(1, 4),
10854                        primary: None,
10855                    },
10856                    ExcerptRange {
10857                        context: Point::new(1, 0)..Point::new(2, 4),
10858                        primary: None,
10859                    },
10860                ],
10861                cx,
10862            )
10863            .into_iter()
10864            .next();
10865        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10866        multibuffer
10867    });
10868
10869    let editor = cx.add_window(|window, cx| {
10870        let mut editor = build_editor(multibuffer.clone(), window, cx);
10871        let snapshot = editor.snapshot(window, cx);
10872        editor.change_selections(None, window, cx, |s| {
10873            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10874        });
10875        editor.begin_selection(
10876            Point::new(2, 1).to_display_point(&snapshot),
10877            true,
10878            1,
10879            window,
10880            cx,
10881        );
10882        assert_eq!(
10883            editor.selections.ranges(cx),
10884            [
10885                Point::new(1, 3)..Point::new(1, 3),
10886                Point::new(2, 1)..Point::new(2, 1),
10887            ]
10888        );
10889        editor
10890    });
10891
10892    // Refreshing selections is a no-op when excerpts haven't changed.
10893    _ = editor.update(cx, |editor, window, cx| {
10894        editor.change_selections(None, window, cx, |s| s.refresh());
10895        assert_eq!(
10896            editor.selections.ranges(cx),
10897            [
10898                Point::new(1, 3)..Point::new(1, 3),
10899                Point::new(2, 1)..Point::new(2, 1),
10900            ]
10901        );
10902    });
10903
10904    multibuffer.update(cx, |multibuffer, cx| {
10905        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10906    });
10907    _ = editor.update(cx, |editor, window, cx| {
10908        // Removing an excerpt causes the first selection to become degenerate.
10909        assert_eq!(
10910            editor.selections.ranges(cx),
10911            [
10912                Point::new(0, 0)..Point::new(0, 0),
10913                Point::new(0, 1)..Point::new(0, 1)
10914            ]
10915        );
10916
10917        // Refreshing selections will relocate the first selection to the original buffer
10918        // location.
10919        editor.change_selections(None, window, cx, |s| s.refresh());
10920        assert_eq!(
10921            editor.selections.ranges(cx),
10922            [
10923                Point::new(0, 1)..Point::new(0, 1),
10924                Point::new(0, 3)..Point::new(0, 3)
10925            ]
10926        );
10927        assert!(editor.selections.pending_anchor().is_some());
10928    });
10929}
10930
10931#[gpui::test]
10932fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10933    init_test(cx, |_| {});
10934
10935    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10936    let mut excerpt1_id = None;
10937    let multibuffer = cx.new(|cx| {
10938        let mut multibuffer = MultiBuffer::new(ReadWrite);
10939        excerpt1_id = multibuffer
10940            .push_excerpts(
10941                buffer.clone(),
10942                [
10943                    ExcerptRange {
10944                        context: Point::new(0, 0)..Point::new(1, 4),
10945                        primary: None,
10946                    },
10947                    ExcerptRange {
10948                        context: Point::new(1, 0)..Point::new(2, 4),
10949                        primary: None,
10950                    },
10951                ],
10952                cx,
10953            )
10954            .into_iter()
10955            .next();
10956        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10957        multibuffer
10958    });
10959
10960    let editor = cx.add_window(|window, cx| {
10961        let mut editor = build_editor(multibuffer.clone(), window, cx);
10962        let snapshot = editor.snapshot(window, cx);
10963        editor.begin_selection(
10964            Point::new(1, 3).to_display_point(&snapshot),
10965            false,
10966            1,
10967            window,
10968            cx,
10969        );
10970        assert_eq!(
10971            editor.selections.ranges(cx),
10972            [Point::new(1, 3)..Point::new(1, 3)]
10973        );
10974        editor
10975    });
10976
10977    multibuffer.update(cx, |multibuffer, cx| {
10978        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10979    });
10980    _ = editor.update(cx, |editor, window, cx| {
10981        assert_eq!(
10982            editor.selections.ranges(cx),
10983            [Point::new(0, 0)..Point::new(0, 0)]
10984        );
10985
10986        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10987        editor.change_selections(None, window, cx, |s| s.refresh());
10988        assert_eq!(
10989            editor.selections.ranges(cx),
10990            [Point::new(0, 3)..Point::new(0, 3)]
10991        );
10992        assert!(editor.selections.pending_anchor().is_some());
10993    });
10994}
10995
10996#[gpui::test]
10997async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10998    init_test(cx, |_| {});
10999
11000    let language = Arc::new(
11001        Language::new(
11002            LanguageConfig {
11003                brackets: BracketPairConfig {
11004                    pairs: vec![
11005                        BracketPair {
11006                            start: "{".to_string(),
11007                            end: "}".to_string(),
11008                            close: true,
11009                            surround: true,
11010                            newline: true,
11011                        },
11012                        BracketPair {
11013                            start: "/* ".to_string(),
11014                            end: " */".to_string(),
11015                            close: true,
11016                            surround: true,
11017                            newline: true,
11018                        },
11019                    ],
11020                    ..Default::default()
11021                },
11022                ..Default::default()
11023            },
11024            Some(tree_sitter_rust::LANGUAGE.into()),
11025        )
11026        .with_indents_query("")
11027        .unwrap(),
11028    );
11029
11030    let text = concat!(
11031        "{   }\n",     //
11032        "  x\n",       //
11033        "  /*   */\n", //
11034        "x\n",         //
11035        "{{} }\n",     //
11036    );
11037
11038    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11039    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11040    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11041    editor
11042        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11043        .await;
11044
11045    editor.update_in(cx, |editor, window, cx| {
11046        editor.change_selections(None, window, cx, |s| {
11047            s.select_display_ranges([
11048                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11049                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11050                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11051            ])
11052        });
11053        editor.newline(&Newline, window, cx);
11054
11055        assert_eq!(
11056            editor.buffer().read(cx).read(cx).text(),
11057            concat!(
11058                "{ \n",    // Suppress rustfmt
11059                "\n",      //
11060                "}\n",     //
11061                "  x\n",   //
11062                "  /* \n", //
11063                "  \n",    //
11064                "  */\n",  //
11065                "x\n",     //
11066                "{{} \n",  //
11067                "}\n",     //
11068            )
11069        );
11070    });
11071}
11072
11073#[gpui::test]
11074fn test_highlighted_ranges(cx: &mut TestAppContext) {
11075    init_test(cx, |_| {});
11076
11077    let editor = cx.add_window(|window, cx| {
11078        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11079        build_editor(buffer.clone(), window, cx)
11080    });
11081
11082    _ = editor.update(cx, |editor, window, cx| {
11083        struct Type1;
11084        struct Type2;
11085
11086        let buffer = editor.buffer.read(cx).snapshot(cx);
11087
11088        let anchor_range =
11089            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11090
11091        editor.highlight_background::<Type1>(
11092            &[
11093                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11094                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11095                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11096                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11097            ],
11098            |_| Hsla::red(),
11099            cx,
11100        );
11101        editor.highlight_background::<Type2>(
11102            &[
11103                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11104                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11105                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11106                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11107            ],
11108            |_| Hsla::green(),
11109            cx,
11110        );
11111
11112        let snapshot = editor.snapshot(window, cx);
11113        let mut highlighted_ranges = editor.background_highlights_in_range(
11114            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11115            &snapshot,
11116            cx.theme().colors(),
11117        );
11118        // Enforce a consistent ordering based on color without relying on the ordering of the
11119        // highlight's `TypeId` which is non-executor.
11120        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11121        assert_eq!(
11122            highlighted_ranges,
11123            &[
11124                (
11125                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11126                    Hsla::red(),
11127                ),
11128                (
11129                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11130                    Hsla::red(),
11131                ),
11132                (
11133                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11134                    Hsla::green(),
11135                ),
11136                (
11137                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11138                    Hsla::green(),
11139                ),
11140            ]
11141        );
11142        assert_eq!(
11143            editor.background_highlights_in_range(
11144                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11145                &snapshot,
11146                cx.theme().colors(),
11147            ),
11148            &[(
11149                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11150                Hsla::red(),
11151            )]
11152        );
11153    });
11154}
11155
11156#[gpui::test]
11157async fn test_following(cx: &mut TestAppContext) {
11158    init_test(cx, |_| {});
11159
11160    let fs = FakeFs::new(cx.executor());
11161    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11162
11163    let buffer = project.update(cx, |project, cx| {
11164        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11165        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11166    });
11167    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11168    let follower = cx.update(|cx| {
11169        cx.open_window(
11170            WindowOptions {
11171                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11172                    gpui::Point::new(px(0.), px(0.)),
11173                    gpui::Point::new(px(10.), px(80.)),
11174                ))),
11175                ..Default::default()
11176            },
11177            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11178        )
11179        .unwrap()
11180    });
11181
11182    let is_still_following = Rc::new(RefCell::new(true));
11183    let follower_edit_event_count = Rc::new(RefCell::new(0));
11184    let pending_update = Rc::new(RefCell::new(None));
11185    let leader_entity = leader.root(cx).unwrap();
11186    let follower_entity = follower.root(cx).unwrap();
11187    _ = follower.update(cx, {
11188        let update = pending_update.clone();
11189        let is_still_following = is_still_following.clone();
11190        let follower_edit_event_count = follower_edit_event_count.clone();
11191        |_, window, cx| {
11192            cx.subscribe_in(
11193                &leader_entity,
11194                window,
11195                move |_, leader, event, window, cx| {
11196                    leader.read(cx).add_event_to_update_proto(
11197                        event,
11198                        &mut update.borrow_mut(),
11199                        window,
11200                        cx,
11201                    );
11202                },
11203            )
11204            .detach();
11205
11206            cx.subscribe_in(
11207                &follower_entity,
11208                window,
11209                move |_, _, event: &EditorEvent, _window, _cx| {
11210                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11211                        *is_still_following.borrow_mut() = false;
11212                    }
11213
11214                    if let EditorEvent::BufferEdited = event {
11215                        *follower_edit_event_count.borrow_mut() += 1;
11216                    }
11217                },
11218            )
11219            .detach();
11220        }
11221    });
11222
11223    // Update the selections only
11224    _ = leader.update(cx, |leader, window, cx| {
11225        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11226    });
11227    follower
11228        .update(cx, |follower, window, cx| {
11229            follower.apply_update_proto(
11230                &project,
11231                pending_update.borrow_mut().take().unwrap(),
11232                window,
11233                cx,
11234            )
11235        })
11236        .unwrap()
11237        .await
11238        .unwrap();
11239    _ = follower.update(cx, |follower, _, cx| {
11240        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11241    });
11242    assert!(*is_still_following.borrow());
11243    assert_eq!(*follower_edit_event_count.borrow(), 0);
11244
11245    // Update the scroll position only
11246    _ = leader.update(cx, |leader, window, cx| {
11247        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11248    });
11249    follower
11250        .update(cx, |follower, window, cx| {
11251            follower.apply_update_proto(
11252                &project,
11253                pending_update.borrow_mut().take().unwrap(),
11254                window,
11255                cx,
11256            )
11257        })
11258        .unwrap()
11259        .await
11260        .unwrap();
11261    assert_eq!(
11262        follower
11263            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11264            .unwrap(),
11265        gpui::Point::new(1.5, 3.5)
11266    );
11267    assert!(*is_still_following.borrow());
11268    assert_eq!(*follower_edit_event_count.borrow(), 0);
11269
11270    // Update the selections and scroll position. The follower's scroll position is updated
11271    // via autoscroll, not via the leader's exact scroll position.
11272    _ = leader.update(cx, |leader, window, cx| {
11273        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11274        leader.request_autoscroll(Autoscroll::newest(), cx);
11275        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11276    });
11277    follower
11278        .update(cx, |follower, window, cx| {
11279            follower.apply_update_proto(
11280                &project,
11281                pending_update.borrow_mut().take().unwrap(),
11282                window,
11283                cx,
11284            )
11285        })
11286        .unwrap()
11287        .await
11288        .unwrap();
11289    _ = follower.update(cx, |follower, _, cx| {
11290        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11291        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11292    });
11293    assert!(*is_still_following.borrow());
11294
11295    // Creating a pending selection that precedes another selection
11296    _ = leader.update(cx, |leader, window, cx| {
11297        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11298        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11299    });
11300    follower
11301        .update(cx, |follower, window, cx| {
11302            follower.apply_update_proto(
11303                &project,
11304                pending_update.borrow_mut().take().unwrap(),
11305                window,
11306                cx,
11307            )
11308        })
11309        .unwrap()
11310        .await
11311        .unwrap();
11312    _ = follower.update(cx, |follower, _, cx| {
11313        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11314    });
11315    assert!(*is_still_following.borrow());
11316
11317    // Extend the pending selection so that it surrounds another selection
11318    _ = leader.update(cx, |leader, window, cx| {
11319        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11320    });
11321    follower
11322        .update(cx, |follower, window, cx| {
11323            follower.apply_update_proto(
11324                &project,
11325                pending_update.borrow_mut().take().unwrap(),
11326                window,
11327                cx,
11328            )
11329        })
11330        .unwrap()
11331        .await
11332        .unwrap();
11333    _ = follower.update(cx, |follower, _, cx| {
11334        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11335    });
11336
11337    // Scrolling locally breaks the follow
11338    _ = follower.update(cx, |follower, window, cx| {
11339        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11340        follower.set_scroll_anchor(
11341            ScrollAnchor {
11342                anchor: top_anchor,
11343                offset: gpui::Point::new(0.0, 0.5),
11344            },
11345            window,
11346            cx,
11347        );
11348    });
11349    assert!(!(*is_still_following.borrow()));
11350}
11351
11352#[gpui::test]
11353async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11354    init_test(cx, |_| {});
11355
11356    let fs = FakeFs::new(cx.executor());
11357    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11358    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11359    let pane = workspace
11360        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11361        .unwrap();
11362
11363    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11364
11365    let leader = pane.update_in(cx, |_, window, cx| {
11366        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11367        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11368    });
11369
11370    // Start following the editor when it has no excerpts.
11371    let mut state_message =
11372        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11373    let workspace_entity = workspace.root(cx).unwrap();
11374    let follower_1 = cx
11375        .update_window(*workspace.deref(), |_, window, cx| {
11376            Editor::from_state_proto(
11377                workspace_entity,
11378                ViewId {
11379                    creator: Default::default(),
11380                    id: 0,
11381                },
11382                &mut state_message,
11383                window,
11384                cx,
11385            )
11386        })
11387        .unwrap()
11388        .unwrap()
11389        .await
11390        .unwrap();
11391
11392    let update_message = Rc::new(RefCell::new(None));
11393    follower_1.update_in(cx, {
11394        let update = update_message.clone();
11395        |_, window, cx| {
11396            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11397                leader.read(cx).add_event_to_update_proto(
11398                    event,
11399                    &mut update.borrow_mut(),
11400                    window,
11401                    cx,
11402                );
11403            })
11404            .detach();
11405        }
11406    });
11407
11408    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11409        (
11410            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11411            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11412        )
11413    });
11414
11415    // Insert some excerpts.
11416    leader.update(cx, |leader, cx| {
11417        leader.buffer.update(cx, |multibuffer, cx| {
11418            let excerpt_ids = multibuffer.push_excerpts(
11419                buffer_1.clone(),
11420                [
11421                    ExcerptRange {
11422                        context: 1..6,
11423                        primary: None,
11424                    },
11425                    ExcerptRange {
11426                        context: 12..15,
11427                        primary: None,
11428                    },
11429                    ExcerptRange {
11430                        context: 0..3,
11431                        primary: None,
11432                    },
11433                ],
11434                cx,
11435            );
11436            multibuffer.insert_excerpts_after(
11437                excerpt_ids[0],
11438                buffer_2.clone(),
11439                [
11440                    ExcerptRange {
11441                        context: 8..12,
11442                        primary: None,
11443                    },
11444                    ExcerptRange {
11445                        context: 0..6,
11446                        primary: None,
11447                    },
11448                ],
11449                cx,
11450            );
11451        });
11452    });
11453
11454    // Apply the update of adding the excerpts.
11455    follower_1
11456        .update_in(cx, |follower, window, cx| {
11457            follower.apply_update_proto(
11458                &project,
11459                update_message.borrow().clone().unwrap(),
11460                window,
11461                cx,
11462            )
11463        })
11464        .await
11465        .unwrap();
11466    assert_eq!(
11467        follower_1.update(cx, |editor, cx| editor.text(cx)),
11468        leader.update(cx, |editor, cx| editor.text(cx))
11469    );
11470    update_message.borrow_mut().take();
11471
11472    // Start following separately after it already has excerpts.
11473    let mut state_message =
11474        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11475    let workspace_entity = workspace.root(cx).unwrap();
11476    let follower_2 = cx
11477        .update_window(*workspace.deref(), |_, window, cx| {
11478            Editor::from_state_proto(
11479                workspace_entity,
11480                ViewId {
11481                    creator: Default::default(),
11482                    id: 0,
11483                },
11484                &mut state_message,
11485                window,
11486                cx,
11487            )
11488        })
11489        .unwrap()
11490        .unwrap()
11491        .await
11492        .unwrap();
11493    assert_eq!(
11494        follower_2.update(cx, |editor, cx| editor.text(cx)),
11495        leader.update(cx, |editor, cx| editor.text(cx))
11496    );
11497
11498    // Remove some excerpts.
11499    leader.update(cx, |leader, cx| {
11500        leader.buffer.update(cx, |multibuffer, cx| {
11501            let excerpt_ids = multibuffer.excerpt_ids();
11502            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11503            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11504        });
11505    });
11506
11507    // Apply the update of removing the excerpts.
11508    follower_1
11509        .update_in(cx, |follower, window, cx| {
11510            follower.apply_update_proto(
11511                &project,
11512                update_message.borrow().clone().unwrap(),
11513                window,
11514                cx,
11515            )
11516        })
11517        .await
11518        .unwrap();
11519    follower_2
11520        .update_in(cx, |follower, window, cx| {
11521            follower.apply_update_proto(
11522                &project,
11523                update_message.borrow().clone().unwrap(),
11524                window,
11525                cx,
11526            )
11527        })
11528        .await
11529        .unwrap();
11530    update_message.borrow_mut().take();
11531    assert_eq!(
11532        follower_1.update(cx, |editor, cx| editor.text(cx)),
11533        leader.update(cx, |editor, cx| editor.text(cx))
11534    );
11535}
11536
11537#[gpui::test]
11538async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11539    init_test(cx, |_| {});
11540
11541    let mut cx = EditorTestContext::new(cx).await;
11542    let lsp_store =
11543        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11544
11545    cx.set_state(indoc! {"
11546        ˇfn func(abc def: i32) -> u32 {
11547        }
11548    "});
11549
11550    cx.update(|_, cx| {
11551        lsp_store.update(cx, |lsp_store, cx| {
11552            lsp_store
11553                .update_diagnostics(
11554                    LanguageServerId(0),
11555                    lsp::PublishDiagnosticsParams {
11556                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11557                        version: None,
11558                        diagnostics: vec![
11559                            lsp::Diagnostic {
11560                                range: lsp::Range::new(
11561                                    lsp::Position::new(0, 11),
11562                                    lsp::Position::new(0, 12),
11563                                ),
11564                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11565                                ..Default::default()
11566                            },
11567                            lsp::Diagnostic {
11568                                range: lsp::Range::new(
11569                                    lsp::Position::new(0, 12),
11570                                    lsp::Position::new(0, 15),
11571                                ),
11572                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11573                                ..Default::default()
11574                            },
11575                            lsp::Diagnostic {
11576                                range: lsp::Range::new(
11577                                    lsp::Position::new(0, 25),
11578                                    lsp::Position::new(0, 28),
11579                                ),
11580                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11581                                ..Default::default()
11582                            },
11583                        ],
11584                    },
11585                    &[],
11586                    cx,
11587                )
11588                .unwrap()
11589        });
11590    });
11591
11592    executor.run_until_parked();
11593
11594    cx.update_editor(|editor, window, cx| {
11595        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11596    });
11597
11598    cx.assert_editor_state(indoc! {"
11599        fn func(abc def: i32) -> ˇu32 {
11600        }
11601    "});
11602
11603    cx.update_editor(|editor, window, cx| {
11604        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11605    });
11606
11607    cx.assert_editor_state(indoc! {"
11608        fn func(abc ˇdef: i32) -> u32 {
11609        }
11610    "});
11611
11612    cx.update_editor(|editor, window, cx| {
11613        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11614    });
11615
11616    cx.assert_editor_state(indoc! {"
11617        fn func(abcˇ def: i32) -> u32 {
11618        }
11619    "});
11620
11621    cx.update_editor(|editor, window, cx| {
11622        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11623    });
11624
11625    cx.assert_editor_state(indoc! {"
11626        fn func(abc def: i32) -> ˇu32 {
11627        }
11628    "});
11629}
11630
11631#[gpui::test]
11632async fn cycle_through_same_place_diagnostics(
11633    executor: BackgroundExecutor,
11634    cx: &mut TestAppContext,
11635) {
11636    init_test(cx, |_| {});
11637
11638    let mut cx = EditorTestContext::new(cx).await;
11639    let lsp_store =
11640        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11641
11642    cx.set_state(indoc! {"
11643        ˇfn func(abc def: i32) -> u32 {
11644        }
11645    "});
11646
11647    cx.update(|_, cx| {
11648        lsp_store.update(cx, |lsp_store, cx| {
11649            lsp_store
11650                .update_diagnostics(
11651                    LanguageServerId(0),
11652                    lsp::PublishDiagnosticsParams {
11653                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11654                        version: None,
11655                        diagnostics: vec![
11656                            lsp::Diagnostic {
11657                                range: lsp::Range::new(
11658                                    lsp::Position::new(0, 11),
11659                                    lsp::Position::new(0, 12),
11660                                ),
11661                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11662                                ..Default::default()
11663                            },
11664                            lsp::Diagnostic {
11665                                range: lsp::Range::new(
11666                                    lsp::Position::new(0, 12),
11667                                    lsp::Position::new(0, 15),
11668                                ),
11669                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11670                                ..Default::default()
11671                            },
11672                            lsp::Diagnostic {
11673                                range: lsp::Range::new(
11674                                    lsp::Position::new(0, 12),
11675                                    lsp::Position::new(0, 15),
11676                                ),
11677                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11678                                ..Default::default()
11679                            },
11680                            lsp::Diagnostic {
11681                                range: lsp::Range::new(
11682                                    lsp::Position::new(0, 25),
11683                                    lsp::Position::new(0, 28),
11684                                ),
11685                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11686                                ..Default::default()
11687                            },
11688                        ],
11689                    },
11690                    &[],
11691                    cx,
11692                )
11693                .unwrap()
11694        });
11695    });
11696    executor.run_until_parked();
11697
11698    //// Backward
11699
11700    // Fourth diagnostic
11701    cx.update_editor(|editor, window, cx| {
11702        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11703    });
11704    cx.assert_editor_state(indoc! {"
11705        fn func(abc def: i32) -> ˇu32 {
11706        }
11707    "});
11708
11709    // Third diagnostic
11710    cx.update_editor(|editor, window, cx| {
11711        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11712    });
11713    cx.assert_editor_state(indoc! {"
11714        fn func(abc ˇdef: i32) -> u32 {
11715        }
11716    "});
11717
11718    // Second diagnostic, same place
11719    cx.update_editor(|editor, window, cx| {
11720        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11721    });
11722    cx.assert_editor_state(indoc! {"
11723        fn func(abc ˇdef: i32) -> u32 {
11724        }
11725    "});
11726
11727    // First diagnostic
11728    cx.update_editor(|editor, window, cx| {
11729        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11730    });
11731    cx.assert_editor_state(indoc! {"
11732        fn func(abcˇ def: i32) -> u32 {
11733        }
11734    "});
11735
11736    // Wrapped over, fourth diagnostic
11737    cx.update_editor(|editor, window, cx| {
11738        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11739    });
11740    cx.assert_editor_state(indoc! {"
11741        fn func(abc def: i32) -> ˇu32 {
11742        }
11743    "});
11744
11745    cx.update_editor(|editor, window, cx| {
11746        editor.move_to_beginning(&MoveToBeginning, window, cx);
11747    });
11748    cx.assert_editor_state(indoc! {"
11749        ˇfn func(abc def: i32) -> u32 {
11750        }
11751    "});
11752
11753    //// Forward
11754
11755    // First diagnostic
11756    cx.update_editor(|editor, window, cx| {
11757        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11758    });
11759    cx.assert_editor_state(indoc! {"
11760        fn func(abcˇ def: i32) -> u32 {
11761        }
11762    "});
11763
11764    // Second diagnostic
11765    cx.update_editor(|editor, window, cx| {
11766        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11767    });
11768    cx.assert_editor_state(indoc! {"
11769        fn func(abc ˇdef: i32) -> u32 {
11770        }
11771    "});
11772
11773    // Third diagnostic, same place
11774    cx.update_editor(|editor, window, cx| {
11775        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11776    });
11777    cx.assert_editor_state(indoc! {"
11778        fn func(abc ˇdef: i32) -> u32 {
11779        }
11780    "});
11781
11782    // Fourth diagnostic
11783    cx.update_editor(|editor, window, cx| {
11784        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11785    });
11786    cx.assert_editor_state(indoc! {"
11787        fn func(abc def: i32) -> ˇu32 {
11788        }
11789    "});
11790
11791    // Wrapped around, first diagnostic
11792    cx.update_editor(|editor, window, cx| {
11793        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11794    });
11795    cx.assert_editor_state(indoc! {"
11796        fn func(abcˇ def: i32) -> u32 {
11797        }
11798    "});
11799}
11800
11801#[gpui::test]
11802async fn active_diagnostics_dismiss_after_invalidation(
11803    executor: BackgroundExecutor,
11804    cx: &mut TestAppContext,
11805) {
11806    init_test(cx, |_| {});
11807
11808    let mut cx = EditorTestContext::new(cx).await;
11809    let lsp_store =
11810        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11811
11812    cx.set_state(indoc! {"
11813        ˇfn func(abc def: i32) -> u32 {
11814        }
11815    "});
11816
11817    let message = "Something's wrong!";
11818    cx.update(|_, cx| {
11819        lsp_store.update(cx, |lsp_store, cx| {
11820            lsp_store
11821                .update_diagnostics(
11822                    LanguageServerId(0),
11823                    lsp::PublishDiagnosticsParams {
11824                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11825                        version: None,
11826                        diagnostics: vec![lsp::Diagnostic {
11827                            range: lsp::Range::new(
11828                                lsp::Position::new(0, 11),
11829                                lsp::Position::new(0, 12),
11830                            ),
11831                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11832                            message: message.to_string(),
11833                            ..Default::default()
11834                        }],
11835                    },
11836                    &[],
11837                    cx,
11838                )
11839                .unwrap()
11840        });
11841    });
11842    executor.run_until_parked();
11843
11844    cx.update_editor(|editor, window, cx| {
11845        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11846        assert_eq!(
11847            editor
11848                .active_diagnostics
11849                .as_ref()
11850                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11851            Some(message),
11852            "Should have a diagnostics group activated"
11853        );
11854    });
11855    cx.assert_editor_state(indoc! {"
11856        fn func(abcˇ def: i32) -> u32 {
11857        }
11858    "});
11859
11860    cx.update(|_, cx| {
11861        lsp_store.update(cx, |lsp_store, cx| {
11862            lsp_store
11863                .update_diagnostics(
11864                    LanguageServerId(0),
11865                    lsp::PublishDiagnosticsParams {
11866                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11867                        version: None,
11868                        diagnostics: Vec::new(),
11869                    },
11870                    &[],
11871                    cx,
11872                )
11873                .unwrap()
11874        });
11875    });
11876    executor.run_until_parked();
11877    cx.update_editor(|editor, _, _| {
11878        assert_eq!(
11879            editor.active_diagnostics, None,
11880            "After no diagnostics set to the editor, no diagnostics should be active"
11881        );
11882    });
11883    cx.assert_editor_state(indoc! {"
11884        fn func(abcˇ def: i32) -> u32 {
11885        }
11886    "});
11887
11888    cx.update_editor(|editor, window, cx| {
11889        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11890        assert_eq!(
11891            editor.active_diagnostics, None,
11892            "Should be no diagnostics to go to and activate"
11893        );
11894    });
11895    cx.assert_editor_state(indoc! {"
11896        fn func(abcˇ def: i32) -> u32 {
11897        }
11898    "});
11899}
11900
11901#[gpui::test]
11902async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11903    init_test(cx, |_| {});
11904
11905    let mut cx = EditorTestContext::new(cx).await;
11906
11907    cx.set_state(indoc! {"
11908        fn func(abˇc def: i32) -> u32 {
11909        }
11910    "});
11911    let lsp_store =
11912        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11913
11914    cx.update(|_, cx| {
11915        lsp_store.update(cx, |lsp_store, cx| {
11916            lsp_store.update_diagnostics(
11917                LanguageServerId(0),
11918                lsp::PublishDiagnosticsParams {
11919                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11920                    version: None,
11921                    diagnostics: vec![lsp::Diagnostic {
11922                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11923                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11924                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11925                        ..Default::default()
11926                    }],
11927                },
11928                &[],
11929                cx,
11930            )
11931        })
11932    }).unwrap();
11933    cx.run_until_parked();
11934    cx.update_editor(|editor, window, cx| {
11935        hover_popover::hover(editor, &Default::default(), window, cx)
11936    });
11937    cx.run_until_parked();
11938    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11939}
11940
11941#[gpui::test]
11942async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11943    init_test(cx, |_| {});
11944
11945    let mut cx = EditorTestContext::new(cx).await;
11946
11947    let diff_base = r#"
11948        use some::mod;
11949
11950        const A: u32 = 42;
11951
11952        fn main() {
11953            println!("hello");
11954
11955            println!("world");
11956        }
11957        "#
11958    .unindent();
11959
11960    // Edits are modified, removed, modified, added
11961    cx.set_state(
11962        &r#"
11963        use some::modified;
11964
11965        ˇ
11966        fn main() {
11967            println!("hello there");
11968
11969            println!("around the");
11970            println!("world");
11971        }
11972        "#
11973        .unindent(),
11974    );
11975
11976    cx.set_head_text(&diff_base);
11977    executor.run_until_parked();
11978
11979    cx.update_editor(|editor, window, cx| {
11980        //Wrap around the bottom of the buffer
11981        for _ in 0..3 {
11982            editor.go_to_next_hunk(&GoToHunk, window, cx);
11983        }
11984    });
11985
11986    cx.assert_editor_state(
11987        &r#"
11988        ˇuse some::modified;
11989
11990
11991        fn main() {
11992            println!("hello there");
11993
11994            println!("around the");
11995            println!("world");
11996        }
11997        "#
11998        .unindent(),
11999    );
12000
12001    cx.update_editor(|editor, window, cx| {
12002        //Wrap around the top of the buffer
12003        for _ in 0..2 {
12004            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12005        }
12006    });
12007
12008    cx.assert_editor_state(
12009        &r#"
12010        use some::modified;
12011
12012
12013        fn main() {
12014        ˇ    println!("hello there");
12015
12016            println!("around the");
12017            println!("world");
12018        }
12019        "#
12020        .unindent(),
12021    );
12022
12023    cx.update_editor(|editor, window, cx| {
12024        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12025    });
12026
12027    cx.assert_editor_state(
12028        &r#"
12029        use some::modified;
12030
12031        ˇ
12032        fn main() {
12033            println!("hello there");
12034
12035            println!("around the");
12036            println!("world");
12037        }
12038        "#
12039        .unindent(),
12040    );
12041
12042    cx.update_editor(|editor, window, cx| {
12043        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12044    });
12045
12046    cx.assert_editor_state(
12047        &r#"
12048        ˇuse some::modified;
12049
12050
12051        fn main() {
12052            println!("hello there");
12053
12054            println!("around the");
12055            println!("world");
12056        }
12057        "#
12058        .unindent(),
12059    );
12060
12061    cx.update_editor(|editor, window, cx| {
12062        for _ in 0..2 {
12063            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12064        }
12065    });
12066
12067    cx.assert_editor_state(
12068        &r#"
12069        use some::modified;
12070
12071
12072        fn main() {
12073        ˇ    println!("hello there");
12074
12075            println!("around the");
12076            println!("world");
12077        }
12078        "#
12079        .unindent(),
12080    );
12081
12082    cx.update_editor(|editor, window, cx| {
12083        editor.fold(&Fold, window, cx);
12084    });
12085
12086    cx.update_editor(|editor, window, cx| {
12087        editor.go_to_next_hunk(&GoToHunk, window, cx);
12088    });
12089
12090    cx.assert_editor_state(
12091        &r#"
12092        ˇuse some::modified;
12093
12094
12095        fn main() {
12096            println!("hello there");
12097
12098            println!("around the");
12099            println!("world");
12100        }
12101        "#
12102        .unindent(),
12103    );
12104}
12105
12106#[test]
12107fn test_split_words() {
12108    fn split(text: &str) -> Vec<&str> {
12109        split_words(text).collect()
12110    }
12111
12112    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12113    assert_eq!(split("hello_world"), &["hello_", "world"]);
12114    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12115    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12116    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12117    assert_eq!(split("helloworld"), &["helloworld"]);
12118
12119    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12120}
12121
12122#[gpui::test]
12123async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12124    init_test(cx, |_| {});
12125
12126    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12127    let mut assert = |before, after| {
12128        let _state_context = cx.set_state(before);
12129        cx.run_until_parked();
12130        cx.update_editor(|editor, window, cx| {
12131            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12132        });
12133        cx.run_until_parked();
12134        cx.assert_editor_state(after);
12135    };
12136
12137    // Outside bracket jumps to outside of matching bracket
12138    assert("console.logˇ(var);", "console.log(var)ˇ;");
12139    assert("console.log(var)ˇ;", "console.logˇ(var);");
12140
12141    // Inside bracket jumps to inside of matching bracket
12142    assert("console.log(ˇvar);", "console.log(varˇ);");
12143    assert("console.log(varˇ);", "console.log(ˇvar);");
12144
12145    // When outside a bracket and inside, favor jumping to the inside bracket
12146    assert(
12147        "console.log('foo', [1, 2, 3]ˇ);",
12148        "console.log(ˇ'foo', [1, 2, 3]);",
12149    );
12150    assert(
12151        "console.log(ˇ'foo', [1, 2, 3]);",
12152        "console.log('foo', [1, 2, 3]ˇ);",
12153    );
12154
12155    // Bias forward if two options are equally likely
12156    assert(
12157        "let result = curried_fun()ˇ();",
12158        "let result = curried_fun()()ˇ;",
12159    );
12160
12161    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12162    assert(
12163        indoc! {"
12164            function test() {
12165                console.log('test')ˇ
12166            }"},
12167        indoc! {"
12168            function test() {
12169                console.logˇ('test')
12170            }"},
12171    );
12172}
12173
12174#[gpui::test]
12175async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12176    init_test(cx, |_| {});
12177
12178    let fs = FakeFs::new(cx.executor());
12179    fs.insert_tree(
12180        path!("/a"),
12181        json!({
12182            "main.rs": "fn main() { let a = 5; }",
12183            "other.rs": "// Test file",
12184        }),
12185    )
12186    .await;
12187    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12188
12189    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12190    language_registry.add(Arc::new(Language::new(
12191        LanguageConfig {
12192            name: "Rust".into(),
12193            matcher: LanguageMatcher {
12194                path_suffixes: vec!["rs".to_string()],
12195                ..Default::default()
12196            },
12197            brackets: BracketPairConfig {
12198                pairs: vec![BracketPair {
12199                    start: "{".to_string(),
12200                    end: "}".to_string(),
12201                    close: true,
12202                    surround: true,
12203                    newline: true,
12204                }],
12205                disabled_scopes_by_bracket_ix: Vec::new(),
12206            },
12207            ..Default::default()
12208        },
12209        Some(tree_sitter_rust::LANGUAGE.into()),
12210    )));
12211    let mut fake_servers = language_registry.register_fake_lsp(
12212        "Rust",
12213        FakeLspAdapter {
12214            capabilities: lsp::ServerCapabilities {
12215                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12216                    first_trigger_character: "{".to_string(),
12217                    more_trigger_character: None,
12218                }),
12219                ..Default::default()
12220            },
12221            ..Default::default()
12222        },
12223    );
12224
12225    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12226
12227    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12228
12229    let worktree_id = workspace
12230        .update(cx, |workspace, _, cx| {
12231            workspace.project().update(cx, |project, cx| {
12232                project.worktrees(cx).next().unwrap().read(cx).id()
12233            })
12234        })
12235        .unwrap();
12236
12237    let buffer = project
12238        .update(cx, |project, cx| {
12239            project.open_local_buffer(path!("/a/main.rs"), cx)
12240        })
12241        .await
12242        .unwrap();
12243    let editor_handle = workspace
12244        .update(cx, |workspace, window, cx| {
12245            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12246        })
12247        .unwrap()
12248        .await
12249        .unwrap()
12250        .downcast::<Editor>()
12251        .unwrap();
12252
12253    cx.executor().start_waiting();
12254    let fake_server = fake_servers.next().await.unwrap();
12255
12256    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12257        |params, _| async move {
12258            assert_eq!(
12259                params.text_document_position.text_document.uri,
12260                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12261            );
12262            assert_eq!(
12263                params.text_document_position.position,
12264                lsp::Position::new(0, 21),
12265            );
12266
12267            Ok(Some(vec![lsp::TextEdit {
12268                new_text: "]".to_string(),
12269                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12270            }]))
12271        },
12272    );
12273
12274    editor_handle.update_in(cx, |editor, window, cx| {
12275        window.focus(&editor.focus_handle(cx));
12276        editor.change_selections(None, window, cx, |s| {
12277            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12278        });
12279        editor.handle_input("{", window, cx);
12280    });
12281
12282    cx.executor().run_until_parked();
12283
12284    buffer.update(cx, |buffer, _| {
12285        assert_eq!(
12286            buffer.text(),
12287            "fn main() { let a = {5}; }",
12288            "No extra braces from on type formatting should appear in the buffer"
12289        )
12290    });
12291}
12292
12293#[gpui::test]
12294async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12295    init_test(cx, |_| {});
12296
12297    let fs = FakeFs::new(cx.executor());
12298    fs.insert_tree(
12299        path!("/a"),
12300        json!({
12301            "main.rs": "fn main() { let a = 5; }",
12302            "other.rs": "// Test file",
12303        }),
12304    )
12305    .await;
12306
12307    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12308
12309    let server_restarts = Arc::new(AtomicUsize::new(0));
12310    let closure_restarts = Arc::clone(&server_restarts);
12311    let language_server_name = "test language server";
12312    let language_name: LanguageName = "Rust".into();
12313
12314    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12315    language_registry.add(Arc::new(Language::new(
12316        LanguageConfig {
12317            name: language_name.clone(),
12318            matcher: LanguageMatcher {
12319                path_suffixes: vec!["rs".to_string()],
12320                ..Default::default()
12321            },
12322            ..Default::default()
12323        },
12324        Some(tree_sitter_rust::LANGUAGE.into()),
12325    )));
12326    let mut fake_servers = language_registry.register_fake_lsp(
12327        "Rust",
12328        FakeLspAdapter {
12329            name: language_server_name,
12330            initialization_options: Some(json!({
12331                "testOptionValue": true
12332            })),
12333            initializer: Some(Box::new(move |fake_server| {
12334                let task_restarts = Arc::clone(&closure_restarts);
12335                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12336                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12337                    futures::future::ready(Ok(()))
12338                });
12339            })),
12340            ..Default::default()
12341        },
12342    );
12343
12344    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12345    let _buffer = project
12346        .update(cx, |project, cx| {
12347            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12348        })
12349        .await
12350        .unwrap();
12351    let _fake_server = fake_servers.next().await.unwrap();
12352    update_test_language_settings(cx, |language_settings| {
12353        language_settings.languages.insert(
12354            language_name.clone(),
12355            LanguageSettingsContent {
12356                tab_size: NonZeroU32::new(8),
12357                ..Default::default()
12358            },
12359        );
12360    });
12361    cx.executor().run_until_parked();
12362    assert_eq!(
12363        server_restarts.load(atomic::Ordering::Acquire),
12364        0,
12365        "Should not restart LSP server on an unrelated change"
12366    );
12367
12368    update_test_project_settings(cx, |project_settings| {
12369        project_settings.lsp.insert(
12370            "Some other server name".into(),
12371            LspSettings {
12372                binary: None,
12373                settings: None,
12374                initialization_options: Some(json!({
12375                    "some other init value": false
12376                })),
12377            },
12378        );
12379    });
12380    cx.executor().run_until_parked();
12381    assert_eq!(
12382        server_restarts.load(atomic::Ordering::Acquire),
12383        0,
12384        "Should not restart LSP server on an unrelated LSP settings change"
12385    );
12386
12387    update_test_project_settings(cx, |project_settings| {
12388        project_settings.lsp.insert(
12389            language_server_name.into(),
12390            LspSettings {
12391                binary: None,
12392                settings: None,
12393                initialization_options: Some(json!({
12394                    "anotherInitValue": false
12395                })),
12396            },
12397        );
12398    });
12399    cx.executor().run_until_parked();
12400    assert_eq!(
12401        server_restarts.load(atomic::Ordering::Acquire),
12402        1,
12403        "Should restart LSP server on a related LSP settings change"
12404    );
12405
12406    update_test_project_settings(cx, |project_settings| {
12407        project_settings.lsp.insert(
12408            language_server_name.into(),
12409            LspSettings {
12410                binary: None,
12411                settings: None,
12412                initialization_options: Some(json!({
12413                    "anotherInitValue": false
12414                })),
12415            },
12416        );
12417    });
12418    cx.executor().run_until_parked();
12419    assert_eq!(
12420        server_restarts.load(atomic::Ordering::Acquire),
12421        1,
12422        "Should not restart LSP server on a related LSP settings change that is the same"
12423    );
12424
12425    update_test_project_settings(cx, |project_settings| {
12426        project_settings.lsp.insert(
12427            language_server_name.into(),
12428            LspSettings {
12429                binary: None,
12430                settings: None,
12431                initialization_options: None,
12432            },
12433        );
12434    });
12435    cx.executor().run_until_parked();
12436    assert_eq!(
12437        server_restarts.load(atomic::Ordering::Acquire),
12438        2,
12439        "Should restart LSP server on another related LSP settings change"
12440    );
12441}
12442
12443#[gpui::test]
12444async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12445    init_test(cx, |_| {});
12446
12447    let mut cx = EditorLspTestContext::new_rust(
12448        lsp::ServerCapabilities {
12449            completion_provider: Some(lsp::CompletionOptions {
12450                trigger_characters: Some(vec![".".to_string()]),
12451                resolve_provider: Some(true),
12452                ..Default::default()
12453            }),
12454            ..Default::default()
12455        },
12456        cx,
12457    )
12458    .await;
12459
12460    cx.set_state("fn main() { let a = 2ˇ; }");
12461    cx.simulate_keystroke(".");
12462    let completion_item = lsp::CompletionItem {
12463        label: "some".into(),
12464        kind: Some(lsp::CompletionItemKind::SNIPPET),
12465        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12466        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12467            kind: lsp::MarkupKind::Markdown,
12468            value: "```rust\nSome(2)\n```".to_string(),
12469        })),
12470        deprecated: Some(false),
12471        sort_text: Some("fffffff2".to_string()),
12472        filter_text: Some("some".to_string()),
12473        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12474        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12475            range: lsp::Range {
12476                start: lsp::Position {
12477                    line: 0,
12478                    character: 22,
12479                },
12480                end: lsp::Position {
12481                    line: 0,
12482                    character: 22,
12483                },
12484            },
12485            new_text: "Some(2)".to_string(),
12486        })),
12487        additional_text_edits: Some(vec![lsp::TextEdit {
12488            range: lsp::Range {
12489                start: lsp::Position {
12490                    line: 0,
12491                    character: 20,
12492                },
12493                end: lsp::Position {
12494                    line: 0,
12495                    character: 22,
12496                },
12497            },
12498            new_text: "".to_string(),
12499        }]),
12500        ..Default::default()
12501    };
12502
12503    let closure_completion_item = completion_item.clone();
12504    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12505        let task_completion_item = closure_completion_item.clone();
12506        async move {
12507            Ok(Some(lsp::CompletionResponse::Array(vec![
12508                task_completion_item,
12509            ])))
12510        }
12511    });
12512
12513    request.next().await;
12514
12515    cx.condition(|editor, _| editor.context_menu_visible())
12516        .await;
12517    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12518        editor
12519            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12520            .unwrap()
12521    });
12522    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12523
12524    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12525        let task_completion_item = completion_item.clone();
12526        async move { Ok(task_completion_item) }
12527    })
12528    .next()
12529    .await
12530    .unwrap();
12531    apply_additional_edits.await.unwrap();
12532    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12533}
12534
12535#[gpui::test]
12536async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12537    init_test(cx, |_| {});
12538
12539    let mut cx = EditorLspTestContext::new_rust(
12540        lsp::ServerCapabilities {
12541            completion_provider: Some(lsp::CompletionOptions {
12542                trigger_characters: Some(vec![".".to_string()]),
12543                resolve_provider: Some(true),
12544                ..Default::default()
12545            }),
12546            ..Default::default()
12547        },
12548        cx,
12549    )
12550    .await;
12551
12552    cx.set_state("fn main() { let a = 2ˇ; }");
12553    cx.simulate_keystroke(".");
12554
12555    let item1 = lsp::CompletionItem {
12556        label: "method id()".to_string(),
12557        filter_text: Some("id".to_string()),
12558        detail: None,
12559        documentation: None,
12560        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12561            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12562            new_text: ".id".to_string(),
12563        })),
12564        ..lsp::CompletionItem::default()
12565    };
12566
12567    let item2 = lsp::CompletionItem {
12568        label: "other".to_string(),
12569        filter_text: Some("other".to_string()),
12570        detail: None,
12571        documentation: None,
12572        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12573            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12574            new_text: ".other".to_string(),
12575        })),
12576        ..lsp::CompletionItem::default()
12577    };
12578
12579    let item1 = item1.clone();
12580    cx.set_request_handler::<lsp::request::Completion, _, _>({
12581        let item1 = item1.clone();
12582        move |_, _, _| {
12583            let item1 = item1.clone();
12584            let item2 = item2.clone();
12585            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12586        }
12587    })
12588    .next()
12589    .await;
12590
12591    cx.condition(|editor, _| editor.context_menu_visible())
12592        .await;
12593    cx.update_editor(|editor, _, _| {
12594        let context_menu = editor.context_menu.borrow_mut();
12595        let context_menu = context_menu
12596            .as_ref()
12597            .expect("Should have the context menu deployed");
12598        match context_menu {
12599            CodeContextMenu::Completions(completions_menu) => {
12600                let completions = completions_menu.completions.borrow_mut();
12601                assert_eq!(
12602                    completions
12603                        .iter()
12604                        .map(|completion| &completion.label.text)
12605                        .collect::<Vec<_>>(),
12606                    vec!["method id()", "other"]
12607                )
12608            }
12609            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12610        }
12611    });
12612
12613    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12614        let item1 = item1.clone();
12615        move |_, item_to_resolve, _| {
12616            let item1 = item1.clone();
12617            async move {
12618                if item1 == item_to_resolve {
12619                    Ok(lsp::CompletionItem {
12620                        label: "method id()".to_string(),
12621                        filter_text: Some("id".to_string()),
12622                        detail: Some("Now resolved!".to_string()),
12623                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12624                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12625                            range: lsp::Range::new(
12626                                lsp::Position::new(0, 22),
12627                                lsp::Position::new(0, 22),
12628                            ),
12629                            new_text: ".id".to_string(),
12630                        })),
12631                        ..lsp::CompletionItem::default()
12632                    })
12633                } else {
12634                    Ok(item_to_resolve)
12635                }
12636            }
12637        }
12638    })
12639    .next()
12640    .await
12641    .unwrap();
12642    cx.run_until_parked();
12643
12644    cx.update_editor(|editor, window, cx| {
12645        editor.context_menu_next(&Default::default(), window, cx);
12646    });
12647
12648    cx.update_editor(|editor, _, _| {
12649        let context_menu = editor.context_menu.borrow_mut();
12650        let context_menu = context_menu
12651            .as_ref()
12652            .expect("Should have the context menu deployed");
12653        match context_menu {
12654            CodeContextMenu::Completions(completions_menu) => {
12655                let completions = completions_menu.completions.borrow_mut();
12656                assert_eq!(
12657                    completions
12658                        .iter()
12659                        .map(|completion| &completion.label.text)
12660                        .collect::<Vec<_>>(),
12661                    vec!["method id() Now resolved!", "other"],
12662                    "Should update first completion label, but not second as the filter text did not match."
12663                );
12664            }
12665            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12666        }
12667    });
12668}
12669
12670#[gpui::test]
12671async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12672    init_test(cx, |_| {});
12673
12674    let mut cx = EditorLspTestContext::new_rust(
12675        lsp::ServerCapabilities {
12676            completion_provider: Some(lsp::CompletionOptions {
12677                trigger_characters: Some(vec![".".to_string()]),
12678                resolve_provider: Some(true),
12679                ..Default::default()
12680            }),
12681            ..Default::default()
12682        },
12683        cx,
12684    )
12685    .await;
12686
12687    cx.set_state("fn main() { let a = 2ˇ; }");
12688    cx.simulate_keystroke(".");
12689
12690    let unresolved_item_1 = lsp::CompletionItem {
12691        label: "id".to_string(),
12692        filter_text: Some("id".to_string()),
12693        detail: None,
12694        documentation: None,
12695        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12696            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12697            new_text: ".id".to_string(),
12698        })),
12699        ..lsp::CompletionItem::default()
12700    };
12701    let resolved_item_1 = lsp::CompletionItem {
12702        additional_text_edits: Some(vec![lsp::TextEdit {
12703            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12704            new_text: "!!".to_string(),
12705        }]),
12706        ..unresolved_item_1.clone()
12707    };
12708    let unresolved_item_2 = lsp::CompletionItem {
12709        label: "other".to_string(),
12710        filter_text: Some("other".to_string()),
12711        detail: None,
12712        documentation: None,
12713        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12714            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12715            new_text: ".other".to_string(),
12716        })),
12717        ..lsp::CompletionItem::default()
12718    };
12719    let resolved_item_2 = lsp::CompletionItem {
12720        additional_text_edits: Some(vec![lsp::TextEdit {
12721            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12722            new_text: "??".to_string(),
12723        }]),
12724        ..unresolved_item_2.clone()
12725    };
12726
12727    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12728    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12729    cx.lsp
12730        .server
12731        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12732            let unresolved_item_1 = unresolved_item_1.clone();
12733            let resolved_item_1 = resolved_item_1.clone();
12734            let unresolved_item_2 = unresolved_item_2.clone();
12735            let resolved_item_2 = resolved_item_2.clone();
12736            let resolve_requests_1 = resolve_requests_1.clone();
12737            let resolve_requests_2 = resolve_requests_2.clone();
12738            move |unresolved_request, _| {
12739                let unresolved_item_1 = unresolved_item_1.clone();
12740                let resolved_item_1 = resolved_item_1.clone();
12741                let unresolved_item_2 = unresolved_item_2.clone();
12742                let resolved_item_2 = resolved_item_2.clone();
12743                let resolve_requests_1 = resolve_requests_1.clone();
12744                let resolve_requests_2 = resolve_requests_2.clone();
12745                async move {
12746                    if unresolved_request == unresolved_item_1 {
12747                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12748                        Ok(resolved_item_1.clone())
12749                    } else if unresolved_request == unresolved_item_2 {
12750                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12751                        Ok(resolved_item_2.clone())
12752                    } else {
12753                        panic!("Unexpected completion item {unresolved_request:?}")
12754                    }
12755                }
12756            }
12757        })
12758        .detach();
12759
12760    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12761        let unresolved_item_1 = unresolved_item_1.clone();
12762        let unresolved_item_2 = unresolved_item_2.clone();
12763        async move {
12764            Ok(Some(lsp::CompletionResponse::Array(vec![
12765                unresolved_item_1,
12766                unresolved_item_2,
12767            ])))
12768        }
12769    })
12770    .next()
12771    .await;
12772
12773    cx.condition(|editor, _| editor.context_menu_visible())
12774        .await;
12775    cx.update_editor(|editor, _, _| {
12776        let context_menu = editor.context_menu.borrow_mut();
12777        let context_menu = context_menu
12778            .as_ref()
12779            .expect("Should have the context menu deployed");
12780        match context_menu {
12781            CodeContextMenu::Completions(completions_menu) => {
12782                let completions = completions_menu.completions.borrow_mut();
12783                assert_eq!(
12784                    completions
12785                        .iter()
12786                        .map(|completion| &completion.label.text)
12787                        .collect::<Vec<_>>(),
12788                    vec!["id", "other"]
12789                )
12790            }
12791            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12792        }
12793    });
12794    cx.run_until_parked();
12795
12796    cx.update_editor(|editor, window, cx| {
12797        editor.context_menu_next(&ContextMenuNext, window, cx);
12798    });
12799    cx.run_until_parked();
12800    cx.update_editor(|editor, window, cx| {
12801        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12802    });
12803    cx.run_until_parked();
12804    cx.update_editor(|editor, window, cx| {
12805        editor.context_menu_next(&ContextMenuNext, window, cx);
12806    });
12807    cx.run_until_parked();
12808    cx.update_editor(|editor, window, cx| {
12809        editor
12810            .compose_completion(&ComposeCompletion::default(), window, cx)
12811            .expect("No task returned")
12812    })
12813    .await
12814    .expect("Completion failed");
12815    cx.run_until_parked();
12816
12817    cx.update_editor(|editor, _, cx| {
12818        assert_eq!(
12819            resolve_requests_1.load(atomic::Ordering::Acquire),
12820            1,
12821            "Should always resolve once despite multiple selections"
12822        );
12823        assert_eq!(
12824            resolve_requests_2.load(atomic::Ordering::Acquire),
12825            1,
12826            "Should always resolve once after multiple selections and applying the completion"
12827        );
12828        assert_eq!(
12829            editor.text(cx),
12830            "fn main() { let a = ??.other; }",
12831            "Should use resolved data when applying the completion"
12832        );
12833    });
12834}
12835
12836#[gpui::test]
12837async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12838    init_test(cx, |_| {});
12839
12840    let item_0 = lsp::CompletionItem {
12841        label: "abs".into(),
12842        insert_text: Some("abs".into()),
12843        data: Some(json!({ "very": "special"})),
12844        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12845        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12846            lsp::InsertReplaceEdit {
12847                new_text: "abs".to_string(),
12848                insert: lsp::Range::default(),
12849                replace: lsp::Range::default(),
12850            },
12851        )),
12852        ..lsp::CompletionItem::default()
12853    };
12854    let items = iter::once(item_0.clone())
12855        .chain((11..51).map(|i| lsp::CompletionItem {
12856            label: format!("item_{}", i),
12857            insert_text: Some(format!("item_{}", i)),
12858            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12859            ..lsp::CompletionItem::default()
12860        }))
12861        .collect::<Vec<_>>();
12862
12863    let default_commit_characters = vec!["?".to_string()];
12864    let default_data = json!({ "default": "data"});
12865    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12866    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12867    let default_edit_range = lsp::Range {
12868        start: lsp::Position {
12869            line: 0,
12870            character: 5,
12871        },
12872        end: lsp::Position {
12873            line: 0,
12874            character: 5,
12875        },
12876    };
12877
12878    let mut cx = EditorLspTestContext::new_rust(
12879        lsp::ServerCapabilities {
12880            completion_provider: Some(lsp::CompletionOptions {
12881                trigger_characters: Some(vec![".".to_string()]),
12882                resolve_provider: Some(true),
12883                ..Default::default()
12884            }),
12885            ..Default::default()
12886        },
12887        cx,
12888    )
12889    .await;
12890
12891    cx.set_state("fn main() { let a = 2ˇ; }");
12892    cx.simulate_keystroke(".");
12893
12894    let completion_data = default_data.clone();
12895    let completion_characters = default_commit_characters.clone();
12896    let completion_items = items.clone();
12897    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12898        let default_data = completion_data.clone();
12899        let default_commit_characters = completion_characters.clone();
12900        let items = completion_items.clone();
12901        async move {
12902            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12903                items,
12904                item_defaults: Some(lsp::CompletionListItemDefaults {
12905                    data: Some(default_data.clone()),
12906                    commit_characters: Some(default_commit_characters.clone()),
12907                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12908                        default_edit_range,
12909                    )),
12910                    insert_text_format: Some(default_insert_text_format),
12911                    insert_text_mode: Some(default_insert_text_mode),
12912                }),
12913                ..lsp::CompletionList::default()
12914            })))
12915        }
12916    })
12917    .next()
12918    .await;
12919
12920    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12921    cx.lsp
12922        .server
12923        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12924            let closure_resolved_items = resolved_items.clone();
12925            move |item_to_resolve, _| {
12926                let closure_resolved_items = closure_resolved_items.clone();
12927                async move {
12928                    closure_resolved_items.lock().push(item_to_resolve.clone());
12929                    Ok(item_to_resolve)
12930                }
12931            }
12932        })
12933        .detach();
12934
12935    cx.condition(|editor, _| editor.context_menu_visible())
12936        .await;
12937    cx.run_until_parked();
12938    cx.update_editor(|editor, _, _| {
12939        let menu = editor.context_menu.borrow_mut();
12940        match menu.as_ref().expect("should have the completions menu") {
12941            CodeContextMenu::Completions(completions_menu) => {
12942                assert_eq!(
12943                    completions_menu
12944                        .entries
12945                        .borrow()
12946                        .iter()
12947                        .map(|mat| mat.string.clone())
12948                        .collect::<Vec<String>>(),
12949                    items
12950                        .iter()
12951                        .map(|completion| completion.label.clone())
12952                        .collect::<Vec<String>>()
12953                );
12954            }
12955            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12956        }
12957    });
12958    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12959    // with 4 from the end.
12960    assert_eq!(
12961        *resolved_items.lock(),
12962        [&items[0..16], &items[items.len() - 4..items.len()]]
12963            .concat()
12964            .iter()
12965            .cloned()
12966            .map(|mut item| {
12967                if item.data.is_none() {
12968                    item.data = Some(default_data.clone());
12969                }
12970                item
12971            })
12972            .collect::<Vec<lsp::CompletionItem>>(),
12973        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12974    );
12975    resolved_items.lock().clear();
12976
12977    cx.update_editor(|editor, window, cx| {
12978        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12979    });
12980    cx.run_until_parked();
12981    // Completions that have already been resolved are skipped.
12982    assert_eq!(
12983        *resolved_items.lock(),
12984        items[items.len() - 16..items.len() - 4]
12985            .iter()
12986            .cloned()
12987            .map(|mut item| {
12988                if item.data.is_none() {
12989                    item.data = Some(default_data.clone());
12990                }
12991                item
12992            })
12993            .collect::<Vec<lsp::CompletionItem>>()
12994    );
12995    resolved_items.lock().clear();
12996}
12997
12998#[gpui::test]
12999async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13000    init_test(cx, |_| {});
13001
13002    let mut cx = EditorLspTestContext::new(
13003        Language::new(
13004            LanguageConfig {
13005                matcher: LanguageMatcher {
13006                    path_suffixes: vec!["jsx".into()],
13007                    ..Default::default()
13008                },
13009                overrides: [(
13010                    "element".into(),
13011                    LanguageConfigOverride {
13012                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13013                        ..Default::default()
13014                    },
13015                )]
13016                .into_iter()
13017                .collect(),
13018                ..Default::default()
13019            },
13020            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13021        )
13022        .with_override_query("(jsx_self_closing_element) @element")
13023        .unwrap(),
13024        lsp::ServerCapabilities {
13025            completion_provider: Some(lsp::CompletionOptions {
13026                trigger_characters: Some(vec![":".to_string()]),
13027                ..Default::default()
13028            }),
13029            ..Default::default()
13030        },
13031        cx,
13032    )
13033    .await;
13034
13035    cx.lsp
13036        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13037            Ok(Some(lsp::CompletionResponse::Array(vec![
13038                lsp::CompletionItem {
13039                    label: "bg-blue".into(),
13040                    ..Default::default()
13041                },
13042                lsp::CompletionItem {
13043                    label: "bg-red".into(),
13044                    ..Default::default()
13045                },
13046                lsp::CompletionItem {
13047                    label: "bg-yellow".into(),
13048                    ..Default::default()
13049                },
13050            ])))
13051        });
13052
13053    cx.set_state(r#"<p class="bgˇ" />"#);
13054
13055    // Trigger completion when typing a dash, because the dash is an extra
13056    // word character in the 'element' scope, which contains the cursor.
13057    cx.simulate_keystroke("-");
13058    cx.executor().run_until_parked();
13059    cx.update_editor(|editor, _, _| {
13060        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13061        {
13062            assert_eq!(
13063                completion_menu_entries(&menu),
13064                &["bg-red", "bg-blue", "bg-yellow"]
13065            );
13066        } else {
13067            panic!("expected completion menu to be open");
13068        }
13069    });
13070
13071    cx.simulate_keystroke("l");
13072    cx.executor().run_until_parked();
13073    cx.update_editor(|editor, _, _| {
13074        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13075        {
13076            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13077        } else {
13078            panic!("expected completion menu to be open");
13079        }
13080    });
13081
13082    // When filtering completions, consider the character after the '-' to
13083    // be the start of a subword.
13084    cx.set_state(r#"<p class="yelˇ" />"#);
13085    cx.simulate_keystroke("l");
13086    cx.executor().run_until_parked();
13087    cx.update_editor(|editor, _, _| {
13088        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13089        {
13090            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13091        } else {
13092            panic!("expected completion menu to be open");
13093        }
13094    });
13095}
13096
13097fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13098    let entries = menu.entries.borrow();
13099    entries.iter().map(|mat| mat.string.clone()).collect()
13100}
13101
13102#[gpui::test]
13103async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13104    init_test(cx, |settings| {
13105        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13106            FormatterList(vec![Formatter::Prettier].into()),
13107        ))
13108    });
13109
13110    let fs = FakeFs::new(cx.executor());
13111    fs.insert_file(path!("/file.ts"), Default::default()).await;
13112
13113    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13114    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13115
13116    language_registry.add(Arc::new(Language::new(
13117        LanguageConfig {
13118            name: "TypeScript".into(),
13119            matcher: LanguageMatcher {
13120                path_suffixes: vec!["ts".to_string()],
13121                ..Default::default()
13122            },
13123            ..Default::default()
13124        },
13125        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13126    )));
13127    update_test_language_settings(cx, |settings| {
13128        settings.defaults.prettier = Some(PrettierSettings {
13129            allowed: true,
13130            ..PrettierSettings::default()
13131        });
13132    });
13133
13134    let test_plugin = "test_plugin";
13135    let _ = language_registry.register_fake_lsp(
13136        "TypeScript",
13137        FakeLspAdapter {
13138            prettier_plugins: vec![test_plugin],
13139            ..Default::default()
13140        },
13141    );
13142
13143    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13144    let buffer = project
13145        .update(cx, |project, cx| {
13146            project.open_local_buffer(path!("/file.ts"), cx)
13147        })
13148        .await
13149        .unwrap();
13150
13151    let buffer_text = "one\ntwo\nthree\n";
13152    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13153    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13154    editor.update_in(cx, |editor, window, cx| {
13155        editor.set_text(buffer_text, window, cx)
13156    });
13157
13158    editor
13159        .update_in(cx, |editor, window, cx| {
13160            editor.perform_format(
13161                project.clone(),
13162                FormatTrigger::Manual,
13163                FormatTarget::Buffers,
13164                window,
13165                cx,
13166            )
13167        })
13168        .unwrap()
13169        .await;
13170    assert_eq!(
13171        editor.update(cx, |editor, cx| editor.text(cx)),
13172        buffer_text.to_string() + prettier_format_suffix,
13173        "Test prettier formatting was not applied to the original buffer text",
13174    );
13175
13176    update_test_language_settings(cx, |settings| {
13177        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13178    });
13179    let format = editor.update_in(cx, |editor, window, cx| {
13180        editor.perform_format(
13181            project.clone(),
13182            FormatTrigger::Manual,
13183            FormatTarget::Buffers,
13184            window,
13185            cx,
13186        )
13187    });
13188    format.await.unwrap();
13189    assert_eq!(
13190        editor.update(cx, |editor, cx| editor.text(cx)),
13191        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13192        "Autoformatting (via test prettier) was not applied to the original buffer text",
13193    );
13194}
13195
13196#[gpui::test]
13197async fn test_addition_reverts(cx: &mut TestAppContext) {
13198    init_test(cx, |_| {});
13199    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13200    let base_text = indoc! {r#"
13201        struct Row;
13202        struct Row1;
13203        struct Row2;
13204
13205        struct Row4;
13206        struct Row5;
13207        struct Row6;
13208
13209        struct Row8;
13210        struct Row9;
13211        struct Row10;"#};
13212
13213    // When addition hunks are not adjacent to carets, no hunk revert is performed
13214    assert_hunk_revert(
13215        indoc! {r#"struct Row;
13216                   struct Row1;
13217                   struct Row1.1;
13218                   struct Row1.2;
13219                   struct Row2;ˇ
13220
13221                   struct Row4;
13222                   struct Row5;
13223                   struct Row6;
13224
13225                   struct Row8;
13226                   ˇstruct Row9;
13227                   struct Row9.1;
13228                   struct Row9.2;
13229                   struct Row9.3;
13230                   struct Row10;"#},
13231        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13232        indoc! {r#"struct Row;
13233                   struct Row1;
13234                   struct Row1.1;
13235                   struct Row1.2;
13236                   struct Row2;ˇ
13237
13238                   struct Row4;
13239                   struct Row5;
13240                   struct Row6;
13241
13242                   struct Row8;
13243                   ˇstruct Row9;
13244                   struct Row9.1;
13245                   struct Row9.2;
13246                   struct Row9.3;
13247                   struct Row10;"#},
13248        base_text,
13249        &mut cx,
13250    );
13251    // Same for selections
13252    assert_hunk_revert(
13253        indoc! {r#"struct Row;
13254                   struct Row1;
13255                   struct Row2;
13256                   struct Row2.1;
13257                   struct Row2.2;
13258                   «ˇ
13259                   struct Row4;
13260                   struct» Row5;
13261                   «struct Row6;
13262                   ˇ»
13263                   struct Row9.1;
13264                   struct Row9.2;
13265                   struct Row9.3;
13266                   struct Row8;
13267                   struct Row9;
13268                   struct Row10;"#},
13269        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13270        indoc! {r#"struct Row;
13271                   struct Row1;
13272                   struct Row2;
13273                   struct Row2.1;
13274                   struct Row2.2;
13275                   «ˇ
13276                   struct Row4;
13277                   struct» Row5;
13278                   «struct Row6;
13279                   ˇ»
13280                   struct Row9.1;
13281                   struct Row9.2;
13282                   struct Row9.3;
13283                   struct Row8;
13284                   struct Row9;
13285                   struct Row10;"#},
13286        base_text,
13287        &mut cx,
13288    );
13289
13290    // When carets and selections intersect the addition hunks, those are reverted.
13291    // Adjacent carets got merged.
13292    assert_hunk_revert(
13293        indoc! {r#"struct Row;
13294                   ˇ// something on the top
13295                   struct Row1;
13296                   struct Row2;
13297                   struct Roˇw3.1;
13298                   struct Row2.2;
13299                   struct Row2.3;ˇ
13300
13301                   struct Row4;
13302                   struct ˇRow5.1;
13303                   struct Row5.2;
13304                   struct «Rowˇ»5.3;
13305                   struct Row5;
13306                   struct Row6;
13307                   ˇ
13308                   struct Row9.1;
13309                   struct «Rowˇ»9.2;
13310                   struct «ˇRow»9.3;
13311                   struct Row8;
13312                   struct Row9;
13313                   «ˇ// something on bottom»
13314                   struct Row10;"#},
13315        vec![
13316            DiffHunkStatusKind::Added,
13317            DiffHunkStatusKind::Added,
13318            DiffHunkStatusKind::Added,
13319            DiffHunkStatusKind::Added,
13320            DiffHunkStatusKind::Added,
13321        ],
13322        indoc! {r#"struct Row;
13323                   ˇstruct Row1;
13324                   struct Row2;
13325                   ˇ
13326                   struct Row4;
13327                   ˇstruct Row5;
13328                   struct Row6;
13329                   ˇ
13330                   ˇstruct Row8;
13331                   struct Row9;
13332                   ˇstruct Row10;"#},
13333        base_text,
13334        &mut cx,
13335    );
13336}
13337
13338#[gpui::test]
13339async fn test_modification_reverts(cx: &mut TestAppContext) {
13340    init_test(cx, |_| {});
13341    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13342    let base_text = indoc! {r#"
13343        struct Row;
13344        struct Row1;
13345        struct Row2;
13346
13347        struct Row4;
13348        struct Row5;
13349        struct Row6;
13350
13351        struct Row8;
13352        struct Row9;
13353        struct Row10;"#};
13354
13355    // Modification hunks behave the same as the addition ones.
13356    assert_hunk_revert(
13357        indoc! {r#"struct Row;
13358                   struct Row1;
13359                   struct Row33;
13360                   ˇ
13361                   struct Row4;
13362                   struct Row5;
13363                   struct Row6;
13364                   ˇ
13365                   struct Row99;
13366                   struct Row9;
13367                   struct Row10;"#},
13368        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13369        indoc! {r#"struct Row;
13370                   struct Row1;
13371                   struct Row33;
13372                   ˇ
13373                   struct Row4;
13374                   struct Row5;
13375                   struct Row6;
13376                   ˇ
13377                   struct Row99;
13378                   struct Row9;
13379                   struct Row10;"#},
13380        base_text,
13381        &mut cx,
13382    );
13383    assert_hunk_revert(
13384        indoc! {r#"struct Row;
13385                   struct Row1;
13386                   struct Row33;
13387                   «ˇ
13388                   struct Row4;
13389                   struct» Row5;
13390                   «struct Row6;
13391                   ˇ»
13392                   struct Row99;
13393                   struct Row9;
13394                   struct Row10;"#},
13395        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13396        indoc! {r#"struct Row;
13397                   struct Row1;
13398                   struct Row33;
13399                   «ˇ
13400                   struct Row4;
13401                   struct» Row5;
13402                   «struct Row6;
13403                   ˇ»
13404                   struct Row99;
13405                   struct Row9;
13406                   struct Row10;"#},
13407        base_text,
13408        &mut cx,
13409    );
13410
13411    assert_hunk_revert(
13412        indoc! {r#"ˇstruct Row1.1;
13413                   struct Row1;
13414                   «ˇstr»uct Row22;
13415
13416                   struct ˇRow44;
13417                   struct Row5;
13418                   struct «Rˇ»ow66;ˇ
13419
13420                   «struˇ»ct Row88;
13421                   struct Row9;
13422                   struct Row1011;ˇ"#},
13423        vec![
13424            DiffHunkStatusKind::Modified,
13425            DiffHunkStatusKind::Modified,
13426            DiffHunkStatusKind::Modified,
13427            DiffHunkStatusKind::Modified,
13428            DiffHunkStatusKind::Modified,
13429            DiffHunkStatusKind::Modified,
13430        ],
13431        indoc! {r#"struct Row;
13432                   ˇstruct Row1;
13433                   struct Row2;
13434                   ˇ
13435                   struct Row4;
13436                   ˇstruct Row5;
13437                   struct Row6;
13438                   ˇ
13439                   struct Row8;
13440                   ˇstruct Row9;
13441                   struct Row10;ˇ"#},
13442        base_text,
13443        &mut cx,
13444    );
13445}
13446
13447#[gpui::test]
13448async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13449    init_test(cx, |_| {});
13450    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13451    let base_text = indoc! {r#"
13452        one
13453
13454        two
13455        three
13456        "#};
13457
13458    cx.set_head_text(base_text);
13459    cx.set_state("\nˇ\n");
13460    cx.executor().run_until_parked();
13461    cx.update_editor(|editor, _window, cx| {
13462        editor.expand_selected_diff_hunks(cx);
13463    });
13464    cx.executor().run_until_parked();
13465    cx.update_editor(|editor, window, cx| {
13466        editor.backspace(&Default::default(), window, cx);
13467    });
13468    cx.run_until_parked();
13469    cx.assert_state_with_diff(
13470        indoc! {r#"
13471
13472        - two
13473        - threeˇ
13474        +
13475        "#}
13476        .to_string(),
13477    );
13478}
13479
13480#[gpui::test]
13481async fn test_deletion_reverts(cx: &mut TestAppContext) {
13482    init_test(cx, |_| {});
13483    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13484    let base_text = indoc! {r#"struct Row;
13485struct Row1;
13486struct Row2;
13487
13488struct Row4;
13489struct Row5;
13490struct Row6;
13491
13492struct Row8;
13493struct Row9;
13494struct Row10;"#};
13495
13496    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13497    assert_hunk_revert(
13498        indoc! {r#"struct Row;
13499                   struct Row2;
13500
13501                   ˇstruct Row4;
13502                   struct Row5;
13503                   struct Row6;
13504                   ˇ
13505                   struct Row8;
13506                   struct Row10;"#},
13507        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13508        indoc! {r#"struct Row;
13509                   struct Row2;
13510
13511                   ˇstruct Row4;
13512                   struct Row5;
13513                   struct Row6;
13514                   ˇ
13515                   struct Row8;
13516                   struct Row10;"#},
13517        base_text,
13518        &mut cx,
13519    );
13520    assert_hunk_revert(
13521        indoc! {r#"struct Row;
13522                   struct Row2;
13523
13524                   «ˇstruct Row4;
13525                   struct» Row5;
13526                   «struct Row6;
13527                   ˇ»
13528                   struct Row8;
13529                   struct Row10;"#},
13530        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13531        indoc! {r#"struct Row;
13532                   struct Row2;
13533
13534                   «ˇstruct Row4;
13535                   struct» Row5;
13536                   «struct Row6;
13537                   ˇ»
13538                   struct Row8;
13539                   struct Row10;"#},
13540        base_text,
13541        &mut cx,
13542    );
13543
13544    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13545    assert_hunk_revert(
13546        indoc! {r#"struct Row;
13547                   ˇstruct Row2;
13548
13549                   struct Row4;
13550                   struct Row5;
13551                   struct Row6;
13552
13553                   struct Row8;ˇ
13554                   struct Row10;"#},
13555        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13556        indoc! {r#"struct Row;
13557                   struct Row1;
13558                   ˇstruct Row2;
13559
13560                   struct Row4;
13561                   struct Row5;
13562                   struct Row6;
13563
13564                   struct Row8;ˇ
13565                   struct Row9;
13566                   struct Row10;"#},
13567        base_text,
13568        &mut cx,
13569    );
13570    assert_hunk_revert(
13571        indoc! {r#"struct Row;
13572                   struct Row2«ˇ;
13573                   struct Row4;
13574                   struct» Row5;
13575                   «struct Row6;
13576
13577                   struct Row8;ˇ»
13578                   struct Row10;"#},
13579        vec![
13580            DiffHunkStatusKind::Deleted,
13581            DiffHunkStatusKind::Deleted,
13582            DiffHunkStatusKind::Deleted,
13583        ],
13584        indoc! {r#"struct Row;
13585                   struct Row1;
13586                   struct Row2«ˇ;
13587
13588                   struct Row4;
13589                   struct» Row5;
13590                   «struct Row6;
13591
13592                   struct Row8;ˇ»
13593                   struct Row9;
13594                   struct Row10;"#},
13595        base_text,
13596        &mut cx,
13597    );
13598}
13599
13600#[gpui::test]
13601async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13602    init_test(cx, |_| {});
13603
13604    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13605    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13606    let base_text_3 =
13607        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13608
13609    let text_1 = edit_first_char_of_every_line(base_text_1);
13610    let text_2 = edit_first_char_of_every_line(base_text_2);
13611    let text_3 = edit_first_char_of_every_line(base_text_3);
13612
13613    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13614    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13615    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13616
13617    let multibuffer = cx.new(|cx| {
13618        let mut multibuffer = MultiBuffer::new(ReadWrite);
13619        multibuffer.push_excerpts(
13620            buffer_1.clone(),
13621            [
13622                ExcerptRange {
13623                    context: Point::new(0, 0)..Point::new(3, 0),
13624                    primary: None,
13625                },
13626                ExcerptRange {
13627                    context: Point::new(5, 0)..Point::new(7, 0),
13628                    primary: None,
13629                },
13630                ExcerptRange {
13631                    context: Point::new(9, 0)..Point::new(10, 4),
13632                    primary: None,
13633                },
13634            ],
13635            cx,
13636        );
13637        multibuffer.push_excerpts(
13638            buffer_2.clone(),
13639            [
13640                ExcerptRange {
13641                    context: Point::new(0, 0)..Point::new(3, 0),
13642                    primary: None,
13643                },
13644                ExcerptRange {
13645                    context: Point::new(5, 0)..Point::new(7, 0),
13646                    primary: None,
13647                },
13648                ExcerptRange {
13649                    context: Point::new(9, 0)..Point::new(10, 4),
13650                    primary: None,
13651                },
13652            ],
13653            cx,
13654        );
13655        multibuffer.push_excerpts(
13656            buffer_3.clone(),
13657            [
13658                ExcerptRange {
13659                    context: Point::new(0, 0)..Point::new(3, 0),
13660                    primary: None,
13661                },
13662                ExcerptRange {
13663                    context: Point::new(5, 0)..Point::new(7, 0),
13664                    primary: None,
13665                },
13666                ExcerptRange {
13667                    context: Point::new(9, 0)..Point::new(10, 4),
13668                    primary: None,
13669                },
13670            ],
13671            cx,
13672        );
13673        multibuffer
13674    });
13675
13676    let fs = FakeFs::new(cx.executor());
13677    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13678    let (editor, cx) = cx
13679        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13680    editor.update_in(cx, |editor, _window, cx| {
13681        for (buffer, diff_base) in [
13682            (buffer_1.clone(), base_text_1),
13683            (buffer_2.clone(), base_text_2),
13684            (buffer_3.clone(), base_text_3),
13685        ] {
13686            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13687            editor
13688                .buffer
13689                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13690        }
13691    });
13692    cx.executor().run_until_parked();
13693
13694    editor.update_in(cx, |editor, window, cx| {
13695        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}");
13696        editor.select_all(&SelectAll, window, cx);
13697        editor.git_restore(&Default::default(), window, cx);
13698    });
13699    cx.executor().run_until_parked();
13700
13701    // When all ranges are selected, all buffer hunks are reverted.
13702    editor.update(cx, |editor, cx| {
13703        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");
13704    });
13705    buffer_1.update(cx, |buffer, _| {
13706        assert_eq!(buffer.text(), base_text_1);
13707    });
13708    buffer_2.update(cx, |buffer, _| {
13709        assert_eq!(buffer.text(), base_text_2);
13710    });
13711    buffer_3.update(cx, |buffer, _| {
13712        assert_eq!(buffer.text(), base_text_3);
13713    });
13714
13715    editor.update_in(cx, |editor, window, cx| {
13716        editor.undo(&Default::default(), window, cx);
13717    });
13718
13719    editor.update_in(cx, |editor, window, cx| {
13720        editor.change_selections(None, window, cx, |s| {
13721            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13722        });
13723        editor.git_restore(&Default::default(), window, cx);
13724    });
13725
13726    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13727    // but not affect buffer_2 and its related excerpts.
13728    editor.update(cx, |editor, cx| {
13729        assert_eq!(
13730            editor.text(cx),
13731            "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}"
13732        );
13733    });
13734    buffer_1.update(cx, |buffer, _| {
13735        assert_eq!(buffer.text(), base_text_1);
13736    });
13737    buffer_2.update(cx, |buffer, _| {
13738        assert_eq!(
13739            buffer.text(),
13740            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13741        );
13742    });
13743    buffer_3.update(cx, |buffer, _| {
13744        assert_eq!(
13745            buffer.text(),
13746            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13747        );
13748    });
13749
13750    fn edit_first_char_of_every_line(text: &str) -> String {
13751        text.split('\n')
13752            .map(|line| format!("X{}", &line[1..]))
13753            .collect::<Vec<_>>()
13754            .join("\n")
13755    }
13756}
13757
13758#[gpui::test]
13759async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13760    init_test(cx, |_| {});
13761
13762    let cols = 4;
13763    let rows = 10;
13764    let sample_text_1 = sample_text(rows, cols, 'a');
13765    assert_eq!(
13766        sample_text_1,
13767        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13768    );
13769    let sample_text_2 = sample_text(rows, cols, 'l');
13770    assert_eq!(
13771        sample_text_2,
13772        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13773    );
13774    let sample_text_3 = sample_text(rows, cols, 'v');
13775    assert_eq!(
13776        sample_text_3,
13777        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13778    );
13779
13780    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13781    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13782    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13783
13784    let multi_buffer = cx.new(|cx| {
13785        let mut multibuffer = MultiBuffer::new(ReadWrite);
13786        multibuffer.push_excerpts(
13787            buffer_1.clone(),
13788            [
13789                ExcerptRange {
13790                    context: Point::new(0, 0)..Point::new(3, 0),
13791                    primary: None,
13792                },
13793                ExcerptRange {
13794                    context: Point::new(5, 0)..Point::new(7, 0),
13795                    primary: None,
13796                },
13797                ExcerptRange {
13798                    context: Point::new(9, 0)..Point::new(10, 4),
13799                    primary: None,
13800                },
13801            ],
13802            cx,
13803        );
13804        multibuffer.push_excerpts(
13805            buffer_2.clone(),
13806            [
13807                ExcerptRange {
13808                    context: Point::new(0, 0)..Point::new(3, 0),
13809                    primary: None,
13810                },
13811                ExcerptRange {
13812                    context: Point::new(5, 0)..Point::new(7, 0),
13813                    primary: None,
13814                },
13815                ExcerptRange {
13816                    context: Point::new(9, 0)..Point::new(10, 4),
13817                    primary: None,
13818                },
13819            ],
13820            cx,
13821        );
13822        multibuffer.push_excerpts(
13823            buffer_3.clone(),
13824            [
13825                ExcerptRange {
13826                    context: Point::new(0, 0)..Point::new(3, 0),
13827                    primary: None,
13828                },
13829                ExcerptRange {
13830                    context: Point::new(5, 0)..Point::new(7, 0),
13831                    primary: None,
13832                },
13833                ExcerptRange {
13834                    context: Point::new(9, 0)..Point::new(10, 4),
13835                    primary: None,
13836                },
13837            ],
13838            cx,
13839        );
13840        multibuffer
13841    });
13842
13843    let fs = FakeFs::new(cx.executor());
13844    fs.insert_tree(
13845        "/a",
13846        json!({
13847            "main.rs": sample_text_1,
13848            "other.rs": sample_text_2,
13849            "lib.rs": sample_text_3,
13850        }),
13851    )
13852    .await;
13853    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13854    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13855    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13856    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13857        Editor::new(
13858            EditorMode::Full,
13859            multi_buffer,
13860            Some(project.clone()),
13861            window,
13862            cx,
13863        )
13864    });
13865    let multibuffer_item_id = workspace
13866        .update(cx, |workspace, window, cx| {
13867            assert!(
13868                workspace.active_item(cx).is_none(),
13869                "active item should be None before the first item is added"
13870            );
13871            workspace.add_item_to_active_pane(
13872                Box::new(multi_buffer_editor.clone()),
13873                None,
13874                true,
13875                window,
13876                cx,
13877            );
13878            let active_item = workspace
13879                .active_item(cx)
13880                .expect("should have an active item after adding the multi buffer");
13881            assert!(
13882                !active_item.is_singleton(cx),
13883                "A multi buffer was expected to active after adding"
13884            );
13885            active_item.item_id()
13886        })
13887        .unwrap();
13888    cx.executor().run_until_parked();
13889
13890    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13891        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13892            s.select_ranges(Some(1..2))
13893        });
13894        editor.open_excerpts(&OpenExcerpts, window, cx);
13895    });
13896    cx.executor().run_until_parked();
13897    let first_item_id = workspace
13898        .update(cx, |workspace, window, cx| {
13899            let active_item = workspace
13900                .active_item(cx)
13901                .expect("should have an active item after navigating into the 1st buffer");
13902            let first_item_id = active_item.item_id();
13903            assert_ne!(
13904                first_item_id, multibuffer_item_id,
13905                "Should navigate into the 1st buffer and activate it"
13906            );
13907            assert!(
13908                active_item.is_singleton(cx),
13909                "New active item should be a singleton buffer"
13910            );
13911            assert_eq!(
13912                active_item
13913                    .act_as::<Editor>(cx)
13914                    .expect("should have navigated into an editor for the 1st buffer")
13915                    .read(cx)
13916                    .text(cx),
13917                sample_text_1
13918            );
13919
13920            workspace
13921                .go_back(workspace.active_pane().downgrade(), window, cx)
13922                .detach_and_log_err(cx);
13923
13924            first_item_id
13925        })
13926        .unwrap();
13927    cx.executor().run_until_parked();
13928    workspace
13929        .update(cx, |workspace, _, cx| {
13930            let active_item = workspace
13931                .active_item(cx)
13932                .expect("should have an active item after navigating back");
13933            assert_eq!(
13934                active_item.item_id(),
13935                multibuffer_item_id,
13936                "Should navigate back to the multi buffer"
13937            );
13938            assert!(!active_item.is_singleton(cx));
13939        })
13940        .unwrap();
13941
13942    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13943        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13944            s.select_ranges(Some(39..40))
13945        });
13946        editor.open_excerpts(&OpenExcerpts, window, cx);
13947    });
13948    cx.executor().run_until_parked();
13949    let second_item_id = workspace
13950        .update(cx, |workspace, window, cx| {
13951            let active_item = workspace
13952                .active_item(cx)
13953                .expect("should have an active item after navigating into the 2nd buffer");
13954            let second_item_id = active_item.item_id();
13955            assert_ne!(
13956                second_item_id, multibuffer_item_id,
13957                "Should navigate away from the multibuffer"
13958            );
13959            assert_ne!(
13960                second_item_id, first_item_id,
13961                "Should navigate into the 2nd buffer and activate it"
13962            );
13963            assert!(
13964                active_item.is_singleton(cx),
13965                "New active item should be a singleton buffer"
13966            );
13967            assert_eq!(
13968                active_item
13969                    .act_as::<Editor>(cx)
13970                    .expect("should have navigated into an editor")
13971                    .read(cx)
13972                    .text(cx),
13973                sample_text_2
13974            );
13975
13976            workspace
13977                .go_back(workspace.active_pane().downgrade(), window, cx)
13978                .detach_and_log_err(cx);
13979
13980            second_item_id
13981        })
13982        .unwrap();
13983    cx.executor().run_until_parked();
13984    workspace
13985        .update(cx, |workspace, _, cx| {
13986            let active_item = workspace
13987                .active_item(cx)
13988                .expect("should have an active item after navigating back from the 2nd buffer");
13989            assert_eq!(
13990                active_item.item_id(),
13991                multibuffer_item_id,
13992                "Should navigate back from the 2nd buffer to the multi buffer"
13993            );
13994            assert!(!active_item.is_singleton(cx));
13995        })
13996        .unwrap();
13997
13998    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13999        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14000            s.select_ranges(Some(70..70))
14001        });
14002        editor.open_excerpts(&OpenExcerpts, window, cx);
14003    });
14004    cx.executor().run_until_parked();
14005    workspace
14006        .update(cx, |workspace, window, cx| {
14007            let active_item = workspace
14008                .active_item(cx)
14009                .expect("should have an active item after navigating into the 3rd buffer");
14010            let third_item_id = active_item.item_id();
14011            assert_ne!(
14012                third_item_id, multibuffer_item_id,
14013                "Should navigate into the 3rd buffer and activate it"
14014            );
14015            assert_ne!(third_item_id, first_item_id);
14016            assert_ne!(third_item_id, second_item_id);
14017            assert!(
14018                active_item.is_singleton(cx),
14019                "New active item should be a singleton buffer"
14020            );
14021            assert_eq!(
14022                active_item
14023                    .act_as::<Editor>(cx)
14024                    .expect("should have navigated into an editor")
14025                    .read(cx)
14026                    .text(cx),
14027                sample_text_3
14028            );
14029
14030            workspace
14031                .go_back(workspace.active_pane().downgrade(), window, cx)
14032                .detach_and_log_err(cx);
14033        })
14034        .unwrap();
14035    cx.executor().run_until_parked();
14036    workspace
14037        .update(cx, |workspace, _, cx| {
14038            let active_item = workspace
14039                .active_item(cx)
14040                .expect("should have an active item after navigating back from the 3rd buffer");
14041            assert_eq!(
14042                active_item.item_id(),
14043                multibuffer_item_id,
14044                "Should navigate back from the 3rd buffer to the multi buffer"
14045            );
14046            assert!(!active_item.is_singleton(cx));
14047        })
14048        .unwrap();
14049}
14050
14051#[gpui::test]
14052async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14053    init_test(cx, |_| {});
14054
14055    let mut cx = EditorTestContext::new(cx).await;
14056
14057    let diff_base = r#"
14058        use some::mod;
14059
14060        const A: u32 = 42;
14061
14062        fn main() {
14063            println!("hello");
14064
14065            println!("world");
14066        }
14067        "#
14068    .unindent();
14069
14070    cx.set_state(
14071        &r#"
14072        use some::modified;
14073
14074        ˇ
14075        fn main() {
14076            println!("hello there");
14077
14078            println!("around the");
14079            println!("world");
14080        }
14081        "#
14082        .unindent(),
14083    );
14084
14085    cx.set_head_text(&diff_base);
14086    executor.run_until_parked();
14087
14088    cx.update_editor(|editor, window, cx| {
14089        editor.go_to_next_hunk(&GoToHunk, window, cx);
14090        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14091    });
14092    executor.run_until_parked();
14093    cx.assert_state_with_diff(
14094        r#"
14095          use some::modified;
14096
14097
14098          fn main() {
14099        -     println!("hello");
14100        + ˇ    println!("hello there");
14101
14102              println!("around the");
14103              println!("world");
14104          }
14105        "#
14106        .unindent(),
14107    );
14108
14109    cx.update_editor(|editor, window, cx| {
14110        for _ in 0..2 {
14111            editor.go_to_next_hunk(&GoToHunk, window, cx);
14112            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14113        }
14114    });
14115    executor.run_until_parked();
14116    cx.assert_state_with_diff(
14117        r#"
14118        - use some::mod;
14119        + ˇuse some::modified;
14120
14121
14122          fn main() {
14123        -     println!("hello");
14124        +     println!("hello there");
14125
14126        +     println!("around the");
14127              println!("world");
14128          }
14129        "#
14130        .unindent(),
14131    );
14132
14133    cx.update_editor(|editor, window, cx| {
14134        editor.go_to_next_hunk(&GoToHunk, window, cx);
14135        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14136    });
14137    executor.run_until_parked();
14138    cx.assert_state_with_diff(
14139        r#"
14140        - use some::mod;
14141        + use some::modified;
14142
14143        - const A: u32 = 42;
14144          ˇ
14145          fn main() {
14146        -     println!("hello");
14147        +     println!("hello there");
14148
14149        +     println!("around the");
14150              println!("world");
14151          }
14152        "#
14153        .unindent(),
14154    );
14155
14156    cx.update_editor(|editor, window, cx| {
14157        editor.cancel(&Cancel, window, cx);
14158    });
14159
14160    cx.assert_state_with_diff(
14161        r#"
14162          use some::modified;
14163
14164          ˇ
14165          fn main() {
14166              println!("hello there");
14167
14168              println!("around the");
14169              println!("world");
14170          }
14171        "#
14172        .unindent(),
14173    );
14174}
14175
14176#[gpui::test]
14177async fn test_diff_base_change_with_expanded_diff_hunks(
14178    executor: BackgroundExecutor,
14179    cx: &mut TestAppContext,
14180) {
14181    init_test(cx, |_| {});
14182
14183    let mut cx = EditorTestContext::new(cx).await;
14184
14185    let diff_base = r#"
14186        use some::mod1;
14187        use some::mod2;
14188
14189        const A: u32 = 42;
14190        const B: u32 = 42;
14191        const C: u32 = 42;
14192
14193        fn main() {
14194            println!("hello");
14195
14196            println!("world");
14197        }
14198        "#
14199    .unindent();
14200
14201    cx.set_state(
14202        &r#"
14203        use some::mod2;
14204
14205        const A: u32 = 42;
14206        const C: u32 = 42;
14207
14208        fn main(ˇ) {
14209            //println!("hello");
14210
14211            println!("world");
14212            //
14213            //
14214        }
14215        "#
14216        .unindent(),
14217    );
14218
14219    cx.set_head_text(&diff_base);
14220    executor.run_until_parked();
14221
14222    cx.update_editor(|editor, window, cx| {
14223        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14224    });
14225    executor.run_until_parked();
14226    cx.assert_state_with_diff(
14227        r#"
14228        - use some::mod1;
14229          use some::mod2;
14230
14231          const A: u32 = 42;
14232        - const B: u32 = 42;
14233          const C: u32 = 42;
14234
14235          fn main(ˇ) {
14236        -     println!("hello");
14237        +     //println!("hello");
14238
14239              println!("world");
14240        +     //
14241        +     //
14242          }
14243        "#
14244        .unindent(),
14245    );
14246
14247    cx.set_head_text("new diff base!");
14248    executor.run_until_parked();
14249    cx.assert_state_with_diff(
14250        r#"
14251        - new diff base!
14252        + use some::mod2;
14253        +
14254        + const A: u32 = 42;
14255        + const C: u32 = 42;
14256        +
14257        + fn main(ˇ) {
14258        +     //println!("hello");
14259        +
14260        +     println!("world");
14261        +     //
14262        +     //
14263        + }
14264        "#
14265        .unindent(),
14266    );
14267}
14268
14269#[gpui::test]
14270async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14271    init_test(cx, |_| {});
14272
14273    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14274    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14275    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14276    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14277    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14278    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14279
14280    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14281    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14282    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14283
14284    let multi_buffer = cx.new(|cx| {
14285        let mut multibuffer = MultiBuffer::new(ReadWrite);
14286        multibuffer.push_excerpts(
14287            buffer_1.clone(),
14288            [
14289                ExcerptRange {
14290                    context: Point::new(0, 0)..Point::new(3, 0),
14291                    primary: None,
14292                },
14293                ExcerptRange {
14294                    context: Point::new(5, 0)..Point::new(7, 0),
14295                    primary: None,
14296                },
14297                ExcerptRange {
14298                    context: Point::new(9, 0)..Point::new(10, 3),
14299                    primary: None,
14300                },
14301            ],
14302            cx,
14303        );
14304        multibuffer.push_excerpts(
14305            buffer_2.clone(),
14306            [
14307                ExcerptRange {
14308                    context: Point::new(0, 0)..Point::new(3, 0),
14309                    primary: None,
14310                },
14311                ExcerptRange {
14312                    context: Point::new(5, 0)..Point::new(7, 0),
14313                    primary: None,
14314                },
14315                ExcerptRange {
14316                    context: Point::new(9, 0)..Point::new(10, 3),
14317                    primary: None,
14318                },
14319            ],
14320            cx,
14321        );
14322        multibuffer.push_excerpts(
14323            buffer_3.clone(),
14324            [
14325                ExcerptRange {
14326                    context: Point::new(0, 0)..Point::new(3, 0),
14327                    primary: None,
14328                },
14329                ExcerptRange {
14330                    context: Point::new(5, 0)..Point::new(7, 0),
14331                    primary: None,
14332                },
14333                ExcerptRange {
14334                    context: Point::new(9, 0)..Point::new(10, 3),
14335                    primary: None,
14336                },
14337            ],
14338            cx,
14339        );
14340        multibuffer
14341    });
14342
14343    let editor =
14344        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14345    editor
14346        .update(cx, |editor, _window, cx| {
14347            for (buffer, diff_base) in [
14348                (buffer_1.clone(), file_1_old),
14349                (buffer_2.clone(), file_2_old),
14350                (buffer_3.clone(), file_3_old),
14351            ] {
14352                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14353                editor
14354                    .buffer
14355                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14356            }
14357        })
14358        .unwrap();
14359
14360    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14361    cx.run_until_parked();
14362
14363    cx.assert_editor_state(
14364        &"
14365            ˇaaa
14366            ccc
14367            ddd
14368
14369            ggg
14370            hhh
14371
14372
14373            lll
14374            mmm
14375            NNN
14376
14377            qqq
14378            rrr
14379
14380            uuu
14381            111
14382            222
14383            333
14384
14385            666
14386            777
14387
14388            000
14389            !!!"
14390        .unindent(),
14391    );
14392
14393    cx.update_editor(|editor, window, cx| {
14394        editor.select_all(&SelectAll, window, cx);
14395        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14396    });
14397    cx.executor().run_until_parked();
14398
14399    cx.assert_state_with_diff(
14400        "
14401            «aaa
14402          - bbb
14403            ccc
14404            ddd
14405
14406            ggg
14407            hhh
14408
14409
14410            lll
14411            mmm
14412          - nnn
14413          + NNN
14414
14415            qqq
14416            rrr
14417
14418            uuu
14419            111
14420            222
14421            333
14422
14423          + 666
14424            777
14425
14426            000
14427            !!!ˇ»"
14428            .unindent(),
14429    );
14430}
14431
14432#[gpui::test]
14433async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14434    init_test(cx, |_| {});
14435
14436    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14437    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14438
14439    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14440    let multi_buffer = cx.new(|cx| {
14441        let mut multibuffer = MultiBuffer::new(ReadWrite);
14442        multibuffer.push_excerpts(
14443            buffer.clone(),
14444            [
14445                ExcerptRange {
14446                    context: Point::new(0, 0)..Point::new(2, 0),
14447                    primary: None,
14448                },
14449                ExcerptRange {
14450                    context: Point::new(4, 0)..Point::new(7, 0),
14451                    primary: None,
14452                },
14453                ExcerptRange {
14454                    context: Point::new(9, 0)..Point::new(10, 0),
14455                    primary: None,
14456                },
14457            ],
14458            cx,
14459        );
14460        multibuffer
14461    });
14462
14463    let editor =
14464        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14465    editor
14466        .update(cx, |editor, _window, cx| {
14467            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14468            editor
14469                .buffer
14470                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14471        })
14472        .unwrap();
14473
14474    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14475    cx.run_until_parked();
14476
14477    cx.update_editor(|editor, window, cx| {
14478        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14479    });
14480    cx.executor().run_until_parked();
14481
14482    // When the start of a hunk coincides with the start of its excerpt,
14483    // the hunk is expanded. When the start of a a hunk is earlier than
14484    // the start of its excerpt, the hunk is not expanded.
14485    cx.assert_state_with_diff(
14486        "
14487            ˇaaa
14488          - bbb
14489          + BBB
14490
14491          - ddd
14492          - eee
14493          + DDD
14494          + EEE
14495            fff
14496
14497            iii
14498        "
14499        .unindent(),
14500    );
14501}
14502
14503#[gpui::test]
14504async fn test_edits_around_expanded_insertion_hunks(
14505    executor: BackgroundExecutor,
14506    cx: &mut TestAppContext,
14507) {
14508    init_test(cx, |_| {});
14509
14510    let mut cx = EditorTestContext::new(cx).await;
14511
14512    let diff_base = r#"
14513        use some::mod1;
14514        use some::mod2;
14515
14516        const A: u32 = 42;
14517
14518        fn main() {
14519            println!("hello");
14520
14521            println!("world");
14522        }
14523        "#
14524    .unindent();
14525    executor.run_until_parked();
14526    cx.set_state(
14527        &r#"
14528        use some::mod1;
14529        use some::mod2;
14530
14531        const A: u32 = 42;
14532        const B: u32 = 42;
14533        const C: u32 = 42;
14534        ˇ
14535
14536        fn main() {
14537            println!("hello");
14538
14539            println!("world");
14540        }
14541        "#
14542        .unindent(),
14543    );
14544
14545    cx.set_head_text(&diff_base);
14546    executor.run_until_parked();
14547
14548    cx.update_editor(|editor, window, cx| {
14549        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14550    });
14551    executor.run_until_parked();
14552
14553    cx.assert_state_with_diff(
14554        r#"
14555        use some::mod1;
14556        use some::mod2;
14557
14558        const A: u32 = 42;
14559      + const B: u32 = 42;
14560      + const C: u32 = 42;
14561      + ˇ
14562
14563        fn main() {
14564            println!("hello");
14565
14566            println!("world");
14567        }
14568      "#
14569        .unindent(),
14570    );
14571
14572    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14573    executor.run_until_parked();
14574
14575    cx.assert_state_with_diff(
14576        r#"
14577        use some::mod1;
14578        use some::mod2;
14579
14580        const A: u32 = 42;
14581      + const B: u32 = 42;
14582      + const C: u32 = 42;
14583      + const D: u32 = 42;
14584      + ˇ
14585
14586        fn main() {
14587            println!("hello");
14588
14589            println!("world");
14590        }
14591      "#
14592        .unindent(),
14593    );
14594
14595    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14596    executor.run_until_parked();
14597
14598    cx.assert_state_with_diff(
14599        r#"
14600        use some::mod1;
14601        use some::mod2;
14602
14603        const A: u32 = 42;
14604      + const B: u32 = 42;
14605      + const C: u32 = 42;
14606      + const D: u32 = 42;
14607      + const E: u32 = 42;
14608      + ˇ
14609
14610        fn main() {
14611            println!("hello");
14612
14613            println!("world");
14614        }
14615      "#
14616        .unindent(),
14617    );
14618
14619    cx.update_editor(|editor, window, cx| {
14620        editor.delete_line(&DeleteLine, window, cx);
14621    });
14622    executor.run_until_parked();
14623
14624    cx.assert_state_with_diff(
14625        r#"
14626        use some::mod1;
14627        use some::mod2;
14628
14629        const A: u32 = 42;
14630      + const B: u32 = 42;
14631      + const C: u32 = 42;
14632      + const D: u32 = 42;
14633      + const E: u32 = 42;
14634        ˇ
14635        fn main() {
14636            println!("hello");
14637
14638            println!("world");
14639        }
14640      "#
14641        .unindent(),
14642    );
14643
14644    cx.update_editor(|editor, window, cx| {
14645        editor.move_up(&MoveUp, window, cx);
14646        editor.delete_line(&DeleteLine, window, cx);
14647        editor.move_up(&MoveUp, window, cx);
14648        editor.delete_line(&DeleteLine, window, cx);
14649        editor.move_up(&MoveUp, window, cx);
14650        editor.delete_line(&DeleteLine, window, cx);
14651    });
14652    executor.run_until_parked();
14653    cx.assert_state_with_diff(
14654        r#"
14655        use some::mod1;
14656        use some::mod2;
14657
14658        const A: u32 = 42;
14659      + const B: u32 = 42;
14660        ˇ
14661        fn main() {
14662            println!("hello");
14663
14664            println!("world");
14665        }
14666      "#
14667        .unindent(),
14668    );
14669
14670    cx.update_editor(|editor, window, cx| {
14671        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14672        editor.delete_line(&DeleteLine, window, cx);
14673    });
14674    executor.run_until_parked();
14675    cx.assert_state_with_diff(
14676        r#"
14677        ˇ
14678        fn main() {
14679            println!("hello");
14680
14681            println!("world");
14682        }
14683      "#
14684        .unindent(),
14685    );
14686}
14687
14688#[gpui::test]
14689async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14690    init_test(cx, |_| {});
14691
14692    let mut cx = EditorTestContext::new(cx).await;
14693    cx.set_head_text(indoc! { "
14694        one
14695        two
14696        three
14697        four
14698        five
14699        "
14700    });
14701    cx.set_state(indoc! { "
14702        one
14703        ˇthree
14704        five
14705    "});
14706    cx.run_until_parked();
14707    cx.update_editor(|editor, window, cx| {
14708        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14709    });
14710    cx.assert_state_with_diff(
14711        indoc! { "
14712        one
14713      - two
14714        ˇthree
14715      - four
14716        five
14717    "}
14718        .to_string(),
14719    );
14720    cx.update_editor(|editor, window, cx| {
14721        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14722    });
14723
14724    cx.assert_state_with_diff(
14725        indoc! { "
14726        one
14727        ˇthree
14728        five
14729    "}
14730        .to_string(),
14731    );
14732
14733    cx.set_state(indoc! { "
14734        one
14735        ˇTWO
14736        three
14737        four
14738        five
14739    "});
14740    cx.run_until_parked();
14741    cx.update_editor(|editor, window, cx| {
14742        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14743    });
14744
14745    cx.assert_state_with_diff(
14746        indoc! { "
14747            one
14748          - two
14749          + ˇTWO
14750            three
14751            four
14752            five
14753        "}
14754        .to_string(),
14755    );
14756    cx.update_editor(|editor, window, cx| {
14757        editor.move_up(&Default::default(), window, cx);
14758        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14759    });
14760    cx.assert_state_with_diff(
14761        indoc! { "
14762            one
14763            ˇTWO
14764            three
14765            four
14766            five
14767        "}
14768        .to_string(),
14769    );
14770}
14771
14772#[gpui::test]
14773async fn test_edits_around_expanded_deletion_hunks(
14774    executor: BackgroundExecutor,
14775    cx: &mut TestAppContext,
14776) {
14777    init_test(cx, |_| {});
14778
14779    let mut cx = EditorTestContext::new(cx).await;
14780
14781    let diff_base = r#"
14782        use some::mod1;
14783        use some::mod2;
14784
14785        const A: u32 = 42;
14786        const B: u32 = 42;
14787        const C: u32 = 42;
14788
14789
14790        fn main() {
14791            println!("hello");
14792
14793            println!("world");
14794        }
14795    "#
14796    .unindent();
14797    executor.run_until_parked();
14798    cx.set_state(
14799        &r#"
14800        use some::mod1;
14801        use some::mod2;
14802
14803        ˇconst B: u32 = 42;
14804        const C: u32 = 42;
14805
14806
14807        fn main() {
14808            println!("hello");
14809
14810            println!("world");
14811        }
14812        "#
14813        .unindent(),
14814    );
14815
14816    cx.set_head_text(&diff_base);
14817    executor.run_until_parked();
14818
14819    cx.update_editor(|editor, window, cx| {
14820        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14821    });
14822    executor.run_until_parked();
14823
14824    cx.assert_state_with_diff(
14825        r#"
14826        use some::mod1;
14827        use some::mod2;
14828
14829      - const A: u32 = 42;
14830        ˇconst B: u32 = 42;
14831        const C: u32 = 42;
14832
14833
14834        fn main() {
14835            println!("hello");
14836
14837            println!("world");
14838        }
14839      "#
14840        .unindent(),
14841    );
14842
14843    cx.update_editor(|editor, window, cx| {
14844        editor.delete_line(&DeleteLine, window, cx);
14845    });
14846    executor.run_until_parked();
14847    cx.assert_state_with_diff(
14848        r#"
14849        use some::mod1;
14850        use some::mod2;
14851
14852      - const A: u32 = 42;
14853      - const B: u32 = 42;
14854        ˇconst C: u32 = 42;
14855
14856
14857        fn main() {
14858            println!("hello");
14859
14860            println!("world");
14861        }
14862      "#
14863        .unindent(),
14864    );
14865
14866    cx.update_editor(|editor, window, cx| {
14867        editor.delete_line(&DeleteLine, window, cx);
14868    });
14869    executor.run_until_parked();
14870    cx.assert_state_with_diff(
14871        r#"
14872        use some::mod1;
14873        use some::mod2;
14874
14875      - const A: u32 = 42;
14876      - const B: u32 = 42;
14877      - const C: u32 = 42;
14878        ˇ
14879
14880        fn main() {
14881            println!("hello");
14882
14883            println!("world");
14884        }
14885      "#
14886        .unindent(),
14887    );
14888
14889    cx.update_editor(|editor, window, cx| {
14890        editor.handle_input("replacement", window, cx);
14891    });
14892    executor.run_until_parked();
14893    cx.assert_state_with_diff(
14894        r#"
14895        use some::mod1;
14896        use some::mod2;
14897
14898      - const A: u32 = 42;
14899      - const B: u32 = 42;
14900      - const C: u32 = 42;
14901      -
14902      + replacementˇ
14903
14904        fn main() {
14905            println!("hello");
14906
14907            println!("world");
14908        }
14909      "#
14910        .unindent(),
14911    );
14912}
14913
14914#[gpui::test]
14915async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14916    init_test(cx, |_| {});
14917
14918    let mut cx = EditorTestContext::new(cx).await;
14919
14920    let base_text = r#"
14921        one
14922        two
14923        three
14924        four
14925        five
14926    "#
14927    .unindent();
14928    executor.run_until_parked();
14929    cx.set_state(
14930        &r#"
14931        one
14932        two
14933        fˇour
14934        five
14935        "#
14936        .unindent(),
14937    );
14938
14939    cx.set_head_text(&base_text);
14940    executor.run_until_parked();
14941
14942    cx.update_editor(|editor, window, cx| {
14943        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14944    });
14945    executor.run_until_parked();
14946
14947    cx.assert_state_with_diff(
14948        r#"
14949          one
14950          two
14951        - three
14952          fˇour
14953          five
14954        "#
14955        .unindent(),
14956    );
14957
14958    cx.update_editor(|editor, window, cx| {
14959        editor.backspace(&Backspace, window, cx);
14960        editor.backspace(&Backspace, window, cx);
14961    });
14962    executor.run_until_parked();
14963    cx.assert_state_with_diff(
14964        r#"
14965          one
14966          two
14967        - threeˇ
14968        - four
14969        + our
14970          five
14971        "#
14972        .unindent(),
14973    );
14974}
14975
14976#[gpui::test]
14977async fn test_edit_after_expanded_modification_hunk(
14978    executor: BackgroundExecutor,
14979    cx: &mut TestAppContext,
14980) {
14981    init_test(cx, |_| {});
14982
14983    let mut cx = EditorTestContext::new(cx).await;
14984
14985    let diff_base = r#"
14986        use some::mod1;
14987        use some::mod2;
14988
14989        const A: u32 = 42;
14990        const B: u32 = 42;
14991        const C: u32 = 42;
14992        const D: u32 = 42;
14993
14994
14995        fn main() {
14996            println!("hello");
14997
14998            println!("world");
14999        }"#
15000    .unindent();
15001
15002    cx.set_state(
15003        &r#"
15004        use some::mod1;
15005        use some::mod2;
15006
15007        const A: u32 = 42;
15008        const B: u32 = 42;
15009        const C: u32 = 43ˇ
15010        const D: u32 = 42;
15011
15012
15013        fn main() {
15014            println!("hello");
15015
15016            println!("world");
15017        }"#
15018        .unindent(),
15019    );
15020
15021    cx.set_head_text(&diff_base);
15022    executor.run_until_parked();
15023    cx.update_editor(|editor, window, cx| {
15024        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15025    });
15026    executor.run_until_parked();
15027
15028    cx.assert_state_with_diff(
15029        r#"
15030        use some::mod1;
15031        use some::mod2;
15032
15033        const A: u32 = 42;
15034        const B: u32 = 42;
15035      - const C: u32 = 42;
15036      + const C: u32 = 43ˇ
15037        const D: u32 = 42;
15038
15039
15040        fn main() {
15041            println!("hello");
15042
15043            println!("world");
15044        }"#
15045        .unindent(),
15046    );
15047
15048    cx.update_editor(|editor, window, cx| {
15049        editor.handle_input("\nnew_line\n", window, cx);
15050    });
15051    executor.run_until_parked();
15052
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      + const C: u32 = 43
15062      + new_line
15063      + ˇ
15064        const D: u32 = 42;
15065
15066
15067        fn main() {
15068            println!("hello");
15069
15070            println!("world");
15071        }"#
15072        .unindent(),
15073    );
15074}
15075
15076#[gpui::test]
15077async fn test_stage_and_unstage_added_file_hunk(
15078    executor: BackgroundExecutor,
15079    cx: &mut TestAppContext,
15080) {
15081    init_test(cx, |_| {});
15082
15083    let mut cx = EditorTestContext::new(cx).await;
15084    cx.update_editor(|editor, _, cx| {
15085        editor.set_expand_all_diff_hunks(cx);
15086    });
15087
15088    let working_copy = r#"
15089            ˇfn main() {
15090                println!("hello, world!");
15091            }
15092        "#
15093    .unindent();
15094
15095    cx.set_state(&working_copy);
15096    executor.run_until_parked();
15097
15098    cx.assert_state_with_diff(
15099        r#"
15100            + ˇfn main() {
15101            +     println!("hello, world!");
15102            + }
15103        "#
15104        .unindent(),
15105    );
15106    cx.assert_index_text(None);
15107
15108    cx.update_editor(|editor, window, cx| {
15109        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15110    });
15111    executor.run_until_parked();
15112    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15113    cx.assert_state_with_diff(
15114        r#"
15115            + ˇfn main() {
15116            +     println!("hello, world!");
15117            + }
15118        "#
15119        .unindent(),
15120    );
15121
15122    cx.update_editor(|editor, window, cx| {
15123        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15124    });
15125    executor.run_until_parked();
15126    cx.assert_index_text(None);
15127}
15128
15129async fn setup_indent_guides_editor(
15130    text: &str,
15131    cx: &mut TestAppContext,
15132) -> (BufferId, EditorTestContext) {
15133    init_test(cx, |_| {});
15134
15135    let mut cx = EditorTestContext::new(cx).await;
15136
15137    let buffer_id = cx.update_editor(|editor, window, cx| {
15138        editor.set_text(text, window, cx);
15139        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15140
15141        buffer_ids[0]
15142    });
15143
15144    (buffer_id, cx)
15145}
15146
15147fn assert_indent_guides(
15148    range: Range<u32>,
15149    expected: Vec<IndentGuide>,
15150    active_indices: Option<Vec<usize>>,
15151    cx: &mut EditorTestContext,
15152) {
15153    let indent_guides = cx.update_editor(|editor, window, cx| {
15154        let snapshot = editor.snapshot(window, cx).display_snapshot;
15155        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15156            editor,
15157            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15158            true,
15159            &snapshot,
15160            cx,
15161        );
15162
15163        indent_guides.sort_by(|a, b| {
15164            a.depth.cmp(&b.depth).then(
15165                a.start_row
15166                    .cmp(&b.start_row)
15167                    .then(a.end_row.cmp(&b.end_row)),
15168            )
15169        });
15170        indent_guides
15171    });
15172
15173    if let Some(expected) = active_indices {
15174        let active_indices = cx.update_editor(|editor, window, cx| {
15175            let snapshot = editor.snapshot(window, cx).display_snapshot;
15176            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15177        });
15178
15179        assert_eq!(
15180            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15181            expected,
15182            "Active indent guide indices do not match"
15183        );
15184    }
15185
15186    assert_eq!(indent_guides, expected, "Indent guides do not match");
15187}
15188
15189fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15190    IndentGuide {
15191        buffer_id,
15192        start_row: MultiBufferRow(start_row),
15193        end_row: MultiBufferRow(end_row),
15194        depth,
15195        tab_size: 4,
15196        settings: IndentGuideSettings {
15197            enabled: true,
15198            line_width: 1,
15199            active_line_width: 1,
15200            ..Default::default()
15201        },
15202    }
15203}
15204
15205#[gpui::test]
15206async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15207    let (buffer_id, mut cx) = setup_indent_guides_editor(
15208        &"
15209    fn main() {
15210        let a = 1;
15211    }"
15212        .unindent(),
15213        cx,
15214    )
15215    .await;
15216
15217    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15218}
15219
15220#[gpui::test]
15221async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15222    let (buffer_id, mut cx) = setup_indent_guides_editor(
15223        &"
15224    fn main() {
15225        let a = 1;
15226        let b = 2;
15227    }"
15228        .unindent(),
15229        cx,
15230    )
15231    .await;
15232
15233    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15234}
15235
15236#[gpui::test]
15237async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15238    let (buffer_id, mut cx) = setup_indent_guides_editor(
15239        &"
15240    fn main() {
15241        let a = 1;
15242        if a == 3 {
15243            let b = 2;
15244        } else {
15245            let c = 3;
15246        }
15247    }"
15248        .unindent(),
15249        cx,
15250    )
15251    .await;
15252
15253    assert_indent_guides(
15254        0..8,
15255        vec![
15256            indent_guide(buffer_id, 1, 6, 0),
15257            indent_guide(buffer_id, 3, 3, 1),
15258            indent_guide(buffer_id, 5, 5, 1),
15259        ],
15260        None,
15261        &mut cx,
15262    );
15263}
15264
15265#[gpui::test]
15266async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15267    let (buffer_id, mut cx) = setup_indent_guides_editor(
15268        &"
15269    fn main() {
15270        let a = 1;
15271            let b = 2;
15272        let c = 3;
15273    }"
15274        .unindent(),
15275        cx,
15276    )
15277    .await;
15278
15279    assert_indent_guides(
15280        0..5,
15281        vec![
15282            indent_guide(buffer_id, 1, 3, 0),
15283            indent_guide(buffer_id, 2, 2, 1),
15284        ],
15285        None,
15286        &mut cx,
15287    );
15288}
15289
15290#[gpui::test]
15291async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15292    let (buffer_id, mut cx) = setup_indent_guides_editor(
15293        &"
15294        fn main() {
15295            let a = 1;
15296
15297            let c = 3;
15298        }"
15299        .unindent(),
15300        cx,
15301    )
15302    .await;
15303
15304    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15305}
15306
15307#[gpui::test]
15308async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15309    let (buffer_id, mut cx) = setup_indent_guides_editor(
15310        &"
15311        fn main() {
15312            let a = 1;
15313
15314            let c = 3;
15315
15316            if a == 3 {
15317                let b = 2;
15318            } else {
15319                let c = 3;
15320            }
15321        }"
15322        .unindent(),
15323        cx,
15324    )
15325    .await;
15326
15327    assert_indent_guides(
15328        0..11,
15329        vec![
15330            indent_guide(buffer_id, 1, 9, 0),
15331            indent_guide(buffer_id, 6, 6, 1),
15332            indent_guide(buffer_id, 8, 8, 1),
15333        ],
15334        None,
15335        &mut cx,
15336    );
15337}
15338
15339#[gpui::test]
15340async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15341    let (buffer_id, mut cx) = setup_indent_guides_editor(
15342        &"
15343        fn main() {
15344            let a = 1;
15345
15346            let c = 3;
15347
15348            if a == 3 {
15349                let b = 2;
15350            } else {
15351                let c = 3;
15352            }
15353        }"
15354        .unindent(),
15355        cx,
15356    )
15357    .await;
15358
15359    assert_indent_guides(
15360        1..11,
15361        vec![
15362            indent_guide(buffer_id, 1, 9, 0),
15363            indent_guide(buffer_id, 6, 6, 1),
15364            indent_guide(buffer_id, 8, 8, 1),
15365        ],
15366        None,
15367        &mut cx,
15368    );
15369}
15370
15371#[gpui::test]
15372async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15373    let (buffer_id, mut cx) = setup_indent_guides_editor(
15374        &"
15375        fn main() {
15376            let a = 1;
15377
15378            let c = 3;
15379
15380            if a == 3 {
15381                let b = 2;
15382            } else {
15383                let c = 3;
15384            }
15385        }"
15386        .unindent(),
15387        cx,
15388    )
15389    .await;
15390
15391    assert_indent_guides(
15392        1..10,
15393        vec![
15394            indent_guide(buffer_id, 1, 9, 0),
15395            indent_guide(buffer_id, 6, 6, 1),
15396            indent_guide(buffer_id, 8, 8, 1),
15397        ],
15398        None,
15399        &mut cx,
15400    );
15401}
15402
15403#[gpui::test]
15404async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15405    let (buffer_id, mut cx) = setup_indent_guides_editor(
15406        &"
15407        block1
15408            block2
15409                block3
15410                    block4
15411            block2
15412        block1
15413        block1"
15414            .unindent(),
15415        cx,
15416    )
15417    .await;
15418
15419    assert_indent_guides(
15420        1..10,
15421        vec![
15422            indent_guide(buffer_id, 1, 4, 0),
15423            indent_guide(buffer_id, 2, 3, 1),
15424            indent_guide(buffer_id, 3, 3, 2),
15425        ],
15426        None,
15427        &mut cx,
15428    );
15429}
15430
15431#[gpui::test]
15432async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15433    let (buffer_id, mut cx) = setup_indent_guides_editor(
15434        &"
15435        block1
15436            block2
15437                block3
15438
15439        block1
15440        block1"
15441            .unindent(),
15442        cx,
15443    )
15444    .await;
15445
15446    assert_indent_guides(
15447        0..6,
15448        vec![
15449            indent_guide(buffer_id, 1, 2, 0),
15450            indent_guide(buffer_id, 2, 2, 1),
15451        ],
15452        None,
15453        &mut cx,
15454    );
15455}
15456
15457#[gpui::test]
15458async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15459    let (buffer_id, mut cx) = setup_indent_guides_editor(
15460        &"
15461        block1
15462
15463
15464
15465            block2
15466        "
15467        .unindent(),
15468        cx,
15469    )
15470    .await;
15471
15472    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15473}
15474
15475#[gpui::test]
15476async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15477    let (buffer_id, mut cx) = setup_indent_guides_editor(
15478        &"
15479        def a:
15480        \tb = 3
15481        \tif True:
15482        \t\tc = 4
15483        \t\td = 5
15484        \tprint(b)
15485        "
15486        .unindent(),
15487        cx,
15488    )
15489    .await;
15490
15491    assert_indent_guides(
15492        0..6,
15493        vec![
15494            indent_guide(buffer_id, 1, 6, 0),
15495            indent_guide(buffer_id, 3, 4, 1),
15496        ],
15497        None,
15498        &mut cx,
15499    );
15500}
15501
15502#[gpui::test]
15503async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15504    let (buffer_id, mut cx) = setup_indent_guides_editor(
15505        &"
15506    fn main() {
15507        let a = 1;
15508    }"
15509        .unindent(),
15510        cx,
15511    )
15512    .await;
15513
15514    cx.update_editor(|editor, window, cx| {
15515        editor.change_selections(None, window, cx, |s| {
15516            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15517        });
15518    });
15519
15520    assert_indent_guides(
15521        0..3,
15522        vec![indent_guide(buffer_id, 1, 1, 0)],
15523        Some(vec![0]),
15524        &mut cx,
15525    );
15526}
15527
15528#[gpui::test]
15529async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15530    let (buffer_id, mut cx) = setup_indent_guides_editor(
15531        &"
15532    fn main() {
15533        if 1 == 2 {
15534            let a = 1;
15535        }
15536    }"
15537        .unindent(),
15538        cx,
15539    )
15540    .await;
15541
15542    cx.update_editor(|editor, window, cx| {
15543        editor.change_selections(None, window, cx, |s| {
15544            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15545        });
15546    });
15547
15548    assert_indent_guides(
15549        0..4,
15550        vec![
15551            indent_guide(buffer_id, 1, 3, 0),
15552            indent_guide(buffer_id, 2, 2, 1),
15553        ],
15554        Some(vec![1]),
15555        &mut cx,
15556    );
15557
15558    cx.update_editor(|editor, window, cx| {
15559        editor.change_selections(None, window, cx, |s| {
15560            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15561        });
15562    });
15563
15564    assert_indent_guides(
15565        0..4,
15566        vec![
15567            indent_guide(buffer_id, 1, 3, 0),
15568            indent_guide(buffer_id, 2, 2, 1),
15569        ],
15570        Some(vec![1]),
15571        &mut cx,
15572    );
15573
15574    cx.update_editor(|editor, window, cx| {
15575        editor.change_selections(None, window, cx, |s| {
15576            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15577        });
15578    });
15579
15580    assert_indent_guides(
15581        0..4,
15582        vec![
15583            indent_guide(buffer_id, 1, 3, 0),
15584            indent_guide(buffer_id, 2, 2, 1),
15585        ],
15586        Some(vec![0]),
15587        &mut cx,
15588    );
15589}
15590
15591#[gpui::test]
15592async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15593    let (buffer_id, mut cx) = setup_indent_guides_editor(
15594        &"
15595    fn main() {
15596        let a = 1;
15597
15598        let b = 2;
15599    }"
15600        .unindent(),
15601        cx,
15602    )
15603    .await;
15604
15605    cx.update_editor(|editor, window, cx| {
15606        editor.change_selections(None, window, cx, |s| {
15607            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15608        });
15609    });
15610
15611    assert_indent_guides(
15612        0..5,
15613        vec![indent_guide(buffer_id, 1, 3, 0)],
15614        Some(vec![0]),
15615        &mut cx,
15616    );
15617}
15618
15619#[gpui::test]
15620async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15621    let (buffer_id, mut cx) = setup_indent_guides_editor(
15622        &"
15623    def m:
15624        a = 1
15625        pass"
15626            .unindent(),
15627        cx,
15628    )
15629    .await;
15630
15631    cx.update_editor(|editor, window, cx| {
15632        editor.change_selections(None, window, cx, |s| {
15633            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15634        });
15635    });
15636
15637    assert_indent_guides(
15638        0..3,
15639        vec![indent_guide(buffer_id, 1, 2, 0)],
15640        Some(vec![0]),
15641        &mut cx,
15642    );
15643}
15644
15645#[gpui::test]
15646async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15647    init_test(cx, |_| {});
15648    let mut cx = EditorTestContext::new(cx).await;
15649    let text = indoc! {
15650        "
15651        impl A {
15652            fn b() {
15653                0;
15654                3;
15655                5;
15656                6;
15657                7;
15658            }
15659        }
15660        "
15661    };
15662    let base_text = indoc! {
15663        "
15664        impl A {
15665            fn b() {
15666                0;
15667                1;
15668                2;
15669                3;
15670                4;
15671            }
15672            fn c() {
15673                5;
15674                6;
15675                7;
15676            }
15677        }
15678        "
15679    };
15680
15681    cx.update_editor(|editor, window, cx| {
15682        editor.set_text(text, window, cx);
15683
15684        editor.buffer().update(cx, |multibuffer, cx| {
15685            let buffer = multibuffer.as_singleton().unwrap();
15686            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15687
15688            multibuffer.set_all_diff_hunks_expanded(cx);
15689            multibuffer.add_diff(diff, cx);
15690
15691            buffer.read(cx).remote_id()
15692        })
15693    });
15694    cx.run_until_parked();
15695
15696    cx.assert_state_with_diff(
15697        indoc! { "
15698          impl A {
15699              fn b() {
15700                  0;
15701        -         1;
15702        -         2;
15703                  3;
15704        -         4;
15705        -     }
15706        -     fn c() {
15707                  5;
15708                  6;
15709                  7;
15710              }
15711          }
15712          ˇ"
15713        }
15714        .to_string(),
15715    );
15716
15717    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15718        editor
15719            .snapshot(window, cx)
15720            .buffer_snapshot
15721            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15722            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15723            .collect::<Vec<_>>()
15724    });
15725    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15726    assert_eq!(
15727        actual_guides,
15728        vec![
15729            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15730            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15731            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15732        ]
15733    );
15734}
15735
15736#[gpui::test]
15737async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15738    init_test(cx, |_| {});
15739    let mut cx = EditorTestContext::new(cx).await;
15740
15741    let diff_base = r#"
15742        a
15743        b
15744        c
15745        "#
15746    .unindent();
15747
15748    cx.set_state(
15749        &r#"
15750        ˇA
15751        b
15752        C
15753        "#
15754        .unindent(),
15755    );
15756    cx.set_head_text(&diff_base);
15757    cx.update_editor(|editor, window, cx| {
15758        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15759    });
15760    executor.run_until_parked();
15761
15762    let both_hunks_expanded = r#"
15763        - a
15764        + ˇA
15765          b
15766        - c
15767        + C
15768        "#
15769    .unindent();
15770
15771    cx.assert_state_with_diff(both_hunks_expanded.clone());
15772
15773    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15774        let snapshot = editor.snapshot(window, cx);
15775        let hunks = editor
15776            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15777            .collect::<Vec<_>>();
15778        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15779        let buffer_id = hunks[0].buffer_id;
15780        hunks
15781            .into_iter()
15782            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15783            .collect::<Vec<_>>()
15784    });
15785    assert_eq!(hunk_ranges.len(), 2);
15786
15787    cx.update_editor(|editor, _, cx| {
15788        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15789    });
15790    executor.run_until_parked();
15791
15792    let second_hunk_expanded = r#"
15793          ˇA
15794          b
15795        - c
15796        + C
15797        "#
15798    .unindent();
15799
15800    cx.assert_state_with_diff(second_hunk_expanded);
15801
15802    cx.update_editor(|editor, _, cx| {
15803        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15804    });
15805    executor.run_until_parked();
15806
15807    cx.assert_state_with_diff(both_hunks_expanded.clone());
15808
15809    cx.update_editor(|editor, _, cx| {
15810        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15811    });
15812    executor.run_until_parked();
15813
15814    let first_hunk_expanded = r#"
15815        - a
15816        + ˇA
15817          b
15818          C
15819        "#
15820    .unindent();
15821
15822    cx.assert_state_with_diff(first_hunk_expanded);
15823
15824    cx.update_editor(|editor, _, cx| {
15825        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15826    });
15827    executor.run_until_parked();
15828
15829    cx.assert_state_with_diff(both_hunks_expanded);
15830
15831    cx.set_state(
15832        &r#"
15833        ˇA
15834        b
15835        "#
15836        .unindent(),
15837    );
15838    cx.run_until_parked();
15839
15840    // TODO this cursor position seems bad
15841    cx.assert_state_with_diff(
15842        r#"
15843        - ˇa
15844        + A
15845          b
15846        "#
15847        .unindent(),
15848    );
15849
15850    cx.update_editor(|editor, window, cx| {
15851        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15852    });
15853
15854    cx.assert_state_with_diff(
15855        r#"
15856            - ˇa
15857            + A
15858              b
15859            - c
15860            "#
15861        .unindent(),
15862    );
15863
15864    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15865        let snapshot = editor.snapshot(window, cx);
15866        let hunks = editor
15867            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15868            .collect::<Vec<_>>();
15869        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15870        let buffer_id = hunks[0].buffer_id;
15871        hunks
15872            .into_iter()
15873            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15874            .collect::<Vec<_>>()
15875    });
15876    assert_eq!(hunk_ranges.len(), 2);
15877
15878    cx.update_editor(|editor, _, cx| {
15879        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15880    });
15881    executor.run_until_parked();
15882
15883    cx.assert_state_with_diff(
15884        r#"
15885        - ˇa
15886        + A
15887          b
15888        "#
15889        .unindent(),
15890    );
15891}
15892
15893#[gpui::test]
15894async fn test_toggle_deletion_hunk_at_start_of_file(
15895    executor: BackgroundExecutor,
15896    cx: &mut TestAppContext,
15897) {
15898    init_test(cx, |_| {});
15899    let mut cx = EditorTestContext::new(cx).await;
15900
15901    let diff_base = r#"
15902        a
15903        b
15904        c
15905        "#
15906    .unindent();
15907
15908    cx.set_state(
15909        &r#"
15910        ˇb
15911        c
15912        "#
15913        .unindent(),
15914    );
15915    cx.set_head_text(&diff_base);
15916    cx.update_editor(|editor, window, cx| {
15917        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15918    });
15919    executor.run_until_parked();
15920
15921    let hunk_expanded = r#"
15922        - a
15923          ˇb
15924          c
15925        "#
15926    .unindent();
15927
15928    cx.assert_state_with_diff(hunk_expanded.clone());
15929
15930    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15931        let snapshot = editor.snapshot(window, cx);
15932        let hunks = editor
15933            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15934            .collect::<Vec<_>>();
15935        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15936        let buffer_id = hunks[0].buffer_id;
15937        hunks
15938            .into_iter()
15939            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15940            .collect::<Vec<_>>()
15941    });
15942    assert_eq!(hunk_ranges.len(), 1);
15943
15944    cx.update_editor(|editor, _, cx| {
15945        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15946    });
15947    executor.run_until_parked();
15948
15949    let hunk_collapsed = r#"
15950          ˇb
15951          c
15952        "#
15953    .unindent();
15954
15955    cx.assert_state_with_diff(hunk_collapsed);
15956
15957    cx.update_editor(|editor, _, cx| {
15958        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15959    });
15960    executor.run_until_parked();
15961
15962    cx.assert_state_with_diff(hunk_expanded.clone());
15963}
15964
15965#[gpui::test]
15966async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15967    init_test(cx, |_| {});
15968
15969    let fs = FakeFs::new(cx.executor());
15970    fs.insert_tree(
15971        path!("/test"),
15972        json!({
15973            ".git": {},
15974            "file-1": "ONE\n",
15975            "file-2": "TWO\n",
15976            "file-3": "THREE\n",
15977        }),
15978    )
15979    .await;
15980
15981    fs.set_head_for_repo(
15982        path!("/test/.git").as_ref(),
15983        &[
15984            ("file-1".into(), "one\n".into()),
15985            ("file-2".into(), "two\n".into()),
15986            ("file-3".into(), "three\n".into()),
15987        ],
15988    );
15989
15990    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15991    let mut buffers = vec![];
15992    for i in 1..=3 {
15993        let buffer = project
15994            .update(cx, |project, cx| {
15995                let path = format!(path!("/test/file-{}"), i);
15996                project.open_local_buffer(path, cx)
15997            })
15998            .await
15999            .unwrap();
16000        buffers.push(buffer);
16001    }
16002
16003    let multibuffer = cx.new(|cx| {
16004        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16005        multibuffer.set_all_diff_hunks_expanded(cx);
16006        for buffer in &buffers {
16007            let snapshot = buffer.read(cx).snapshot();
16008            multibuffer.set_excerpts_for_path(
16009                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
16010                buffer.clone(),
16011                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16012                DEFAULT_MULTIBUFFER_CONTEXT,
16013                cx,
16014            );
16015        }
16016        multibuffer
16017    });
16018
16019    let editor = cx.add_window(|window, cx| {
16020        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16021    });
16022    cx.run_until_parked();
16023
16024    let snapshot = editor
16025        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16026        .unwrap();
16027    let hunks = snapshot
16028        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16029        .map(|hunk| match hunk {
16030            DisplayDiffHunk::Unfolded {
16031                display_row_range, ..
16032            } => display_row_range,
16033            DisplayDiffHunk::Folded { .. } => unreachable!(),
16034        })
16035        .collect::<Vec<_>>();
16036    assert_eq!(
16037        hunks,
16038        [
16039            DisplayRow(2)..DisplayRow(4),
16040            DisplayRow(7)..DisplayRow(9),
16041            DisplayRow(12)..DisplayRow(14),
16042        ]
16043    );
16044}
16045
16046#[gpui::test]
16047async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16048    init_test(cx, |_| {});
16049
16050    let mut cx = EditorTestContext::new(cx).await;
16051    cx.set_head_text(indoc! { "
16052        one
16053        two
16054        three
16055        four
16056        five
16057        "
16058    });
16059    cx.set_index_text(indoc! { "
16060        one
16061        two
16062        three
16063        four
16064        five
16065        "
16066    });
16067    cx.set_state(indoc! {"
16068        one
16069        TWO
16070        ˇTHREE
16071        FOUR
16072        five
16073    "});
16074    cx.run_until_parked();
16075    cx.update_editor(|editor, window, cx| {
16076        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16077    });
16078    cx.run_until_parked();
16079    cx.assert_index_text(Some(indoc! {"
16080        one
16081        TWO
16082        THREE
16083        FOUR
16084        five
16085    "}));
16086    cx.set_state(indoc! { "
16087        one
16088        TWO
16089        ˇTHREE-HUNDRED
16090        FOUR
16091        five
16092    "});
16093    cx.run_until_parked();
16094    cx.update_editor(|editor, window, cx| {
16095        let snapshot = editor.snapshot(window, cx);
16096        let hunks = editor
16097            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16098            .collect::<Vec<_>>();
16099        assert_eq!(hunks.len(), 1);
16100        assert_eq!(
16101            hunks[0].status(),
16102            DiffHunkStatus {
16103                kind: DiffHunkStatusKind::Modified,
16104                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16105            }
16106        );
16107
16108        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16109    });
16110    cx.run_until_parked();
16111    cx.assert_index_text(Some(indoc! {"
16112        one
16113        TWO
16114        THREE-HUNDRED
16115        FOUR
16116        five
16117    "}));
16118}
16119
16120#[gpui::test]
16121fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16122    init_test(cx, |_| {});
16123
16124    let editor = cx.add_window(|window, cx| {
16125        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16126        build_editor(buffer, window, cx)
16127    });
16128
16129    let render_args = Arc::new(Mutex::new(None));
16130    let snapshot = editor
16131        .update(cx, |editor, window, cx| {
16132            let snapshot = editor.buffer().read(cx).snapshot(cx);
16133            let range =
16134                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16135
16136            struct RenderArgs {
16137                row: MultiBufferRow,
16138                folded: bool,
16139                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16140            }
16141
16142            let crease = Crease::inline(
16143                range,
16144                FoldPlaceholder::test(),
16145                {
16146                    let toggle_callback = render_args.clone();
16147                    move |row, folded, callback, _window, _cx| {
16148                        *toggle_callback.lock() = Some(RenderArgs {
16149                            row,
16150                            folded,
16151                            callback,
16152                        });
16153                        div()
16154                    }
16155                },
16156                |_row, _folded, _window, _cx| div(),
16157            );
16158
16159            editor.insert_creases(Some(crease), cx);
16160            let snapshot = editor.snapshot(window, cx);
16161            let _div = snapshot.render_crease_toggle(
16162                MultiBufferRow(1),
16163                false,
16164                cx.entity().clone(),
16165                window,
16166                cx,
16167            );
16168            snapshot
16169        })
16170        .unwrap();
16171
16172    let render_args = render_args.lock().take().unwrap();
16173    assert_eq!(render_args.row, MultiBufferRow(1));
16174    assert!(!render_args.folded);
16175    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16176
16177    cx.update_window(*editor, |_, window, cx| {
16178        (render_args.callback)(true, window, cx)
16179    })
16180    .unwrap();
16181    let snapshot = editor
16182        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16183        .unwrap();
16184    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16185
16186    cx.update_window(*editor, |_, window, cx| {
16187        (render_args.callback)(false, window, cx)
16188    })
16189    .unwrap();
16190    let snapshot = editor
16191        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16192        .unwrap();
16193    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16194}
16195
16196#[gpui::test]
16197async fn test_input_text(cx: &mut TestAppContext) {
16198    init_test(cx, |_| {});
16199    let mut cx = EditorTestContext::new(cx).await;
16200
16201    cx.set_state(
16202        &r#"ˇone
16203        two
16204
16205        three
16206        fourˇ
16207        five
16208
16209        siˇx"#
16210            .unindent(),
16211    );
16212
16213    cx.dispatch_action(HandleInput(String::new()));
16214    cx.assert_editor_state(
16215        &r#"ˇone
16216        two
16217
16218        three
16219        fourˇ
16220        five
16221
16222        siˇx"#
16223            .unindent(),
16224    );
16225
16226    cx.dispatch_action(HandleInput("AAAA".to_string()));
16227    cx.assert_editor_state(
16228        &r#"AAAAˇone
16229        two
16230
16231        three
16232        fourAAAAˇ
16233        five
16234
16235        siAAAAˇx"#
16236            .unindent(),
16237    );
16238}
16239
16240#[gpui::test]
16241async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16242    init_test(cx, |_| {});
16243
16244    let mut cx = EditorTestContext::new(cx).await;
16245    cx.set_state(
16246        r#"let foo = 1;
16247let foo = 2;
16248let foo = 3;
16249let fooˇ = 4;
16250let foo = 5;
16251let foo = 6;
16252let foo = 7;
16253let foo = 8;
16254let foo = 9;
16255let foo = 10;
16256let foo = 11;
16257let foo = 12;
16258let foo = 13;
16259let foo = 14;
16260let foo = 15;"#,
16261    );
16262
16263    cx.update_editor(|e, window, cx| {
16264        assert_eq!(
16265            e.next_scroll_position,
16266            NextScrollCursorCenterTopBottom::Center,
16267            "Default next scroll direction is center",
16268        );
16269
16270        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16271        assert_eq!(
16272            e.next_scroll_position,
16273            NextScrollCursorCenterTopBottom::Top,
16274            "After center, next scroll direction should be top",
16275        );
16276
16277        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16278        assert_eq!(
16279            e.next_scroll_position,
16280            NextScrollCursorCenterTopBottom::Bottom,
16281            "After top, next scroll direction should be bottom",
16282        );
16283
16284        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16285        assert_eq!(
16286            e.next_scroll_position,
16287            NextScrollCursorCenterTopBottom::Center,
16288            "After bottom, scrolling should start over",
16289        );
16290
16291        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16292        assert_eq!(
16293            e.next_scroll_position,
16294            NextScrollCursorCenterTopBottom::Top,
16295            "Scrolling continues if retriggered fast enough"
16296        );
16297    });
16298
16299    cx.executor()
16300        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16301    cx.executor().run_until_parked();
16302    cx.update_editor(|e, _, _| {
16303        assert_eq!(
16304            e.next_scroll_position,
16305            NextScrollCursorCenterTopBottom::Center,
16306            "If scrolling is not triggered fast enough, it should reset"
16307        );
16308    });
16309}
16310
16311#[gpui::test]
16312async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16313    init_test(cx, |_| {});
16314    let mut cx = EditorLspTestContext::new_rust(
16315        lsp::ServerCapabilities {
16316            definition_provider: Some(lsp::OneOf::Left(true)),
16317            references_provider: Some(lsp::OneOf::Left(true)),
16318            ..lsp::ServerCapabilities::default()
16319        },
16320        cx,
16321    )
16322    .await;
16323
16324    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16325        let go_to_definition = cx
16326            .lsp
16327            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16328                move |params, _| async move {
16329                    if empty_go_to_definition {
16330                        Ok(None)
16331                    } else {
16332                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16333                            uri: params.text_document_position_params.text_document.uri,
16334                            range: lsp::Range::new(
16335                                lsp::Position::new(4, 3),
16336                                lsp::Position::new(4, 6),
16337                            ),
16338                        })))
16339                    }
16340                },
16341            );
16342        let references = cx
16343            .lsp
16344            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16345                Ok(Some(vec![lsp::Location {
16346                    uri: params.text_document_position.text_document.uri,
16347                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16348                }]))
16349            });
16350        (go_to_definition, references)
16351    };
16352
16353    cx.set_state(
16354        &r#"fn one() {
16355            let mut a = ˇtwo();
16356        }
16357
16358        fn two() {}"#
16359            .unindent(),
16360    );
16361    set_up_lsp_handlers(false, &mut cx);
16362    let navigated = cx
16363        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16364        .await
16365        .expect("Failed to navigate to definition");
16366    assert_eq!(
16367        navigated,
16368        Navigated::Yes,
16369        "Should have navigated to definition from the GetDefinition response"
16370    );
16371    cx.assert_editor_state(
16372        &r#"fn one() {
16373            let mut a = two();
16374        }
16375
16376        fn «twoˇ»() {}"#
16377            .unindent(),
16378    );
16379
16380    let editors = cx.update_workspace(|workspace, _, cx| {
16381        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16382    });
16383    cx.update_editor(|_, _, test_editor_cx| {
16384        assert_eq!(
16385            editors.len(),
16386            1,
16387            "Initially, only one, test, editor should be open in the workspace"
16388        );
16389        assert_eq!(
16390            test_editor_cx.entity(),
16391            editors.last().expect("Asserted len is 1").clone()
16392        );
16393    });
16394
16395    set_up_lsp_handlers(true, &mut cx);
16396    let navigated = cx
16397        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16398        .await
16399        .expect("Failed to navigate to lookup references");
16400    assert_eq!(
16401        navigated,
16402        Navigated::Yes,
16403        "Should have navigated to references as a fallback after empty GoToDefinition response"
16404    );
16405    // We should not change the selections in the existing file,
16406    // if opening another milti buffer with the references
16407    cx.assert_editor_state(
16408        &r#"fn one() {
16409            let mut a = two();
16410        }
16411
16412        fn «twoˇ»() {}"#
16413            .unindent(),
16414    );
16415    let editors = cx.update_workspace(|workspace, _, cx| {
16416        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16417    });
16418    cx.update_editor(|_, _, test_editor_cx| {
16419        assert_eq!(
16420            editors.len(),
16421            2,
16422            "After falling back to references search, we open a new editor with the results"
16423        );
16424        let references_fallback_text = editors
16425            .into_iter()
16426            .find(|new_editor| *new_editor != test_editor_cx.entity())
16427            .expect("Should have one non-test editor now")
16428            .read(test_editor_cx)
16429            .text(test_editor_cx);
16430        assert_eq!(
16431            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16432            "Should use the range from the references response and not the GoToDefinition one"
16433        );
16434    });
16435}
16436
16437#[gpui::test]
16438async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16439    init_test(cx, |_| {});
16440    cx.update(|cx| {
16441        let mut editor_settings = EditorSettings::get_global(cx).clone();
16442        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16443        EditorSettings::override_global(editor_settings, cx);
16444    });
16445    let mut cx = EditorLspTestContext::new_rust(
16446        lsp::ServerCapabilities {
16447            definition_provider: Some(lsp::OneOf::Left(true)),
16448            references_provider: Some(lsp::OneOf::Left(true)),
16449            ..lsp::ServerCapabilities::default()
16450        },
16451        cx,
16452    )
16453    .await;
16454    let original_state = r#"fn one() {
16455        let mut a = ˇtwo();
16456    }
16457
16458    fn two() {}"#
16459        .unindent();
16460    cx.set_state(&original_state);
16461
16462    let mut go_to_definition = cx
16463        .lsp
16464        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16465            move |_, _| async move { Ok(None) },
16466        );
16467    let _references = cx
16468        .lsp
16469        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16470            panic!("Should not call for references with no go to definition fallback")
16471        });
16472
16473    let navigated = cx
16474        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16475        .await
16476        .expect("Failed to navigate to lookup references");
16477    go_to_definition
16478        .next()
16479        .await
16480        .expect("Should have called the go_to_definition handler");
16481
16482    assert_eq!(
16483        navigated,
16484        Navigated::No,
16485        "Should have navigated to references as a fallback after empty GoToDefinition response"
16486    );
16487    cx.assert_editor_state(&original_state);
16488    let editors = cx.update_workspace(|workspace, _, cx| {
16489        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16490    });
16491    cx.update_editor(|_, _, _| {
16492        assert_eq!(
16493            editors.len(),
16494            1,
16495            "After unsuccessful fallback, no other editor should have been opened"
16496        );
16497    });
16498}
16499
16500#[gpui::test]
16501async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16502    init_test(cx, |_| {});
16503
16504    let language = Arc::new(Language::new(
16505        LanguageConfig::default(),
16506        Some(tree_sitter_rust::LANGUAGE.into()),
16507    ));
16508
16509    let text = r#"
16510        #[cfg(test)]
16511        mod tests() {
16512            #[test]
16513            fn runnable_1() {
16514                let a = 1;
16515            }
16516
16517            #[test]
16518            fn runnable_2() {
16519                let a = 1;
16520                let b = 2;
16521            }
16522        }
16523    "#
16524    .unindent();
16525
16526    let fs = FakeFs::new(cx.executor());
16527    fs.insert_file("/file.rs", Default::default()).await;
16528
16529    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16530    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16531    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16532    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16533    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16534
16535    let editor = cx.new_window_entity(|window, cx| {
16536        Editor::new(
16537            EditorMode::Full,
16538            multi_buffer,
16539            Some(project.clone()),
16540            window,
16541            cx,
16542        )
16543    });
16544
16545    editor.update_in(cx, |editor, window, cx| {
16546        let snapshot = editor.buffer().read(cx).snapshot(cx);
16547        editor.tasks.insert(
16548            (buffer.read(cx).remote_id(), 3),
16549            RunnableTasks {
16550                templates: vec![],
16551                offset: snapshot.anchor_before(43),
16552                column: 0,
16553                extra_variables: HashMap::default(),
16554                context_range: BufferOffset(43)..BufferOffset(85),
16555            },
16556        );
16557        editor.tasks.insert(
16558            (buffer.read(cx).remote_id(), 8),
16559            RunnableTasks {
16560                templates: vec![],
16561                offset: snapshot.anchor_before(86),
16562                column: 0,
16563                extra_variables: HashMap::default(),
16564                context_range: BufferOffset(86)..BufferOffset(191),
16565            },
16566        );
16567
16568        // Test finding task when cursor is inside function body
16569        editor.change_selections(None, window, cx, |s| {
16570            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16571        });
16572        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16573        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16574
16575        // Test finding task when cursor is on function name
16576        editor.change_selections(None, window, cx, |s| {
16577            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16578        });
16579        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16580        assert_eq!(row, 8, "Should find task when cursor is on function name");
16581    });
16582}
16583
16584#[gpui::test]
16585async fn test_folding_buffers(cx: &mut TestAppContext) {
16586    init_test(cx, |_| {});
16587
16588    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16589    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16590    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16591
16592    let fs = FakeFs::new(cx.executor());
16593    fs.insert_tree(
16594        path!("/a"),
16595        json!({
16596            "first.rs": sample_text_1,
16597            "second.rs": sample_text_2,
16598            "third.rs": sample_text_3,
16599        }),
16600    )
16601    .await;
16602    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16603    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16604    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16605    let worktree = project.update(cx, |project, cx| {
16606        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16607        assert_eq!(worktrees.len(), 1);
16608        worktrees.pop().unwrap()
16609    });
16610    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16611
16612    let buffer_1 = project
16613        .update(cx, |project, cx| {
16614            project.open_buffer((worktree_id, "first.rs"), cx)
16615        })
16616        .await
16617        .unwrap();
16618    let buffer_2 = project
16619        .update(cx, |project, cx| {
16620            project.open_buffer((worktree_id, "second.rs"), cx)
16621        })
16622        .await
16623        .unwrap();
16624    let buffer_3 = project
16625        .update(cx, |project, cx| {
16626            project.open_buffer((worktree_id, "third.rs"), cx)
16627        })
16628        .await
16629        .unwrap();
16630
16631    let multi_buffer = cx.new(|cx| {
16632        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16633        multi_buffer.push_excerpts(
16634            buffer_1.clone(),
16635            [
16636                ExcerptRange {
16637                    context: Point::new(0, 0)..Point::new(3, 0),
16638                    primary: None,
16639                },
16640                ExcerptRange {
16641                    context: Point::new(5, 0)..Point::new(7, 0),
16642                    primary: None,
16643                },
16644                ExcerptRange {
16645                    context: Point::new(9, 0)..Point::new(10, 4),
16646                    primary: None,
16647                },
16648            ],
16649            cx,
16650        );
16651        multi_buffer.push_excerpts(
16652            buffer_2.clone(),
16653            [
16654                ExcerptRange {
16655                    context: Point::new(0, 0)..Point::new(3, 0),
16656                    primary: None,
16657                },
16658                ExcerptRange {
16659                    context: Point::new(5, 0)..Point::new(7, 0),
16660                    primary: None,
16661                },
16662                ExcerptRange {
16663                    context: Point::new(9, 0)..Point::new(10, 4),
16664                    primary: None,
16665                },
16666            ],
16667            cx,
16668        );
16669        multi_buffer.push_excerpts(
16670            buffer_3.clone(),
16671            [
16672                ExcerptRange {
16673                    context: Point::new(0, 0)..Point::new(3, 0),
16674                    primary: None,
16675                },
16676                ExcerptRange {
16677                    context: Point::new(5, 0)..Point::new(7, 0),
16678                    primary: None,
16679                },
16680                ExcerptRange {
16681                    context: Point::new(9, 0)..Point::new(10, 4),
16682                    primary: None,
16683                },
16684            ],
16685            cx,
16686        );
16687        multi_buffer
16688    });
16689    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16690        Editor::new(
16691            EditorMode::Full,
16692            multi_buffer.clone(),
16693            Some(project.clone()),
16694            window,
16695            cx,
16696        )
16697    });
16698
16699    assert_eq!(
16700        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16701        "\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",
16702    );
16703
16704    multi_buffer_editor.update(cx, |editor, cx| {
16705        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16706    });
16707    assert_eq!(
16708        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16709        "\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",
16710        "After folding the first buffer, its text should not be displayed"
16711    );
16712
16713    multi_buffer_editor.update(cx, |editor, cx| {
16714        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16715    });
16716    assert_eq!(
16717        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16718        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16719        "After folding the second buffer, its text should not be displayed"
16720    );
16721
16722    multi_buffer_editor.update(cx, |editor, cx| {
16723        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16724    });
16725    assert_eq!(
16726        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16727        "\n\n\n\n\n",
16728        "After folding the third buffer, its text should not be displayed"
16729    );
16730
16731    // Emulate selection inside the fold logic, that should work
16732    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16733        editor
16734            .snapshot(window, cx)
16735            .next_line_boundary(Point::new(0, 4));
16736    });
16737
16738    multi_buffer_editor.update(cx, |editor, cx| {
16739        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16740    });
16741    assert_eq!(
16742        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16743        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16744        "After unfolding the second buffer, its text should be displayed"
16745    );
16746
16747    // Typing inside of buffer 1 causes that buffer to be unfolded.
16748    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16749        assert_eq!(
16750            multi_buffer
16751                .read(cx)
16752                .snapshot(cx)
16753                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16754                .collect::<String>(),
16755            "bbbb"
16756        );
16757        editor.change_selections(None, window, cx, |selections| {
16758            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16759        });
16760        editor.handle_input("B", window, cx);
16761    });
16762
16763    assert_eq!(
16764        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16765        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16766        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16767    );
16768
16769    multi_buffer_editor.update(cx, |editor, cx| {
16770        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16771    });
16772    assert_eq!(
16773        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16774        "\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",
16775        "After unfolding the all buffers, all original text should be displayed"
16776    );
16777}
16778
16779#[gpui::test]
16780async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16781    init_test(cx, |_| {});
16782
16783    let sample_text_1 = "1111\n2222\n3333".to_string();
16784    let sample_text_2 = "4444\n5555\n6666".to_string();
16785    let sample_text_3 = "7777\n8888\n9999".to_string();
16786
16787    let fs = FakeFs::new(cx.executor());
16788    fs.insert_tree(
16789        path!("/a"),
16790        json!({
16791            "first.rs": sample_text_1,
16792            "second.rs": sample_text_2,
16793            "third.rs": sample_text_3,
16794        }),
16795    )
16796    .await;
16797    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16798    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16799    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16800    let worktree = project.update(cx, |project, cx| {
16801        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16802        assert_eq!(worktrees.len(), 1);
16803        worktrees.pop().unwrap()
16804    });
16805    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16806
16807    let buffer_1 = project
16808        .update(cx, |project, cx| {
16809            project.open_buffer((worktree_id, "first.rs"), cx)
16810        })
16811        .await
16812        .unwrap();
16813    let buffer_2 = project
16814        .update(cx, |project, cx| {
16815            project.open_buffer((worktree_id, "second.rs"), cx)
16816        })
16817        .await
16818        .unwrap();
16819    let buffer_3 = project
16820        .update(cx, |project, cx| {
16821            project.open_buffer((worktree_id, "third.rs"), cx)
16822        })
16823        .await
16824        .unwrap();
16825
16826    let multi_buffer = cx.new(|cx| {
16827        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16828        multi_buffer.push_excerpts(
16829            buffer_1.clone(),
16830            [ExcerptRange {
16831                context: Point::new(0, 0)..Point::new(3, 0),
16832                primary: None,
16833            }],
16834            cx,
16835        );
16836        multi_buffer.push_excerpts(
16837            buffer_2.clone(),
16838            [ExcerptRange {
16839                context: Point::new(0, 0)..Point::new(3, 0),
16840                primary: None,
16841            }],
16842            cx,
16843        );
16844        multi_buffer.push_excerpts(
16845            buffer_3.clone(),
16846            [ExcerptRange {
16847                context: Point::new(0, 0)..Point::new(3, 0),
16848                primary: None,
16849            }],
16850            cx,
16851        );
16852        multi_buffer
16853    });
16854
16855    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16856        Editor::new(
16857            EditorMode::Full,
16858            multi_buffer,
16859            Some(project.clone()),
16860            window,
16861            cx,
16862        )
16863    });
16864
16865    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16866    assert_eq!(
16867        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16868        full_text,
16869    );
16870
16871    multi_buffer_editor.update(cx, |editor, cx| {
16872        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16873    });
16874    assert_eq!(
16875        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16876        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16877        "After folding the first buffer, its text should not be displayed"
16878    );
16879
16880    multi_buffer_editor.update(cx, |editor, cx| {
16881        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16882    });
16883
16884    assert_eq!(
16885        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16886        "\n\n\n\n\n\n7777\n8888\n9999",
16887        "After folding the second buffer, its text should not be displayed"
16888    );
16889
16890    multi_buffer_editor.update(cx, |editor, cx| {
16891        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16892    });
16893    assert_eq!(
16894        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16895        "\n\n\n\n\n",
16896        "After folding the third buffer, its text should not be displayed"
16897    );
16898
16899    multi_buffer_editor.update(cx, |editor, cx| {
16900        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16901    });
16902    assert_eq!(
16903        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16904        "\n\n\n\n4444\n5555\n6666\n\n",
16905        "After unfolding the second buffer, its text should be displayed"
16906    );
16907
16908    multi_buffer_editor.update(cx, |editor, cx| {
16909        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16910    });
16911    assert_eq!(
16912        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16913        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16914        "After unfolding the first buffer, its text should be displayed"
16915    );
16916
16917    multi_buffer_editor.update(cx, |editor, cx| {
16918        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16919    });
16920    assert_eq!(
16921        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16922        full_text,
16923        "After unfolding all buffers, all original text should be displayed"
16924    );
16925}
16926
16927#[gpui::test]
16928async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16929    init_test(cx, |_| {});
16930
16931    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16932
16933    let fs = FakeFs::new(cx.executor());
16934    fs.insert_tree(
16935        path!("/a"),
16936        json!({
16937            "main.rs": sample_text,
16938        }),
16939    )
16940    .await;
16941    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16942    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16943    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16944    let worktree = project.update(cx, |project, cx| {
16945        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16946        assert_eq!(worktrees.len(), 1);
16947        worktrees.pop().unwrap()
16948    });
16949    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16950
16951    let buffer_1 = project
16952        .update(cx, |project, cx| {
16953            project.open_buffer((worktree_id, "main.rs"), cx)
16954        })
16955        .await
16956        .unwrap();
16957
16958    let multi_buffer = cx.new(|cx| {
16959        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16960        multi_buffer.push_excerpts(
16961            buffer_1.clone(),
16962            [ExcerptRange {
16963                context: Point::new(0, 0)
16964                    ..Point::new(
16965                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16966                        0,
16967                    ),
16968                primary: None,
16969            }],
16970            cx,
16971        );
16972        multi_buffer
16973    });
16974    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16975        Editor::new(
16976            EditorMode::Full,
16977            multi_buffer,
16978            Some(project.clone()),
16979            window,
16980            cx,
16981        )
16982    });
16983
16984    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16985    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16986        enum TestHighlight {}
16987        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16988        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16989        editor.highlight_text::<TestHighlight>(
16990            vec![highlight_range.clone()],
16991            HighlightStyle::color(Hsla::green()),
16992            cx,
16993        );
16994        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16995    });
16996
16997    let full_text = format!("\n\n{sample_text}");
16998    assert_eq!(
16999        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17000        full_text,
17001    );
17002}
17003
17004#[gpui::test]
17005async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17006    init_test(cx, |_| {});
17007    cx.update(|cx| {
17008        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17009            "keymaps/default-linux.json",
17010            cx,
17011        )
17012        .unwrap();
17013        cx.bind_keys(default_key_bindings);
17014    });
17015
17016    let (editor, cx) = cx.add_window_view(|window, cx| {
17017        let multi_buffer = MultiBuffer::build_multi(
17018            [
17019                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17020                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17021                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17022                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17023            ],
17024            cx,
17025        );
17026        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17027
17028        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17029        // fold all but the second buffer, so that we test navigating between two
17030        // adjacent folded buffers, as well as folded buffers at the start and
17031        // end the multibuffer
17032        editor.fold_buffer(buffer_ids[0], cx);
17033        editor.fold_buffer(buffer_ids[2], cx);
17034        editor.fold_buffer(buffer_ids[3], cx);
17035
17036        editor
17037    });
17038    cx.simulate_resize(size(px(1000.), px(1000.)));
17039
17040    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17041    cx.assert_excerpts_with_selections(indoc! {"
17042        [EXCERPT]
17043        ˇ[FOLDED]
17044        [EXCERPT]
17045        a1
17046        b1
17047        [EXCERPT]
17048        [FOLDED]
17049        [EXCERPT]
17050        [FOLDED]
17051        "
17052    });
17053    cx.simulate_keystroke("down");
17054    cx.assert_excerpts_with_selections(indoc! {"
17055        [EXCERPT]
17056        [FOLDED]
17057        [EXCERPT]
17058        ˇa1
17059        b1
17060        [EXCERPT]
17061        [FOLDED]
17062        [EXCERPT]
17063        [FOLDED]
17064        "
17065    });
17066    cx.simulate_keystroke("down");
17067    cx.assert_excerpts_with_selections(indoc! {"
17068        [EXCERPT]
17069        [FOLDED]
17070        [EXCERPT]
17071        a1
17072        ˇb1
17073        [EXCERPT]
17074        [FOLDED]
17075        [EXCERPT]
17076        [FOLDED]
17077        "
17078    });
17079    cx.simulate_keystroke("down");
17080    cx.assert_excerpts_with_selections(indoc! {"
17081        [EXCERPT]
17082        [FOLDED]
17083        [EXCERPT]
17084        a1
17085        b1
17086        ˇ[EXCERPT]
17087        [FOLDED]
17088        [EXCERPT]
17089        [FOLDED]
17090        "
17091    });
17092    cx.simulate_keystroke("down");
17093    cx.assert_excerpts_with_selections(indoc! {"
17094        [EXCERPT]
17095        [FOLDED]
17096        [EXCERPT]
17097        a1
17098        b1
17099        [EXCERPT]
17100        ˇ[FOLDED]
17101        [EXCERPT]
17102        [FOLDED]
17103        "
17104    });
17105    for _ in 0..5 {
17106        cx.simulate_keystroke("down");
17107        cx.assert_excerpts_with_selections(indoc! {"
17108            [EXCERPT]
17109            [FOLDED]
17110            [EXCERPT]
17111            a1
17112            b1
17113            [EXCERPT]
17114            [FOLDED]
17115            [EXCERPT]
17116            ˇ[FOLDED]
17117            "
17118        });
17119    }
17120
17121    cx.simulate_keystroke("up");
17122    cx.assert_excerpts_with_selections(indoc! {"
17123        [EXCERPT]
17124        [FOLDED]
17125        [EXCERPT]
17126        a1
17127        b1
17128        [EXCERPT]
17129        ˇ[FOLDED]
17130        [EXCERPT]
17131        [FOLDED]
17132        "
17133    });
17134    cx.simulate_keystroke("up");
17135    cx.assert_excerpts_with_selections(indoc! {"
17136        [EXCERPT]
17137        [FOLDED]
17138        [EXCERPT]
17139        a1
17140        b1
17141        ˇ[EXCERPT]
17142        [FOLDED]
17143        [EXCERPT]
17144        [FOLDED]
17145        "
17146    });
17147    cx.simulate_keystroke("up");
17148    cx.assert_excerpts_with_selections(indoc! {"
17149        [EXCERPT]
17150        [FOLDED]
17151        [EXCERPT]
17152        a1
17153        ˇb1
17154        [EXCERPT]
17155        [FOLDED]
17156        [EXCERPT]
17157        [FOLDED]
17158        "
17159    });
17160    cx.simulate_keystroke("up");
17161    cx.assert_excerpts_with_selections(indoc! {"
17162        [EXCERPT]
17163        [FOLDED]
17164        [EXCERPT]
17165        ˇa1
17166        b1
17167        [EXCERPT]
17168        [FOLDED]
17169        [EXCERPT]
17170        [FOLDED]
17171        "
17172    });
17173    for _ in 0..5 {
17174        cx.simulate_keystroke("up");
17175        cx.assert_excerpts_with_selections(indoc! {"
17176            [EXCERPT]
17177            ˇ[FOLDED]
17178            [EXCERPT]
17179            a1
17180            b1
17181            [EXCERPT]
17182            [FOLDED]
17183            [EXCERPT]
17184            [FOLDED]
17185            "
17186        });
17187    }
17188}
17189
17190#[gpui::test]
17191async fn test_inline_completion_text(cx: &mut TestAppContext) {
17192    init_test(cx, |_| {});
17193
17194    // Simple insertion
17195    assert_highlighted_edits(
17196        "Hello, world!",
17197        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17198        true,
17199        cx,
17200        |highlighted_edits, cx| {
17201            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17202            assert_eq!(highlighted_edits.highlights.len(), 1);
17203            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17204            assert_eq!(
17205                highlighted_edits.highlights[0].1.background_color,
17206                Some(cx.theme().status().created_background)
17207            );
17208        },
17209    )
17210    .await;
17211
17212    // Replacement
17213    assert_highlighted_edits(
17214        "This is a test.",
17215        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17216        false,
17217        cx,
17218        |highlighted_edits, cx| {
17219            assert_eq!(highlighted_edits.text, "That is a test.");
17220            assert_eq!(highlighted_edits.highlights.len(), 1);
17221            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17222            assert_eq!(
17223                highlighted_edits.highlights[0].1.background_color,
17224                Some(cx.theme().status().created_background)
17225            );
17226        },
17227    )
17228    .await;
17229
17230    // Multiple edits
17231    assert_highlighted_edits(
17232        "Hello, world!",
17233        vec![
17234            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17235            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17236        ],
17237        false,
17238        cx,
17239        |highlighted_edits, cx| {
17240            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17241            assert_eq!(highlighted_edits.highlights.len(), 2);
17242            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17243            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17244            assert_eq!(
17245                highlighted_edits.highlights[0].1.background_color,
17246                Some(cx.theme().status().created_background)
17247            );
17248            assert_eq!(
17249                highlighted_edits.highlights[1].1.background_color,
17250                Some(cx.theme().status().created_background)
17251            );
17252        },
17253    )
17254    .await;
17255
17256    // Multiple lines with edits
17257    assert_highlighted_edits(
17258        "First line\nSecond line\nThird line\nFourth line",
17259        vec![
17260            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17261            (
17262                Point::new(2, 0)..Point::new(2, 10),
17263                "New third line".to_string(),
17264            ),
17265            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17266        ],
17267        false,
17268        cx,
17269        |highlighted_edits, cx| {
17270            assert_eq!(
17271                highlighted_edits.text,
17272                "Second modified\nNew third line\nFourth updated line"
17273            );
17274            assert_eq!(highlighted_edits.highlights.len(), 3);
17275            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17276            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17277            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17278            for highlight in &highlighted_edits.highlights {
17279                assert_eq!(
17280                    highlight.1.background_color,
17281                    Some(cx.theme().status().created_background)
17282                );
17283            }
17284        },
17285    )
17286    .await;
17287}
17288
17289#[gpui::test]
17290async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17291    init_test(cx, |_| {});
17292
17293    // Deletion
17294    assert_highlighted_edits(
17295        "Hello, world!",
17296        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17297        true,
17298        cx,
17299        |highlighted_edits, cx| {
17300            assert_eq!(highlighted_edits.text, "Hello, world!");
17301            assert_eq!(highlighted_edits.highlights.len(), 1);
17302            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17303            assert_eq!(
17304                highlighted_edits.highlights[0].1.background_color,
17305                Some(cx.theme().status().deleted_background)
17306            );
17307        },
17308    )
17309    .await;
17310
17311    // Insertion
17312    assert_highlighted_edits(
17313        "Hello, world!",
17314        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17315        true,
17316        cx,
17317        |highlighted_edits, cx| {
17318            assert_eq!(highlighted_edits.highlights.len(), 1);
17319            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17320            assert_eq!(
17321                highlighted_edits.highlights[0].1.background_color,
17322                Some(cx.theme().status().created_background)
17323            );
17324        },
17325    )
17326    .await;
17327}
17328
17329async fn assert_highlighted_edits(
17330    text: &str,
17331    edits: Vec<(Range<Point>, String)>,
17332    include_deletions: bool,
17333    cx: &mut TestAppContext,
17334    assertion_fn: impl Fn(HighlightedText, &App),
17335) {
17336    let window = cx.add_window(|window, cx| {
17337        let buffer = MultiBuffer::build_simple(text, cx);
17338        Editor::new(EditorMode::Full, buffer, None, window, cx)
17339    });
17340    let cx = &mut VisualTestContext::from_window(*window, cx);
17341
17342    let (buffer, snapshot) = window
17343        .update(cx, |editor, _window, cx| {
17344            (
17345                editor.buffer().clone(),
17346                editor.buffer().read(cx).snapshot(cx),
17347            )
17348        })
17349        .unwrap();
17350
17351    let edits = edits
17352        .into_iter()
17353        .map(|(range, edit)| {
17354            (
17355                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17356                edit,
17357            )
17358        })
17359        .collect::<Vec<_>>();
17360
17361    let text_anchor_edits = edits
17362        .clone()
17363        .into_iter()
17364        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17365        .collect::<Vec<_>>();
17366
17367    let edit_preview = window
17368        .update(cx, |_, _window, cx| {
17369            buffer
17370                .read(cx)
17371                .as_singleton()
17372                .unwrap()
17373                .read(cx)
17374                .preview_edits(text_anchor_edits.into(), cx)
17375        })
17376        .unwrap()
17377        .await;
17378
17379    cx.update(|_window, cx| {
17380        let highlighted_edits = inline_completion_edit_text(
17381            &snapshot.as_singleton().unwrap().2,
17382            &edits,
17383            &edit_preview,
17384            include_deletions,
17385            cx,
17386        );
17387        assertion_fn(highlighted_edits, cx)
17388    });
17389}
17390
17391#[track_caller]
17392fn assert_breakpoint(
17393    breakpoints: &BTreeMap<Arc<Path>, Vec<SerializedBreakpoint>>,
17394    path: &Arc<Path>,
17395    expected: Vec<(u32, BreakpointKind)>,
17396) {
17397    if expected.len() == 0usize {
17398        assert!(!breakpoints.contains_key(path));
17399    } else {
17400        let mut breakpoint = breakpoints
17401            .get(path)
17402            .unwrap()
17403            .into_iter()
17404            .map(|breakpoint| (breakpoint.position, breakpoint.kind.clone()))
17405            .collect::<Vec<_>>();
17406
17407        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17408
17409        assert_eq!(expected, breakpoint);
17410    }
17411}
17412
17413fn add_log_breakpoint_at_cursor(
17414    editor: &mut Editor,
17415    log_message: &str,
17416    window: &mut Window,
17417    cx: &mut Context<Editor>,
17418) {
17419    let (anchor, bp) = editor
17420        .breakpoint_at_cursor_head(window, cx)
17421        .unwrap_or_else(|| {
17422            let cursor_position: Point = editor.selections.newest(cx).head();
17423
17424            let breakpoint_position = editor
17425                .snapshot(window, cx)
17426                .display_snapshot
17427                .buffer_snapshot
17428                .anchor_before(Point::new(cursor_position.row, 0));
17429
17430            let kind = BreakpointKind::Log(Arc::from(log_message));
17431
17432            (breakpoint_position, Breakpoint { kind })
17433        });
17434
17435    editor.edit_breakpoint_at_anchor(
17436        anchor,
17437        bp.kind,
17438        BreakpointEditAction::EditLogMessage(log_message.into()),
17439        cx,
17440    );
17441}
17442
17443#[gpui::test]
17444async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17445    init_test(cx, |_| {});
17446
17447    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17448    let fs = FakeFs::new(cx.executor());
17449    fs.insert_tree(
17450        path!("/a"),
17451        json!({
17452            "main.rs": sample_text,
17453        }),
17454    )
17455    .await;
17456    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17457    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17458    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17459
17460    let fs = FakeFs::new(cx.executor());
17461    fs.insert_tree(
17462        path!("/a"),
17463        json!({
17464            "main.rs": sample_text,
17465        }),
17466    )
17467    .await;
17468    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17469    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17470    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17471    let worktree_id = workspace
17472        .update(cx, |workspace, _window, cx| {
17473            workspace.project().update(cx, |project, cx| {
17474                project.worktrees(cx).next().unwrap().read(cx).id()
17475            })
17476        })
17477        .unwrap();
17478
17479    let buffer = project
17480        .update(cx, |project, cx| {
17481            project.open_buffer((worktree_id, "main.rs"), cx)
17482        })
17483        .await
17484        .unwrap();
17485
17486    let (editor, cx) = cx.add_window_view(|window, cx| {
17487        Editor::new(
17488            EditorMode::Full,
17489            MultiBuffer::build_from_buffer(buffer, cx),
17490            Some(project.clone()),
17491            window,
17492            cx,
17493        )
17494    });
17495
17496    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17497    let abs_path = project.read_with(cx, |project, cx| {
17498        project
17499            .absolute_path(&project_path, cx)
17500            .map(|path_buf| Arc::from(path_buf.to_owned()))
17501            .unwrap()
17502    });
17503
17504    // assert we can add breakpoint on the first line
17505    editor.update_in(cx, |editor, window, cx| {
17506        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17507        editor.move_to_end(&MoveToEnd, window, cx);
17508        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17509    });
17510
17511    let breakpoints = editor.update(cx, |editor, cx| {
17512        editor
17513            .breakpoint_store()
17514            .as_ref()
17515            .unwrap()
17516            .read(cx)
17517            .all_breakpoints(cx)
17518            .clone()
17519    });
17520
17521    assert_eq!(1, breakpoints.len());
17522    assert_breakpoint(
17523        &breakpoints,
17524        &abs_path,
17525        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17526    );
17527
17528    editor.update_in(cx, |editor, window, cx| {
17529        editor.move_to_beginning(&MoveToBeginning, window, cx);
17530        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17531    });
17532
17533    let breakpoints = editor.update(cx, |editor, cx| {
17534        editor
17535            .breakpoint_store()
17536            .as_ref()
17537            .unwrap()
17538            .read(cx)
17539            .all_breakpoints(cx)
17540            .clone()
17541    });
17542
17543    assert_eq!(1, breakpoints.len());
17544    assert_breakpoint(&breakpoints, &abs_path, vec![(3, BreakpointKind::Standard)]);
17545
17546    editor.update_in(cx, |editor, window, cx| {
17547        editor.move_to_end(&MoveToEnd, window, cx);
17548        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17549    });
17550
17551    let breakpoints = editor.update(cx, |editor, cx| {
17552        editor
17553            .breakpoint_store()
17554            .as_ref()
17555            .unwrap()
17556            .read(cx)
17557            .all_breakpoints(cx)
17558            .clone()
17559    });
17560
17561    assert_eq!(0, breakpoints.len());
17562    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17563}
17564
17565#[gpui::test]
17566async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17567    init_test(cx, |_| {});
17568
17569    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17570
17571    let fs = FakeFs::new(cx.executor());
17572    fs.insert_tree(
17573        path!("/a"),
17574        json!({
17575            "main.rs": sample_text,
17576        }),
17577    )
17578    .await;
17579    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17580    let (workspace, cx) =
17581        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17582
17583    let worktree_id = workspace.update(cx, |workspace, cx| {
17584        workspace.project().update(cx, |project, cx| {
17585            project.worktrees(cx).next().unwrap().read(cx).id()
17586        })
17587    });
17588
17589    let buffer = project
17590        .update(cx, |project, cx| {
17591            project.open_buffer((worktree_id, "main.rs"), cx)
17592        })
17593        .await
17594        .unwrap();
17595
17596    let (editor, cx) = cx.add_window_view(|window, cx| {
17597        Editor::new(
17598            EditorMode::Full,
17599            MultiBuffer::build_from_buffer(buffer, cx),
17600            Some(project.clone()),
17601            window,
17602            cx,
17603        )
17604    });
17605
17606    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17607    let abs_path = project.read_with(cx, |project, cx| {
17608        project
17609            .absolute_path(&project_path, cx)
17610            .map(|path_buf| Arc::from(path_buf.to_owned()))
17611            .unwrap()
17612    });
17613
17614    editor.update_in(cx, |editor, window, cx| {
17615        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17616    });
17617
17618    let breakpoints = editor.update(cx, |editor, cx| {
17619        editor
17620            .breakpoint_store()
17621            .as_ref()
17622            .unwrap()
17623            .read(cx)
17624            .all_breakpoints(cx)
17625            .clone()
17626    });
17627
17628    assert_breakpoint(
17629        &breakpoints,
17630        &abs_path,
17631        vec![(0, BreakpointKind::Log("hello world".into()))],
17632    );
17633
17634    // Removing a log message from a log breakpoint should remove it
17635    editor.update_in(cx, |editor, window, cx| {
17636        add_log_breakpoint_at_cursor(editor, "", window, cx);
17637    });
17638
17639    let breakpoints = editor.update(cx, |editor, cx| {
17640        editor
17641            .breakpoint_store()
17642            .as_ref()
17643            .unwrap()
17644            .read(cx)
17645            .all_breakpoints(cx)
17646            .clone()
17647    });
17648
17649    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17650
17651    editor.update_in(cx, |editor, window, cx| {
17652        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17653        editor.move_to_end(&MoveToEnd, window, cx);
17654        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17655        // Not adding a log message to a standard breakpoint shouldn't remove it
17656        add_log_breakpoint_at_cursor(editor, "", window, cx);
17657    });
17658
17659    let breakpoints = editor.update(cx, |editor, cx| {
17660        editor
17661            .breakpoint_store()
17662            .as_ref()
17663            .unwrap()
17664            .read(cx)
17665            .all_breakpoints(cx)
17666            .clone()
17667    });
17668
17669    assert_breakpoint(
17670        &breakpoints,
17671        &abs_path,
17672        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17673    );
17674
17675    editor.update_in(cx, |editor, window, cx| {
17676        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17677    });
17678
17679    let breakpoints = editor.update(cx, |editor, cx| {
17680        editor
17681            .breakpoint_store()
17682            .as_ref()
17683            .unwrap()
17684            .read(cx)
17685            .all_breakpoints(cx)
17686            .clone()
17687    });
17688
17689    assert_breakpoint(
17690        &breakpoints,
17691        &abs_path,
17692        vec![
17693            (0, BreakpointKind::Standard),
17694            (3, BreakpointKind::Log("hello world".into())),
17695        ],
17696    );
17697
17698    editor.update_in(cx, |editor, window, cx| {
17699        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17700    });
17701
17702    let breakpoints = editor.update(cx, |editor, cx| {
17703        editor
17704            .breakpoint_store()
17705            .as_ref()
17706            .unwrap()
17707            .read(cx)
17708            .all_breakpoints(cx)
17709            .clone()
17710    });
17711
17712    assert_breakpoint(
17713        &breakpoints,
17714        &abs_path,
17715        vec![
17716            (0, BreakpointKind::Standard),
17717            (3, BreakpointKind::Log("hello Earth !!".into())),
17718        ],
17719    );
17720}
17721
17722#[gpui::test]
17723async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17724    init_test(cx, |_| {});
17725    let capabilities = lsp::ServerCapabilities {
17726        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17727            prepare_provider: Some(true),
17728            work_done_progress_options: Default::default(),
17729        })),
17730        ..Default::default()
17731    };
17732    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17733
17734    cx.set_state(indoc! {"
17735        struct Fˇoo {}
17736    "});
17737
17738    cx.update_editor(|editor, _, cx| {
17739        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17740        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17741        editor.highlight_background::<DocumentHighlightRead>(
17742            &[highlight_range],
17743            |c| c.editor_document_highlight_read_background,
17744            cx,
17745        );
17746    });
17747
17748    let mut prepare_rename_handler = cx
17749        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17750            move |_, _, _| async move {
17751                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17752                    start: lsp::Position {
17753                        line: 0,
17754                        character: 7,
17755                    },
17756                    end: lsp::Position {
17757                        line: 0,
17758                        character: 10,
17759                    },
17760                })))
17761            },
17762        );
17763    let prepare_rename_task = cx
17764        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17765        .expect("Prepare rename was not started");
17766    prepare_rename_handler.next().await.unwrap();
17767    prepare_rename_task.await.expect("Prepare rename failed");
17768
17769    let mut rename_handler =
17770        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17771            let edit = lsp::TextEdit {
17772                range: lsp::Range {
17773                    start: lsp::Position {
17774                        line: 0,
17775                        character: 7,
17776                    },
17777                    end: lsp::Position {
17778                        line: 0,
17779                        character: 10,
17780                    },
17781                },
17782                new_text: "FooRenamed".to_string(),
17783            };
17784            Ok(Some(lsp::WorkspaceEdit::new(
17785                // Specify the same edit twice
17786                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17787            )))
17788        });
17789    let rename_task = cx
17790        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17791        .expect("Confirm rename was not started");
17792    rename_handler.next().await.unwrap();
17793    rename_task.await.expect("Confirm rename failed");
17794    cx.run_until_parked();
17795
17796    // Despite two edits, only one is actually applied as those are identical
17797    cx.assert_editor_state(indoc! {"
17798        struct FooRenamedˇ {}
17799    "});
17800}
17801
17802#[gpui::test]
17803async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17804    init_test(cx, |_| {});
17805    // These capabilities indicate that the server does not support prepare rename.
17806    let capabilities = lsp::ServerCapabilities {
17807        rename_provider: Some(lsp::OneOf::Left(true)),
17808        ..Default::default()
17809    };
17810    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17811
17812    cx.set_state(indoc! {"
17813        struct Fˇoo {}
17814    "});
17815
17816    cx.update_editor(|editor, _window, cx| {
17817        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17818        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17819        editor.highlight_background::<DocumentHighlightRead>(
17820            &[highlight_range],
17821            |c| c.editor_document_highlight_read_background,
17822            cx,
17823        );
17824    });
17825
17826    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17827        .expect("Prepare rename was not started")
17828        .await
17829        .expect("Prepare rename failed");
17830
17831    let mut rename_handler =
17832        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17833            let edit = lsp::TextEdit {
17834                range: lsp::Range {
17835                    start: lsp::Position {
17836                        line: 0,
17837                        character: 7,
17838                    },
17839                    end: lsp::Position {
17840                        line: 0,
17841                        character: 10,
17842                    },
17843                },
17844                new_text: "FooRenamed".to_string(),
17845            };
17846            Ok(Some(lsp::WorkspaceEdit::new(
17847                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17848            )))
17849        });
17850    let rename_task = cx
17851        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17852        .expect("Confirm rename was not started");
17853    rename_handler.next().await.unwrap();
17854    rename_task.await.expect("Confirm rename failed");
17855    cx.run_until_parked();
17856
17857    // Correct range is renamed, as `surrounding_word` is used to find it.
17858    cx.assert_editor_state(indoc! {"
17859        struct FooRenamedˇ {}
17860    "});
17861}
17862
17863#[gpui::test]
17864async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17865    init_test(cx, |_| {});
17866    let mut cx = EditorTestContext::new(cx).await;
17867
17868    let language = Arc::new(
17869        Language::new(
17870            LanguageConfig::default(),
17871            Some(tree_sitter_html::LANGUAGE.into()),
17872        )
17873        .with_brackets_query(
17874            r#"
17875            ("<" @open "/>" @close)
17876            ("</" @open ">" @close)
17877            ("<" @open ">" @close)
17878            ("\"" @open "\"" @close)
17879            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17880        "#,
17881        )
17882        .unwrap(),
17883    );
17884    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17885
17886    cx.set_state(indoc! {"
17887        <span>ˇ</span>
17888    "});
17889    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17890    cx.assert_editor_state(indoc! {"
17891        <span>
17892        ˇ
17893        </span>
17894    "});
17895
17896    cx.set_state(indoc! {"
17897        <span><span></span>ˇ</span>
17898    "});
17899    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17900    cx.assert_editor_state(indoc! {"
17901        <span><span></span>
17902        ˇ</span>
17903    "});
17904
17905    cx.set_state(indoc! {"
17906        <span>ˇ
17907        </span>
17908    "});
17909    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17910    cx.assert_editor_state(indoc! {"
17911        <span>
17912        ˇ
17913        </span>
17914    "});
17915}
17916
17917#[gpui::test(iterations = 10)]
17918async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17919    init_test(cx, |_| {});
17920
17921    let fs = FakeFs::new(cx.executor());
17922    fs.insert_tree(
17923        path!("/dir"),
17924        json!({
17925            "a.ts": "a",
17926        }),
17927    )
17928    .await;
17929
17930    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17931    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17932    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17933
17934    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17935    language_registry.add(Arc::new(Language::new(
17936        LanguageConfig {
17937            name: "TypeScript".into(),
17938            matcher: LanguageMatcher {
17939                path_suffixes: vec!["ts".to_string()],
17940                ..Default::default()
17941            },
17942            ..Default::default()
17943        },
17944        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17945    )));
17946    let mut fake_language_servers = language_registry.register_fake_lsp(
17947        "TypeScript",
17948        FakeLspAdapter {
17949            capabilities: lsp::ServerCapabilities {
17950                code_lens_provider: Some(lsp::CodeLensOptions {
17951                    resolve_provider: Some(true),
17952                }),
17953                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17954                    commands: vec!["_the/command".to_string()],
17955                    ..lsp::ExecuteCommandOptions::default()
17956                }),
17957                ..lsp::ServerCapabilities::default()
17958            },
17959            ..FakeLspAdapter::default()
17960        },
17961    );
17962
17963    let (buffer, _handle) = project
17964        .update(cx, |p, cx| {
17965            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17966        })
17967        .await
17968        .unwrap();
17969    cx.executor().run_until_parked();
17970
17971    let fake_server = fake_language_servers.next().await.unwrap();
17972
17973    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17974    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17975    drop(buffer_snapshot);
17976    let actions = cx
17977        .update_window(*workspace, |_, window, cx| {
17978            project.code_actions(&buffer, anchor..anchor, window, cx)
17979        })
17980        .unwrap();
17981
17982    fake_server
17983        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17984            Ok(Some(vec![
17985                lsp::CodeLens {
17986                    range: lsp::Range::default(),
17987                    command: Some(lsp::Command {
17988                        title: "Code lens command".to_owned(),
17989                        command: "_the/command".to_owned(),
17990                        arguments: None,
17991                    }),
17992                    data: None,
17993                },
17994                lsp::CodeLens {
17995                    range: lsp::Range::default(),
17996                    command: Some(lsp::Command {
17997                        title: "Command not in capabilities".to_owned(),
17998                        command: "not in capabilities".to_owned(),
17999                        arguments: None,
18000                    }),
18001                    data: None,
18002                },
18003                lsp::CodeLens {
18004                    range: lsp::Range {
18005                        start: lsp::Position {
18006                            line: 1,
18007                            character: 1,
18008                        },
18009                        end: lsp::Position {
18010                            line: 1,
18011                            character: 1,
18012                        },
18013                    },
18014                    command: Some(lsp::Command {
18015                        title: "Command not in range".to_owned(),
18016                        command: "_the/command".to_owned(),
18017                        arguments: None,
18018                    }),
18019                    data: None,
18020                },
18021            ]))
18022        })
18023        .next()
18024        .await;
18025
18026    let actions = actions.await.unwrap();
18027    assert_eq!(
18028        actions.len(),
18029        1,
18030        "Should have only one valid action for the 0..0 range"
18031    );
18032    let action = actions[0].clone();
18033    let apply = project.update(cx, |project, cx| {
18034        project.apply_code_action(buffer.clone(), action, true, cx)
18035    });
18036
18037    // Resolving the code action does not populate its edits. In absence of
18038    // edits, we must execute the given command.
18039    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18040        |mut lens, _| async move {
18041            let lens_command = lens.command.as_mut().expect("should have a command");
18042            assert_eq!(lens_command.title, "Code lens command");
18043            lens_command.arguments = Some(vec![json!("the-argument")]);
18044            Ok(lens)
18045        },
18046    );
18047
18048    // While executing the command, the language server sends the editor
18049    // a `workspaceEdit` request.
18050    fake_server
18051        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18052            let fake = fake_server.clone();
18053            move |params, _| {
18054                assert_eq!(params.command, "_the/command");
18055                let fake = fake.clone();
18056                async move {
18057                    fake.server
18058                        .request::<lsp::request::ApplyWorkspaceEdit>(
18059                            lsp::ApplyWorkspaceEditParams {
18060                                label: None,
18061                                edit: lsp::WorkspaceEdit {
18062                                    changes: Some(
18063                                        [(
18064                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18065                                            vec![lsp::TextEdit {
18066                                                range: lsp::Range::new(
18067                                                    lsp::Position::new(0, 0),
18068                                                    lsp::Position::new(0, 0),
18069                                                ),
18070                                                new_text: "X".into(),
18071                                            }],
18072                                        )]
18073                                        .into_iter()
18074                                        .collect(),
18075                                    ),
18076                                    ..Default::default()
18077                                },
18078                            },
18079                        )
18080                        .await
18081                        .unwrap();
18082                    Ok(Some(json!(null)))
18083                }
18084            }
18085        })
18086        .next()
18087        .await;
18088
18089    // Applying the code lens command returns a project transaction containing the edits
18090    // sent by the language server in its `workspaceEdit` request.
18091    let transaction = apply.await.unwrap();
18092    assert!(transaction.0.contains_key(&buffer));
18093    buffer.update(cx, |buffer, cx| {
18094        assert_eq!(buffer.text(), "Xa");
18095        buffer.undo(cx);
18096        assert_eq!(buffer.text(), "a");
18097    });
18098}
18099
18100fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18101    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18102    point..point
18103}
18104
18105fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18106    let (text, ranges) = marked_text_ranges(marked_text, true);
18107    assert_eq!(editor.text(cx), text);
18108    assert_eq!(
18109        editor.selections.ranges(cx),
18110        ranges,
18111        "Assert selections are {}",
18112        marked_text
18113    );
18114}
18115
18116pub fn handle_signature_help_request(
18117    cx: &mut EditorLspTestContext,
18118    mocked_response: lsp::SignatureHelp,
18119) -> impl Future<Output = ()> {
18120    let mut request =
18121        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18122            let mocked_response = mocked_response.clone();
18123            async move { Ok(Some(mocked_response)) }
18124        });
18125
18126    async move {
18127        request.next().await;
18128    }
18129}
18130
18131/// Handle completion request passing a marked string specifying where the completion
18132/// should be triggered from using '|' character, what range should be replaced, and what completions
18133/// should be returned using '<' and '>' to delimit the range
18134pub fn handle_completion_request(
18135    cx: &mut EditorLspTestContext,
18136    marked_string: &str,
18137    completions: Vec<&'static str>,
18138    counter: Arc<AtomicUsize>,
18139) -> impl Future<Output = ()> {
18140    let complete_from_marker: TextRangeMarker = '|'.into();
18141    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18142    let (_, mut marked_ranges) = marked_text_ranges_by(
18143        marked_string,
18144        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18145    );
18146
18147    let complete_from_position =
18148        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18149    let replace_range =
18150        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18151
18152    let mut request =
18153        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18154            let completions = completions.clone();
18155            counter.fetch_add(1, atomic::Ordering::Release);
18156            async move {
18157                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18158                assert_eq!(
18159                    params.text_document_position.position,
18160                    complete_from_position
18161                );
18162                Ok(Some(lsp::CompletionResponse::Array(
18163                    completions
18164                        .iter()
18165                        .map(|completion_text| lsp::CompletionItem {
18166                            label: completion_text.to_string(),
18167                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18168                                range: replace_range,
18169                                new_text: completion_text.to_string(),
18170                            })),
18171                            ..Default::default()
18172                        })
18173                        .collect(),
18174                )))
18175            }
18176        });
18177
18178    async move {
18179        request.next().await;
18180    }
18181}
18182
18183fn handle_resolve_completion_request(
18184    cx: &mut EditorLspTestContext,
18185    edits: Option<Vec<(&'static str, &'static str)>>,
18186) -> impl Future<Output = ()> {
18187    let edits = edits.map(|edits| {
18188        edits
18189            .iter()
18190            .map(|(marked_string, new_text)| {
18191                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18192                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18193                lsp::TextEdit::new(replace_range, new_text.to_string())
18194            })
18195            .collect::<Vec<_>>()
18196    });
18197
18198    let mut request =
18199        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18200            let edits = edits.clone();
18201            async move {
18202                Ok(lsp::CompletionItem {
18203                    additional_text_edits: edits,
18204                    ..Default::default()
18205                })
18206            }
18207        });
18208
18209    async move {
18210        request.next().await;
18211    }
18212}
18213
18214pub(crate) fn update_test_language_settings(
18215    cx: &mut TestAppContext,
18216    f: impl Fn(&mut AllLanguageSettingsContent),
18217) {
18218    cx.update(|cx| {
18219        SettingsStore::update_global(cx, |store, cx| {
18220            store.update_user_settings::<AllLanguageSettings>(cx, f);
18221        });
18222    });
18223}
18224
18225pub(crate) fn update_test_project_settings(
18226    cx: &mut TestAppContext,
18227    f: impl Fn(&mut ProjectSettings),
18228) {
18229    cx.update(|cx| {
18230        SettingsStore::update_global(cx, |store, cx| {
18231            store.update_user_settings::<ProjectSettings>(cx, f);
18232        });
18233    });
18234}
18235
18236pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18237    cx.update(|cx| {
18238        assets::Assets.load_test_fonts(cx);
18239        let store = SettingsStore::test(cx);
18240        cx.set_global(store);
18241        theme::init(theme::LoadThemes::JustBase, cx);
18242        release_channel::init(SemanticVersion::default(), cx);
18243        client::init_settings(cx);
18244        language::init(cx);
18245        Project::init_settings(cx);
18246        workspace::init_settings(cx);
18247        crate::init(cx);
18248    });
18249
18250    update_test_language_settings(cx, f);
18251}
18252
18253#[track_caller]
18254fn assert_hunk_revert(
18255    not_reverted_text_with_selections: &str,
18256    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18257    expected_reverted_text_with_selections: &str,
18258    base_text: &str,
18259    cx: &mut EditorLspTestContext,
18260) {
18261    cx.set_state(not_reverted_text_with_selections);
18262    cx.set_head_text(base_text);
18263    cx.executor().run_until_parked();
18264
18265    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18266        let snapshot = editor.snapshot(window, cx);
18267        let reverted_hunk_statuses = snapshot
18268            .buffer_snapshot
18269            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18270            .map(|hunk| hunk.status().kind)
18271            .collect::<Vec<_>>();
18272
18273        editor.git_restore(&Default::default(), window, cx);
18274        reverted_hunk_statuses
18275    });
18276    cx.executor().run_until_parked();
18277    cx.assert_editor_state(expected_reverted_text_with_selections);
18278    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18279}