editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use buffer_diff::{BufferDiff, DiffHunkStatus};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, PrettierSettings,
   20    },
   21    BracketPairConfig,
   22    Capability::ReadWrite,
   23    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   24    Override, Point,
   25};
   26use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   27use multi_buffer::IndentGuide;
   28use parking_lot::Mutex;
   29use pretty_assertions::{assert_eq, assert_ne};
   30use project::project_settings::{LspSettings, ProjectSettings};
   31use project::FakeFs;
   32use serde_json::{self, json};
   33use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
   34use std::{
   35    iter,
   36    sync::atomic::{self, AtomicUsize},
   37};
   38use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   39use unindent::Unindent;
   40use util::{
   41    assert_set_eq, path,
   42    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   43    uri,
   44};
   45use workspace::{
   46    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   47    NavigationEntry, ViewId,
   48};
   49
   50#[gpui::test]
   51fn test_edit_events(cx: &mut TestAppContext) {
   52    init_test(cx, |_| {});
   53
   54    let buffer = cx.new(|cx| {
   55        let mut buffer = language::Buffer::local("123456", cx);
   56        buffer.set_group_interval(Duration::from_secs(1));
   57        buffer
   58    });
   59
   60    let events = Rc::new(RefCell::new(Vec::new()));
   61    let editor1 = cx.add_window({
   62        let events = events.clone();
   63        |window, cx| {
   64            let entity = cx.entity().clone();
   65            cx.subscribe_in(
   66                &entity,
   67                window,
   68                move |_, _, event: &EditorEvent, _, _| match event {
   69                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   70                    EditorEvent::BufferEdited => {
   71                        events.borrow_mut().push(("editor1", "buffer edited"))
   72                    }
   73                    _ => {}
   74                },
   75            )
   76            .detach();
   77            Editor::for_buffer(buffer.clone(), None, window, cx)
   78        }
   79    });
   80
   81    let editor2 = cx.add_window({
   82        let events = events.clone();
   83        |window, cx| {
   84            cx.subscribe_in(
   85                &cx.entity().clone(),
   86                window,
   87                move |_, _, event: &EditorEvent, _, _| match event {
   88                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   89                    EditorEvent::BufferEdited => {
   90                        events.borrow_mut().push(("editor2", "buffer edited"))
   91                    }
   92                    _ => {}
   93                },
   94            )
   95            .detach();
   96            Editor::for_buffer(buffer.clone(), None, window, cx)
   97        }
   98    });
   99
  100    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  101
  102    // Mutating editor 1 will emit an `Edited` event only for that editor.
  103    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  104    assert_eq!(
  105        mem::take(&mut *events.borrow_mut()),
  106        [
  107            ("editor1", "edited"),
  108            ("editor1", "buffer edited"),
  109            ("editor2", "buffer edited"),
  110        ]
  111    );
  112
  113    // Mutating editor 2 will emit an `Edited` event only for that editor.
  114    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  115    assert_eq!(
  116        mem::take(&mut *events.borrow_mut()),
  117        [
  118            ("editor2", "edited"),
  119            ("editor1", "buffer edited"),
  120            ("editor2", "buffer edited"),
  121        ]
  122    );
  123
  124    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  125    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  126    assert_eq!(
  127        mem::take(&mut *events.borrow_mut()),
  128        [
  129            ("editor1", "edited"),
  130            ("editor1", "buffer edited"),
  131            ("editor2", "buffer edited"),
  132        ]
  133    );
  134
  135    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  136    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  137    assert_eq!(
  138        mem::take(&mut *events.borrow_mut()),
  139        [
  140            ("editor1", "edited"),
  141            ("editor1", "buffer edited"),
  142            ("editor2", "buffer edited"),
  143        ]
  144    );
  145
  146    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  147    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  148    assert_eq!(
  149        mem::take(&mut *events.borrow_mut()),
  150        [
  151            ("editor2", "edited"),
  152            ("editor1", "buffer edited"),
  153            ("editor2", "buffer edited"),
  154        ]
  155    );
  156
  157    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  158    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  159    assert_eq!(
  160        mem::take(&mut *events.borrow_mut()),
  161        [
  162            ("editor2", "edited"),
  163            ("editor1", "buffer edited"),
  164            ("editor2", "buffer edited"),
  165        ]
  166    );
  167
  168    // No event is emitted when the mutation is a no-op.
  169    _ = editor2.update(cx, |editor, window, cx| {
  170        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  171
  172        editor.backspace(&Backspace, window, cx);
  173    });
  174    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  175}
  176
  177#[gpui::test]
  178fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  179    init_test(cx, |_| {});
  180
  181    let mut now = Instant::now();
  182    let group_interval = Duration::from_millis(1);
  183    let buffer = cx.new(|cx| {
  184        let mut buf = language::Buffer::local("123456", cx);
  185        buf.set_group_interval(group_interval);
  186        buf
  187    });
  188    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  189    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  190
  191    _ = editor.update(cx, |editor, window, cx| {
  192        editor.start_transaction_at(now, window, cx);
  193        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  194
  195        editor.insert("cd", window, cx);
  196        editor.end_transaction_at(now, cx);
  197        assert_eq!(editor.text(cx), "12cd56");
  198        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  199
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  202        editor.insert("e", window, cx);
  203        editor.end_transaction_at(now, cx);
  204        assert_eq!(editor.text(cx), "12cde6");
  205        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  206
  207        now += group_interval + Duration::from_millis(1);
  208        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  209
  210        // Simulate an edit in another editor
  211        buffer.update(cx, |buffer, cx| {
  212            buffer.start_transaction_at(now, cx);
  213            buffer.edit([(0..1, "a")], None, cx);
  214            buffer.edit([(1..1, "b")], None, cx);
  215            buffer.end_transaction_at(now, cx);
  216        });
  217
  218        assert_eq!(editor.text(cx), "ab2cde6");
  219        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  220
  221        // Last transaction happened past the group interval in a different editor.
  222        // Undo it individually and don't restore selections.
  223        editor.undo(&Undo, window, cx);
  224        assert_eq!(editor.text(cx), "12cde6");
  225        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  226
  227        // First two transactions happened within the group interval in this editor.
  228        // Undo them together and restore selections.
  229        editor.undo(&Undo, window, cx);
  230        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  231        assert_eq!(editor.text(cx), "123456");
  232        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  233
  234        // Redo the first two transactions together.
  235        editor.redo(&Redo, window, cx);
  236        assert_eq!(editor.text(cx), "12cde6");
  237        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  238
  239        // Redo the last transaction on its own.
  240        editor.redo(&Redo, window, cx);
  241        assert_eq!(editor.text(cx), "ab2cde6");
  242        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  243
  244        // Test empty transactions.
  245        editor.start_transaction_at(now, window, cx);
  246        editor.end_transaction_at(now, cx);
  247        editor.undo(&Undo, window, cx);
  248        assert_eq!(editor.text(cx), "12cde6");
  249    });
  250}
  251
  252#[gpui::test]
  253fn test_ime_composition(cx: &mut TestAppContext) {
  254    init_test(cx, |_| {});
  255
  256    let buffer = cx.new(|cx| {
  257        let mut buffer = language::Buffer::local("abcde", cx);
  258        // Ensure automatic grouping doesn't occur.
  259        buffer.set_group_interval(Duration::ZERO);
  260        buffer
  261    });
  262
  263    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  264    cx.add_window(|window, cx| {
  265        let mut editor = build_editor(buffer.clone(), window, cx);
  266
  267        // Start a new IME composition.
  268        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  269        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  270        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  271        assert_eq!(editor.text(cx), "äbcde");
  272        assert_eq!(
  273            editor.marked_text_ranges(cx),
  274            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  275        );
  276
  277        // Finalize IME composition.
  278        editor.replace_text_in_range(None, "ā", window, cx);
  279        assert_eq!(editor.text(cx), "ābcde");
  280        assert_eq!(editor.marked_text_ranges(cx), None);
  281
  282        // IME composition edits are grouped and are undone/redone at once.
  283        editor.undo(&Default::default(), window, cx);
  284        assert_eq!(editor.text(cx), "abcde");
  285        assert_eq!(editor.marked_text_ranges(cx), None);
  286        editor.redo(&Default::default(), window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // Start a new IME composition.
  291        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  292        assert_eq!(
  293            editor.marked_text_ranges(cx),
  294            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  295        );
  296
  297        // Undoing during an IME composition cancels it.
  298        editor.undo(&Default::default(), window, cx);
  299        assert_eq!(editor.text(cx), "ābcde");
  300        assert_eq!(editor.marked_text_ranges(cx), None);
  301
  302        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  303        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  304        assert_eq!(editor.text(cx), "ābcdè");
  305        assert_eq!(
  306            editor.marked_text_ranges(cx),
  307            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  308        );
  309
  310        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  311        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  312        assert_eq!(editor.text(cx), "ābcdę");
  313        assert_eq!(editor.marked_text_ranges(cx), None);
  314
  315        // Start a new IME composition with multiple cursors.
  316        editor.change_selections(None, window, cx, |s| {
  317            s.select_ranges([
  318                OffsetUtf16(1)..OffsetUtf16(1),
  319                OffsetUtf16(3)..OffsetUtf16(3),
  320                OffsetUtf16(5)..OffsetUtf16(5),
  321            ])
  322        });
  323        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  324        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  325        assert_eq!(
  326            editor.marked_text_ranges(cx),
  327            Some(vec![
  328                OffsetUtf16(0)..OffsetUtf16(3),
  329                OffsetUtf16(4)..OffsetUtf16(7),
  330                OffsetUtf16(8)..OffsetUtf16(11)
  331            ])
  332        );
  333
  334        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  335        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  336        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  337        assert_eq!(
  338            editor.marked_text_ranges(cx),
  339            Some(vec![
  340                OffsetUtf16(1)..OffsetUtf16(2),
  341                OffsetUtf16(5)..OffsetUtf16(6),
  342                OffsetUtf16(9)..OffsetUtf16(10)
  343            ])
  344        );
  345
  346        // Finalize IME composition with multiple cursors.
  347        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  348        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  349        assert_eq!(editor.marked_text_ranges(cx), None);
  350
  351        editor
  352    });
  353}
  354
  355#[gpui::test]
  356fn test_selection_with_mouse(cx: &mut TestAppContext) {
  357    init_test(cx, |_| {});
  358
  359    let editor = cx.add_window(|window, cx| {
  360        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  361        build_editor(buffer, window, cx)
  362    });
  363
  364    _ = editor.update(cx, |editor, window, cx| {
  365        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  366    });
  367    assert_eq!(
  368        editor
  369            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  370            .unwrap(),
  371        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  372    );
  373
  374    _ = editor.update(cx, |editor, window, cx| {
  375        editor.update_selection(
  376            DisplayPoint::new(DisplayRow(3), 3),
  377            0,
  378            gpui::Point::<f32>::default(),
  379            window,
  380            cx,
  381        );
  382    });
  383
  384    assert_eq!(
  385        editor
  386            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  387            .unwrap(),
  388        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  389    );
  390
  391    _ = editor.update(cx, |editor, window, cx| {
  392        editor.update_selection(
  393            DisplayPoint::new(DisplayRow(1), 1),
  394            0,
  395            gpui::Point::<f32>::default(),
  396            window,
  397            cx,
  398        );
  399    });
  400
  401    assert_eq!(
  402        editor
  403            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  404            .unwrap(),
  405        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  406    );
  407
  408    _ = editor.update(cx, |editor, window, cx| {
  409        editor.end_selection(window, cx);
  410        editor.update_selection(
  411            DisplayPoint::new(DisplayRow(3), 3),
  412            0,
  413            gpui::Point::<f32>::default(),
  414            window,
  415            cx,
  416        );
  417    });
  418
  419    assert_eq!(
  420        editor
  421            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  422            .unwrap(),
  423        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  424    );
  425
  426    _ = editor.update(cx, |editor, window, cx| {
  427        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  428        editor.update_selection(
  429            DisplayPoint::new(DisplayRow(0), 0),
  430            0,
  431            gpui::Point::<f32>::default(),
  432            window,
  433            cx,
  434        );
  435    });
  436
  437    assert_eq!(
  438        editor
  439            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  440            .unwrap(),
  441        [
  442            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  443            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  444        ]
  445    );
  446
  447    _ = editor.update(cx, |editor, window, cx| {
  448        editor.end_selection(window, cx);
  449    });
  450
  451    assert_eq!(
  452        editor
  453            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  454            .unwrap(),
  455        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  456    );
  457}
  458
  459#[gpui::test]
  460fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  461    init_test(cx, |_| {});
  462
  463    let editor = cx.add_window(|window, cx| {
  464        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  465        build_editor(buffer, window, cx)
  466    });
  467
  468    _ = editor.update(cx, |editor, window, cx| {
  469        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  470    });
  471
  472    _ = editor.update(cx, |editor, window, cx| {
  473        editor.end_selection(window, cx);
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    assert_eq!(
  485        editor
  486            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  487            .unwrap(),
  488        [
  489            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  490            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  491        ]
  492    );
  493
  494    _ = editor.update(cx, |editor, window, cx| {
  495        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  496    });
  497
  498    _ = editor.update(cx, |editor, window, cx| {
  499        editor.end_selection(window, cx);
  500    });
  501
  502    assert_eq!(
  503        editor
  504            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  505            .unwrap(),
  506        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  507    );
  508}
  509
  510#[gpui::test]
  511fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  512    init_test(cx, |_| {});
  513
  514    let editor = cx.add_window(|window, cx| {
  515        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  516        build_editor(buffer, window, cx)
  517    });
  518
  519    _ = editor.update(cx, |editor, window, cx| {
  520        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  521        assert_eq!(
  522            editor.selections.display_ranges(cx),
  523            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  524        );
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.update_selection(
  529            DisplayPoint::new(DisplayRow(3), 3),
  530            0,
  531            gpui::Point::<f32>::default(),
  532            window,
  533            cx,
  534        );
  535        assert_eq!(
  536            editor.selections.display_ranges(cx),
  537            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  538        );
  539    });
  540
  541    _ = editor.update(cx, |editor, window, cx| {
  542        editor.cancel(&Cancel, window, cx);
  543        editor.update_selection(
  544            DisplayPoint::new(DisplayRow(1), 1),
  545            0,
  546            gpui::Point::<f32>::default(),
  547            window,
  548            cx,
  549        );
  550        assert_eq!(
  551            editor.selections.display_ranges(cx),
  552            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  553        );
  554    });
  555}
  556
  557#[gpui::test]
  558fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  559    init_test(cx, |_| {});
  560
  561    let editor = cx.add_window(|window, cx| {
  562        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  563        build_editor(buffer, window, cx)
  564    });
  565
  566    _ = editor.update(cx, |editor, window, cx| {
  567        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  568        assert_eq!(
  569            editor.selections.display_ranges(cx),
  570            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  571        );
  572
  573        editor.move_down(&Default::default(), window, cx);
  574        assert_eq!(
  575            editor.selections.display_ranges(cx),
  576            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  577        );
  578
  579        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  580        assert_eq!(
  581            editor.selections.display_ranges(cx),
  582            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  583        );
  584
  585        editor.move_up(&Default::default(), window, cx);
  586        assert_eq!(
  587            editor.selections.display_ranges(cx),
  588            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  589        );
  590    });
  591}
  592
  593#[gpui::test]
  594fn test_clone(cx: &mut TestAppContext) {
  595    init_test(cx, |_| {});
  596
  597    let (text, selection_ranges) = marked_text_ranges(
  598        indoc! {"
  599            one
  600            two
  601            threeˇ
  602            four
  603            fiveˇ
  604        "},
  605        true,
  606    );
  607
  608    let editor = cx.add_window(|window, cx| {
  609        let buffer = MultiBuffer::build_simple(&text, cx);
  610        build_editor(buffer, window, cx)
  611    });
  612
  613    _ = editor.update(cx, |editor, window, cx| {
  614        editor.change_selections(None, window, cx, |s| {
  615            s.select_ranges(selection_ranges.clone())
  616        });
  617        editor.fold_creases(
  618            vec![
  619                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  620                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  621            ],
  622            true,
  623            window,
  624            cx,
  625        );
  626    });
  627
  628    let cloned_editor = editor
  629        .update(cx, |editor, _, cx| {
  630            cx.open_window(Default::default(), |window, cx| {
  631                cx.new(|cx| editor.clone(window, cx))
  632            })
  633        })
  634        .unwrap()
  635        .unwrap();
  636
  637    let snapshot = editor
  638        .update(cx, |e, window, cx| e.snapshot(window, cx))
  639        .unwrap();
  640    let cloned_snapshot = cloned_editor
  641        .update(cx, |e, window, cx| e.snapshot(window, cx))
  642        .unwrap();
  643
  644    assert_eq!(
  645        cloned_editor
  646            .update(cx, |e, _, cx| e.display_text(cx))
  647            .unwrap(),
  648        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  649    );
  650    assert_eq!(
  651        cloned_snapshot
  652            .folds_in_range(0..text.len())
  653            .collect::<Vec<_>>(),
  654        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  655    );
  656    assert_set_eq!(
  657        cloned_editor
  658            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  659            .unwrap(),
  660        editor
  661            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  662            .unwrap()
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  670            .unwrap()
  671    );
  672}
  673
  674#[gpui::test]
  675async fn test_navigation_history(cx: &mut TestAppContext) {
  676    init_test(cx, |_| {});
  677
  678    use workspace::item::Item;
  679
  680    let fs = FakeFs::new(cx.executor());
  681    let project = Project::test(fs, [], cx).await;
  682    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  683    let pane = workspace
  684        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  685        .unwrap();
  686
  687    _ = workspace.update(cx, |_v, window, cx| {
  688        cx.new(|cx| {
  689            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  690            let mut editor = build_editor(buffer.clone(), window, cx);
  691            let handle = cx.entity();
  692            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  693
  694            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  695                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  696            }
  697
  698            // Move the cursor a small distance.
  699            // Nothing is added to the navigation history.
  700            editor.change_selections(None, window, cx, |s| {
  701                s.select_display_ranges([
  702                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  703                ])
  704            });
  705            editor.change_selections(None, window, cx, |s| {
  706                s.select_display_ranges([
  707                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  708                ])
  709            });
  710            assert!(pop_history(&mut editor, cx).is_none());
  711
  712            // Move the cursor a large distance.
  713            // The history can jump back to the previous position.
  714            editor.change_selections(None, window, cx, |s| {
  715                s.select_display_ranges([
  716                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  717                ])
  718            });
  719            let nav_entry = pop_history(&mut editor, cx).unwrap();
  720            editor.navigate(nav_entry.data.unwrap(), window, cx);
  721            assert_eq!(nav_entry.item.id(), cx.entity_id());
  722            assert_eq!(
  723                editor.selections.display_ranges(cx),
  724                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  725            );
  726            assert!(pop_history(&mut editor, cx).is_none());
  727
  728            // Move the cursor a small distance via the mouse.
  729            // Nothing is added to the navigation history.
  730            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  731            editor.end_selection(window, cx);
  732            assert_eq!(
  733                editor.selections.display_ranges(cx),
  734                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  735            );
  736            assert!(pop_history(&mut editor, cx).is_none());
  737
  738            // Move the cursor a large distance via the mouse.
  739            // The history can jump back to the previous position.
  740            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  741            editor.end_selection(window, cx);
  742            assert_eq!(
  743                editor.selections.display_ranges(cx),
  744                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  745            );
  746            let nav_entry = pop_history(&mut editor, cx).unwrap();
  747            editor.navigate(nav_entry.data.unwrap(), window, cx);
  748            assert_eq!(nav_entry.item.id(), cx.entity_id());
  749            assert_eq!(
  750                editor.selections.display_ranges(cx),
  751                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  752            );
  753            assert!(pop_history(&mut editor, cx).is_none());
  754
  755            // Set scroll position to check later
  756            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  757            let original_scroll_position = editor.scroll_manager.anchor();
  758
  759            // Jump to the end of the document and adjust scroll
  760            editor.move_to_end(&MoveToEnd, window, cx);
  761            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  762            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  763
  764            let nav_entry = pop_history(&mut editor, cx).unwrap();
  765            editor.navigate(nav_entry.data.unwrap(), window, cx);
  766            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  767
  768            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  769            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  770            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  771            let invalid_point = Point::new(9999, 0);
  772            editor.navigate(
  773                Box::new(NavigationData {
  774                    cursor_anchor: invalid_anchor,
  775                    cursor_position: invalid_point,
  776                    scroll_anchor: ScrollAnchor {
  777                        anchor: invalid_anchor,
  778                        offset: Default::default(),
  779                    },
  780                    scroll_top_row: invalid_point.row,
  781                }),
  782                window,
  783                cx,
  784            );
  785            assert_eq!(
  786                editor.selections.display_ranges(cx),
  787                &[editor.max_point(cx)..editor.max_point(cx)]
  788            );
  789            assert_eq!(
  790                editor.scroll_position(cx),
  791                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  792            );
  793
  794            editor
  795        })
  796    });
  797}
  798
  799#[gpui::test]
  800fn test_cancel(cx: &mut TestAppContext) {
  801    init_test(cx, |_| {});
  802
  803    let editor = cx.add_window(|window, cx| {
  804        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  805        build_editor(buffer, window, cx)
  806    });
  807
  808    _ = editor.update(cx, |editor, window, cx| {
  809        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  810        editor.update_selection(
  811            DisplayPoint::new(DisplayRow(1), 1),
  812            0,
  813            gpui::Point::<f32>::default(),
  814            window,
  815            cx,
  816        );
  817        editor.end_selection(window, cx);
  818
  819        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  820        editor.update_selection(
  821            DisplayPoint::new(DisplayRow(0), 3),
  822            0,
  823            gpui::Point::<f32>::default(),
  824            window,
  825            cx,
  826        );
  827        editor.end_selection(window, cx);
  828        assert_eq!(
  829            editor.selections.display_ranges(cx),
  830            [
  831                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  832                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  833            ]
  834        );
  835    });
  836
  837    _ = editor.update(cx, |editor, window, cx| {
  838        editor.cancel(&Cancel, window, cx);
  839        assert_eq!(
  840            editor.selections.display_ranges(cx),
  841            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  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(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852}
  853
  854#[gpui::test]
  855fn test_fold_action(cx: &mut TestAppContext) {
  856    init_test(cx, |_| {});
  857
  858    let editor = cx.add_window(|window, cx| {
  859        let buffer = MultiBuffer::build_simple(
  860            &"
  861                impl Foo {
  862                    // Hello!
  863
  864                    fn a() {
  865                        1
  866                    }
  867
  868                    fn b() {
  869                        2
  870                    }
  871
  872                    fn c() {
  873                        3
  874                    }
  875                }
  876            "
  877            .unindent(),
  878            cx,
  879        );
  880        build_editor(buffer.clone(), window, cx)
  881    });
  882
  883    _ = editor.update(cx, |editor, window, cx| {
  884        editor.change_selections(None, window, cx, |s| {
  885            s.select_display_ranges([
  886                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  887            ]);
  888        });
  889        editor.fold(&Fold, window, cx);
  890        assert_eq!(
  891            editor.display_text(cx),
  892            "
  893                impl Foo {
  894                    // Hello!
  895
  896                    fn a() {
  897                        1
  898                    }
  899
  900                    fn b() {⋯
  901                    }
  902
  903                    fn c() {⋯
  904                    }
  905                }
  906            "
  907            .unindent(),
  908        );
  909
  910        editor.fold(&Fold, window, cx);
  911        assert_eq!(
  912            editor.display_text(cx),
  913            "
  914                impl Foo {⋯
  915                }
  916            "
  917            .unindent(),
  918        );
  919
  920        editor.unfold_lines(&UnfoldLines, window, cx);
  921        assert_eq!(
  922            editor.display_text(cx),
  923            "
  924                impl Foo {
  925                    // Hello!
  926
  927                    fn a() {
  928                        1
  929                    }
  930
  931                    fn b() {⋯
  932                    }
  933
  934                    fn c() {⋯
  935                    }
  936                }
  937            "
  938            .unindent(),
  939        );
  940
  941        editor.unfold_lines(&UnfoldLines, window, cx);
  942        assert_eq!(
  943            editor.display_text(cx),
  944            editor.buffer.read(cx).read(cx).text()
  945        );
  946    });
  947}
  948
  949#[gpui::test]
  950fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  951    init_test(cx, |_| {});
  952
  953    let editor = cx.add_window(|window, cx| {
  954        let buffer = MultiBuffer::build_simple(
  955            &"
  956                class Foo:
  957                    # Hello!
  958
  959                    def a():
  960                        print(1)
  961
  962                    def b():
  963                        print(2)
  964
  965                    def c():
  966                        print(3)
  967            "
  968            .unindent(),
  969            cx,
  970        );
  971        build_editor(buffer.clone(), window, cx)
  972    });
  973
  974    _ = editor.update(cx, |editor, window, cx| {
  975        editor.change_selections(None, window, cx, |s| {
  976            s.select_display_ranges([
  977                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  978            ]);
  979        });
  980        editor.fold(&Fold, window, cx);
  981        assert_eq!(
  982            editor.display_text(cx),
  983            "
  984                class Foo:
  985                    # Hello!
  986
  987                    def a():
  988                        print(1)
  989
  990                    def b():⋯
  991
  992                    def c():⋯
  993            "
  994            .unindent(),
  995        );
  996
  997        editor.fold(&Fold, window, cx);
  998        assert_eq!(
  999            editor.display_text(cx),
 1000            "
 1001                class Foo:⋯
 1002            "
 1003            .unindent(),
 1004        );
 1005
 1006        editor.unfold_lines(&UnfoldLines, window, cx);
 1007        assert_eq!(
 1008            editor.display_text(cx),
 1009            "
 1010                class Foo:
 1011                    # Hello!
 1012
 1013                    def a():
 1014                        print(1)
 1015
 1016                    def b():⋯
 1017
 1018                    def c():⋯
 1019            "
 1020            .unindent(),
 1021        );
 1022
 1023        editor.unfold_lines(&UnfoldLines, window, cx);
 1024        assert_eq!(
 1025            editor.display_text(cx),
 1026            editor.buffer.read(cx).read(cx).text()
 1027        );
 1028    });
 1029}
 1030
 1031#[gpui::test]
 1032fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1033    init_test(cx, |_| {});
 1034
 1035    let editor = cx.add_window(|window, cx| {
 1036        let buffer = MultiBuffer::build_simple(
 1037            &"
 1038                class Foo:
 1039                    # Hello!
 1040
 1041                    def a():
 1042                        print(1)
 1043
 1044                    def b():
 1045                        print(2)
 1046
 1047
 1048                    def c():
 1049                        print(3)
 1050
 1051
 1052            "
 1053            .unindent(),
 1054            cx,
 1055        );
 1056        build_editor(buffer.clone(), window, cx)
 1057    });
 1058
 1059    _ = editor.update(cx, |editor, window, cx| {
 1060        editor.change_selections(None, window, cx, |s| {
 1061            s.select_display_ranges([
 1062                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1063            ]);
 1064        });
 1065        editor.fold(&Fold, window, cx);
 1066        assert_eq!(
 1067            editor.display_text(cx),
 1068            "
 1069                class Foo:
 1070                    # Hello!
 1071
 1072                    def a():
 1073                        print(1)
 1074
 1075                    def b():⋯
 1076
 1077
 1078                    def c():⋯
 1079
 1080
 1081            "
 1082            .unindent(),
 1083        );
 1084
 1085        editor.fold(&Fold, window, cx);
 1086        assert_eq!(
 1087            editor.display_text(cx),
 1088            "
 1089                class Foo:⋯
 1090
 1091
 1092            "
 1093            .unindent(),
 1094        );
 1095
 1096        editor.unfold_lines(&UnfoldLines, window, cx);
 1097        assert_eq!(
 1098            editor.display_text(cx),
 1099            "
 1100                class Foo:
 1101                    # Hello!
 1102
 1103                    def a():
 1104                        print(1)
 1105
 1106                    def b():⋯
 1107
 1108
 1109                    def c():⋯
 1110
 1111
 1112            "
 1113            .unindent(),
 1114        );
 1115
 1116        editor.unfold_lines(&UnfoldLines, window, cx);
 1117        assert_eq!(
 1118            editor.display_text(cx),
 1119            editor.buffer.read(cx).read(cx).text()
 1120        );
 1121    });
 1122}
 1123
 1124#[gpui::test]
 1125fn test_fold_at_level(cx: &mut TestAppContext) {
 1126    init_test(cx, |_| {});
 1127
 1128    let editor = cx.add_window(|window, cx| {
 1129        let buffer = MultiBuffer::build_simple(
 1130            &"
 1131                class Foo:
 1132                    # Hello!
 1133
 1134                    def a():
 1135                        print(1)
 1136
 1137                    def b():
 1138                        print(2)
 1139
 1140
 1141                class Bar:
 1142                    # World!
 1143
 1144                    def a():
 1145                        print(1)
 1146
 1147                    def b():
 1148                        print(2)
 1149
 1150
 1151            "
 1152            .unindent(),
 1153            cx,
 1154        );
 1155        build_editor(buffer.clone(), window, cx)
 1156    });
 1157
 1158    _ = editor.update(cx, |editor, window, cx| {
 1159        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1160        assert_eq!(
 1161            editor.display_text(cx),
 1162            "
 1163                class Foo:
 1164                    # Hello!
 1165
 1166                    def a():⋯
 1167
 1168                    def b():⋯
 1169
 1170
 1171                class Bar:
 1172                    # World!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179            "
 1180            .unindent(),
 1181        );
 1182
 1183        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1184        assert_eq!(
 1185            editor.display_text(cx),
 1186            "
 1187                class Foo:⋯
 1188
 1189
 1190                class Bar:⋯
 1191
 1192
 1193            "
 1194            .unindent(),
 1195        );
 1196
 1197        editor.unfold_all(&UnfoldAll, window, cx);
 1198        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1199        assert_eq!(
 1200            editor.display_text(cx),
 1201            "
 1202                class Foo:
 1203                    # Hello!
 1204
 1205                    def a():
 1206                        print(1)
 1207
 1208                    def b():
 1209                        print(2)
 1210
 1211
 1212                class Bar:
 1213                    # World!
 1214
 1215                    def a():
 1216                        print(1)
 1217
 1218                    def b():
 1219                        print(2)
 1220
 1221
 1222            "
 1223            .unindent(),
 1224        );
 1225
 1226        assert_eq!(
 1227            editor.display_text(cx),
 1228            editor.buffer.read(cx).read(cx).text()
 1229        );
 1230    });
 1231}
 1232
 1233#[gpui::test]
 1234fn test_move_cursor(cx: &mut TestAppContext) {
 1235    init_test(cx, |_| {});
 1236
 1237    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1238    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1239
 1240    buffer.update(cx, |buffer, cx| {
 1241        buffer.edit(
 1242            vec![
 1243                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1244                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1245            ],
 1246            None,
 1247            cx,
 1248        );
 1249    });
 1250    _ = editor.update(cx, |editor, window, cx| {
 1251        assert_eq!(
 1252            editor.selections.display_ranges(cx),
 1253            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1254        );
 1255
 1256        editor.move_down(&MoveDown, window, cx);
 1257        assert_eq!(
 1258            editor.selections.display_ranges(cx),
 1259            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1260        );
 1261
 1262        editor.move_right(&MoveRight, window, cx);
 1263        assert_eq!(
 1264            editor.selections.display_ranges(cx),
 1265            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1266        );
 1267
 1268        editor.move_left(&MoveLeft, window, cx);
 1269        assert_eq!(
 1270            editor.selections.display_ranges(cx),
 1271            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1272        );
 1273
 1274        editor.move_up(&MoveUp, window, cx);
 1275        assert_eq!(
 1276            editor.selections.display_ranges(cx),
 1277            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1278        );
 1279
 1280        editor.move_to_end(&MoveToEnd, window, cx);
 1281        assert_eq!(
 1282            editor.selections.display_ranges(cx),
 1283            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1284        );
 1285
 1286        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1287        assert_eq!(
 1288            editor.selections.display_ranges(cx),
 1289            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1290        );
 1291
 1292        editor.change_selections(None, window, cx, |s| {
 1293            s.select_display_ranges([
 1294                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1295            ]);
 1296        });
 1297        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1298        assert_eq!(
 1299            editor.selections.display_ranges(cx),
 1300            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1301        );
 1302
 1303        editor.select_to_end(&SelectToEnd, window, cx);
 1304        assert_eq!(
 1305            editor.selections.display_ranges(cx),
 1306            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1307        );
 1308    });
 1309}
 1310
 1311// TODO: Re-enable this test
 1312#[cfg(target_os = "macos")]
 1313#[gpui::test]
 1314fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1315    init_test(cx, |_| {});
 1316
 1317    let editor = cx.add_window(|window, cx| {
 1318        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1319        build_editor(buffer.clone(), window, cx)
 1320    });
 1321
 1322    assert_eq!('🟥'.len_utf8(), 4);
 1323    assert_eq!('α'.len_utf8(), 2);
 1324
 1325    _ = editor.update(cx, |editor, window, cx| {
 1326        editor.fold_creases(
 1327            vec![
 1328                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1329                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1330                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1331            ],
 1332            true,
 1333            window,
 1334            cx,
 1335        );
 1336        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1337
 1338        editor.move_right(&MoveRight, window, cx);
 1339        assert_eq!(
 1340            editor.selections.display_ranges(cx),
 1341            &[empty_range(0, "🟥".len())]
 1342        );
 1343        editor.move_right(&MoveRight, window, cx);
 1344        assert_eq!(
 1345            editor.selections.display_ranges(cx),
 1346            &[empty_range(0, "🟥🟧".len())]
 1347        );
 1348        editor.move_right(&MoveRight, window, cx);
 1349        assert_eq!(
 1350            editor.selections.display_ranges(cx),
 1351            &[empty_range(0, "🟥🟧⋯".len())]
 1352        );
 1353
 1354        editor.move_down(&MoveDown, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(1, "ab⋯e".len())]
 1358        );
 1359        editor.move_left(&MoveLeft, window, cx);
 1360        assert_eq!(
 1361            editor.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯".len())]
 1363        );
 1364        editor.move_left(&MoveLeft, window, cx);
 1365        assert_eq!(
 1366            editor.selections.display_ranges(cx),
 1367            &[empty_range(1, "ab".len())]
 1368        );
 1369        editor.move_left(&MoveLeft, window, cx);
 1370        assert_eq!(
 1371            editor.selections.display_ranges(cx),
 1372            &[empty_range(1, "a".len())]
 1373        );
 1374
 1375        editor.move_down(&MoveDown, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(2, "α".len())]
 1379        );
 1380        editor.move_right(&MoveRight, window, cx);
 1381        assert_eq!(
 1382            editor.selections.display_ranges(cx),
 1383            &[empty_range(2, "αβ".len())]
 1384        );
 1385        editor.move_right(&MoveRight, window, cx);
 1386        assert_eq!(
 1387            editor.selections.display_ranges(cx),
 1388            &[empty_range(2, "αβ⋯".len())]
 1389        );
 1390        editor.move_right(&MoveRight, window, cx);
 1391        assert_eq!(
 1392            editor.selections.display_ranges(cx),
 1393            &[empty_range(2, "αβ⋯ε".len())]
 1394        );
 1395
 1396        editor.move_up(&MoveUp, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(1, "ab⋯e".len())]
 1400        );
 1401        editor.move_down(&MoveDown, window, cx);
 1402        assert_eq!(
 1403            editor.selections.display_ranges(cx),
 1404            &[empty_range(2, "αβ⋯ε".len())]
 1405        );
 1406        editor.move_up(&MoveUp, window, cx);
 1407        assert_eq!(
 1408            editor.selections.display_ranges(cx),
 1409            &[empty_range(1, "ab⋯e".len())]
 1410        );
 1411
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(0, "🟥🟧".len())]
 1416        );
 1417        editor.move_left(&MoveLeft, window, cx);
 1418        assert_eq!(
 1419            editor.selections.display_ranges(cx),
 1420            &[empty_range(0, "🟥".len())]
 1421        );
 1422        editor.move_left(&MoveLeft, window, cx);
 1423        assert_eq!(
 1424            editor.selections.display_ranges(cx),
 1425            &[empty_range(0, "".len())]
 1426        );
 1427    });
 1428}
 1429
 1430#[gpui::test]
 1431fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1432    init_test(cx, |_| {});
 1433
 1434    let editor = cx.add_window(|window, cx| {
 1435        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1436        build_editor(buffer.clone(), window, cx)
 1437    });
 1438    _ = editor.update(cx, |editor, window, cx| {
 1439        editor.change_selections(None, window, cx, |s| {
 1440            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1441        });
 1442
 1443        // moving above start of document should move selection to start of document,
 1444        // but the next move down should still be at the original goal_x
 1445        editor.move_up(&MoveUp, window, cx);
 1446        assert_eq!(
 1447            editor.selections.display_ranges(cx),
 1448            &[empty_range(0, "".len())]
 1449        );
 1450
 1451        editor.move_down(&MoveDown, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(1, "abcd".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(2, "αβγ".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(3, "abcd".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1473        );
 1474
 1475        // moving past end of document should not change goal_x
 1476        editor.move_down(&MoveDown, window, cx);
 1477        assert_eq!(
 1478            editor.selections.display_ranges(cx),
 1479            &[empty_range(5, "".len())]
 1480        );
 1481
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_up(&MoveUp, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(3, "abcd".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(2, "αβγ".len())]
 1504        );
 1505    });
 1506}
 1507
 1508#[gpui::test]
 1509fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1510    init_test(cx, |_| {});
 1511    let move_to_beg = MoveToBeginningOfLine {
 1512        stop_at_soft_wraps: true,
 1513    };
 1514
 1515    let move_to_end = MoveToEndOfLine {
 1516        stop_at_soft_wraps: true,
 1517    };
 1518
 1519    let editor = cx.add_window(|window, cx| {
 1520        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1521        build_editor(buffer, window, cx)
 1522    });
 1523    _ = editor.update(cx, |editor, window, cx| {
 1524        editor.change_selections(None, window, cx, |s| {
 1525            s.select_display_ranges([
 1526                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1527                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1528            ]);
 1529        });
 1530    });
 1531
 1532    _ = editor.update(cx, |editor, window, cx| {
 1533        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1534        assert_eq!(
 1535            editor.selections.display_ranges(cx),
 1536            &[
 1537                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1538                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1539            ]
 1540        );
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_end_of_line(&move_to_end, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1571                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1572            ]
 1573        );
 1574    });
 1575
 1576    // Moving to the end of line again is a no-op.
 1577    _ = editor.update(cx, |editor, window, cx| {
 1578        editor.move_to_end_of_line(&move_to_end, window, cx);
 1579        assert_eq!(
 1580            editor.selections.display_ranges(cx),
 1581            &[
 1582                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1583                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1584            ]
 1585        );
 1586    });
 1587
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_left(&MoveLeft, window, cx);
 1590        editor.select_to_beginning_of_line(
 1591            &SelectToBeginningOfLine {
 1592                stop_at_soft_wraps: true,
 1593            },
 1594            window,
 1595            cx,
 1596        );
 1597        assert_eq!(
 1598            editor.selections.display_ranges(cx),
 1599            &[
 1600                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1601                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1602            ]
 1603        );
 1604    });
 1605
 1606    _ = editor.update(cx, |editor, window, cx| {
 1607        editor.select_to_beginning_of_line(
 1608            &SelectToBeginningOfLine {
 1609                stop_at_soft_wraps: true,
 1610            },
 1611            window,
 1612            cx,
 1613        );
 1614        assert_eq!(
 1615            editor.selections.display_ranges(cx),
 1616            &[
 1617                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1618                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1619            ]
 1620        );
 1621    });
 1622
 1623    _ = editor.update(cx, |editor, window, cx| {
 1624        editor.select_to_beginning_of_line(
 1625            &SelectToBeginningOfLine {
 1626                stop_at_soft_wraps: true,
 1627            },
 1628            window,
 1629            cx,
 1630        );
 1631        assert_eq!(
 1632            editor.selections.display_ranges(cx),
 1633            &[
 1634                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1635                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1636            ]
 1637        );
 1638    });
 1639
 1640    _ = editor.update(cx, |editor, window, cx| {
 1641        editor.select_to_end_of_line(
 1642            &SelectToEndOfLine {
 1643                stop_at_soft_wraps: true,
 1644            },
 1645            window,
 1646            cx,
 1647        );
 1648        assert_eq!(
 1649            editor.selections.display_ranges(cx),
 1650            &[
 1651                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1652                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1653            ]
 1654        );
 1655    });
 1656
 1657    _ = editor.update(cx, |editor, window, cx| {
 1658        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1659        assert_eq!(editor.display_text(cx), "ab\n  de");
 1660        assert_eq!(
 1661            editor.selections.display_ranges(cx),
 1662            &[
 1663                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1664                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1665            ]
 1666        );
 1667    });
 1668
 1669    _ = editor.update(cx, |editor, window, cx| {
 1670        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 1671        assert_eq!(editor.display_text(cx), "\n");
 1672        assert_eq!(
 1673            editor.selections.display_ranges(cx),
 1674            &[
 1675                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1676                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1677            ]
 1678        );
 1679    });
 1680}
 1681
 1682#[gpui::test]
 1683fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1684    init_test(cx, |_| {});
 1685    let move_to_beg = MoveToBeginningOfLine {
 1686        stop_at_soft_wraps: false,
 1687    };
 1688
 1689    let move_to_end = MoveToEndOfLine {
 1690        stop_at_soft_wraps: false,
 1691    };
 1692
 1693    let editor = cx.add_window(|window, cx| {
 1694        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1695        build_editor(buffer, window, cx)
 1696    });
 1697
 1698    _ = editor.update(cx, |editor, window, cx| {
 1699        editor.set_wrap_width(Some(140.0.into()), cx);
 1700
 1701        // We expect the following lines after wrapping
 1702        // ```
 1703        // thequickbrownfox
 1704        // jumpedoverthelazydo
 1705        // gs
 1706        // ```
 1707        // The final `gs` was soft-wrapped onto a new line.
 1708        assert_eq!(
 1709            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1710            editor.display_text(cx),
 1711        );
 1712
 1713        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1714        // Start the cursor at the `k` on the first line
 1715        editor.change_selections(None, window, cx, |s| {
 1716            s.select_display_ranges([
 1717                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1718            ]);
 1719        });
 1720
 1721        // Moving to the beginning of the line should put us at the beginning of the line.
 1722        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1723        assert_eq!(
 1724            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1725            editor.selections.display_ranges(cx)
 1726        );
 1727
 1728        // Moving to the end of the line should put us at the end of the line.
 1729        editor.move_to_end_of_line(&move_to_end, window, cx);
 1730        assert_eq!(
 1731            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1732            editor.selections.display_ranges(cx)
 1733        );
 1734
 1735        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1736        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1737        editor.change_selections(None, window, cx, |s| {
 1738            s.select_display_ranges([
 1739                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1740            ]);
 1741        });
 1742
 1743        // Moving to the beginning of the line should put us at the start of the second line of
 1744        // display text, i.e., the `j`.
 1745        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1746        assert_eq!(
 1747            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1748            editor.selections.display_ranges(cx)
 1749        );
 1750
 1751        // Moving to the beginning of the line again should be a no-op.
 1752        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1753        assert_eq!(
 1754            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1755            editor.selections.display_ranges(cx)
 1756        );
 1757
 1758        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1759        // next display line.
 1760        editor.move_to_end_of_line(&move_to_end, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the end of the line again should be a no-op.
 1767        editor.move_to_end_of_line(&move_to_end, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772    });
 1773}
 1774
 1775#[gpui::test]
 1776fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1777    init_test(cx, |_| {});
 1778
 1779    let editor = cx.add_window(|window, cx| {
 1780        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1781        build_editor(buffer, window, cx)
 1782    });
 1783    _ = editor.update(cx, |editor, window, cx| {
 1784        editor.change_selections(None, window, cx, |s| {
 1785            s.select_display_ranges([
 1786                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1787                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1788            ])
 1789        });
 1790
 1791        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1792        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1793
 1794        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1795        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1796
 1797        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1798        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1799
 1800        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1801        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1802
 1803        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1804        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1805
 1806        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1807        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1808
 1809        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1810        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1811
 1812        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1813        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1814
 1815        editor.move_right(&MoveRight, window, cx);
 1816        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1817        assert_selection_ranges(
 1818            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1819            editor,
 1820            cx,
 1821        );
 1822
 1823        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1824        assert_selection_ranges(
 1825            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1826            editor,
 1827            cx,
 1828        );
 1829
 1830        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1831        assert_selection_ranges(
 1832            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1833            editor,
 1834            cx,
 1835        );
 1836    });
 1837}
 1838
 1839#[gpui::test]
 1840fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1841    init_test(cx, |_| {});
 1842
 1843    let editor = cx.add_window(|window, cx| {
 1844        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1845        build_editor(buffer, window, cx)
 1846    });
 1847
 1848    _ = editor.update(cx, |editor, window, cx| {
 1849        editor.set_wrap_width(Some(140.0.into()), cx);
 1850        assert_eq!(
 1851            editor.display_text(cx),
 1852            "use one::{\n    two::three::\n    four::five\n};"
 1853        );
 1854
 1855        editor.change_selections(None, window, cx, |s| {
 1856            s.select_display_ranges([
 1857                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1858            ]);
 1859        });
 1860
 1861        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1862        assert_eq!(
 1863            editor.selections.display_ranges(cx),
 1864            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1865        );
 1866
 1867        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1868        assert_eq!(
 1869            editor.selections.display_ranges(cx),
 1870            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1871        );
 1872
 1873        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1877        );
 1878
 1879        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1880        assert_eq!(
 1881            editor.selections.display_ranges(cx),
 1882            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1883        );
 1884
 1885        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1886        assert_eq!(
 1887            editor.selections.display_ranges(cx),
 1888            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1889        );
 1890
 1891        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1892        assert_eq!(
 1893            editor.selections.display_ranges(cx),
 1894            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1895        );
 1896    });
 1897}
 1898
 1899#[gpui::test]
 1900async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
 1901    init_test(cx, |_| {});
 1902    let mut cx = EditorTestContext::new(cx).await;
 1903
 1904    let line_height = cx.editor(|editor, window, _| {
 1905        editor
 1906            .style()
 1907            .unwrap()
 1908            .text
 1909            .line_height_in_pixels(window.rem_size())
 1910    });
 1911    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 1912
 1913    cx.set_state(
 1914        &r#"ˇone
 1915        two
 1916
 1917        three
 1918        fourˇ
 1919        five
 1920
 1921        six"#
 1922            .unindent(),
 1923    );
 1924
 1925    cx.update_editor(|editor, window, cx| {
 1926        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1927    });
 1928    cx.assert_editor_state(
 1929        &r#"one
 1930        two
 1931        ˇ
 1932        three
 1933        four
 1934        five
 1935        ˇ
 1936        six"#
 1937            .unindent(),
 1938    );
 1939
 1940    cx.update_editor(|editor, window, cx| {
 1941        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1942    });
 1943    cx.assert_editor_state(
 1944        &r#"one
 1945        two
 1946
 1947        three
 1948        four
 1949        five
 1950        ˇ
 1951        sixˇ"#
 1952            .unindent(),
 1953    );
 1954
 1955    cx.update_editor(|editor, window, cx| {
 1956        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 1957    });
 1958    cx.assert_editor_state(
 1959        &r#"one
 1960        two
 1961
 1962        three
 1963        four
 1964        five
 1965
 1966        sixˇ"#
 1967            .unindent(),
 1968    );
 1969
 1970    cx.update_editor(|editor, window, cx| {
 1971        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1972    });
 1973    cx.assert_editor_state(
 1974        &r#"one
 1975        two
 1976
 1977        three
 1978        four
 1979        five
 1980        ˇ
 1981        six"#
 1982            .unindent(),
 1983    );
 1984
 1985    cx.update_editor(|editor, window, cx| {
 1986        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 1987    });
 1988    cx.assert_editor_state(
 1989        &r#"one
 1990        two
 1991        ˇ
 1992        three
 1993        four
 1994        five
 1995
 1996        six"#
 1997            .unindent(),
 1998    );
 1999
 2000    cx.update_editor(|editor, window, cx| {
 2001        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2002    });
 2003    cx.assert_editor_state(
 2004        &r#"ˇone
 2005        two
 2006
 2007        three
 2008        four
 2009        five
 2010
 2011        six"#
 2012            .unindent(),
 2013    );
 2014}
 2015
 2016#[gpui::test]
 2017async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2018    init_test(cx, |_| {});
 2019    let mut cx = EditorTestContext::new(cx).await;
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    let window = cx.window;
 2028    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2029
 2030    cx.set_state(
 2031        r#"ˇone
 2032        two
 2033        three
 2034        four
 2035        five
 2036        six
 2037        seven
 2038        eight
 2039        nine
 2040        ten
 2041        "#,
 2042    );
 2043
 2044    cx.update_editor(|editor, window, cx| {
 2045        assert_eq!(
 2046            editor.snapshot(window, cx).scroll_position(),
 2047            gpui::Point::new(0., 0.)
 2048        );
 2049        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2050        assert_eq!(
 2051            editor.snapshot(window, cx).scroll_position(),
 2052            gpui::Point::new(0., 3.)
 2053        );
 2054        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2055        assert_eq!(
 2056            editor.snapshot(window, cx).scroll_position(),
 2057            gpui::Point::new(0., 6.)
 2058        );
 2059        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2060        assert_eq!(
 2061            editor.snapshot(window, cx).scroll_position(),
 2062            gpui::Point::new(0., 3.)
 2063        );
 2064
 2065        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2066        assert_eq!(
 2067            editor.snapshot(window, cx).scroll_position(),
 2068            gpui::Point::new(0., 1.)
 2069        );
 2070        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2071        assert_eq!(
 2072            editor.snapshot(window, cx).scroll_position(),
 2073            gpui::Point::new(0., 3.)
 2074        );
 2075    });
 2076}
 2077
 2078#[gpui::test]
 2079async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
 2080    init_test(cx, |_| {});
 2081    let mut cx = EditorTestContext::new(cx).await;
 2082
 2083    let line_height = cx.update_editor(|editor, window, cx| {
 2084        editor.set_vertical_scroll_margin(2, cx);
 2085        editor
 2086            .style()
 2087            .unwrap()
 2088            .text
 2089            .line_height_in_pixels(window.rem_size())
 2090    });
 2091    let window = cx.window;
 2092    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2093
 2094    cx.set_state(
 2095        r#"ˇone
 2096            two
 2097            three
 2098            four
 2099            five
 2100            six
 2101            seven
 2102            eight
 2103            nine
 2104            ten
 2105        "#,
 2106    );
 2107    cx.update_editor(|editor, window, cx| {
 2108        assert_eq!(
 2109            editor.snapshot(window, cx).scroll_position(),
 2110            gpui::Point::new(0., 0.0)
 2111        );
 2112    });
 2113
 2114    // Add a cursor below the visible area. Since both cursors cannot fit
 2115    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2116    // allows the vertical scroll margin below that cursor.
 2117    cx.update_editor(|editor, window, cx| {
 2118        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2119            selections.select_ranges([
 2120                Point::new(0, 0)..Point::new(0, 0),
 2121                Point::new(6, 0)..Point::new(6, 0),
 2122            ]);
 2123        })
 2124    });
 2125    cx.update_editor(|editor, window, cx| {
 2126        assert_eq!(
 2127            editor.snapshot(window, cx).scroll_position(),
 2128            gpui::Point::new(0., 3.0)
 2129        );
 2130    });
 2131
 2132    // Move down. The editor cursor scrolls down to track the newest cursor.
 2133    cx.update_editor(|editor, window, cx| {
 2134        editor.move_down(&Default::default(), window, cx);
 2135    });
 2136    cx.update_editor(|editor, window, cx| {
 2137        assert_eq!(
 2138            editor.snapshot(window, cx).scroll_position(),
 2139            gpui::Point::new(0., 4.0)
 2140        );
 2141    });
 2142
 2143    // Add a cursor above the visible area. Since both cursors fit on screen,
 2144    // the editor scrolls to show both.
 2145    cx.update_editor(|editor, window, cx| {
 2146        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2147            selections.select_ranges([
 2148                Point::new(1, 0)..Point::new(1, 0),
 2149                Point::new(6, 0)..Point::new(6, 0),
 2150            ]);
 2151        })
 2152    });
 2153    cx.update_editor(|editor, window, cx| {
 2154        assert_eq!(
 2155            editor.snapshot(window, cx).scroll_position(),
 2156            gpui::Point::new(0., 1.0)
 2157        );
 2158    });
 2159}
 2160
 2161#[gpui::test]
 2162async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
 2163    init_test(cx, |_| {});
 2164    let mut cx = EditorTestContext::new(cx).await;
 2165
 2166    let line_height = cx.editor(|editor, window, _cx| {
 2167        editor
 2168            .style()
 2169            .unwrap()
 2170            .text
 2171            .line_height_in_pixels(window.rem_size())
 2172    });
 2173    let window = cx.window;
 2174    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2175    cx.set_state(
 2176        &r#"
 2177        ˇone
 2178        two
 2179        threeˇ
 2180        four
 2181        five
 2182        six
 2183        seven
 2184        eight
 2185        nine
 2186        ten
 2187        "#
 2188        .unindent(),
 2189    );
 2190
 2191    cx.update_editor(|editor, window, cx| {
 2192        editor.move_page_down(&MovePageDown::default(), window, cx)
 2193    });
 2194    cx.assert_editor_state(
 2195        &r#"
 2196        one
 2197        two
 2198        three
 2199        ˇfour
 2200        five
 2201        sixˇ
 2202        seven
 2203        eight
 2204        nine
 2205        ten
 2206        "#
 2207        .unindent(),
 2208    );
 2209
 2210    cx.update_editor(|editor, window, cx| {
 2211        editor.move_page_down(&MovePageDown::default(), window, cx)
 2212    });
 2213    cx.assert_editor_state(
 2214        &r#"
 2215        one
 2216        two
 2217        three
 2218        four
 2219        five
 2220        six
 2221        ˇseven
 2222        eight
 2223        nineˇ
 2224        ten
 2225        "#
 2226        .unindent(),
 2227    );
 2228
 2229    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2230    cx.assert_editor_state(
 2231        &r#"
 2232        one
 2233        two
 2234        three
 2235        ˇfour
 2236        five
 2237        sixˇ
 2238        seven
 2239        eight
 2240        nine
 2241        ten
 2242        "#
 2243        .unindent(),
 2244    );
 2245
 2246    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2247    cx.assert_editor_state(
 2248        &r#"
 2249        ˇone
 2250        two
 2251        threeˇ
 2252        four
 2253        five
 2254        six
 2255        seven
 2256        eight
 2257        nine
 2258        ten
 2259        "#
 2260        .unindent(),
 2261    );
 2262
 2263    // Test select collapsing
 2264    cx.update_editor(|editor, window, cx| {
 2265        editor.move_page_down(&MovePageDown::default(), window, cx);
 2266        editor.move_page_down(&MovePageDown::default(), window, cx);
 2267        editor.move_page_down(&MovePageDown::default(), window, cx);
 2268    });
 2269    cx.assert_editor_state(
 2270        &r#"
 2271        one
 2272        two
 2273        three
 2274        four
 2275        five
 2276        six
 2277        seven
 2278        eight
 2279        nine
 2280        ˇten
 2281        ˇ"#
 2282        .unindent(),
 2283    );
 2284}
 2285
 2286#[gpui::test]
 2287async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 2288    init_test(cx, |_| {});
 2289    let mut cx = EditorTestContext::new(cx).await;
 2290    cx.set_state("one «two threeˇ» four");
 2291    cx.update_editor(|editor, window, cx| {
 2292        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx);
 2293        assert_eq!(editor.text(cx), " four");
 2294    });
 2295}
 2296
 2297#[gpui::test]
 2298fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2299    init_test(cx, |_| {});
 2300
 2301    let editor = cx.add_window(|window, cx| {
 2302        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2303        build_editor(buffer.clone(), window, cx)
 2304    });
 2305
 2306    _ = editor.update(cx, |editor, window, cx| {
 2307        editor.change_selections(None, window, cx, |s| {
 2308            s.select_display_ranges([
 2309                // an empty selection - the preceding word fragment is deleted
 2310                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2311                // characters selected - they are deleted
 2312                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2313            ])
 2314        });
 2315        editor.delete_to_previous_word_start(
 2316            &DeleteToPreviousWordStart {
 2317                ignore_newlines: false,
 2318            },
 2319            window,
 2320            cx,
 2321        );
 2322        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2323    });
 2324
 2325    _ = editor.update(cx, |editor, window, cx| {
 2326        editor.change_selections(None, window, cx, |s| {
 2327            s.select_display_ranges([
 2328                // an empty selection - the following word fragment is deleted
 2329                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2330                // characters selected - they are deleted
 2331                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2332            ])
 2333        });
 2334        editor.delete_to_next_word_end(
 2335            &DeleteToNextWordEnd {
 2336                ignore_newlines: false,
 2337            },
 2338            window,
 2339            cx,
 2340        );
 2341        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2342    });
 2343}
 2344
 2345#[gpui::test]
 2346fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2347    init_test(cx, |_| {});
 2348
 2349    let editor = cx.add_window(|window, cx| {
 2350        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2351        build_editor(buffer.clone(), window, cx)
 2352    });
 2353    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2354        ignore_newlines: false,
 2355    };
 2356    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2357        ignore_newlines: true,
 2358    };
 2359
 2360    _ = editor.update(cx, |editor, window, cx| {
 2361        editor.change_selections(None, window, cx, |s| {
 2362            s.select_display_ranges([
 2363                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2364            ])
 2365        });
 2366        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2367        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2368        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2369        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2370        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2371        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2372        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2373        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2374        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2375        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2376        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2377        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2378    });
 2379}
 2380
 2381#[gpui::test]
 2382fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2383    init_test(cx, |_| {});
 2384
 2385    let editor = cx.add_window(|window, cx| {
 2386        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2387        build_editor(buffer.clone(), window, cx)
 2388    });
 2389    let del_to_next_word_end = DeleteToNextWordEnd {
 2390        ignore_newlines: false,
 2391    };
 2392    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2393        ignore_newlines: true,
 2394    };
 2395
 2396    _ = editor.update(cx, |editor, window, cx| {
 2397        editor.change_selections(None, window, cx, |s| {
 2398            s.select_display_ranges([
 2399                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2400            ])
 2401        });
 2402        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2403        assert_eq!(
 2404            editor.buffer.read(cx).read(cx).text(),
 2405            "one\n   two\nthree\n   four"
 2406        );
 2407        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2408        assert_eq!(
 2409            editor.buffer.read(cx).read(cx).text(),
 2410            "\n   two\nthree\n   four"
 2411        );
 2412        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2413        assert_eq!(
 2414            editor.buffer.read(cx).read(cx).text(),
 2415            "two\nthree\n   four"
 2416        );
 2417        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2418        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2419        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2420        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2421        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2422        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2423    });
 2424}
 2425
 2426#[gpui::test]
 2427fn test_newline(cx: &mut TestAppContext) {
 2428    init_test(cx, |_| {});
 2429
 2430    let editor = cx.add_window(|window, cx| {
 2431        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2432        build_editor(buffer.clone(), window, cx)
 2433    });
 2434
 2435    _ = editor.update(cx, |editor, window, cx| {
 2436        editor.change_selections(None, window, cx, |s| {
 2437            s.select_display_ranges([
 2438                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2439                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2440                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2441            ])
 2442        });
 2443
 2444        editor.newline(&Newline, window, cx);
 2445        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2446    });
 2447}
 2448
 2449#[gpui::test]
 2450fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2451    init_test(cx, |_| {});
 2452
 2453    let editor = cx.add_window(|window, cx| {
 2454        let buffer = MultiBuffer::build_simple(
 2455            "
 2456                a
 2457                b(
 2458                    X
 2459                )
 2460                c(
 2461                    X
 2462                )
 2463            "
 2464            .unindent()
 2465            .as_str(),
 2466            cx,
 2467        );
 2468        let mut editor = build_editor(buffer.clone(), window, cx);
 2469        editor.change_selections(None, window, cx, |s| {
 2470            s.select_ranges([
 2471                Point::new(2, 4)..Point::new(2, 5),
 2472                Point::new(5, 4)..Point::new(5, 5),
 2473            ])
 2474        });
 2475        editor
 2476    });
 2477
 2478    _ = editor.update(cx, |editor, window, cx| {
 2479        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2480        editor.buffer.update(cx, |buffer, cx| {
 2481            buffer.edit(
 2482                [
 2483                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2484                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2485                ],
 2486                None,
 2487                cx,
 2488            );
 2489            assert_eq!(
 2490                buffer.read(cx).text(),
 2491                "
 2492                    a
 2493                    b()
 2494                    c()
 2495                "
 2496                .unindent()
 2497            );
 2498        });
 2499        assert_eq!(
 2500            editor.selections.ranges(cx),
 2501            &[
 2502                Point::new(1, 2)..Point::new(1, 2),
 2503                Point::new(2, 2)..Point::new(2, 2),
 2504            ],
 2505        );
 2506
 2507        editor.newline(&Newline, window, cx);
 2508        assert_eq!(
 2509            editor.text(cx),
 2510            "
 2511                a
 2512                b(
 2513                )
 2514                c(
 2515                )
 2516            "
 2517            .unindent()
 2518        );
 2519
 2520        // The selections are moved after the inserted newlines
 2521        assert_eq!(
 2522            editor.selections.ranges(cx),
 2523            &[
 2524                Point::new(2, 0)..Point::new(2, 0),
 2525                Point::new(4, 0)..Point::new(4, 0),
 2526            ],
 2527        );
 2528    });
 2529}
 2530
 2531#[gpui::test]
 2532async fn test_newline_above(cx: &mut gpui::TestAppContext) {
 2533    init_test(cx, |settings| {
 2534        settings.defaults.tab_size = NonZeroU32::new(4)
 2535    });
 2536
 2537    let language = Arc::new(
 2538        Language::new(
 2539            LanguageConfig::default(),
 2540            Some(tree_sitter_rust::LANGUAGE.into()),
 2541        )
 2542        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2543        .unwrap(),
 2544    );
 2545
 2546    let mut cx = EditorTestContext::new(cx).await;
 2547    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2548    cx.set_state(indoc! {"
 2549        const a: ˇA = (
 2550 2551                «const_functionˇ»(ˇ),
 2552                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2553 2554        ˇ);ˇ
 2555    "});
 2556
 2557    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2558    cx.assert_editor_state(indoc! {"
 2559        ˇ
 2560        const a: A = (
 2561            ˇ
 2562            (
 2563                ˇ
 2564                ˇ
 2565                const_function(),
 2566                ˇ
 2567                ˇ
 2568                ˇ
 2569                ˇ
 2570                something_else,
 2571                ˇ
 2572            )
 2573            ˇ
 2574            ˇ
 2575        );
 2576    "});
 2577}
 2578
 2579#[gpui::test]
 2580async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 2581    init_test(cx, |settings| {
 2582        settings.defaults.tab_size = NonZeroU32::new(4)
 2583    });
 2584
 2585    let language = Arc::new(
 2586        Language::new(
 2587            LanguageConfig::default(),
 2588            Some(tree_sitter_rust::LANGUAGE.into()),
 2589        )
 2590        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2591        .unwrap(),
 2592    );
 2593
 2594    let mut cx = EditorTestContext::new(cx).await;
 2595    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2596    cx.set_state(indoc! {"
 2597        const a: ˇA = (
 2598 2599                «const_functionˇ»(ˇ),
 2600                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2601 2602        ˇ);ˇ
 2603    "});
 2604
 2605    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2606    cx.assert_editor_state(indoc! {"
 2607        const a: A = (
 2608            ˇ
 2609            (
 2610                ˇ
 2611                const_function(),
 2612                ˇ
 2613                ˇ
 2614                something_else,
 2615                ˇ
 2616                ˇ
 2617                ˇ
 2618                ˇ
 2619            )
 2620            ˇ
 2621        );
 2622        ˇ
 2623        ˇ
 2624    "});
 2625}
 2626
 2627#[gpui::test]
 2628async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
 2629    init_test(cx, |settings| {
 2630        settings.defaults.tab_size = NonZeroU32::new(4)
 2631    });
 2632
 2633    let language = Arc::new(Language::new(
 2634        LanguageConfig {
 2635            line_comments: vec!["//".into()],
 2636            ..LanguageConfig::default()
 2637        },
 2638        None,
 2639    ));
 2640    {
 2641        let mut cx = EditorTestContext::new(cx).await;
 2642        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2643        cx.set_state(indoc! {"
 2644        // Fooˇ
 2645    "});
 2646
 2647        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2648        cx.assert_editor_state(indoc! {"
 2649        // Foo
 2650        //ˇ
 2651    "});
 2652        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2653        cx.set_state(indoc! {"
 2654        ˇ// Foo
 2655    "});
 2656        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2657        cx.assert_editor_state(indoc! {"
 2658
 2659        ˇ// Foo
 2660    "});
 2661    }
 2662    // Ensure that comment continuations can be disabled.
 2663    update_test_language_settings(cx, |settings| {
 2664        settings.defaults.extend_comment_on_newline = Some(false);
 2665    });
 2666    let mut cx = EditorTestContext::new(cx).await;
 2667    cx.set_state(indoc! {"
 2668        // Fooˇ
 2669    "});
 2670    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2671    cx.assert_editor_state(indoc! {"
 2672        // Foo
 2673        ˇ
 2674    "});
 2675}
 2676
 2677#[gpui::test]
 2678fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2679    init_test(cx, |_| {});
 2680
 2681    let editor = cx.add_window(|window, cx| {
 2682        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2683        let mut editor = build_editor(buffer.clone(), window, cx);
 2684        editor.change_selections(None, window, cx, |s| {
 2685            s.select_ranges([3..4, 11..12, 19..20])
 2686        });
 2687        editor
 2688    });
 2689
 2690    _ = editor.update(cx, |editor, window, cx| {
 2691        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2692        editor.buffer.update(cx, |buffer, cx| {
 2693            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2694            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2695        });
 2696        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2697
 2698        editor.insert("Z", window, cx);
 2699        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2700
 2701        // The selections are moved after the inserted characters
 2702        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2703    });
 2704}
 2705
 2706#[gpui::test]
 2707async fn test_tab(cx: &mut gpui::TestAppContext) {
 2708    init_test(cx, |settings| {
 2709        settings.defaults.tab_size = NonZeroU32::new(3)
 2710    });
 2711
 2712    let mut cx = EditorTestContext::new(cx).await;
 2713    cx.set_state(indoc! {"
 2714        ˇabˇc
 2715        ˇ🏀ˇ🏀ˇefg
 2716 2717    "});
 2718    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2719    cx.assert_editor_state(indoc! {"
 2720           ˇab ˇc
 2721           ˇ🏀  ˇ🏀  ˇefg
 2722        d  ˇ
 2723    "});
 2724
 2725    cx.set_state(indoc! {"
 2726        a
 2727        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2728    "});
 2729    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        a
 2732           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2733    "});
 2734}
 2735
 2736#[gpui::test]
 2737async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
 2738    init_test(cx, |_| {});
 2739
 2740    let mut cx = EditorTestContext::new(cx).await;
 2741    let language = Arc::new(
 2742        Language::new(
 2743            LanguageConfig::default(),
 2744            Some(tree_sitter_rust::LANGUAGE.into()),
 2745        )
 2746        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2747        .unwrap(),
 2748    );
 2749    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2750
 2751    // cursors that are already at the suggested indent level insert
 2752    // a soft tab. cursors that are to the left of the suggested indent
 2753    // auto-indent their line.
 2754    cx.set_state(indoc! {"
 2755        ˇ
 2756        const a: B = (
 2757            c(
 2758                d(
 2759        ˇ
 2760                )
 2761        ˇ
 2762        ˇ    )
 2763        );
 2764    "});
 2765    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2766    cx.assert_editor_state(indoc! {"
 2767            ˇ
 2768        const a: B = (
 2769            c(
 2770                d(
 2771                    ˇ
 2772                )
 2773                ˇ
 2774            ˇ)
 2775        );
 2776    "});
 2777
 2778    // handle auto-indent when there are multiple cursors on the same line
 2779    cx.set_state(indoc! {"
 2780        const a: B = (
 2781            c(
 2782        ˇ    ˇ
 2783        ˇ    )
 2784        );
 2785    "});
 2786    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2787    cx.assert_editor_state(indoc! {"
 2788        const a: B = (
 2789            c(
 2790                ˇ
 2791            ˇ)
 2792        );
 2793    "});
 2794}
 2795
 2796#[gpui::test]
 2797async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
 2798    init_test(cx, |settings| {
 2799        settings.defaults.tab_size = NonZeroU32::new(4)
 2800    });
 2801
 2802    let language = Arc::new(
 2803        Language::new(
 2804            LanguageConfig::default(),
 2805            Some(tree_sitter_rust::LANGUAGE.into()),
 2806        )
 2807        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2808        .unwrap(),
 2809    );
 2810
 2811    let mut cx = EditorTestContext::new(cx).await;
 2812    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2813    cx.set_state(indoc! {"
 2814        fn a() {
 2815            if b {
 2816        \t ˇc
 2817            }
 2818        }
 2819    "});
 2820
 2821    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2822    cx.assert_editor_state(indoc! {"
 2823        fn a() {
 2824            if b {
 2825                ˇc
 2826            }
 2827        }
 2828    "});
 2829}
 2830
 2831#[gpui::test]
 2832async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
 2833    init_test(cx, |settings| {
 2834        settings.defaults.tab_size = NonZeroU32::new(4);
 2835    });
 2836
 2837    let mut cx = EditorTestContext::new(cx).await;
 2838
 2839    cx.set_state(indoc! {"
 2840          «oneˇ» «twoˇ»
 2841        three
 2842         four
 2843    "});
 2844    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2845    cx.assert_editor_state(indoc! {"
 2846            «oneˇ» «twoˇ»
 2847        three
 2848         four
 2849    "});
 2850
 2851    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        «oneˇ» «twoˇ»
 2854        three
 2855         four
 2856    "});
 2857
 2858    // select across line ending
 2859    cx.set_state(indoc! {"
 2860        one two
 2861        t«hree
 2862        ˇ» four
 2863    "});
 2864    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2865    cx.assert_editor_state(indoc! {"
 2866        one two
 2867            t«hree
 2868        ˇ» four
 2869    "});
 2870
 2871    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2872    cx.assert_editor_state(indoc! {"
 2873        one two
 2874        t«hree
 2875        ˇ» four
 2876    "});
 2877
 2878    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2879    cx.set_state(indoc! {"
 2880        one two
 2881        ˇthree
 2882            four
 2883    "});
 2884    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2885    cx.assert_editor_state(indoc! {"
 2886        one two
 2887            ˇthree
 2888            four
 2889    "});
 2890
 2891    cx.set_state(indoc! {"
 2892        one two
 2893        ˇ    three
 2894            four
 2895    "});
 2896    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2897    cx.assert_editor_state(indoc! {"
 2898        one two
 2899        ˇthree
 2900            four
 2901    "});
 2902}
 2903
 2904#[gpui::test]
 2905async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 2906    init_test(cx, |settings| {
 2907        settings.defaults.hard_tabs = Some(true);
 2908    });
 2909
 2910    let mut cx = EditorTestContext::new(cx).await;
 2911
 2912    // select two ranges on one line
 2913    cx.set_state(indoc! {"
 2914        «oneˇ» «twoˇ»
 2915        three
 2916        four
 2917    "});
 2918    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2919    cx.assert_editor_state(indoc! {"
 2920        \t«oneˇ» «twoˇ»
 2921        three
 2922        four
 2923    "});
 2924    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2925    cx.assert_editor_state(indoc! {"
 2926        \t\t«oneˇ» «twoˇ»
 2927        three
 2928        four
 2929    "});
 2930    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2931    cx.assert_editor_state(indoc! {"
 2932        \t«oneˇ» «twoˇ»
 2933        three
 2934        four
 2935    "});
 2936    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2937    cx.assert_editor_state(indoc! {"
 2938        «oneˇ» «twoˇ»
 2939        three
 2940        four
 2941    "});
 2942
 2943    // select across a line ending
 2944    cx.set_state(indoc! {"
 2945        one two
 2946        t«hree
 2947        ˇ»four
 2948    "});
 2949    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2950    cx.assert_editor_state(indoc! {"
 2951        one two
 2952        \tt«hree
 2953        ˇ»four
 2954    "});
 2955    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2956    cx.assert_editor_state(indoc! {"
 2957        one two
 2958        \t\tt«hree
 2959        ˇ»four
 2960    "});
 2961    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2962    cx.assert_editor_state(indoc! {"
 2963        one two
 2964        \tt«hree
 2965        ˇ»four
 2966    "});
 2967    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2968    cx.assert_editor_state(indoc! {"
 2969        one two
 2970        t«hree
 2971        ˇ»four
 2972    "});
 2973
 2974    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2975    cx.set_state(indoc! {"
 2976        one two
 2977        ˇthree
 2978        four
 2979    "});
 2980    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2981    cx.assert_editor_state(indoc! {"
 2982        one two
 2983        ˇthree
 2984        four
 2985    "});
 2986    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2987    cx.assert_editor_state(indoc! {"
 2988        one two
 2989        \tˇthree
 2990        four
 2991    "});
 2992    cx.update_editor(|e, window, cx| e.tab_prev(&TabPrev, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        one two
 2995        ˇthree
 2996        four
 2997    "});
 2998}
 2999
 3000#[gpui::test]
 3001fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3002    init_test(cx, |settings| {
 3003        settings.languages.extend([
 3004            (
 3005                "TOML".into(),
 3006                LanguageSettingsContent {
 3007                    tab_size: NonZeroU32::new(2),
 3008                    ..Default::default()
 3009                },
 3010            ),
 3011            (
 3012                "Rust".into(),
 3013                LanguageSettingsContent {
 3014                    tab_size: NonZeroU32::new(4),
 3015                    ..Default::default()
 3016                },
 3017            ),
 3018        ]);
 3019    });
 3020
 3021    let toml_language = Arc::new(Language::new(
 3022        LanguageConfig {
 3023            name: "TOML".into(),
 3024            ..Default::default()
 3025        },
 3026        None,
 3027    ));
 3028    let rust_language = Arc::new(Language::new(
 3029        LanguageConfig {
 3030            name: "Rust".into(),
 3031            ..Default::default()
 3032        },
 3033        None,
 3034    ));
 3035
 3036    let toml_buffer =
 3037        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3038    let rust_buffer =
 3039        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3040    let multibuffer = cx.new(|cx| {
 3041        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3042        multibuffer.push_excerpts(
 3043            toml_buffer.clone(),
 3044            [ExcerptRange {
 3045                context: Point::new(0, 0)..Point::new(2, 0),
 3046                primary: None,
 3047            }],
 3048            cx,
 3049        );
 3050        multibuffer.push_excerpts(
 3051            rust_buffer.clone(),
 3052            [ExcerptRange {
 3053                context: Point::new(0, 0)..Point::new(1, 0),
 3054                primary: None,
 3055            }],
 3056            cx,
 3057        );
 3058        multibuffer
 3059    });
 3060
 3061    cx.add_window(|window, cx| {
 3062        let mut editor = build_editor(multibuffer, window, cx);
 3063
 3064        assert_eq!(
 3065            editor.text(cx),
 3066            indoc! {"
 3067                a = 1
 3068                b = 2
 3069
 3070                const c: usize = 3;
 3071            "}
 3072        );
 3073
 3074        select_ranges(
 3075            &mut editor,
 3076            indoc! {"
 3077                «aˇ» = 1
 3078                b = 2
 3079
 3080                «const c:ˇ» usize = 3;
 3081            "},
 3082            window,
 3083            cx,
 3084        );
 3085
 3086        editor.tab(&Tab, window, cx);
 3087        assert_text_with_selections(
 3088            &mut editor,
 3089            indoc! {"
 3090                  «aˇ» = 1
 3091                b = 2
 3092
 3093                    «const c:ˇ» usize = 3;
 3094            "},
 3095            cx,
 3096        );
 3097        editor.tab_prev(&TabPrev, window, cx);
 3098        assert_text_with_selections(
 3099            &mut editor,
 3100            indoc! {"
 3101                «aˇ» = 1
 3102                b = 2
 3103
 3104                «const c:ˇ» usize = 3;
 3105            "},
 3106            cx,
 3107        );
 3108
 3109        editor
 3110    });
 3111}
 3112
 3113#[gpui::test]
 3114async fn test_backspace(cx: &mut gpui::TestAppContext) {
 3115    init_test(cx, |_| {});
 3116
 3117    let mut cx = EditorTestContext::new(cx).await;
 3118
 3119    // Basic backspace
 3120    cx.set_state(indoc! {"
 3121        onˇe two three
 3122        fou«rˇ» five six
 3123        seven «ˇeight nine
 3124        »ten
 3125    "});
 3126    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3127    cx.assert_editor_state(indoc! {"
 3128        oˇe two three
 3129        fouˇ five six
 3130        seven ˇten
 3131    "});
 3132
 3133    // Test backspace inside and around indents
 3134    cx.set_state(indoc! {"
 3135        zero
 3136            ˇone
 3137                ˇtwo
 3138            ˇ ˇ ˇ  three
 3139        ˇ  ˇ  four
 3140    "});
 3141    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3142    cx.assert_editor_state(indoc! {"
 3143        zero
 3144        ˇone
 3145            ˇtwo
 3146        ˇ  threeˇ  four
 3147    "});
 3148
 3149    // Test backspace with line_mode set to true
 3150    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3151    cx.set_state(indoc! {"
 3152        The ˇquick ˇbrown
 3153        fox jumps over
 3154        the lazy dog
 3155        ˇThe qu«ick bˇ»rown"});
 3156    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3157    cx.assert_editor_state(indoc! {"
 3158        ˇfox jumps over
 3159        the lazy dogˇ"});
 3160}
 3161
 3162#[gpui::test]
 3163async fn test_delete(cx: &mut gpui::TestAppContext) {
 3164    init_test(cx, |_| {});
 3165
 3166    let mut cx = EditorTestContext::new(cx).await;
 3167    cx.set_state(indoc! {"
 3168        onˇe two three
 3169        fou«rˇ» five six
 3170        seven «ˇeight nine
 3171        »ten
 3172    "});
 3173    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3174    cx.assert_editor_state(indoc! {"
 3175        onˇ two three
 3176        fouˇ five six
 3177        seven ˇten
 3178    "});
 3179
 3180    // Test backspace with line_mode set to true
 3181    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3182    cx.set_state(indoc! {"
 3183        The ˇquick ˇbrown
 3184        fox «ˇjum»ps over
 3185        the lazy dog
 3186        ˇThe qu«ick bˇ»rown"});
 3187    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3188    cx.assert_editor_state("ˇthe lazy dogˇ");
 3189}
 3190
 3191#[gpui::test]
 3192fn test_delete_line(cx: &mut TestAppContext) {
 3193    init_test(cx, |_| {});
 3194
 3195    let editor = cx.add_window(|window, cx| {
 3196        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3197        build_editor(buffer, window, cx)
 3198    });
 3199    _ = editor.update(cx, |editor, window, cx| {
 3200        editor.change_selections(None, window, cx, |s| {
 3201            s.select_display_ranges([
 3202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3203                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3204                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3205            ])
 3206        });
 3207        editor.delete_line(&DeleteLine, window, cx);
 3208        assert_eq!(editor.display_text(cx), "ghi");
 3209        assert_eq!(
 3210            editor.selections.display_ranges(cx),
 3211            vec![
 3212                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3213                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3214            ]
 3215        );
 3216    });
 3217
 3218    let editor = cx.add_window(|window, cx| {
 3219        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3220        build_editor(buffer, window, cx)
 3221    });
 3222    _ = editor.update(cx, |editor, window, cx| {
 3223        editor.change_selections(None, window, cx, |s| {
 3224            s.select_display_ranges([
 3225                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3226            ])
 3227        });
 3228        editor.delete_line(&DeleteLine, window, cx);
 3229        assert_eq!(editor.display_text(cx), "ghi\n");
 3230        assert_eq!(
 3231            editor.selections.display_ranges(cx),
 3232            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3233        );
 3234    });
 3235}
 3236
 3237#[gpui::test]
 3238fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3239    init_test(cx, |_| {});
 3240
 3241    cx.add_window(|window, cx| {
 3242        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3243        let mut editor = build_editor(buffer.clone(), window, cx);
 3244        let buffer = buffer.read(cx).as_singleton().unwrap();
 3245
 3246        assert_eq!(
 3247            editor.selections.ranges::<Point>(cx),
 3248            &[Point::new(0, 0)..Point::new(0, 0)]
 3249        );
 3250
 3251        // When on single line, replace newline at end by space
 3252        editor.join_lines(&JoinLines, window, cx);
 3253        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3254        assert_eq!(
 3255            editor.selections.ranges::<Point>(cx),
 3256            &[Point::new(0, 3)..Point::new(0, 3)]
 3257        );
 3258
 3259        // When multiple lines are selected, remove newlines that are spanned by the selection
 3260        editor.change_selections(None, window, cx, |s| {
 3261            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3262        });
 3263        editor.join_lines(&JoinLines, window, cx);
 3264        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3265        assert_eq!(
 3266            editor.selections.ranges::<Point>(cx),
 3267            &[Point::new(0, 11)..Point::new(0, 11)]
 3268        );
 3269
 3270        // Undo should be transactional
 3271        editor.undo(&Undo, window, cx);
 3272        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3273        assert_eq!(
 3274            editor.selections.ranges::<Point>(cx),
 3275            &[Point::new(0, 5)..Point::new(2, 2)]
 3276        );
 3277
 3278        // When joining an empty line don't insert a space
 3279        editor.change_selections(None, window, cx, |s| {
 3280            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3281        });
 3282        editor.join_lines(&JoinLines, window, cx);
 3283        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3284        assert_eq!(
 3285            editor.selections.ranges::<Point>(cx),
 3286            [Point::new(2, 3)..Point::new(2, 3)]
 3287        );
 3288
 3289        // We can remove trailing newlines
 3290        editor.join_lines(&JoinLines, window, cx);
 3291        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3292        assert_eq!(
 3293            editor.selections.ranges::<Point>(cx),
 3294            [Point::new(2, 3)..Point::new(2, 3)]
 3295        );
 3296
 3297        // We don't blow up on the last line
 3298        editor.join_lines(&JoinLines, window, cx);
 3299        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3300        assert_eq!(
 3301            editor.selections.ranges::<Point>(cx),
 3302            [Point::new(2, 3)..Point::new(2, 3)]
 3303        );
 3304
 3305        // reset to test indentation
 3306        editor.buffer.update(cx, |buffer, cx| {
 3307            buffer.edit(
 3308                [
 3309                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3310                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3311                ],
 3312                None,
 3313                cx,
 3314            )
 3315        });
 3316
 3317        // We remove any leading spaces
 3318        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3321        });
 3322        editor.join_lines(&JoinLines, window, cx);
 3323        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3324
 3325        // We don't insert a space for a line containing only spaces
 3326        editor.join_lines(&JoinLines, window, cx);
 3327        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3328
 3329        // We ignore any leading tabs
 3330        editor.join_lines(&JoinLines, window, cx);
 3331        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3332
 3333        editor
 3334    });
 3335}
 3336
 3337#[gpui::test]
 3338fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3339    init_test(cx, |_| {});
 3340
 3341    cx.add_window(|window, cx| {
 3342        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3343        let mut editor = build_editor(buffer.clone(), window, cx);
 3344        let buffer = buffer.read(cx).as_singleton().unwrap();
 3345
 3346        editor.change_selections(None, window, cx, |s| {
 3347            s.select_ranges([
 3348                Point::new(0, 2)..Point::new(1, 1),
 3349                Point::new(1, 2)..Point::new(1, 2),
 3350                Point::new(3, 1)..Point::new(3, 2),
 3351            ])
 3352        });
 3353
 3354        editor.join_lines(&JoinLines, window, cx);
 3355        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3356
 3357        assert_eq!(
 3358            editor.selections.ranges::<Point>(cx),
 3359            [
 3360                Point::new(0, 7)..Point::new(0, 7),
 3361                Point::new(1, 3)..Point::new(1, 3)
 3362            ]
 3363        );
 3364        editor
 3365    });
 3366}
 3367
 3368#[gpui::test]
 3369async fn test_join_lines_with_git_diff_base(
 3370    executor: BackgroundExecutor,
 3371    cx: &mut gpui::TestAppContext,
 3372) {
 3373    init_test(cx, |_| {});
 3374
 3375    let mut cx = EditorTestContext::new(cx).await;
 3376
 3377    let diff_base = r#"
 3378        Line 0
 3379        Line 1
 3380        Line 2
 3381        Line 3
 3382        "#
 3383    .unindent();
 3384
 3385    cx.set_state(
 3386        &r#"
 3387        ˇLine 0
 3388        Line 1
 3389        Line 2
 3390        Line 3
 3391        "#
 3392        .unindent(),
 3393    );
 3394
 3395    cx.set_diff_base(&diff_base);
 3396    executor.run_until_parked();
 3397
 3398    // Join lines
 3399    cx.update_editor(|editor, window, cx| {
 3400        editor.join_lines(&JoinLines, window, cx);
 3401    });
 3402    executor.run_until_parked();
 3403
 3404    cx.assert_editor_state(
 3405        &r#"
 3406        Line 0ˇ Line 1
 3407        Line 2
 3408        Line 3
 3409        "#
 3410        .unindent(),
 3411    );
 3412    // Join again
 3413    cx.update_editor(|editor, window, cx| {
 3414        editor.join_lines(&JoinLines, window, cx);
 3415    });
 3416    executor.run_until_parked();
 3417
 3418    cx.assert_editor_state(
 3419        &r#"
 3420        Line 0 Line 1ˇ Line 2
 3421        Line 3
 3422        "#
 3423        .unindent(),
 3424    );
 3425}
 3426
 3427#[gpui::test]
 3428async fn test_custom_newlines_cause_no_false_positive_diffs(
 3429    executor: BackgroundExecutor,
 3430    cx: &mut gpui::TestAppContext,
 3431) {
 3432    init_test(cx, |_| {});
 3433    let mut cx = EditorTestContext::new(cx).await;
 3434    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3435    cx.set_diff_base("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3436    executor.run_until_parked();
 3437
 3438    cx.update_editor(|editor, window, cx| {
 3439        let snapshot = editor.snapshot(window, cx);
 3440        assert_eq!(
 3441            snapshot
 3442                .buffer_snapshot
 3443                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3444                .collect::<Vec<_>>(),
 3445            Vec::new(),
 3446            "Should not have any diffs for files with custom newlines"
 3447        );
 3448    });
 3449}
 3450
 3451#[gpui::test]
 3452async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3453    init_test(cx, |_| {});
 3454
 3455    let mut cx = EditorTestContext::new(cx).await;
 3456
 3457    // Test sort_lines_case_insensitive()
 3458    cx.set_state(indoc! {"
 3459        «z
 3460        y
 3461        x
 3462        Z
 3463        Y
 3464        Xˇ»
 3465    "});
 3466    cx.update_editor(|e, window, cx| {
 3467        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3468    });
 3469    cx.assert_editor_state(indoc! {"
 3470        «x
 3471        X
 3472        y
 3473        Y
 3474        z
 3475        Zˇ»
 3476    "});
 3477
 3478    // Test reverse_lines()
 3479    cx.set_state(indoc! {"
 3480        «5
 3481        4
 3482        3
 3483        2
 3484        1ˇ»
 3485    "});
 3486    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3487    cx.assert_editor_state(indoc! {"
 3488        «1
 3489        2
 3490        3
 3491        4
 3492        5ˇ»
 3493    "});
 3494
 3495    // Skip testing shuffle_line()
 3496
 3497    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3498    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3499
 3500    // Don't manipulate when cursor is on single line, but expand the selection
 3501    cx.set_state(indoc! {"
 3502        ddˇdd
 3503        ccc
 3504        bb
 3505        a
 3506    "});
 3507    cx.update_editor(|e, window, cx| {
 3508        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3509    });
 3510    cx.assert_editor_state(indoc! {"
 3511        «ddddˇ»
 3512        ccc
 3513        bb
 3514        a
 3515    "});
 3516
 3517    // Basic manipulate case
 3518    // Start selection moves to column 0
 3519    // End of selection shrinks to fit shorter line
 3520    cx.set_state(indoc! {"
 3521        dd«d
 3522        ccc
 3523        bb
 3524        aaaaaˇ»
 3525    "});
 3526    cx.update_editor(|e, window, cx| {
 3527        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3528    });
 3529    cx.assert_editor_state(indoc! {"
 3530        «aaaaa
 3531        bb
 3532        ccc
 3533        dddˇ»
 3534    "});
 3535
 3536    // Manipulate case with newlines
 3537    cx.set_state(indoc! {"
 3538        dd«d
 3539        ccc
 3540
 3541        bb
 3542        aaaaa
 3543
 3544        ˇ»
 3545    "});
 3546    cx.update_editor(|e, window, cx| {
 3547        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3548    });
 3549    cx.assert_editor_state(indoc! {"
 3550        «
 3551
 3552        aaaaa
 3553        bb
 3554        ccc
 3555        dddˇ»
 3556
 3557    "});
 3558
 3559    // Adding new line
 3560    cx.set_state(indoc! {"
 3561        aa«a
 3562        bbˇ»b
 3563    "});
 3564    cx.update_editor(|e, window, cx| {
 3565        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3566    });
 3567    cx.assert_editor_state(indoc! {"
 3568        «aaa
 3569        bbb
 3570        added_lineˇ»
 3571    "});
 3572
 3573    // Removing line
 3574    cx.set_state(indoc! {"
 3575        aa«a
 3576        bbbˇ»
 3577    "});
 3578    cx.update_editor(|e, window, cx| {
 3579        e.manipulate_lines(window, cx, |lines| {
 3580            lines.pop();
 3581        })
 3582    });
 3583    cx.assert_editor_state(indoc! {"
 3584        «aaaˇ»
 3585    "});
 3586
 3587    // Removing all lines
 3588    cx.set_state(indoc! {"
 3589        aa«a
 3590        bbbˇ»
 3591    "});
 3592    cx.update_editor(|e, window, cx| {
 3593        e.manipulate_lines(window, cx, |lines| {
 3594            lines.drain(..);
 3595        })
 3596    });
 3597    cx.assert_editor_state(indoc! {"
 3598        ˇ
 3599    "});
 3600}
 3601
 3602#[gpui::test]
 3603async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3604    init_test(cx, |_| {});
 3605
 3606    let mut cx = EditorTestContext::new(cx).await;
 3607
 3608    // Consider continuous selection as single selection
 3609    cx.set_state(indoc! {"
 3610        Aaa«aa
 3611        cˇ»c«c
 3612        bb
 3613        aaaˇ»aa
 3614    "});
 3615    cx.update_editor(|e, window, cx| {
 3616        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3617    });
 3618    cx.assert_editor_state(indoc! {"
 3619        «Aaaaa
 3620        ccc
 3621        bb
 3622        aaaaaˇ»
 3623    "});
 3624
 3625    cx.set_state(indoc! {"
 3626        Aaa«aa
 3627        cˇ»c«c
 3628        bb
 3629        aaaˇ»aa
 3630    "});
 3631    cx.update_editor(|e, window, cx| {
 3632        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3633    });
 3634    cx.assert_editor_state(indoc! {"
 3635        «Aaaaa
 3636        ccc
 3637        bbˇ»
 3638    "});
 3639
 3640    // Consider non continuous selection as distinct dedup operations
 3641    cx.set_state(indoc! {"
 3642        «aaaaa
 3643        bb
 3644        aaaaa
 3645        aaaaaˇ»
 3646
 3647        aaa«aaˇ»
 3648    "});
 3649    cx.update_editor(|e, window, cx| {
 3650        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3651    });
 3652    cx.assert_editor_state(indoc! {"
 3653        «aaaaa
 3654        bbˇ»
 3655
 3656        «aaaaaˇ»
 3657    "});
 3658}
 3659
 3660#[gpui::test]
 3661async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3662    init_test(cx, |_| {});
 3663
 3664    let mut cx = EditorTestContext::new(cx).await;
 3665
 3666    cx.set_state(indoc! {"
 3667        «Aaa
 3668        aAa
 3669        Aaaˇ»
 3670    "});
 3671    cx.update_editor(|e, window, cx| {
 3672        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3673    });
 3674    cx.assert_editor_state(indoc! {"
 3675        «Aaa
 3676        aAaˇ»
 3677    "});
 3678
 3679    cx.set_state(indoc! {"
 3680        «Aaa
 3681        aAa
 3682        aaAˇ»
 3683    "});
 3684    cx.update_editor(|e, window, cx| {
 3685        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3686    });
 3687    cx.assert_editor_state(indoc! {"
 3688        «Aaaˇ»
 3689    "});
 3690}
 3691
 3692#[gpui::test]
 3693async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3694    init_test(cx, |_| {});
 3695
 3696    let mut cx = EditorTestContext::new(cx).await;
 3697
 3698    // Manipulate with multiple selections on a single line
 3699    cx.set_state(indoc! {"
 3700        dd«dd
 3701        cˇ»c«c
 3702        bb
 3703        aaaˇ»aa
 3704    "});
 3705    cx.update_editor(|e, window, cx| {
 3706        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3707    });
 3708    cx.assert_editor_state(indoc! {"
 3709        «aaaaa
 3710        bb
 3711        ccc
 3712        ddddˇ»
 3713    "});
 3714
 3715    // Manipulate with multiple disjoin selections
 3716    cx.set_state(indoc! {"
 3717 3718        4
 3719        3
 3720        2
 3721        1ˇ»
 3722
 3723        dd«dd
 3724        ccc
 3725        bb
 3726        aaaˇ»aa
 3727    "});
 3728    cx.update_editor(|e, window, cx| {
 3729        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3730    });
 3731    cx.assert_editor_state(indoc! {"
 3732        «1
 3733        2
 3734        3
 3735        4
 3736        5ˇ»
 3737
 3738        «aaaaa
 3739        bb
 3740        ccc
 3741        ddddˇ»
 3742    "});
 3743
 3744    // Adding lines on each selection
 3745    cx.set_state(indoc! {"
 3746 3747        1ˇ»
 3748
 3749        bb«bb
 3750        aaaˇ»aa
 3751    "});
 3752    cx.update_editor(|e, window, cx| {
 3753        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3754    });
 3755    cx.assert_editor_state(indoc! {"
 3756        «2
 3757        1
 3758        added lineˇ»
 3759
 3760        «bbbb
 3761        aaaaa
 3762        added lineˇ»
 3763    "});
 3764
 3765    // Removing lines on each selection
 3766    cx.set_state(indoc! {"
 3767 3768        1ˇ»
 3769
 3770        bb«bb
 3771        aaaˇ»aa
 3772    "});
 3773    cx.update_editor(|e, window, cx| {
 3774        e.manipulate_lines(window, cx, |lines| {
 3775            lines.pop();
 3776        })
 3777    });
 3778    cx.assert_editor_state(indoc! {"
 3779        «2ˇ»
 3780
 3781        «bbbbˇ»
 3782    "});
 3783}
 3784
 3785#[gpui::test]
 3786async fn test_manipulate_text(cx: &mut TestAppContext) {
 3787    init_test(cx, |_| {});
 3788
 3789    let mut cx = EditorTestContext::new(cx).await;
 3790
 3791    // Test convert_to_upper_case()
 3792    cx.set_state(indoc! {"
 3793        «hello worldˇ»
 3794    "});
 3795    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3796    cx.assert_editor_state(indoc! {"
 3797        «HELLO WORLDˇ»
 3798    "});
 3799
 3800    // Test convert_to_lower_case()
 3801    cx.set_state(indoc! {"
 3802        «HELLO WORLDˇ»
 3803    "});
 3804    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3805    cx.assert_editor_state(indoc! {"
 3806        «hello worldˇ»
 3807    "});
 3808
 3809    // Test multiple line, single selection case
 3810    cx.set_state(indoc! {"
 3811        «The quick brown
 3812        fox jumps over
 3813        the lazy dogˇ»
 3814    "});
 3815    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3816    cx.assert_editor_state(indoc! {"
 3817        «The Quick Brown
 3818        Fox Jumps Over
 3819        The Lazy Dogˇ»
 3820    "});
 3821
 3822    // Test multiple line, single selection case
 3823    cx.set_state(indoc! {"
 3824        «The quick brown
 3825        fox jumps over
 3826        the lazy dogˇ»
 3827    "});
 3828    cx.update_editor(|e, window, cx| {
 3829        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3830    });
 3831    cx.assert_editor_state(indoc! {"
 3832        «TheQuickBrown
 3833        FoxJumpsOver
 3834        TheLazyDogˇ»
 3835    "});
 3836
 3837    // From here on out, test more complex cases of manipulate_text()
 3838
 3839    // Test no selection case - should affect words cursors are in
 3840    // Cursor at beginning, middle, and end of word
 3841    cx.set_state(indoc! {"
 3842        ˇhello big beauˇtiful worldˇ
 3843    "});
 3844    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3845    cx.assert_editor_state(indoc! {"
 3846        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3847    "});
 3848
 3849    // Test multiple selections on a single line and across multiple lines
 3850    cx.set_state(indoc! {"
 3851        «Theˇ» quick «brown
 3852        foxˇ» jumps «overˇ»
 3853        the «lazyˇ» dog
 3854    "});
 3855    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3856    cx.assert_editor_state(indoc! {"
 3857        «THEˇ» quick «BROWN
 3858        FOXˇ» jumps «OVERˇ»
 3859        the «LAZYˇ» dog
 3860    "});
 3861
 3862    // Test case where text length grows
 3863    cx.set_state(indoc! {"
 3864        «tschüߡ»
 3865    "});
 3866    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3867    cx.assert_editor_state(indoc! {"
 3868        «TSCHÜSSˇ»
 3869    "});
 3870
 3871    // Test to make sure we don't crash when text shrinks
 3872    cx.set_state(indoc! {"
 3873        aaa_bbbˇ
 3874    "});
 3875    cx.update_editor(|e, window, cx| {
 3876        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3877    });
 3878    cx.assert_editor_state(indoc! {"
 3879        «aaaBbbˇ»
 3880    "});
 3881
 3882    // Test to make sure we all aware of the fact that each word can grow and shrink
 3883    // Final selections should be aware of this fact
 3884    cx.set_state(indoc! {"
 3885        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3886    "});
 3887    cx.update_editor(|e, window, cx| {
 3888        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3889    });
 3890    cx.assert_editor_state(indoc! {"
 3891        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3892    "});
 3893
 3894    cx.set_state(indoc! {"
 3895        «hElLo, WoRld!ˇ»
 3896    "});
 3897    cx.update_editor(|e, window, cx| {
 3898        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3899    });
 3900    cx.assert_editor_state(indoc! {"
 3901        «HeLlO, wOrLD!ˇ»
 3902    "});
 3903}
 3904
 3905#[gpui::test]
 3906fn test_duplicate_line(cx: &mut TestAppContext) {
 3907    init_test(cx, |_| {});
 3908
 3909    let editor = cx.add_window(|window, cx| {
 3910        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3911        build_editor(buffer, window, cx)
 3912    });
 3913    _ = editor.update(cx, |editor, window, cx| {
 3914        editor.change_selections(None, window, cx, |s| {
 3915            s.select_display_ranges([
 3916                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3917                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3918                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3919                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3920            ])
 3921        });
 3922        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3923        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3924        assert_eq!(
 3925            editor.selections.display_ranges(cx),
 3926            vec![
 3927                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3928                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 3929                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3930                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3931            ]
 3932        );
 3933    });
 3934
 3935    let editor = cx.add_window(|window, cx| {
 3936        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3937        build_editor(buffer, window, cx)
 3938    });
 3939    _ = editor.update(cx, |editor, window, cx| {
 3940        editor.change_selections(None, window, cx, |s| {
 3941            s.select_display_ranges([
 3942                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3943                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3944            ])
 3945        });
 3946        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 3947        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3948        assert_eq!(
 3949            editor.selections.display_ranges(cx),
 3950            vec![
 3951                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 3952                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 3953            ]
 3954        );
 3955    });
 3956
 3957    // With `move_upwards` the selections stay in place, except for
 3958    // the lines inserted above them
 3959    let editor = cx.add_window(|window, cx| {
 3960        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3961        build_editor(buffer, window, cx)
 3962    });
 3963    _ = editor.update(cx, |editor, window, cx| {
 3964        editor.change_selections(None, window, cx, |s| {
 3965            s.select_display_ranges([
 3966                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3967                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3968                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 3969                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3970            ])
 3971        });
 3972        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3973        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 3974        assert_eq!(
 3975            editor.selections.display_ranges(cx),
 3976            vec![
 3977                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 3978                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 3979                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 3980                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 3981            ]
 3982        );
 3983    });
 3984
 3985    let editor = cx.add_window(|window, cx| {
 3986        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3987        build_editor(buffer, window, cx)
 3988    });
 3989    _ = editor.update(cx, |editor, window, cx| {
 3990        editor.change_selections(None, window, cx, |s| {
 3991            s.select_display_ranges([
 3992                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 3993                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 3994            ])
 3995        });
 3996        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 3997        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 3998        assert_eq!(
 3999            editor.selections.display_ranges(cx),
 4000            vec![
 4001                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4002                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4003            ]
 4004        );
 4005    });
 4006
 4007    let editor = cx.add_window(|window, cx| {
 4008        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4009        build_editor(buffer, window, cx)
 4010    });
 4011    _ = editor.update(cx, |editor, window, cx| {
 4012        editor.change_selections(None, window, cx, |s| {
 4013            s.select_display_ranges([
 4014                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4015                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4016            ])
 4017        });
 4018        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4019        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4020        assert_eq!(
 4021            editor.selections.display_ranges(cx),
 4022            vec![
 4023                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4024                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4025            ]
 4026        );
 4027    });
 4028}
 4029
 4030#[gpui::test]
 4031fn test_move_line_up_down(cx: &mut TestAppContext) {
 4032    init_test(cx, |_| {});
 4033
 4034    let editor = cx.add_window(|window, cx| {
 4035        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4036        build_editor(buffer, window, cx)
 4037    });
 4038    _ = editor.update(cx, |editor, window, cx| {
 4039        editor.fold_creases(
 4040            vec![
 4041                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4042                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4043                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4044            ],
 4045            true,
 4046            window,
 4047            cx,
 4048        );
 4049        editor.change_selections(None, window, cx, |s| {
 4050            s.select_display_ranges([
 4051                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4052                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4053                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4054                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4055            ])
 4056        });
 4057        assert_eq!(
 4058            editor.display_text(cx),
 4059            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4060        );
 4061
 4062        editor.move_line_up(&MoveLineUp, window, cx);
 4063        assert_eq!(
 4064            editor.display_text(cx),
 4065            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4066        );
 4067        assert_eq!(
 4068            editor.selections.display_ranges(cx),
 4069            vec![
 4070                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4071                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4072                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4073                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4074            ]
 4075        );
 4076    });
 4077
 4078    _ = editor.update(cx, |editor, window, cx| {
 4079        editor.move_line_down(&MoveLineDown, window, cx);
 4080        assert_eq!(
 4081            editor.display_text(cx),
 4082            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4083        );
 4084        assert_eq!(
 4085            editor.selections.display_ranges(cx),
 4086            vec![
 4087                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4088                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4089                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4090                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4091            ]
 4092        );
 4093    });
 4094
 4095    _ = editor.update(cx, |editor, window, cx| {
 4096        editor.move_line_down(&MoveLineDown, window, cx);
 4097        assert_eq!(
 4098            editor.display_text(cx),
 4099            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4100        );
 4101        assert_eq!(
 4102            editor.selections.display_ranges(cx),
 4103            vec![
 4104                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4105                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4106                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4107                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4108            ]
 4109        );
 4110    });
 4111
 4112    _ = editor.update(cx, |editor, window, cx| {
 4113        editor.move_line_up(&MoveLineUp, window, cx);
 4114        assert_eq!(
 4115            editor.display_text(cx),
 4116            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4117        );
 4118        assert_eq!(
 4119            editor.selections.display_ranges(cx),
 4120            vec![
 4121                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4122                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4123                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4124                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4125            ]
 4126        );
 4127    });
 4128}
 4129
 4130#[gpui::test]
 4131fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4132    init_test(cx, |_| {});
 4133
 4134    let editor = cx.add_window(|window, cx| {
 4135        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4136        build_editor(buffer, window, cx)
 4137    });
 4138    _ = editor.update(cx, |editor, window, cx| {
 4139        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4140        editor.insert_blocks(
 4141            [BlockProperties {
 4142                style: BlockStyle::Fixed,
 4143                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4144                height: 1,
 4145                render: Arc::new(|_| div().into_any()),
 4146                priority: 0,
 4147            }],
 4148            Some(Autoscroll::fit()),
 4149            cx,
 4150        );
 4151        editor.change_selections(None, window, cx, |s| {
 4152            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4153        });
 4154        editor.move_line_down(&MoveLineDown, window, cx);
 4155    });
 4156}
 4157
 4158#[gpui::test]
 4159async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4160    init_test(cx, |_| {});
 4161
 4162    let mut cx = EditorTestContext::new(cx).await;
 4163    cx.set_state(
 4164        &"
 4165            ˇzero
 4166            one
 4167            two
 4168            three
 4169            four
 4170            five
 4171        "
 4172        .unindent(),
 4173    );
 4174
 4175    // Create a four-line block that replaces three lines of text.
 4176    cx.update_editor(|editor, window, cx| {
 4177        let snapshot = editor.snapshot(window, cx);
 4178        let snapshot = &snapshot.buffer_snapshot;
 4179        let placement = BlockPlacement::Replace(
 4180            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4181        );
 4182        editor.insert_blocks(
 4183            [BlockProperties {
 4184                placement,
 4185                height: 4,
 4186                style: BlockStyle::Sticky,
 4187                render: Arc::new(|_| gpui::div().into_any_element()),
 4188                priority: 0,
 4189            }],
 4190            None,
 4191            cx,
 4192        );
 4193    });
 4194
 4195    // Move down so that the cursor touches the block.
 4196    cx.update_editor(|editor, window, cx| {
 4197        editor.move_down(&Default::default(), window, cx);
 4198    });
 4199    cx.assert_editor_state(
 4200        &"
 4201            zero
 4202            «one
 4203            two
 4204            threeˇ»
 4205            four
 4206            five
 4207        "
 4208        .unindent(),
 4209    );
 4210
 4211    // Move down past the block.
 4212    cx.update_editor(|editor, window, cx| {
 4213        editor.move_down(&Default::default(), window, cx);
 4214    });
 4215    cx.assert_editor_state(
 4216        &"
 4217            zero
 4218            one
 4219            two
 4220            three
 4221            ˇfour
 4222            five
 4223        "
 4224        .unindent(),
 4225    );
 4226}
 4227
 4228#[gpui::test]
 4229fn test_transpose(cx: &mut TestAppContext) {
 4230    init_test(cx, |_| {});
 4231
 4232    _ = cx.add_window(|window, cx| {
 4233        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4234        editor.set_style(EditorStyle::default(), window, cx);
 4235        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4236        editor.transpose(&Default::default(), window, cx);
 4237        assert_eq!(editor.text(cx), "bac");
 4238        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4239
 4240        editor.transpose(&Default::default(), window, cx);
 4241        assert_eq!(editor.text(cx), "bca");
 4242        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4243
 4244        editor.transpose(&Default::default(), window, cx);
 4245        assert_eq!(editor.text(cx), "bac");
 4246        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4247
 4248        editor
 4249    });
 4250
 4251    _ = cx.add_window(|window, cx| {
 4252        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4253        editor.set_style(EditorStyle::default(), window, cx);
 4254        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4255        editor.transpose(&Default::default(), window, cx);
 4256        assert_eq!(editor.text(cx), "acb\nde");
 4257        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4258
 4259        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4260        editor.transpose(&Default::default(), window, cx);
 4261        assert_eq!(editor.text(cx), "acbd\ne");
 4262        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4263
 4264        editor.transpose(&Default::default(), window, cx);
 4265        assert_eq!(editor.text(cx), "acbde\n");
 4266        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4267
 4268        editor.transpose(&Default::default(), window, cx);
 4269        assert_eq!(editor.text(cx), "acbd\ne");
 4270        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4271
 4272        editor
 4273    });
 4274
 4275    _ = cx.add_window(|window, cx| {
 4276        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4277        editor.set_style(EditorStyle::default(), window, cx);
 4278        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4279        editor.transpose(&Default::default(), window, cx);
 4280        assert_eq!(editor.text(cx), "bacd\ne");
 4281        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4282
 4283        editor.transpose(&Default::default(), window, cx);
 4284        assert_eq!(editor.text(cx), "bcade\n");
 4285        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4286
 4287        editor.transpose(&Default::default(), window, cx);
 4288        assert_eq!(editor.text(cx), "bcda\ne");
 4289        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4290
 4291        editor.transpose(&Default::default(), window, cx);
 4292        assert_eq!(editor.text(cx), "bcade\n");
 4293        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4294
 4295        editor.transpose(&Default::default(), window, cx);
 4296        assert_eq!(editor.text(cx), "bcaed\n");
 4297        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4298
 4299        editor
 4300    });
 4301
 4302    _ = cx.add_window(|window, cx| {
 4303        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4304        editor.set_style(EditorStyle::default(), window, cx);
 4305        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4306        editor.transpose(&Default::default(), window, cx);
 4307        assert_eq!(editor.text(cx), "🏀🍐✋");
 4308        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4309
 4310        editor.transpose(&Default::default(), window, cx);
 4311        assert_eq!(editor.text(cx), "🏀✋🍐");
 4312        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4313
 4314        editor.transpose(&Default::default(), window, cx);
 4315        assert_eq!(editor.text(cx), "🏀🍐✋");
 4316        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4317
 4318        editor
 4319    });
 4320}
 4321
 4322#[gpui::test]
 4323async fn test_rewrap(cx: &mut TestAppContext) {
 4324    init_test(cx, |settings| {
 4325        settings.languages.extend([
 4326            (
 4327                "Markdown".into(),
 4328                LanguageSettingsContent {
 4329                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4330                    ..Default::default()
 4331                },
 4332            ),
 4333            (
 4334                "Plain Text".into(),
 4335                LanguageSettingsContent {
 4336                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4337                    ..Default::default()
 4338                },
 4339            ),
 4340        ])
 4341    });
 4342
 4343    let mut cx = EditorTestContext::new(cx).await;
 4344
 4345    let language_with_c_comments = Arc::new(Language::new(
 4346        LanguageConfig {
 4347            line_comments: vec!["// ".into()],
 4348            ..LanguageConfig::default()
 4349        },
 4350        None,
 4351    ));
 4352    let language_with_pound_comments = Arc::new(Language::new(
 4353        LanguageConfig {
 4354            line_comments: vec!["# ".into()],
 4355            ..LanguageConfig::default()
 4356        },
 4357        None,
 4358    ));
 4359    let markdown_language = Arc::new(Language::new(
 4360        LanguageConfig {
 4361            name: "Markdown".into(),
 4362            ..LanguageConfig::default()
 4363        },
 4364        None,
 4365    ));
 4366    let language_with_doc_comments = Arc::new(Language::new(
 4367        LanguageConfig {
 4368            line_comments: vec!["// ".into(), "/// ".into()],
 4369            ..LanguageConfig::default()
 4370        },
 4371        Some(tree_sitter_rust::LANGUAGE.into()),
 4372    ));
 4373
 4374    let plaintext_language = Arc::new(Language::new(
 4375        LanguageConfig {
 4376            name: "Plain Text".into(),
 4377            ..LanguageConfig::default()
 4378        },
 4379        None,
 4380    ));
 4381
 4382    assert_rewrap(
 4383        indoc! {"
 4384            // ˇ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.
 4385        "},
 4386        indoc! {"
 4387            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4388            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4389            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4390            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4391            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4392            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4393            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4394            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4395            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4396            // porttitor id. Aliquam id accumsan eros.
 4397        "},
 4398        language_with_c_comments.clone(),
 4399        &mut cx,
 4400    );
 4401
 4402    // Test that rewrapping works inside of a selection
 4403    assert_rewrap(
 4404        indoc! {"
 4405            «// 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.ˇ»
 4406        "},
 4407        indoc! {"
 4408            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4409            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4410            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4411            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4412            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4413            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4414            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4415            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4416            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4417            // porttitor id. Aliquam id accumsan eros.ˇ»
 4418        "},
 4419        language_with_c_comments.clone(),
 4420        &mut cx,
 4421    );
 4422
 4423    // Test that cursors that expand to the same region are collapsed.
 4424    assert_rewrap(
 4425        indoc! {"
 4426            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4427            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4428            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4429            // ˇ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.
 4430        "},
 4431        indoc! {"
 4432            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4433            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4434            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4435            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4436            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4437            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4438            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4439            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4440            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4441            // porttitor id. Aliquam id accumsan eros.
 4442        "},
 4443        language_with_c_comments.clone(),
 4444        &mut cx,
 4445    );
 4446
 4447    // Test that non-contiguous selections are treated separately.
 4448    assert_rewrap(
 4449        indoc! {"
 4450            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4451            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4452            //
 4453            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4454            // ˇ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.
 4455        "},
 4456        indoc! {"
 4457            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4458            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4459            // auctor, eu lacinia sapien scelerisque.
 4460            //
 4461            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4462            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4463            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4464            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4465            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4466            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4467            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4468        "},
 4469        language_with_c_comments.clone(),
 4470        &mut cx,
 4471    );
 4472
 4473    // Test that different comment prefixes are supported.
 4474    assert_rewrap(
 4475        indoc! {"
 4476            # ˇ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.
 4477        "},
 4478        indoc! {"
 4479            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4480            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4481            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4482            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4483            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4484            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4485            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4486            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4487            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4488            # accumsan eros.
 4489        "},
 4490        language_with_pound_comments.clone(),
 4491        &mut cx,
 4492    );
 4493
 4494    // Test that rewrapping is ignored outside of comments in most languages.
 4495    assert_rewrap(
 4496        indoc! {"
 4497            /// Adds two numbers.
 4498            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4499            fn add(a: u32, b: u32) -> u32 {
 4500                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ˇ
 4501            }
 4502        "},
 4503        indoc! {"
 4504            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4505            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4506            fn add(a: u32, b: u32) -> u32 {
 4507                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ˇ
 4508            }
 4509        "},
 4510        language_with_doc_comments.clone(),
 4511        &mut cx,
 4512    );
 4513
 4514    // Test that rewrapping works in Markdown and Plain Text languages.
 4515    assert_rewrap(
 4516        indoc! {"
 4517            # Hello
 4518
 4519            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.
 4520        "},
 4521        indoc! {"
 4522            # Hello
 4523
 4524            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4525            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4526            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4527            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4528            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4529            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4530            Integer sit amet scelerisque nisi.
 4531        "},
 4532        markdown_language,
 4533        &mut cx,
 4534    );
 4535
 4536    assert_rewrap(
 4537        indoc! {"
 4538            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.
 4539        "},
 4540        indoc! {"
 4541            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4542            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4543            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4544            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4545            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4546            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4547            Integer sit amet scelerisque nisi.
 4548        "},
 4549        plaintext_language,
 4550        &mut cx,
 4551    );
 4552
 4553    // Test rewrapping unaligned comments in a selection.
 4554    assert_rewrap(
 4555        indoc! {"
 4556            fn foo() {
 4557                if true {
 4558            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4559            // Praesent semper egestas tellus id dignissim.ˇ»
 4560                    do_something();
 4561                } else {
 4562                    //
 4563                }
 4564            }
 4565        "},
 4566        indoc! {"
 4567            fn foo() {
 4568                if true {
 4569            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4570                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4571                    // egestas tellus id dignissim.ˇ»
 4572                    do_something();
 4573                } else {
 4574                    //
 4575                }
 4576            }
 4577        "},
 4578        language_with_doc_comments.clone(),
 4579        &mut cx,
 4580    );
 4581
 4582    assert_rewrap(
 4583        indoc! {"
 4584            fn foo() {
 4585                if true {
 4586            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4587            // Praesent semper egestas tellus id dignissim.»
 4588                    do_something();
 4589                } else {
 4590                    //
 4591                }
 4592
 4593            }
 4594        "},
 4595        indoc! {"
 4596            fn foo() {
 4597                if true {
 4598            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4599                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4600                    // egestas tellus id dignissim.»
 4601                    do_something();
 4602                } else {
 4603                    //
 4604                }
 4605
 4606            }
 4607        "},
 4608        language_with_doc_comments.clone(),
 4609        &mut cx,
 4610    );
 4611
 4612    #[track_caller]
 4613    fn assert_rewrap(
 4614        unwrapped_text: &str,
 4615        wrapped_text: &str,
 4616        language: Arc<Language>,
 4617        cx: &mut EditorTestContext,
 4618    ) {
 4619        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4620        cx.set_state(unwrapped_text);
 4621        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4622        cx.assert_editor_state(wrapped_text);
 4623    }
 4624}
 4625
 4626#[gpui::test]
 4627async fn test_clipboard(cx: &mut gpui::TestAppContext) {
 4628    init_test(cx, |_| {});
 4629
 4630    let mut cx = EditorTestContext::new(cx).await;
 4631
 4632    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4633    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4634    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4635
 4636    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4637    cx.set_state("two ˇfour ˇsix ˇ");
 4638    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4639    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4640
 4641    // Paste again but with only two cursors. Since the number of cursors doesn't
 4642    // match the number of slices in the clipboard, the entire clipboard text
 4643    // is pasted at each cursor.
 4644    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4645    cx.update_editor(|e, window, cx| {
 4646        e.handle_input("( ", window, cx);
 4647        e.paste(&Paste, window, cx);
 4648        e.handle_input(") ", window, cx);
 4649    });
 4650    cx.assert_editor_state(
 4651        &([
 4652            "( one✅ ",
 4653            "three ",
 4654            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4655            "three ",
 4656            "five ) ˇ",
 4657        ]
 4658        .join("\n")),
 4659    );
 4660
 4661    // Cut with three selections, one of which is full-line.
 4662    cx.set_state(indoc! {"
 4663        1«2ˇ»3
 4664        4ˇ567
 4665        «8ˇ»9"});
 4666    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4667    cx.assert_editor_state(indoc! {"
 4668        1ˇ3
 4669        ˇ9"});
 4670
 4671    // Paste with three selections, noticing how the copied selection that was full-line
 4672    // gets inserted before the second cursor.
 4673    cx.set_state(indoc! {"
 4674        1ˇ3
 4675 4676        «oˇ»ne"});
 4677    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4678    cx.assert_editor_state(indoc! {"
 4679        12ˇ3
 4680        4567
 4681 4682        8ˇne"});
 4683
 4684    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4685    cx.set_state(indoc! {"
 4686        The quick brown
 4687        fox juˇmps over
 4688        the lazy dog"});
 4689    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4690    assert_eq!(
 4691        cx.read_from_clipboard()
 4692            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4693        Some("fox jumps over\n".to_string())
 4694    );
 4695
 4696    // Paste with three selections, noticing how the copied full-line selection is inserted
 4697    // before the empty selections but replaces the selection that is non-empty.
 4698    cx.set_state(indoc! {"
 4699        Tˇhe quick brown
 4700        «foˇ»x jumps over
 4701        tˇhe lazy dog"});
 4702    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4703    cx.assert_editor_state(indoc! {"
 4704        fox jumps over
 4705        Tˇhe quick brown
 4706        fox jumps over
 4707        ˇx jumps over
 4708        fox jumps over
 4709        tˇhe lazy dog"});
 4710}
 4711
 4712#[gpui::test]
 4713async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 4714    init_test(cx, |_| {});
 4715
 4716    let mut cx = EditorTestContext::new(cx).await;
 4717    let language = Arc::new(Language::new(
 4718        LanguageConfig::default(),
 4719        Some(tree_sitter_rust::LANGUAGE.into()),
 4720    ));
 4721    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4722
 4723    // Cut an indented block, without the leading whitespace.
 4724    cx.set_state(indoc! {"
 4725        const a: B = (
 4726            c(),
 4727            «d(
 4728                e,
 4729                f
 4730            )ˇ»
 4731        );
 4732    "});
 4733    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4734    cx.assert_editor_state(indoc! {"
 4735        const a: B = (
 4736            c(),
 4737            ˇ
 4738        );
 4739    "});
 4740
 4741    // Paste it at the same position.
 4742    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4743    cx.assert_editor_state(indoc! {"
 4744        const a: B = (
 4745            c(),
 4746            d(
 4747                e,
 4748                f
 4749 4750        );
 4751    "});
 4752
 4753    // Paste it at a line with a lower indent level.
 4754    cx.set_state(indoc! {"
 4755        ˇ
 4756        const a: B = (
 4757            c(),
 4758        );
 4759    "});
 4760    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4761    cx.assert_editor_state(indoc! {"
 4762        d(
 4763            e,
 4764            f
 4765 4766        const a: B = (
 4767            c(),
 4768        );
 4769    "});
 4770
 4771    // Cut an indented block, with the leading whitespace.
 4772    cx.set_state(indoc! {"
 4773        const a: B = (
 4774            c(),
 4775        «    d(
 4776                e,
 4777                f
 4778            )
 4779        ˇ»);
 4780    "});
 4781    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4782    cx.assert_editor_state(indoc! {"
 4783        const a: B = (
 4784            c(),
 4785        ˇ);
 4786    "});
 4787
 4788    // Paste it at the same position.
 4789    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4790    cx.assert_editor_state(indoc! {"
 4791        const a: B = (
 4792            c(),
 4793            d(
 4794                e,
 4795                f
 4796            )
 4797        ˇ);
 4798    "});
 4799
 4800    // Paste it at a line with a higher indent level.
 4801    cx.set_state(indoc! {"
 4802        const a: B = (
 4803            c(),
 4804            d(
 4805                e,
 4806 4807            )
 4808        );
 4809    "});
 4810    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4811    cx.assert_editor_state(indoc! {"
 4812        const a: B = (
 4813            c(),
 4814            d(
 4815                e,
 4816                f    d(
 4817                    e,
 4818                    f
 4819                )
 4820        ˇ
 4821            )
 4822        );
 4823    "});
 4824}
 4825
 4826#[gpui::test]
 4827fn test_select_all(cx: &mut TestAppContext) {
 4828    init_test(cx, |_| {});
 4829
 4830    let editor = cx.add_window(|window, cx| {
 4831        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 4832        build_editor(buffer, window, cx)
 4833    });
 4834    _ = editor.update(cx, |editor, window, cx| {
 4835        editor.select_all(&SelectAll, window, cx);
 4836        assert_eq!(
 4837            editor.selections.display_ranges(cx),
 4838            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 4839        );
 4840    });
 4841}
 4842
 4843#[gpui::test]
 4844fn test_select_line(cx: &mut TestAppContext) {
 4845    init_test(cx, |_| {});
 4846
 4847    let editor = cx.add_window(|window, cx| {
 4848        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 4849        build_editor(buffer, window, cx)
 4850    });
 4851    _ = editor.update(cx, |editor, window, cx| {
 4852        editor.change_selections(None, window, cx, |s| {
 4853            s.select_display_ranges([
 4854                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4855                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4856                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4857                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 4858            ])
 4859        });
 4860        editor.select_line(&SelectLine, window, cx);
 4861        assert_eq!(
 4862            editor.selections.display_ranges(cx),
 4863            vec![
 4864                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4865                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 4866            ]
 4867        );
 4868    });
 4869
 4870    _ = editor.update(cx, |editor, window, cx| {
 4871        editor.select_line(&SelectLine, window, cx);
 4872        assert_eq!(
 4873            editor.selections.display_ranges(cx),
 4874            vec![
 4875                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4876                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 4877            ]
 4878        );
 4879    });
 4880
 4881    _ = editor.update(cx, |editor, window, cx| {
 4882        editor.select_line(&SelectLine, window, cx);
 4883        assert_eq!(
 4884            editor.selections.display_ranges(cx),
 4885            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 4886        );
 4887    });
 4888}
 4889
 4890#[gpui::test]
 4891async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 4892    init_test(cx, |_| {});
 4893    let mut cx = EditorTestContext::new(cx).await;
 4894
 4895    #[track_caller]
 4896    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 4897        cx.set_state(initial_state);
 4898        cx.update_editor(|e, window, cx| {
 4899            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 4900        });
 4901        cx.assert_editor_state(expected_state);
 4902    }
 4903
 4904    // Selection starts and ends at the middle of lines, left-to-right
 4905    test(
 4906        &mut cx,
 4907        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 4908        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4909    );
 4910    // Same thing, right-to-left
 4911    test(
 4912        &mut cx,
 4913        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 4914        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 4915    );
 4916
 4917    // Whole buffer, left-to-right, last line *doesn't* end with newline
 4918    test(
 4919        &mut cx,
 4920        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 4921        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4922    );
 4923    // Same thing, right-to-left
 4924    test(
 4925        &mut cx,
 4926        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 4927        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 4928    );
 4929
 4930    // Whole buffer, left-to-right, last line ends with newline
 4931    test(
 4932        &mut cx,
 4933        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 4934        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4935    );
 4936    // Same thing, right-to-left
 4937    test(
 4938        &mut cx,
 4939        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 4940        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 4941    );
 4942
 4943    // Starts at the end of a line, ends at the start of another
 4944    test(
 4945        &mut cx,
 4946        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 4947        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 4948    );
 4949}
 4950
 4951#[gpui::test]
 4952async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 4953    init_test(cx, |_| {});
 4954
 4955    let editor = cx.add_window(|window, cx| {
 4956        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 4957        build_editor(buffer, window, cx)
 4958    });
 4959
 4960    // setup
 4961    _ = editor.update(cx, |editor, window, cx| {
 4962        editor.fold_creases(
 4963            vec![
 4964                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4965                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4966                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4967            ],
 4968            true,
 4969            window,
 4970            cx,
 4971        );
 4972        assert_eq!(
 4973            editor.display_text(cx),
 4974            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4975        );
 4976    });
 4977
 4978    _ = editor.update(cx, |editor, window, cx| {
 4979        editor.change_selections(None, window, cx, |s| {
 4980            s.select_display_ranges([
 4981                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4982                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4983                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4984                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 4985            ])
 4986        });
 4987        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 4988        assert_eq!(
 4989            editor.display_text(cx),
 4990            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 4991        );
 4992    });
 4993    EditorTestContext::for_editor(editor, cx)
 4994        .await
 4995        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 4996
 4997    _ = editor.update(cx, |editor, window, cx| {
 4998        editor.change_selections(None, window, cx, |s| {
 4999            s.select_display_ranges([
 5000                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5001            ])
 5002        });
 5003        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5004        assert_eq!(
 5005            editor.display_text(cx),
 5006            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5007        );
 5008        assert_eq!(
 5009            editor.selections.display_ranges(cx),
 5010            [
 5011                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5012                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5013                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5014                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5015                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5016                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5017                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5018            ]
 5019        );
 5020    });
 5021    EditorTestContext::for_editor(editor, cx)
 5022        .await
 5023        .assert_editor_state(
 5024            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5025        );
 5026}
 5027
 5028#[gpui::test]
 5029async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5030    init_test(cx, |_| {});
 5031
 5032    let mut cx = EditorTestContext::new(cx).await;
 5033
 5034    cx.set_state(indoc!(
 5035        r#"abc
 5036           defˇghi
 5037
 5038           jk
 5039           nlmo
 5040           "#
 5041    ));
 5042
 5043    cx.update_editor(|editor, window, cx| {
 5044        editor.add_selection_above(&Default::default(), window, cx);
 5045    });
 5046
 5047    cx.assert_editor_state(indoc!(
 5048        r#"abcˇ
 5049           defˇghi
 5050
 5051           jk
 5052           nlmo
 5053           "#
 5054    ));
 5055
 5056    cx.update_editor(|editor, window, cx| {
 5057        editor.add_selection_above(&Default::default(), window, cx);
 5058    });
 5059
 5060    cx.assert_editor_state(indoc!(
 5061        r#"abcˇ
 5062            defˇghi
 5063
 5064            jk
 5065            nlmo
 5066            "#
 5067    ));
 5068
 5069    cx.update_editor(|editor, window, cx| {
 5070        editor.add_selection_below(&Default::default(), window, cx);
 5071    });
 5072
 5073    cx.assert_editor_state(indoc!(
 5074        r#"abc
 5075           defˇghi
 5076
 5077           jk
 5078           nlmo
 5079           "#
 5080    ));
 5081
 5082    cx.update_editor(|editor, window, cx| {
 5083        editor.undo_selection(&Default::default(), window, cx);
 5084    });
 5085
 5086    cx.assert_editor_state(indoc!(
 5087        r#"abcˇ
 5088           defˇghi
 5089
 5090           jk
 5091           nlmo
 5092           "#
 5093    ));
 5094
 5095    cx.update_editor(|editor, window, cx| {
 5096        editor.redo_selection(&Default::default(), window, cx);
 5097    });
 5098
 5099    cx.assert_editor_state(indoc!(
 5100        r#"abc
 5101           defˇghi
 5102
 5103           jk
 5104           nlmo
 5105           "#
 5106    ));
 5107
 5108    cx.update_editor(|editor, window, cx| {
 5109        editor.add_selection_below(&Default::default(), window, cx);
 5110    });
 5111
 5112    cx.assert_editor_state(indoc!(
 5113        r#"abc
 5114           defˇghi
 5115
 5116           jk
 5117           nlmˇo
 5118           "#
 5119    ));
 5120
 5121    cx.update_editor(|editor, window, cx| {
 5122        editor.add_selection_below(&Default::default(), window, cx);
 5123    });
 5124
 5125    cx.assert_editor_state(indoc!(
 5126        r#"abc
 5127           defˇghi
 5128
 5129           jk
 5130           nlmˇo
 5131           "#
 5132    ));
 5133
 5134    // change selections
 5135    cx.set_state(indoc!(
 5136        r#"abc
 5137           def«ˇg»hi
 5138
 5139           jk
 5140           nlmo
 5141           "#
 5142    ));
 5143
 5144    cx.update_editor(|editor, window, cx| {
 5145        editor.add_selection_below(&Default::default(), window, cx);
 5146    });
 5147
 5148    cx.assert_editor_state(indoc!(
 5149        r#"abc
 5150           def«ˇg»hi
 5151
 5152           jk
 5153           nlm«ˇo»
 5154           "#
 5155    ));
 5156
 5157    cx.update_editor(|editor, window, cx| {
 5158        editor.add_selection_below(&Default::default(), window, cx);
 5159    });
 5160
 5161    cx.assert_editor_state(indoc!(
 5162        r#"abc
 5163           def«ˇg»hi
 5164
 5165           jk
 5166           nlm«ˇo»
 5167           "#
 5168    ));
 5169
 5170    cx.update_editor(|editor, window, cx| {
 5171        editor.add_selection_above(&Default::default(), window, cx);
 5172    });
 5173
 5174    cx.assert_editor_state(indoc!(
 5175        r#"abc
 5176           def«ˇg»hi
 5177
 5178           jk
 5179           nlmo
 5180           "#
 5181    ));
 5182
 5183    cx.update_editor(|editor, window, cx| {
 5184        editor.add_selection_above(&Default::default(), window, cx);
 5185    });
 5186
 5187    cx.assert_editor_state(indoc!(
 5188        r#"abc
 5189           def«ˇg»hi
 5190
 5191           jk
 5192           nlmo
 5193           "#
 5194    ));
 5195
 5196    // Change selections again
 5197    cx.set_state(indoc!(
 5198        r#"a«bc
 5199           defgˇ»hi
 5200
 5201           jk
 5202           nlmo
 5203           "#
 5204    ));
 5205
 5206    cx.update_editor(|editor, window, cx| {
 5207        editor.add_selection_below(&Default::default(), window, cx);
 5208    });
 5209
 5210    cx.assert_editor_state(indoc!(
 5211        r#"a«bcˇ»
 5212           d«efgˇ»hi
 5213
 5214           j«kˇ»
 5215           nlmo
 5216           "#
 5217    ));
 5218
 5219    cx.update_editor(|editor, window, cx| {
 5220        editor.add_selection_below(&Default::default(), window, cx);
 5221    });
 5222    cx.assert_editor_state(indoc!(
 5223        r#"a«bcˇ»
 5224           d«efgˇ»hi
 5225
 5226           j«kˇ»
 5227           n«lmoˇ»
 5228           "#
 5229    ));
 5230    cx.update_editor(|editor, window, cx| {
 5231        editor.add_selection_above(&Default::default(), window, cx);
 5232    });
 5233
 5234    cx.assert_editor_state(indoc!(
 5235        r#"a«bcˇ»
 5236           d«efgˇ»hi
 5237
 5238           j«kˇ»
 5239           nlmo
 5240           "#
 5241    ));
 5242
 5243    // Change selections again
 5244    cx.set_state(indoc!(
 5245        r#"abc
 5246           d«ˇefghi
 5247
 5248           jk
 5249           nlm»o
 5250           "#
 5251    ));
 5252
 5253    cx.update_editor(|editor, window, cx| {
 5254        editor.add_selection_above(&Default::default(), window, cx);
 5255    });
 5256
 5257    cx.assert_editor_state(indoc!(
 5258        r#"a«ˇbc»
 5259           d«ˇef»ghi
 5260
 5261           j«ˇk»
 5262           n«ˇlm»o
 5263           "#
 5264    ));
 5265
 5266    cx.update_editor(|editor, window, cx| {
 5267        editor.add_selection_below(&Default::default(), window, cx);
 5268    });
 5269
 5270    cx.assert_editor_state(indoc!(
 5271        r#"abc
 5272           d«ˇef»ghi
 5273
 5274           j«ˇk»
 5275           n«ˇlm»o
 5276           "#
 5277    ));
 5278}
 5279
 5280#[gpui::test]
 5281async fn test_select_next(cx: &mut gpui::TestAppContext) {
 5282    init_test(cx, |_| {});
 5283
 5284    let mut cx = EditorTestContext::new(cx).await;
 5285    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5286
 5287    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5288        .unwrap();
 5289    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5290
 5291    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5292        .unwrap();
 5293    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5294
 5295    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5296    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5297
 5298    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5299    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5300
 5301    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5302        .unwrap();
 5303    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5304
 5305    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5306        .unwrap();
 5307    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5308}
 5309
 5310#[gpui::test]
 5311async fn test_select_all_matches(cx: &mut gpui::TestAppContext) {
 5312    init_test(cx, |_| {});
 5313
 5314    let mut cx = EditorTestContext::new(cx).await;
 5315
 5316    // Test caret-only selections
 5317    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5318    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5319        .unwrap();
 5320    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5321
 5322    // Test left-to-right selections
 5323    cx.set_state("abc\n«abcˇ»\nabc");
 5324    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5325        .unwrap();
 5326    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5327
 5328    // Test right-to-left selections
 5329    cx.set_state("abc\n«ˇabc»\nabc");
 5330    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5331        .unwrap();
 5332    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5333
 5334    // Test selecting whitespace with caret selection
 5335    cx.set_state("abc\nˇ   abc\nabc");
 5336    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5337        .unwrap();
 5338    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5339
 5340    // Test selecting whitespace with left-to-right selection
 5341    cx.set_state("abc\n«ˇ  »abc\nabc");
 5342    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5343        .unwrap();
 5344    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5345
 5346    // Test no matches with right-to-left selection
 5347    cx.set_state("abc\n«  ˇ»abc\nabc");
 5348    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5349        .unwrap();
 5350    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5351}
 5352
 5353#[gpui::test]
 5354async fn test_select_next_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5355    init_test(cx, |_| {});
 5356
 5357    let mut cx = EditorTestContext::new(cx).await;
 5358    cx.set_state(
 5359        r#"let foo = 2;
 5360lˇet foo = 2;
 5361let fooˇ = 2;
 5362let foo = 2;
 5363let foo = ˇ2;"#,
 5364    );
 5365
 5366    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5367        .unwrap();
 5368    cx.assert_editor_state(
 5369        r#"let foo = 2;
 5370«letˇ» foo = 2;
 5371let «fooˇ» = 2;
 5372let foo = 2;
 5373let foo = «2ˇ»;"#,
 5374    );
 5375
 5376    // noop for multiple selections with different contents
 5377    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5378        .unwrap();
 5379    cx.assert_editor_state(
 5380        r#"let foo = 2;
 5381«letˇ» foo = 2;
 5382let «fooˇ» = 2;
 5383let foo = 2;
 5384let foo = «2ˇ»;"#,
 5385    );
 5386}
 5387
 5388#[gpui::test]
 5389async fn test_select_previous_multibuffer(cx: &mut gpui::TestAppContext) {
 5390    init_test(cx, |_| {});
 5391
 5392    let mut cx =
 5393        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5394
 5395    cx.assert_editor_state(indoc! {"
 5396        ˇbbb
 5397        ccc
 5398
 5399        bbb
 5400        ccc
 5401        "});
 5402    cx.dispatch_action(SelectPrevious::default());
 5403    cx.assert_editor_state(indoc! {"
 5404                «bbbˇ»
 5405                ccc
 5406
 5407                bbb
 5408                ccc
 5409                "});
 5410    cx.dispatch_action(SelectPrevious::default());
 5411    cx.assert_editor_state(indoc! {"
 5412                «bbbˇ»
 5413                ccc
 5414
 5415                «bbbˇ»
 5416                ccc
 5417                "});
 5418}
 5419
 5420#[gpui::test]
 5421async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) {
 5422    init_test(cx, |_| {});
 5423
 5424    let mut cx = EditorTestContext::new(cx).await;
 5425    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5426
 5427    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5428        .unwrap();
 5429    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5430
 5431    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5432        .unwrap();
 5433    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5434
 5435    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5436    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5437
 5438    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5439    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5440
 5441    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5442        .unwrap();
 5443    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5444
 5445    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5446        .unwrap();
 5447    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5448
 5449    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5450        .unwrap();
 5451    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5452}
 5453
 5454#[gpui::test]
 5455async fn test_select_previous_empty_buffer(cx: &mut gpui::TestAppContext) {
 5456    init_test(cx, |_| {});
 5457
 5458    let mut cx = EditorTestContext::new(cx).await;
 5459    cx.set_state("");
 5460
 5461    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5462        .unwrap();
 5463    cx.assert_editor_state("«aˇ»");
 5464    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5465        .unwrap();
 5466    cx.assert_editor_state("«aˇ»");
 5467}
 5468
 5469#[gpui::test]
 5470async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) {
 5471    init_test(cx, |_| {});
 5472
 5473    let mut cx = EditorTestContext::new(cx).await;
 5474    cx.set_state(
 5475        r#"let foo = 2;
 5476lˇet foo = 2;
 5477let fooˇ = 2;
 5478let foo = 2;
 5479let foo = ˇ2;"#,
 5480    );
 5481
 5482    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5483        .unwrap();
 5484    cx.assert_editor_state(
 5485        r#"let foo = 2;
 5486«letˇ» foo = 2;
 5487let «fooˇ» = 2;
 5488let foo = 2;
 5489let foo = «2ˇ»;"#,
 5490    );
 5491
 5492    // noop for multiple selections with different contents
 5493    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5494        .unwrap();
 5495    cx.assert_editor_state(
 5496        r#"let foo = 2;
 5497«letˇ» foo = 2;
 5498let «fooˇ» = 2;
 5499let foo = 2;
 5500let foo = «2ˇ»;"#,
 5501    );
 5502}
 5503
 5504#[gpui::test]
 5505async fn test_select_previous_with_single_selection(cx: &mut gpui::TestAppContext) {
 5506    init_test(cx, |_| {});
 5507
 5508    let mut cx = EditorTestContext::new(cx).await;
 5509    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5510
 5511    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5512        .unwrap();
 5513    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5514
 5515    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5516        .unwrap();
 5517    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5518
 5519    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5520    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5521
 5522    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5523    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5524
 5525    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5526        .unwrap();
 5527    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5528
 5529    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5530        .unwrap();
 5531    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5532}
 5533
 5534#[gpui::test]
 5535async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
 5536    init_test(cx, |_| {});
 5537
 5538    let language = Arc::new(Language::new(
 5539        LanguageConfig::default(),
 5540        Some(tree_sitter_rust::LANGUAGE.into()),
 5541    ));
 5542
 5543    let text = r#"
 5544        use mod1::mod2::{mod3, mod4};
 5545
 5546        fn fn_1(param1: bool, param2: &str) {
 5547            let var1 = "text";
 5548        }
 5549    "#
 5550    .unindent();
 5551
 5552    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5553    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5554    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5555
 5556    editor
 5557        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5558        .await;
 5559
 5560    editor.update_in(cx, |editor, window, cx| {
 5561        editor.change_selections(None, window, cx, |s| {
 5562            s.select_display_ranges([
 5563                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5564                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5565                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5566            ]);
 5567        });
 5568        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5569    });
 5570    editor.update(cx, |editor, cx| {
 5571        assert_text_with_selections(
 5572            editor,
 5573            indoc! {r#"
 5574                use mod1::mod2::{mod3, «mod4ˇ»};
 5575
 5576                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5577                    let var1 = "«textˇ»";
 5578                }
 5579            "#},
 5580            cx,
 5581        );
 5582    });
 5583
 5584    editor.update_in(cx, |editor, window, cx| {
 5585        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5586    });
 5587    editor.update(cx, |editor, cx| {
 5588        assert_text_with_selections(
 5589            editor,
 5590            indoc! {r#"
 5591                use mod1::mod2::«{mod3, mod4}ˇ»;
 5592
 5593                «ˇfn fn_1(param1: bool, param2: &str) {
 5594                    let var1 = "text";
 5595 5596            "#},
 5597            cx,
 5598        );
 5599    });
 5600
 5601    editor.update_in(cx, |editor, window, cx| {
 5602        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5603    });
 5604    assert_eq!(
 5605        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5606        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5607    );
 5608
 5609    // Trying to expand the selected syntax node one more time has no effect.
 5610    editor.update_in(cx, |editor, window, cx| {
 5611        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5612    });
 5613    assert_eq!(
 5614        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5615        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5616    );
 5617
 5618    editor.update_in(cx, |editor, window, cx| {
 5619        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5620    });
 5621    editor.update(cx, |editor, cx| {
 5622        assert_text_with_selections(
 5623            editor,
 5624            indoc! {r#"
 5625                use mod1::mod2::«{mod3, mod4}ˇ»;
 5626
 5627                «ˇfn fn_1(param1: bool, param2: &str) {
 5628                    let var1 = "text";
 5629 5630            "#},
 5631            cx,
 5632        );
 5633    });
 5634
 5635    editor.update_in(cx, |editor, window, cx| {
 5636        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5637    });
 5638    editor.update(cx, |editor, cx| {
 5639        assert_text_with_selections(
 5640            editor,
 5641            indoc! {r#"
 5642                use mod1::mod2::{mod3, «mod4ˇ»};
 5643
 5644                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5645                    let var1 = "«textˇ»";
 5646                }
 5647            "#},
 5648            cx,
 5649        );
 5650    });
 5651
 5652    editor.update_in(cx, |editor, window, cx| {
 5653        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5654    });
 5655    editor.update(cx, |editor, cx| {
 5656        assert_text_with_selections(
 5657            editor,
 5658            indoc! {r#"
 5659                use mod1::mod2::{mod3, mo«ˇ»d4};
 5660
 5661                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5662                    let var1 = "te«ˇ»xt";
 5663                }
 5664            "#},
 5665            cx,
 5666        );
 5667    });
 5668
 5669    // Trying to shrink the selected syntax node one more time has no effect.
 5670    editor.update_in(cx, |editor, window, cx| {
 5671        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5672    });
 5673    editor.update_in(cx, |editor, _, cx| {
 5674        assert_text_with_selections(
 5675            editor,
 5676            indoc! {r#"
 5677                use mod1::mod2::{mod3, mo«ˇ»d4};
 5678
 5679                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5680                    let var1 = "te«ˇ»xt";
 5681                }
 5682            "#},
 5683            cx,
 5684        );
 5685    });
 5686
 5687    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5688    // a fold.
 5689    editor.update_in(cx, |editor, window, cx| {
 5690        editor.fold_creases(
 5691            vec![
 5692                Crease::simple(
 5693                    Point::new(0, 21)..Point::new(0, 24),
 5694                    FoldPlaceholder::test(),
 5695                ),
 5696                Crease::simple(
 5697                    Point::new(3, 20)..Point::new(3, 22),
 5698                    FoldPlaceholder::test(),
 5699                ),
 5700            ],
 5701            true,
 5702            window,
 5703            cx,
 5704        );
 5705        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5706    });
 5707    editor.update(cx, |editor, cx| {
 5708        assert_text_with_selections(
 5709            editor,
 5710            indoc! {r#"
 5711                use mod1::mod2::«{mod3, mod4}ˇ»;
 5712
 5713                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5714                    «let var1 = "text";ˇ»
 5715                }
 5716            "#},
 5717            cx,
 5718        );
 5719    });
 5720}
 5721
 5722#[gpui::test]
 5723async fn test_fold_function_bodies(cx: &mut gpui::TestAppContext) {
 5724    init_test(cx, |_| {});
 5725
 5726    let base_text = r#"
 5727        impl A {
 5728            // this is an uncommitted comment
 5729
 5730            fn b() {
 5731                c();
 5732            }
 5733
 5734            // this is another uncommitted comment
 5735
 5736            fn d() {
 5737                // e
 5738                // f
 5739            }
 5740        }
 5741
 5742        fn g() {
 5743            // h
 5744        }
 5745    "#
 5746    .unindent();
 5747
 5748    let text = r#"
 5749        ˇimpl A {
 5750
 5751            fn b() {
 5752                c();
 5753            }
 5754
 5755            fn d() {
 5756                // e
 5757                // f
 5758            }
 5759        }
 5760
 5761        fn g() {
 5762            // h
 5763        }
 5764    "#
 5765    .unindent();
 5766
 5767    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5768    cx.set_state(&text);
 5769    cx.set_diff_base(&base_text);
 5770    cx.update_editor(|editor, window, cx| {
 5771        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5772    });
 5773
 5774    cx.assert_state_with_diff(
 5775        "
 5776        ˇimpl A {
 5777      -     // this is an uncommitted comment
 5778
 5779            fn b() {
 5780                c();
 5781            }
 5782
 5783      -     // this is another uncommitted comment
 5784      -
 5785            fn d() {
 5786                // e
 5787                // f
 5788            }
 5789        }
 5790
 5791        fn g() {
 5792            // h
 5793        }
 5794    "
 5795        .unindent(),
 5796    );
 5797
 5798    let expected_display_text = "
 5799        impl A {
 5800            // this is an uncommitted comment
 5801
 5802            fn b() {
 5803 5804            }
 5805
 5806            // this is another uncommitted comment
 5807
 5808            fn d() {
 5809 5810            }
 5811        }
 5812
 5813        fn g() {
 5814 5815        }
 5816        "
 5817    .unindent();
 5818
 5819    cx.update_editor(|editor, window, cx| {
 5820        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 5821        assert_eq!(editor.display_text(cx), expected_display_text);
 5822    });
 5823}
 5824
 5825#[gpui::test]
 5826async fn test_autoindent(cx: &mut gpui::TestAppContext) {
 5827    init_test(cx, |_| {});
 5828
 5829    let language = Arc::new(
 5830        Language::new(
 5831            LanguageConfig {
 5832                brackets: BracketPairConfig {
 5833                    pairs: vec![
 5834                        BracketPair {
 5835                            start: "{".to_string(),
 5836                            end: "}".to_string(),
 5837                            close: false,
 5838                            surround: false,
 5839                            newline: true,
 5840                        },
 5841                        BracketPair {
 5842                            start: "(".to_string(),
 5843                            end: ")".to_string(),
 5844                            close: false,
 5845                            surround: false,
 5846                            newline: true,
 5847                        },
 5848                    ],
 5849                    ..Default::default()
 5850                },
 5851                ..Default::default()
 5852            },
 5853            Some(tree_sitter_rust::LANGUAGE.into()),
 5854        )
 5855        .with_indents_query(
 5856            r#"
 5857                (_ "(" ")" @end) @indent
 5858                (_ "{" "}" @end) @indent
 5859            "#,
 5860        )
 5861        .unwrap(),
 5862    );
 5863
 5864    let text = "fn a() {}";
 5865
 5866    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5867    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5868    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5869    editor
 5870        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5871        .await;
 5872
 5873    editor.update_in(cx, |editor, window, cx| {
 5874        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 5875        editor.newline(&Newline, window, cx);
 5876        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 5877        assert_eq!(
 5878            editor.selections.ranges(cx),
 5879            &[
 5880                Point::new(1, 4)..Point::new(1, 4),
 5881                Point::new(3, 4)..Point::new(3, 4),
 5882                Point::new(5, 0)..Point::new(5, 0)
 5883            ]
 5884        );
 5885    });
 5886}
 5887
 5888#[gpui::test]
 5889async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
 5890    init_test(cx, |_| {});
 5891
 5892    {
 5893        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5894        cx.set_state(indoc! {"
 5895            impl A {
 5896
 5897                fn b() {}
 5898
 5899            «fn c() {
 5900
 5901            }ˇ»
 5902            }
 5903        "});
 5904
 5905        cx.update_editor(|editor, window, cx| {
 5906            editor.autoindent(&Default::default(), window, cx);
 5907        });
 5908
 5909        cx.assert_editor_state(indoc! {"
 5910            impl A {
 5911
 5912                fn b() {}
 5913
 5914                «fn c() {
 5915
 5916                }ˇ»
 5917            }
 5918        "});
 5919    }
 5920
 5921    {
 5922        let mut cx = EditorTestContext::new_multibuffer(
 5923            cx,
 5924            [indoc! { "
 5925                impl A {
 5926                «
 5927                // a
 5928                fn b(){}
 5929                »
 5930                «
 5931                    }
 5932                    fn c(){}
 5933                »
 5934            "}],
 5935        );
 5936
 5937        let buffer = cx.update_editor(|editor, _, cx| {
 5938            let buffer = editor.buffer().update(cx, |buffer, _| {
 5939                buffer.all_buffers().iter().next().unwrap().clone()
 5940            });
 5941            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5942            buffer
 5943        });
 5944
 5945        cx.run_until_parked();
 5946        cx.update_editor(|editor, window, cx| {
 5947            editor.select_all(&Default::default(), window, cx);
 5948            editor.autoindent(&Default::default(), window, cx)
 5949        });
 5950        cx.run_until_parked();
 5951
 5952        cx.update(|_, cx| {
 5953            pretty_assertions::assert_eq!(
 5954                buffer.read(cx).text(),
 5955                indoc! { "
 5956                    impl A {
 5957
 5958                        // a
 5959                        fn b(){}
 5960
 5961
 5962                    }
 5963                    fn c(){}
 5964
 5965                " }
 5966            )
 5967        });
 5968    }
 5969}
 5970
 5971#[gpui::test]
 5972async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
 5973    init_test(cx, |_| {});
 5974
 5975    let mut cx = EditorTestContext::new(cx).await;
 5976
 5977    let language = Arc::new(Language::new(
 5978        LanguageConfig {
 5979            brackets: BracketPairConfig {
 5980                pairs: vec![
 5981                    BracketPair {
 5982                        start: "{".to_string(),
 5983                        end: "}".to_string(),
 5984                        close: true,
 5985                        surround: true,
 5986                        newline: true,
 5987                    },
 5988                    BracketPair {
 5989                        start: "(".to_string(),
 5990                        end: ")".to_string(),
 5991                        close: true,
 5992                        surround: true,
 5993                        newline: true,
 5994                    },
 5995                    BracketPair {
 5996                        start: "/*".to_string(),
 5997                        end: " */".to_string(),
 5998                        close: true,
 5999                        surround: true,
 6000                        newline: true,
 6001                    },
 6002                    BracketPair {
 6003                        start: "[".to_string(),
 6004                        end: "]".to_string(),
 6005                        close: false,
 6006                        surround: false,
 6007                        newline: true,
 6008                    },
 6009                    BracketPair {
 6010                        start: "\"".to_string(),
 6011                        end: "\"".to_string(),
 6012                        close: true,
 6013                        surround: true,
 6014                        newline: false,
 6015                    },
 6016                    BracketPair {
 6017                        start: "<".to_string(),
 6018                        end: ">".to_string(),
 6019                        close: false,
 6020                        surround: true,
 6021                        newline: true,
 6022                    },
 6023                ],
 6024                ..Default::default()
 6025            },
 6026            autoclose_before: "})]".to_string(),
 6027            ..Default::default()
 6028        },
 6029        Some(tree_sitter_rust::LANGUAGE.into()),
 6030    ));
 6031
 6032    cx.language_registry().add(language.clone());
 6033    cx.update_buffer(|buffer, cx| {
 6034        buffer.set_language(Some(language), cx);
 6035    });
 6036
 6037    cx.set_state(
 6038        &r#"
 6039            🏀ˇ
 6040            εˇ
 6041            ❤️ˇ
 6042        "#
 6043        .unindent(),
 6044    );
 6045
 6046    // autoclose multiple nested brackets at multiple cursors
 6047    cx.update_editor(|editor, window, cx| {
 6048        editor.handle_input("{", window, cx);
 6049        editor.handle_input("{", window, cx);
 6050        editor.handle_input("{", window, cx);
 6051    });
 6052    cx.assert_editor_state(
 6053        &"
 6054            🏀{{{ˇ}}}
 6055            ε{{{ˇ}}}
 6056            ❤️{{{ˇ}}}
 6057        "
 6058        .unindent(),
 6059    );
 6060
 6061    // insert a different closing bracket
 6062    cx.update_editor(|editor, window, cx| {
 6063        editor.handle_input(")", window, cx);
 6064    });
 6065    cx.assert_editor_state(
 6066        &"
 6067            🏀{{{)ˇ}}}
 6068            ε{{{)ˇ}}}
 6069            ❤️{{{)ˇ}}}
 6070        "
 6071        .unindent(),
 6072    );
 6073
 6074    // skip over the auto-closed brackets when typing a closing bracket
 6075    cx.update_editor(|editor, window, cx| {
 6076        editor.move_right(&MoveRight, window, cx);
 6077        editor.handle_input("}", window, cx);
 6078        editor.handle_input("}", window, cx);
 6079        editor.handle_input("}", window, cx);
 6080    });
 6081    cx.assert_editor_state(
 6082        &"
 6083            🏀{{{)}}}}ˇ
 6084            ε{{{)}}}}ˇ
 6085            ❤️{{{)}}}}ˇ
 6086        "
 6087        .unindent(),
 6088    );
 6089
 6090    // autoclose multi-character pairs
 6091    cx.set_state(
 6092        &"
 6093            ˇ
 6094            ˇ
 6095        "
 6096        .unindent(),
 6097    );
 6098    cx.update_editor(|editor, window, cx| {
 6099        editor.handle_input("/", window, cx);
 6100        editor.handle_input("*", window, cx);
 6101    });
 6102    cx.assert_editor_state(
 6103        &"
 6104            /*ˇ */
 6105            /*ˇ */
 6106        "
 6107        .unindent(),
 6108    );
 6109
 6110    // one cursor autocloses a multi-character pair, one cursor
 6111    // does not autoclose.
 6112    cx.set_state(
 6113        &"
 6114 6115            ˇ
 6116        "
 6117        .unindent(),
 6118    );
 6119    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6120    cx.assert_editor_state(
 6121        &"
 6122            /*ˇ */
 6123 6124        "
 6125        .unindent(),
 6126    );
 6127
 6128    // Don't autoclose if the next character isn't whitespace and isn't
 6129    // listed in the language's "autoclose_before" section.
 6130    cx.set_state("ˇa b");
 6131    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6132    cx.assert_editor_state("{ˇa b");
 6133
 6134    // Don't autoclose if `close` is false for the bracket pair
 6135    cx.set_state("ˇ");
 6136    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6137    cx.assert_editor_state("");
 6138
 6139    // Surround with brackets if text is selected
 6140    cx.set_state("«aˇ» b");
 6141    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6142    cx.assert_editor_state("{«aˇ»} b");
 6143
 6144    // Autclose pair where the start and end characters are the same
 6145    cx.set_state("");
 6146    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6147    cx.assert_editor_state("a\"ˇ\"");
 6148    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6149    cx.assert_editor_state("a\"\"ˇ");
 6150
 6151    // Don't autoclose pair if autoclose is disabled
 6152    cx.set_state("ˇ");
 6153    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6154    cx.assert_editor_state("");
 6155
 6156    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6157    cx.set_state("«aˇ» b");
 6158    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6159    cx.assert_editor_state("<«aˇ»> b");
 6160}
 6161
 6162#[gpui::test]
 6163async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestAppContext) {
 6164    init_test(cx, |settings| {
 6165        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6166    });
 6167
 6168    let mut cx = EditorTestContext::new(cx).await;
 6169
 6170    let language = Arc::new(Language::new(
 6171        LanguageConfig {
 6172            brackets: BracketPairConfig {
 6173                pairs: vec![
 6174                    BracketPair {
 6175                        start: "{".to_string(),
 6176                        end: "}".to_string(),
 6177                        close: true,
 6178                        surround: true,
 6179                        newline: true,
 6180                    },
 6181                    BracketPair {
 6182                        start: "(".to_string(),
 6183                        end: ")".to_string(),
 6184                        close: true,
 6185                        surround: true,
 6186                        newline: true,
 6187                    },
 6188                    BracketPair {
 6189                        start: "[".to_string(),
 6190                        end: "]".to_string(),
 6191                        close: false,
 6192                        surround: false,
 6193                        newline: true,
 6194                    },
 6195                ],
 6196                ..Default::default()
 6197            },
 6198            autoclose_before: "})]".to_string(),
 6199            ..Default::default()
 6200        },
 6201        Some(tree_sitter_rust::LANGUAGE.into()),
 6202    ));
 6203
 6204    cx.language_registry().add(language.clone());
 6205    cx.update_buffer(|buffer, cx| {
 6206        buffer.set_language(Some(language), cx);
 6207    });
 6208
 6209    cx.set_state(
 6210        &"
 6211            ˇ
 6212            ˇ
 6213            ˇ
 6214        "
 6215        .unindent(),
 6216    );
 6217
 6218    // ensure only matching closing brackets are skipped over
 6219    cx.update_editor(|editor, window, cx| {
 6220        editor.handle_input("}", window, cx);
 6221        editor.move_left(&MoveLeft, window, cx);
 6222        editor.handle_input(")", window, cx);
 6223        editor.move_left(&MoveLeft, window, cx);
 6224    });
 6225    cx.assert_editor_state(
 6226        &"
 6227            ˇ)}
 6228            ˇ)}
 6229            ˇ)}
 6230        "
 6231        .unindent(),
 6232    );
 6233
 6234    // skip-over closing brackets at multiple cursors
 6235    cx.update_editor(|editor, window, cx| {
 6236        editor.handle_input(")", window, cx);
 6237        editor.handle_input("}", window, cx);
 6238    });
 6239    cx.assert_editor_state(
 6240        &"
 6241            )}ˇ
 6242            )}ˇ
 6243            )}ˇ
 6244        "
 6245        .unindent(),
 6246    );
 6247
 6248    // ignore non-close brackets
 6249    cx.update_editor(|editor, window, cx| {
 6250        editor.handle_input("]", window, cx);
 6251        editor.move_left(&MoveLeft, window, cx);
 6252        editor.handle_input("]", window, cx);
 6253    });
 6254    cx.assert_editor_state(
 6255        &"
 6256            )}]ˇ]
 6257            )}]ˇ]
 6258            )}]ˇ]
 6259        "
 6260        .unindent(),
 6261    );
 6262}
 6263
 6264#[gpui::test]
 6265async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
 6266    init_test(cx, |_| {});
 6267
 6268    let mut cx = EditorTestContext::new(cx).await;
 6269
 6270    let html_language = Arc::new(
 6271        Language::new(
 6272            LanguageConfig {
 6273                name: "HTML".into(),
 6274                brackets: BracketPairConfig {
 6275                    pairs: vec![
 6276                        BracketPair {
 6277                            start: "<".into(),
 6278                            end: ">".into(),
 6279                            close: true,
 6280                            ..Default::default()
 6281                        },
 6282                        BracketPair {
 6283                            start: "{".into(),
 6284                            end: "}".into(),
 6285                            close: true,
 6286                            ..Default::default()
 6287                        },
 6288                        BracketPair {
 6289                            start: "(".into(),
 6290                            end: ")".into(),
 6291                            close: true,
 6292                            ..Default::default()
 6293                        },
 6294                    ],
 6295                    ..Default::default()
 6296                },
 6297                autoclose_before: "})]>".into(),
 6298                ..Default::default()
 6299            },
 6300            Some(tree_sitter_html::LANGUAGE.into()),
 6301        )
 6302        .with_injection_query(
 6303            r#"
 6304            (script_element
 6305                (raw_text) @injection.content
 6306                (#set! injection.language "javascript"))
 6307            "#,
 6308        )
 6309        .unwrap(),
 6310    );
 6311
 6312    let javascript_language = Arc::new(Language::new(
 6313        LanguageConfig {
 6314            name: "JavaScript".into(),
 6315            brackets: BracketPairConfig {
 6316                pairs: vec![
 6317                    BracketPair {
 6318                        start: "/*".into(),
 6319                        end: " */".into(),
 6320                        close: true,
 6321                        ..Default::default()
 6322                    },
 6323                    BracketPair {
 6324                        start: "{".into(),
 6325                        end: "}".into(),
 6326                        close: true,
 6327                        ..Default::default()
 6328                    },
 6329                    BracketPair {
 6330                        start: "(".into(),
 6331                        end: ")".into(),
 6332                        close: true,
 6333                        ..Default::default()
 6334                    },
 6335                ],
 6336                ..Default::default()
 6337            },
 6338            autoclose_before: "})]>".into(),
 6339            ..Default::default()
 6340        },
 6341        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6342    ));
 6343
 6344    cx.language_registry().add(html_language.clone());
 6345    cx.language_registry().add(javascript_language.clone());
 6346
 6347    cx.update_buffer(|buffer, cx| {
 6348        buffer.set_language(Some(html_language), cx);
 6349    });
 6350
 6351    cx.set_state(
 6352        &r#"
 6353            <body>ˇ
 6354                <script>
 6355                    var x = 1;ˇ
 6356                </script>
 6357            </body>ˇ
 6358        "#
 6359        .unindent(),
 6360    );
 6361
 6362    // Precondition: different languages are active at different locations.
 6363    cx.update_editor(|editor, window, cx| {
 6364        let snapshot = editor.snapshot(window, cx);
 6365        let cursors = editor.selections.ranges::<usize>(cx);
 6366        let languages = cursors
 6367            .iter()
 6368            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6369            .collect::<Vec<_>>();
 6370        assert_eq!(
 6371            languages,
 6372            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6373        );
 6374    });
 6375
 6376    // Angle brackets autoclose in HTML, but not JavaScript.
 6377    cx.update_editor(|editor, window, cx| {
 6378        editor.handle_input("<", window, cx);
 6379        editor.handle_input("a", window, cx);
 6380    });
 6381    cx.assert_editor_state(
 6382        &r#"
 6383            <body><aˇ>
 6384                <script>
 6385                    var x = 1;<aˇ
 6386                </script>
 6387            </body><aˇ>
 6388        "#
 6389        .unindent(),
 6390    );
 6391
 6392    // Curly braces and parens autoclose in both HTML and JavaScript.
 6393    cx.update_editor(|editor, window, cx| {
 6394        editor.handle_input(" b=", window, cx);
 6395        editor.handle_input("{", window, cx);
 6396        editor.handle_input("c", window, cx);
 6397        editor.handle_input("(", window, cx);
 6398    });
 6399    cx.assert_editor_state(
 6400        &r#"
 6401            <body><a b={c(ˇ)}>
 6402                <script>
 6403                    var x = 1;<a b={c(ˇ)}
 6404                </script>
 6405            </body><a b={c(ˇ)}>
 6406        "#
 6407        .unindent(),
 6408    );
 6409
 6410    // Brackets that were already autoclosed are skipped.
 6411    cx.update_editor(|editor, window, cx| {
 6412        editor.handle_input(")", window, cx);
 6413        editor.handle_input("d", window, cx);
 6414        editor.handle_input("}", window, cx);
 6415    });
 6416    cx.assert_editor_state(
 6417        &r#"
 6418            <body><a b={c()d}ˇ>
 6419                <script>
 6420                    var x = 1;<a b={c()d}ˇ
 6421                </script>
 6422            </body><a b={c()d}ˇ>
 6423        "#
 6424        .unindent(),
 6425    );
 6426    cx.update_editor(|editor, window, cx| {
 6427        editor.handle_input(">", window, cx);
 6428    });
 6429    cx.assert_editor_state(
 6430        &r#"
 6431            <body><a b={c()d}>ˇ
 6432                <script>
 6433                    var x = 1;<a b={c()d}>ˇ
 6434                </script>
 6435            </body><a b={c()d}>ˇ
 6436        "#
 6437        .unindent(),
 6438    );
 6439
 6440    // Reset
 6441    cx.set_state(
 6442        &r#"
 6443            <body>ˇ
 6444                <script>
 6445                    var x = 1;ˇ
 6446                </script>
 6447            </body>ˇ
 6448        "#
 6449        .unindent(),
 6450    );
 6451
 6452    cx.update_editor(|editor, window, cx| {
 6453        editor.handle_input("<", window, cx);
 6454    });
 6455    cx.assert_editor_state(
 6456        &r#"
 6457            <body><ˇ>
 6458                <script>
 6459                    var x = 1;<ˇ
 6460                </script>
 6461            </body><ˇ>
 6462        "#
 6463        .unindent(),
 6464    );
 6465
 6466    // When backspacing, the closing angle brackets are removed.
 6467    cx.update_editor(|editor, window, cx| {
 6468        editor.backspace(&Backspace, window, cx);
 6469    });
 6470    cx.assert_editor_state(
 6471        &r#"
 6472            <body>ˇ
 6473                <script>
 6474                    var x = 1;ˇ
 6475                </script>
 6476            </body>ˇ
 6477        "#
 6478        .unindent(),
 6479    );
 6480
 6481    // Block comments autoclose in JavaScript, but not HTML.
 6482    cx.update_editor(|editor, window, cx| {
 6483        editor.handle_input("/", window, cx);
 6484        editor.handle_input("*", window, cx);
 6485    });
 6486    cx.assert_editor_state(
 6487        &r#"
 6488            <body>/*ˇ
 6489                <script>
 6490                    var x = 1;/*ˇ */
 6491                </script>
 6492            </body>/*ˇ
 6493        "#
 6494        .unindent(),
 6495    );
 6496}
 6497
 6498#[gpui::test]
 6499async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
 6500    init_test(cx, |_| {});
 6501
 6502    let mut cx = EditorTestContext::new(cx).await;
 6503
 6504    let rust_language = Arc::new(
 6505        Language::new(
 6506            LanguageConfig {
 6507                name: "Rust".into(),
 6508                brackets: serde_json::from_value(json!([
 6509                    { "start": "{", "end": "}", "close": true, "newline": true },
 6510                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6511                ]))
 6512                .unwrap(),
 6513                autoclose_before: "})]>".into(),
 6514                ..Default::default()
 6515            },
 6516            Some(tree_sitter_rust::LANGUAGE.into()),
 6517        )
 6518        .with_override_query("(string_literal) @string")
 6519        .unwrap(),
 6520    );
 6521
 6522    cx.language_registry().add(rust_language.clone());
 6523    cx.update_buffer(|buffer, cx| {
 6524        buffer.set_language(Some(rust_language), cx);
 6525    });
 6526
 6527    cx.set_state(
 6528        &r#"
 6529            let x = ˇ
 6530        "#
 6531        .unindent(),
 6532    );
 6533
 6534    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6535    cx.update_editor(|editor, window, cx| {
 6536        editor.handle_input("\"", window, cx);
 6537    });
 6538    cx.assert_editor_state(
 6539        &r#"
 6540            let x = "ˇ"
 6541        "#
 6542        .unindent(),
 6543    );
 6544
 6545    // Inserting another quotation mark. The cursor moves across the existing
 6546    // automatically-inserted quotation mark.
 6547    cx.update_editor(|editor, window, cx| {
 6548        editor.handle_input("\"", window, cx);
 6549    });
 6550    cx.assert_editor_state(
 6551        &r#"
 6552            let x = ""ˇ
 6553        "#
 6554        .unindent(),
 6555    );
 6556
 6557    // Reset
 6558    cx.set_state(
 6559        &r#"
 6560            let x = ˇ
 6561        "#
 6562        .unindent(),
 6563    );
 6564
 6565    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6566    cx.update_editor(|editor, window, cx| {
 6567        editor.handle_input("\"", window, cx);
 6568        editor.handle_input(" ", window, cx);
 6569        editor.move_left(&Default::default(), window, cx);
 6570        editor.handle_input("\\", window, cx);
 6571        editor.handle_input("\"", window, cx);
 6572    });
 6573    cx.assert_editor_state(
 6574        &r#"
 6575            let x = "\"ˇ "
 6576        "#
 6577        .unindent(),
 6578    );
 6579
 6580    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6581    // mark. Nothing is inserted.
 6582    cx.update_editor(|editor, window, cx| {
 6583        editor.move_right(&Default::default(), window, cx);
 6584        editor.handle_input("\"", window, cx);
 6585    });
 6586    cx.assert_editor_state(
 6587        &r#"
 6588            let x = "\" "ˇ
 6589        "#
 6590        .unindent(),
 6591    );
 6592}
 6593
 6594#[gpui::test]
 6595async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
 6596    init_test(cx, |_| {});
 6597
 6598    let language = Arc::new(Language::new(
 6599        LanguageConfig {
 6600            brackets: BracketPairConfig {
 6601                pairs: vec![
 6602                    BracketPair {
 6603                        start: "{".to_string(),
 6604                        end: "}".to_string(),
 6605                        close: true,
 6606                        surround: true,
 6607                        newline: true,
 6608                    },
 6609                    BracketPair {
 6610                        start: "/* ".to_string(),
 6611                        end: "*/".to_string(),
 6612                        close: true,
 6613                        surround: true,
 6614                        ..Default::default()
 6615                    },
 6616                ],
 6617                ..Default::default()
 6618            },
 6619            ..Default::default()
 6620        },
 6621        Some(tree_sitter_rust::LANGUAGE.into()),
 6622    ));
 6623
 6624    let text = r#"
 6625        a
 6626        b
 6627        c
 6628    "#
 6629    .unindent();
 6630
 6631    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6632    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6633    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6634    editor
 6635        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6636        .await;
 6637
 6638    editor.update_in(cx, |editor, window, cx| {
 6639        editor.change_selections(None, window, cx, |s| {
 6640            s.select_display_ranges([
 6641                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6642                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6643                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6644            ])
 6645        });
 6646
 6647        editor.handle_input("{", window, cx);
 6648        editor.handle_input("{", window, cx);
 6649        editor.handle_input("{", window, cx);
 6650        assert_eq!(
 6651            editor.text(cx),
 6652            "
 6653                {{{a}}}
 6654                {{{b}}}
 6655                {{{c}}}
 6656            "
 6657            .unindent()
 6658        );
 6659        assert_eq!(
 6660            editor.selections.display_ranges(cx),
 6661            [
 6662                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6663                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6664                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6665            ]
 6666        );
 6667
 6668        editor.undo(&Undo, window, cx);
 6669        editor.undo(&Undo, window, cx);
 6670        editor.undo(&Undo, window, cx);
 6671        assert_eq!(
 6672            editor.text(cx),
 6673            "
 6674                a
 6675                b
 6676                c
 6677            "
 6678            .unindent()
 6679        );
 6680        assert_eq!(
 6681            editor.selections.display_ranges(cx),
 6682            [
 6683                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6684                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6685                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6686            ]
 6687        );
 6688
 6689        // Ensure inserting the first character of a multi-byte bracket pair
 6690        // doesn't surround the selections with the bracket.
 6691        editor.handle_input("/", window, cx);
 6692        assert_eq!(
 6693            editor.text(cx),
 6694            "
 6695                /
 6696                /
 6697                /
 6698            "
 6699            .unindent()
 6700        );
 6701        assert_eq!(
 6702            editor.selections.display_ranges(cx),
 6703            [
 6704                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6705                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6706                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6707            ]
 6708        );
 6709
 6710        editor.undo(&Undo, window, cx);
 6711        assert_eq!(
 6712            editor.text(cx),
 6713            "
 6714                a
 6715                b
 6716                c
 6717            "
 6718            .unindent()
 6719        );
 6720        assert_eq!(
 6721            editor.selections.display_ranges(cx),
 6722            [
 6723                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6724                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6725                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6726            ]
 6727        );
 6728
 6729        // Ensure inserting the last character of a multi-byte bracket pair
 6730        // doesn't surround the selections with the bracket.
 6731        editor.handle_input("*", window, cx);
 6732        assert_eq!(
 6733            editor.text(cx),
 6734            "
 6735                *
 6736                *
 6737                *
 6738            "
 6739            .unindent()
 6740        );
 6741        assert_eq!(
 6742            editor.selections.display_ranges(cx),
 6743            [
 6744                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6745                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6746                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6747            ]
 6748        );
 6749    });
 6750}
 6751
 6752#[gpui::test]
 6753async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 6754    init_test(cx, |_| {});
 6755
 6756    let language = Arc::new(Language::new(
 6757        LanguageConfig {
 6758            brackets: BracketPairConfig {
 6759                pairs: vec![BracketPair {
 6760                    start: "{".to_string(),
 6761                    end: "}".to_string(),
 6762                    close: true,
 6763                    surround: true,
 6764                    newline: true,
 6765                }],
 6766                ..Default::default()
 6767            },
 6768            autoclose_before: "}".to_string(),
 6769            ..Default::default()
 6770        },
 6771        Some(tree_sitter_rust::LANGUAGE.into()),
 6772    ));
 6773
 6774    let text = r#"
 6775        a
 6776        b
 6777        c
 6778    "#
 6779    .unindent();
 6780
 6781    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6782    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6783    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6784    editor
 6785        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6786        .await;
 6787
 6788    editor.update_in(cx, |editor, window, cx| {
 6789        editor.change_selections(None, window, cx, |s| {
 6790            s.select_ranges([
 6791                Point::new(0, 1)..Point::new(0, 1),
 6792                Point::new(1, 1)..Point::new(1, 1),
 6793                Point::new(2, 1)..Point::new(2, 1),
 6794            ])
 6795        });
 6796
 6797        editor.handle_input("{", window, cx);
 6798        editor.handle_input("{", window, cx);
 6799        editor.handle_input("_", window, cx);
 6800        assert_eq!(
 6801            editor.text(cx),
 6802            "
 6803                a{{_}}
 6804                b{{_}}
 6805                c{{_}}
 6806            "
 6807            .unindent()
 6808        );
 6809        assert_eq!(
 6810            editor.selections.ranges::<Point>(cx),
 6811            [
 6812                Point::new(0, 4)..Point::new(0, 4),
 6813                Point::new(1, 4)..Point::new(1, 4),
 6814                Point::new(2, 4)..Point::new(2, 4)
 6815            ]
 6816        );
 6817
 6818        editor.backspace(&Default::default(), window, cx);
 6819        editor.backspace(&Default::default(), window, cx);
 6820        assert_eq!(
 6821            editor.text(cx),
 6822            "
 6823                a{}
 6824                b{}
 6825                c{}
 6826            "
 6827            .unindent()
 6828        );
 6829        assert_eq!(
 6830            editor.selections.ranges::<Point>(cx),
 6831            [
 6832                Point::new(0, 2)..Point::new(0, 2),
 6833                Point::new(1, 2)..Point::new(1, 2),
 6834                Point::new(2, 2)..Point::new(2, 2)
 6835            ]
 6836        );
 6837
 6838        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 6839        assert_eq!(
 6840            editor.text(cx),
 6841            "
 6842                a
 6843                b
 6844                c
 6845            "
 6846            .unindent()
 6847        );
 6848        assert_eq!(
 6849            editor.selections.ranges::<Point>(cx),
 6850            [
 6851                Point::new(0, 1)..Point::new(0, 1),
 6852                Point::new(1, 1)..Point::new(1, 1),
 6853                Point::new(2, 1)..Point::new(2, 1)
 6854            ]
 6855        );
 6856    });
 6857}
 6858
 6859#[gpui::test]
 6860async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppContext) {
 6861    init_test(cx, |settings| {
 6862        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6863    });
 6864
 6865    let mut cx = EditorTestContext::new(cx).await;
 6866
 6867    let language = Arc::new(Language::new(
 6868        LanguageConfig {
 6869            brackets: BracketPairConfig {
 6870                pairs: vec![
 6871                    BracketPair {
 6872                        start: "{".to_string(),
 6873                        end: "}".to_string(),
 6874                        close: true,
 6875                        surround: true,
 6876                        newline: true,
 6877                    },
 6878                    BracketPair {
 6879                        start: "(".to_string(),
 6880                        end: ")".to_string(),
 6881                        close: true,
 6882                        surround: true,
 6883                        newline: true,
 6884                    },
 6885                    BracketPair {
 6886                        start: "[".to_string(),
 6887                        end: "]".to_string(),
 6888                        close: false,
 6889                        surround: true,
 6890                        newline: true,
 6891                    },
 6892                ],
 6893                ..Default::default()
 6894            },
 6895            autoclose_before: "})]".to_string(),
 6896            ..Default::default()
 6897        },
 6898        Some(tree_sitter_rust::LANGUAGE.into()),
 6899    ));
 6900
 6901    cx.language_registry().add(language.clone());
 6902    cx.update_buffer(|buffer, cx| {
 6903        buffer.set_language(Some(language), cx);
 6904    });
 6905
 6906    cx.set_state(
 6907        &"
 6908            {(ˇ)}
 6909            [[ˇ]]
 6910            {(ˇ)}
 6911        "
 6912        .unindent(),
 6913    );
 6914
 6915    cx.update_editor(|editor, window, cx| {
 6916        editor.backspace(&Default::default(), window, cx);
 6917        editor.backspace(&Default::default(), window, cx);
 6918    });
 6919
 6920    cx.assert_editor_state(
 6921        &"
 6922            ˇ
 6923            ˇ]]
 6924            ˇ
 6925        "
 6926        .unindent(),
 6927    );
 6928
 6929    cx.update_editor(|editor, window, cx| {
 6930        editor.handle_input("{", window, cx);
 6931        editor.handle_input("{", window, cx);
 6932        editor.move_right(&MoveRight, window, cx);
 6933        editor.move_right(&MoveRight, window, cx);
 6934        editor.move_left(&MoveLeft, window, cx);
 6935        editor.move_left(&MoveLeft, window, cx);
 6936        editor.backspace(&Default::default(), window, cx);
 6937    });
 6938
 6939    cx.assert_editor_state(
 6940        &"
 6941            {ˇ}
 6942            {ˇ}]]
 6943            {ˇ}
 6944        "
 6945        .unindent(),
 6946    );
 6947
 6948    cx.update_editor(|editor, window, cx| {
 6949        editor.backspace(&Default::default(), window, cx);
 6950    });
 6951
 6952    cx.assert_editor_state(
 6953        &"
 6954            ˇ
 6955            ˇ]]
 6956            ˇ
 6957        "
 6958        .unindent(),
 6959    );
 6960}
 6961
 6962#[gpui::test]
 6963async fn test_auto_replace_emoji_shortcode(cx: &mut gpui::TestAppContext) {
 6964    init_test(cx, |_| {});
 6965
 6966    let language = Arc::new(Language::new(
 6967        LanguageConfig::default(),
 6968        Some(tree_sitter_rust::LANGUAGE.into()),
 6969    ));
 6970
 6971    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 6972    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6973    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6974    editor
 6975        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6976        .await;
 6977
 6978    editor.update_in(cx, |editor, window, cx| {
 6979        editor.set_auto_replace_emoji_shortcode(true);
 6980
 6981        editor.handle_input("Hello ", window, cx);
 6982        editor.handle_input(":wave", window, cx);
 6983        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 6984
 6985        editor.handle_input(":", window, cx);
 6986        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 6987
 6988        editor.handle_input(" :smile", window, cx);
 6989        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 6990
 6991        editor.handle_input(":", window, cx);
 6992        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 6993
 6994        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 6995        editor.handle_input(":wave", window, cx);
 6996        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 6997
 6998        editor.handle_input(":", window, cx);
 6999        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7000
 7001        editor.handle_input(":1", window, cx);
 7002        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7003
 7004        editor.handle_input(":", window, cx);
 7005        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7006
 7007        // Ensure shortcode does not get replaced when it is part of a word
 7008        editor.handle_input(" Test:wave", window, cx);
 7009        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7010
 7011        editor.handle_input(":", window, cx);
 7012        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7013
 7014        editor.set_auto_replace_emoji_shortcode(false);
 7015
 7016        // Ensure shortcode does not get replaced when auto replace is off
 7017        editor.handle_input(" :wave", window, cx);
 7018        assert_eq!(
 7019            editor.text(cx),
 7020            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7021        );
 7022
 7023        editor.handle_input(":", window, cx);
 7024        assert_eq!(
 7025            editor.text(cx),
 7026            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7027        );
 7028    });
 7029}
 7030
 7031#[gpui::test]
 7032async fn test_snippet_placeholder_choices(cx: &mut gpui::TestAppContext) {
 7033    init_test(cx, |_| {});
 7034
 7035    let (text, insertion_ranges) = marked_text_ranges(
 7036        indoc! {"
 7037            ˇ
 7038        "},
 7039        false,
 7040    );
 7041
 7042    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7043    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7044
 7045    _ = editor.update_in(cx, |editor, window, cx| {
 7046        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7047
 7048        editor
 7049            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7050            .unwrap();
 7051
 7052        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7053            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7054            assert_eq!(editor.text(cx), expected_text);
 7055            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7056        }
 7057
 7058        assert(
 7059            editor,
 7060            cx,
 7061            indoc! {"
 7062            type «» =•
 7063            "},
 7064        );
 7065
 7066        assert!(editor.context_menu_visible(), "There should be a matches");
 7067    });
 7068}
 7069
 7070#[gpui::test]
 7071async fn test_snippets(cx: &mut gpui::TestAppContext) {
 7072    init_test(cx, |_| {});
 7073
 7074    let (text, insertion_ranges) = marked_text_ranges(
 7075        indoc! {"
 7076            a.ˇ b
 7077            a.ˇ b
 7078            a.ˇ b
 7079        "},
 7080        false,
 7081    );
 7082
 7083    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7084    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7085
 7086    editor.update_in(cx, |editor, window, cx| {
 7087        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7088
 7089        editor
 7090            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7091            .unwrap();
 7092
 7093        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7094            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7095            assert_eq!(editor.text(cx), expected_text);
 7096            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7097        }
 7098
 7099        assert(
 7100            editor,
 7101            cx,
 7102            indoc! {"
 7103                a.f(«one», two, «three») b
 7104                a.f(«one», two, «three») b
 7105                a.f(«one», two, «three») b
 7106            "},
 7107        );
 7108
 7109        // Can't move earlier than the first tab stop
 7110        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7111        assert(
 7112            editor,
 7113            cx,
 7114            indoc! {"
 7115                a.f(«one», two, «three») b
 7116                a.f(«one», two, «three») b
 7117                a.f(«one», two, «three») b
 7118            "},
 7119        );
 7120
 7121        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7122        assert(
 7123            editor,
 7124            cx,
 7125            indoc! {"
 7126                a.f(one, «two», three) b
 7127                a.f(one, «two», three) b
 7128                a.f(one, «two», three) b
 7129            "},
 7130        );
 7131
 7132        editor.move_to_prev_snippet_tabstop(window, cx);
 7133        assert(
 7134            editor,
 7135            cx,
 7136            indoc! {"
 7137                a.f(«one», two, «three») b
 7138                a.f(«one», two, «three») b
 7139                a.f(«one», two, «three») b
 7140            "},
 7141        );
 7142
 7143        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7144        assert(
 7145            editor,
 7146            cx,
 7147            indoc! {"
 7148                a.f(one, «two», three) b
 7149                a.f(one, «two», three) b
 7150                a.f(one, «two», three) b
 7151            "},
 7152        );
 7153        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7154        assert(
 7155            editor,
 7156            cx,
 7157            indoc! {"
 7158                a.f(one, two, three)ˇ b
 7159                a.f(one, two, three)ˇ b
 7160                a.f(one, two, three)ˇ b
 7161            "},
 7162        );
 7163
 7164        // As soon as the last tab stop is reached, snippet state is gone
 7165        editor.move_to_prev_snippet_tabstop(window, cx);
 7166        assert(
 7167            editor,
 7168            cx,
 7169            indoc! {"
 7170                a.f(one, two, three)ˇ b
 7171                a.f(one, two, three)ˇ b
 7172                a.f(one, two, three)ˇ b
 7173            "},
 7174        );
 7175    });
 7176}
 7177
 7178#[gpui::test]
 7179async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
 7180    init_test(cx, |_| {});
 7181
 7182    let fs = FakeFs::new(cx.executor());
 7183    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7184
 7185    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7186
 7187    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7188    language_registry.add(rust_lang());
 7189    let mut fake_servers = language_registry.register_fake_lsp(
 7190        "Rust",
 7191        FakeLspAdapter {
 7192            capabilities: lsp::ServerCapabilities {
 7193                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7194                ..Default::default()
 7195            },
 7196            ..Default::default()
 7197        },
 7198    );
 7199
 7200    let buffer = project
 7201        .update(cx, |project, cx| {
 7202            project.open_local_buffer(path!("/file.rs"), cx)
 7203        })
 7204        .await
 7205        .unwrap();
 7206
 7207    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7208    let (editor, cx) = cx.add_window_view(|window, cx| {
 7209        build_editor_with_project(project.clone(), buffer, window, cx)
 7210    });
 7211    editor.update_in(cx, |editor, window, cx| {
 7212        editor.set_text("one\ntwo\nthree\n", window, cx)
 7213    });
 7214    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7215
 7216    cx.executor().start_waiting();
 7217    let fake_server = fake_servers.next().await.unwrap();
 7218
 7219    let save = editor
 7220        .update_in(cx, |editor, window, cx| {
 7221            editor.save(true, project.clone(), window, cx)
 7222        })
 7223        .unwrap();
 7224    fake_server
 7225        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7226            assert_eq!(
 7227                params.text_document.uri,
 7228                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7229            );
 7230            assert_eq!(params.options.tab_size, 4);
 7231            Ok(Some(vec![lsp::TextEdit::new(
 7232                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7233                ", ".to_string(),
 7234            )]))
 7235        })
 7236        .next()
 7237        .await;
 7238    cx.executor().start_waiting();
 7239    save.await;
 7240
 7241    assert_eq!(
 7242        editor.update(cx, |editor, cx| editor.text(cx)),
 7243        "one, two\nthree\n"
 7244    );
 7245    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7246
 7247    editor.update_in(cx, |editor, window, cx| {
 7248        editor.set_text("one\ntwo\nthree\n", window, cx)
 7249    });
 7250    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7251
 7252    // Ensure we can still save even if formatting hangs.
 7253    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7254        assert_eq!(
 7255            params.text_document.uri,
 7256            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7257        );
 7258        futures::future::pending::<()>().await;
 7259        unreachable!()
 7260    });
 7261    let save = editor
 7262        .update_in(cx, |editor, window, cx| {
 7263            editor.save(true, project.clone(), window, cx)
 7264        })
 7265        .unwrap();
 7266    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7267    cx.executor().start_waiting();
 7268    save.await;
 7269    assert_eq!(
 7270        editor.update(cx, |editor, cx| editor.text(cx)),
 7271        "one\ntwo\nthree\n"
 7272    );
 7273    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7274
 7275    // For non-dirty buffer, no formatting request should be sent
 7276    let save = editor
 7277        .update_in(cx, |editor, window, cx| {
 7278            editor.save(true, project.clone(), window, cx)
 7279        })
 7280        .unwrap();
 7281    let _pending_format_request = fake_server
 7282        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7283            panic!("Should not be invoked on non-dirty buffer");
 7284        })
 7285        .next();
 7286    cx.executor().start_waiting();
 7287    save.await;
 7288
 7289    // Set rust language override and assert overridden tabsize is sent to language server
 7290    update_test_language_settings(cx, |settings| {
 7291        settings.languages.insert(
 7292            "Rust".into(),
 7293            LanguageSettingsContent {
 7294                tab_size: NonZeroU32::new(8),
 7295                ..Default::default()
 7296            },
 7297        );
 7298    });
 7299
 7300    editor.update_in(cx, |editor, window, cx| {
 7301        editor.set_text("somehting_new\n", window, cx)
 7302    });
 7303    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7304    let save = editor
 7305        .update_in(cx, |editor, window, cx| {
 7306            editor.save(true, project.clone(), window, cx)
 7307        })
 7308        .unwrap();
 7309    fake_server
 7310        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7311            assert_eq!(
 7312                params.text_document.uri,
 7313                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7314            );
 7315            assert_eq!(params.options.tab_size, 8);
 7316            Ok(Some(vec![]))
 7317        })
 7318        .next()
 7319        .await;
 7320    cx.executor().start_waiting();
 7321    save.await;
 7322}
 7323
 7324#[gpui::test]
 7325async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) {
 7326    init_test(cx, |_| {});
 7327
 7328    let cols = 4;
 7329    let rows = 10;
 7330    let sample_text_1 = sample_text(rows, cols, 'a');
 7331    assert_eq!(
 7332        sample_text_1,
 7333        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7334    );
 7335    let sample_text_2 = sample_text(rows, cols, 'l');
 7336    assert_eq!(
 7337        sample_text_2,
 7338        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7339    );
 7340    let sample_text_3 = sample_text(rows, cols, 'v');
 7341    assert_eq!(
 7342        sample_text_3,
 7343        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7344    );
 7345
 7346    let fs = FakeFs::new(cx.executor());
 7347    fs.insert_tree(
 7348        path!("/a"),
 7349        json!({
 7350            "main.rs": sample_text_1,
 7351            "other.rs": sample_text_2,
 7352            "lib.rs": sample_text_3,
 7353        }),
 7354    )
 7355    .await;
 7356
 7357    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7358    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7359    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7360
 7361    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7362    language_registry.add(rust_lang());
 7363    let mut fake_servers = language_registry.register_fake_lsp(
 7364        "Rust",
 7365        FakeLspAdapter {
 7366            capabilities: lsp::ServerCapabilities {
 7367                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7368                ..Default::default()
 7369            },
 7370            ..Default::default()
 7371        },
 7372    );
 7373
 7374    let worktree = project.update(cx, |project, cx| {
 7375        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7376        assert_eq!(worktrees.len(), 1);
 7377        worktrees.pop().unwrap()
 7378    });
 7379    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7380
 7381    let buffer_1 = project
 7382        .update(cx, |project, cx| {
 7383            project.open_buffer((worktree_id, "main.rs"), cx)
 7384        })
 7385        .await
 7386        .unwrap();
 7387    let buffer_2 = project
 7388        .update(cx, |project, cx| {
 7389            project.open_buffer((worktree_id, "other.rs"), cx)
 7390        })
 7391        .await
 7392        .unwrap();
 7393    let buffer_3 = project
 7394        .update(cx, |project, cx| {
 7395            project.open_buffer((worktree_id, "lib.rs"), cx)
 7396        })
 7397        .await
 7398        .unwrap();
 7399
 7400    let multi_buffer = cx.new(|cx| {
 7401        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7402        multi_buffer.push_excerpts(
 7403            buffer_1.clone(),
 7404            [
 7405                ExcerptRange {
 7406                    context: Point::new(0, 0)..Point::new(3, 0),
 7407                    primary: None,
 7408                },
 7409                ExcerptRange {
 7410                    context: Point::new(5, 0)..Point::new(7, 0),
 7411                    primary: None,
 7412                },
 7413                ExcerptRange {
 7414                    context: Point::new(9, 0)..Point::new(10, 4),
 7415                    primary: None,
 7416                },
 7417            ],
 7418            cx,
 7419        );
 7420        multi_buffer.push_excerpts(
 7421            buffer_2.clone(),
 7422            [
 7423                ExcerptRange {
 7424                    context: Point::new(0, 0)..Point::new(3, 0),
 7425                    primary: None,
 7426                },
 7427                ExcerptRange {
 7428                    context: Point::new(5, 0)..Point::new(7, 0),
 7429                    primary: None,
 7430                },
 7431                ExcerptRange {
 7432                    context: Point::new(9, 0)..Point::new(10, 4),
 7433                    primary: None,
 7434                },
 7435            ],
 7436            cx,
 7437        );
 7438        multi_buffer.push_excerpts(
 7439            buffer_3.clone(),
 7440            [
 7441                ExcerptRange {
 7442                    context: Point::new(0, 0)..Point::new(3, 0),
 7443                    primary: None,
 7444                },
 7445                ExcerptRange {
 7446                    context: Point::new(5, 0)..Point::new(7, 0),
 7447                    primary: None,
 7448                },
 7449                ExcerptRange {
 7450                    context: Point::new(9, 0)..Point::new(10, 4),
 7451                    primary: None,
 7452                },
 7453            ],
 7454            cx,
 7455        );
 7456        multi_buffer
 7457    });
 7458    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7459        Editor::new(
 7460            EditorMode::Full,
 7461            multi_buffer,
 7462            Some(project.clone()),
 7463            true,
 7464            window,
 7465            cx,
 7466        )
 7467    });
 7468
 7469    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7470        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7471            s.select_ranges(Some(1..2))
 7472        });
 7473        editor.insert("|one|two|three|", window, cx);
 7474    });
 7475    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7476    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7477        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7478            s.select_ranges(Some(60..70))
 7479        });
 7480        editor.insert("|four|five|six|", window, cx);
 7481    });
 7482    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7483
 7484    // First two buffers should be edited, but not the third one.
 7485    assert_eq!(
 7486        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7487        "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}",
 7488    );
 7489    buffer_1.update(cx, |buffer, _| {
 7490        assert!(buffer.is_dirty());
 7491        assert_eq!(
 7492            buffer.text(),
 7493            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7494        )
 7495    });
 7496    buffer_2.update(cx, |buffer, _| {
 7497        assert!(buffer.is_dirty());
 7498        assert_eq!(
 7499            buffer.text(),
 7500            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7501        )
 7502    });
 7503    buffer_3.update(cx, |buffer, _| {
 7504        assert!(!buffer.is_dirty());
 7505        assert_eq!(buffer.text(), sample_text_3,)
 7506    });
 7507    cx.executor().run_until_parked();
 7508
 7509    cx.executor().start_waiting();
 7510    let save = multi_buffer_editor
 7511        .update_in(cx, |editor, window, cx| {
 7512            editor.save(true, project.clone(), window, cx)
 7513        })
 7514        .unwrap();
 7515
 7516    let fake_server = fake_servers.next().await.unwrap();
 7517    fake_server
 7518        .server
 7519        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7520            Ok(Some(vec![lsp::TextEdit::new(
 7521                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7522                format!("[{} formatted]", params.text_document.uri),
 7523            )]))
 7524        })
 7525        .detach();
 7526    save.await;
 7527
 7528    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7529    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7530    assert_eq!(
 7531        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7532        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}"),
 7533    );
 7534    buffer_1.update(cx, |buffer, _| {
 7535        assert!(!buffer.is_dirty());
 7536        assert_eq!(
 7537            buffer.text(),
 7538            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7539        )
 7540    });
 7541    buffer_2.update(cx, |buffer, _| {
 7542        assert!(!buffer.is_dirty());
 7543        assert_eq!(
 7544            buffer.text(),
 7545            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7546        )
 7547    });
 7548    buffer_3.update(cx, |buffer, _| {
 7549        assert!(!buffer.is_dirty());
 7550        assert_eq!(buffer.text(), sample_text_3,)
 7551    });
 7552}
 7553
 7554#[gpui::test]
 7555async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
 7556    init_test(cx, |_| {});
 7557
 7558    let fs = FakeFs::new(cx.executor());
 7559    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7560
 7561    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7562
 7563    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7564    language_registry.add(rust_lang());
 7565    let mut fake_servers = language_registry.register_fake_lsp(
 7566        "Rust",
 7567        FakeLspAdapter {
 7568            capabilities: lsp::ServerCapabilities {
 7569                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7570                ..Default::default()
 7571            },
 7572            ..Default::default()
 7573        },
 7574    );
 7575
 7576    let buffer = project
 7577        .update(cx, |project, cx| {
 7578            project.open_local_buffer(path!("/file.rs"), cx)
 7579        })
 7580        .await
 7581        .unwrap();
 7582
 7583    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7584    let (editor, cx) = cx.add_window_view(|window, cx| {
 7585        build_editor_with_project(project.clone(), buffer, window, cx)
 7586    });
 7587    editor.update_in(cx, |editor, window, cx| {
 7588        editor.set_text("one\ntwo\nthree\n", window, cx)
 7589    });
 7590    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7591
 7592    cx.executor().start_waiting();
 7593    let fake_server = fake_servers.next().await.unwrap();
 7594
 7595    let save = editor
 7596        .update_in(cx, |editor, window, cx| {
 7597            editor.save(true, project.clone(), window, cx)
 7598        })
 7599        .unwrap();
 7600    fake_server
 7601        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7602            assert_eq!(
 7603                params.text_document.uri,
 7604                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7605            );
 7606            assert_eq!(params.options.tab_size, 4);
 7607            Ok(Some(vec![lsp::TextEdit::new(
 7608                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7609                ", ".to_string(),
 7610            )]))
 7611        })
 7612        .next()
 7613        .await;
 7614    cx.executor().start_waiting();
 7615    save.await;
 7616    assert_eq!(
 7617        editor.update(cx, |editor, cx| editor.text(cx)),
 7618        "one, two\nthree\n"
 7619    );
 7620    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7621
 7622    editor.update_in(cx, |editor, window, cx| {
 7623        editor.set_text("one\ntwo\nthree\n", window, cx)
 7624    });
 7625    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7626
 7627    // Ensure we can still save even if formatting hangs.
 7628    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7629        move |params, _| async move {
 7630            assert_eq!(
 7631                params.text_document.uri,
 7632                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7633            );
 7634            futures::future::pending::<()>().await;
 7635            unreachable!()
 7636        },
 7637    );
 7638    let save = editor
 7639        .update_in(cx, |editor, window, cx| {
 7640            editor.save(true, project.clone(), window, cx)
 7641        })
 7642        .unwrap();
 7643    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7644    cx.executor().start_waiting();
 7645    save.await;
 7646    assert_eq!(
 7647        editor.update(cx, |editor, cx| editor.text(cx)),
 7648        "one\ntwo\nthree\n"
 7649    );
 7650    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7651
 7652    // For non-dirty buffer, no formatting request should be sent
 7653    let save = editor
 7654        .update_in(cx, |editor, window, cx| {
 7655            editor.save(true, project.clone(), window, cx)
 7656        })
 7657        .unwrap();
 7658    let _pending_format_request = fake_server
 7659        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7660            panic!("Should not be invoked on non-dirty buffer");
 7661        })
 7662        .next();
 7663    cx.executor().start_waiting();
 7664    save.await;
 7665
 7666    // Set Rust language override and assert overridden tabsize is sent to language server
 7667    update_test_language_settings(cx, |settings| {
 7668        settings.languages.insert(
 7669            "Rust".into(),
 7670            LanguageSettingsContent {
 7671                tab_size: NonZeroU32::new(8),
 7672                ..Default::default()
 7673            },
 7674        );
 7675    });
 7676
 7677    editor.update_in(cx, |editor, window, cx| {
 7678        editor.set_text("somehting_new\n", window, cx)
 7679    });
 7680    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7681    let save = editor
 7682        .update_in(cx, |editor, window, cx| {
 7683            editor.save(true, project.clone(), window, cx)
 7684        })
 7685        .unwrap();
 7686    fake_server
 7687        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7688            assert_eq!(
 7689                params.text_document.uri,
 7690                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7691            );
 7692            assert_eq!(params.options.tab_size, 8);
 7693            Ok(Some(vec![]))
 7694        })
 7695        .next()
 7696        .await;
 7697    cx.executor().start_waiting();
 7698    save.await;
 7699}
 7700
 7701#[gpui::test]
 7702async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
 7703    init_test(cx, |settings| {
 7704        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7705            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7706        ))
 7707    });
 7708
 7709    let fs = FakeFs::new(cx.executor());
 7710    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7711
 7712    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7713
 7714    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7715    language_registry.add(Arc::new(Language::new(
 7716        LanguageConfig {
 7717            name: "Rust".into(),
 7718            matcher: LanguageMatcher {
 7719                path_suffixes: vec!["rs".to_string()],
 7720                ..Default::default()
 7721            },
 7722            ..LanguageConfig::default()
 7723        },
 7724        Some(tree_sitter_rust::LANGUAGE.into()),
 7725    )));
 7726    update_test_language_settings(cx, |settings| {
 7727        // Enable Prettier formatting for the same buffer, and ensure
 7728        // LSP is called instead of Prettier.
 7729        settings.defaults.prettier = Some(PrettierSettings {
 7730            allowed: true,
 7731            ..PrettierSettings::default()
 7732        });
 7733    });
 7734    let mut fake_servers = language_registry.register_fake_lsp(
 7735        "Rust",
 7736        FakeLspAdapter {
 7737            capabilities: lsp::ServerCapabilities {
 7738                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7739                ..Default::default()
 7740            },
 7741            ..Default::default()
 7742        },
 7743    );
 7744
 7745    let buffer = project
 7746        .update(cx, |project, cx| {
 7747            project.open_local_buffer(path!("/file.rs"), cx)
 7748        })
 7749        .await
 7750        .unwrap();
 7751
 7752    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7753    let (editor, cx) = cx.add_window_view(|window, cx| {
 7754        build_editor_with_project(project.clone(), buffer, window, cx)
 7755    });
 7756    editor.update_in(cx, |editor, window, cx| {
 7757        editor.set_text("one\ntwo\nthree\n", window, cx)
 7758    });
 7759
 7760    cx.executor().start_waiting();
 7761    let fake_server = fake_servers.next().await.unwrap();
 7762
 7763    let format = editor
 7764        .update_in(cx, |editor, window, cx| {
 7765            editor.perform_format(
 7766                project.clone(),
 7767                FormatTrigger::Manual,
 7768                FormatTarget::Buffers,
 7769                window,
 7770                cx,
 7771            )
 7772        })
 7773        .unwrap();
 7774    fake_server
 7775        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7776            assert_eq!(
 7777                params.text_document.uri,
 7778                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7779            );
 7780            assert_eq!(params.options.tab_size, 4);
 7781            Ok(Some(vec![lsp::TextEdit::new(
 7782                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7783                ", ".to_string(),
 7784            )]))
 7785        })
 7786        .next()
 7787        .await;
 7788    cx.executor().start_waiting();
 7789    format.await;
 7790    assert_eq!(
 7791        editor.update(cx, |editor, cx| editor.text(cx)),
 7792        "one, two\nthree\n"
 7793    );
 7794
 7795    editor.update_in(cx, |editor, window, cx| {
 7796        editor.set_text("one\ntwo\nthree\n", window, cx)
 7797    });
 7798    // Ensure we don't lock if formatting hangs.
 7799    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7800        assert_eq!(
 7801            params.text_document.uri,
 7802            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7803        );
 7804        futures::future::pending::<()>().await;
 7805        unreachable!()
 7806    });
 7807    let format = editor
 7808        .update_in(cx, |editor, window, cx| {
 7809            editor.perform_format(
 7810                project,
 7811                FormatTrigger::Manual,
 7812                FormatTarget::Buffers,
 7813                window,
 7814                cx,
 7815            )
 7816        })
 7817        .unwrap();
 7818    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7819    cx.executor().start_waiting();
 7820    format.await;
 7821    assert_eq!(
 7822        editor.update(cx, |editor, cx| editor.text(cx)),
 7823        "one\ntwo\nthree\n"
 7824    );
 7825}
 7826
 7827#[gpui::test]
 7828async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
 7829    init_test(cx, |_| {});
 7830
 7831    let mut cx = EditorLspTestContext::new_rust(
 7832        lsp::ServerCapabilities {
 7833            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7834            ..Default::default()
 7835        },
 7836        cx,
 7837    )
 7838    .await;
 7839
 7840    cx.set_state(indoc! {"
 7841        one.twoˇ
 7842    "});
 7843
 7844    // The format request takes a long time. When it completes, it inserts
 7845    // a newline and an indent before the `.`
 7846    cx.lsp
 7847        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 7848            let executor = cx.background_executor().clone();
 7849            async move {
 7850                executor.timer(Duration::from_millis(100)).await;
 7851                Ok(Some(vec![lsp::TextEdit {
 7852                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 7853                    new_text: "\n    ".into(),
 7854                }]))
 7855            }
 7856        });
 7857
 7858    // Submit a format request.
 7859    let format_1 = cx
 7860        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7861        .unwrap();
 7862    cx.executor().run_until_parked();
 7863
 7864    // Submit a second format request.
 7865    let format_2 = cx
 7866        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7867        .unwrap();
 7868    cx.executor().run_until_parked();
 7869
 7870    // Wait for both format requests to complete
 7871    cx.executor().advance_clock(Duration::from_millis(200));
 7872    cx.executor().start_waiting();
 7873    format_1.await.unwrap();
 7874    cx.executor().start_waiting();
 7875    format_2.await.unwrap();
 7876
 7877    // The formatting edits only happens once.
 7878    cx.assert_editor_state(indoc! {"
 7879        one
 7880            .twoˇ
 7881    "});
 7882}
 7883
 7884#[gpui::test]
 7885async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
 7886    init_test(cx, |settings| {
 7887        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 7888    });
 7889
 7890    let mut cx = EditorLspTestContext::new_rust(
 7891        lsp::ServerCapabilities {
 7892            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7893            ..Default::default()
 7894        },
 7895        cx,
 7896    )
 7897    .await;
 7898
 7899    // Set up a buffer white some trailing whitespace and no trailing newline.
 7900    cx.set_state(
 7901        &[
 7902            "one ",   //
 7903            "twoˇ",   //
 7904            "three ", //
 7905            "four",   //
 7906        ]
 7907        .join("\n"),
 7908    );
 7909
 7910    // Submit a format request.
 7911    let format = cx
 7912        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 7913        .unwrap();
 7914
 7915    // Record which buffer changes have been sent to the language server
 7916    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 7917    cx.lsp
 7918        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 7919            let buffer_changes = buffer_changes.clone();
 7920            move |params, _| {
 7921                buffer_changes.lock().extend(
 7922                    params
 7923                        .content_changes
 7924                        .into_iter()
 7925                        .map(|e| (e.range.unwrap(), e.text)),
 7926                );
 7927            }
 7928        });
 7929
 7930    // Handle formatting requests to the language server.
 7931    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 7932        let buffer_changes = buffer_changes.clone();
 7933        move |_, _| {
 7934            // When formatting is requested, trailing whitespace has already been stripped,
 7935            // and the trailing newline has already been added.
 7936            assert_eq!(
 7937                &buffer_changes.lock()[1..],
 7938                &[
 7939                    (
 7940                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 7941                        "".into()
 7942                    ),
 7943                    (
 7944                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 7945                        "".into()
 7946                    ),
 7947                    (
 7948                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 7949                        "\n".into()
 7950                    ),
 7951                ]
 7952            );
 7953
 7954            // Insert blank lines between each line of the buffer.
 7955            async move {
 7956                Ok(Some(vec![
 7957                    lsp::TextEdit {
 7958                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 7959                        new_text: "\n".into(),
 7960                    },
 7961                    lsp::TextEdit {
 7962                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 7963                        new_text: "\n".into(),
 7964                    },
 7965                ]))
 7966            }
 7967        }
 7968    });
 7969
 7970    // After formatting the buffer, the trailing whitespace is stripped,
 7971    // a newline is appended, and the edits provided by the language server
 7972    // have been applied.
 7973    format.await.unwrap();
 7974    cx.assert_editor_state(
 7975        &[
 7976            "one",   //
 7977            "",      //
 7978            "twoˇ",  //
 7979            "",      //
 7980            "three", //
 7981            "four",  //
 7982            "",      //
 7983        ]
 7984        .join("\n"),
 7985    );
 7986
 7987    // Undoing the formatting undoes the trailing whitespace removal, the
 7988    // trailing newline, and the LSP edits.
 7989    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 7990    cx.assert_editor_state(
 7991        &[
 7992            "one ",   //
 7993            "twoˇ",   //
 7994            "three ", //
 7995            "four",   //
 7996        ]
 7997        .join("\n"),
 7998    );
 7999}
 8000
 8001#[gpui::test]
 8002async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8003    cx: &mut gpui::TestAppContext,
 8004) {
 8005    init_test(cx, |_| {});
 8006
 8007    cx.update(|cx| {
 8008        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8009            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8010                settings.auto_signature_help = Some(true);
 8011            });
 8012        });
 8013    });
 8014
 8015    let mut cx = EditorLspTestContext::new_rust(
 8016        lsp::ServerCapabilities {
 8017            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8018                ..Default::default()
 8019            }),
 8020            ..Default::default()
 8021        },
 8022        cx,
 8023    )
 8024    .await;
 8025
 8026    let language = Language::new(
 8027        LanguageConfig {
 8028            name: "Rust".into(),
 8029            brackets: BracketPairConfig {
 8030                pairs: vec![
 8031                    BracketPair {
 8032                        start: "{".to_string(),
 8033                        end: "}".to_string(),
 8034                        close: true,
 8035                        surround: true,
 8036                        newline: true,
 8037                    },
 8038                    BracketPair {
 8039                        start: "(".to_string(),
 8040                        end: ")".to_string(),
 8041                        close: true,
 8042                        surround: true,
 8043                        newline: true,
 8044                    },
 8045                    BracketPair {
 8046                        start: "/*".to_string(),
 8047                        end: " */".to_string(),
 8048                        close: true,
 8049                        surround: true,
 8050                        newline: true,
 8051                    },
 8052                    BracketPair {
 8053                        start: "[".to_string(),
 8054                        end: "]".to_string(),
 8055                        close: false,
 8056                        surround: false,
 8057                        newline: true,
 8058                    },
 8059                    BracketPair {
 8060                        start: "\"".to_string(),
 8061                        end: "\"".to_string(),
 8062                        close: true,
 8063                        surround: true,
 8064                        newline: false,
 8065                    },
 8066                    BracketPair {
 8067                        start: "<".to_string(),
 8068                        end: ">".to_string(),
 8069                        close: false,
 8070                        surround: true,
 8071                        newline: true,
 8072                    },
 8073                ],
 8074                ..Default::default()
 8075            },
 8076            autoclose_before: "})]".to_string(),
 8077            ..Default::default()
 8078        },
 8079        Some(tree_sitter_rust::LANGUAGE.into()),
 8080    );
 8081    let language = Arc::new(language);
 8082
 8083    cx.language_registry().add(language.clone());
 8084    cx.update_buffer(|buffer, cx| {
 8085        buffer.set_language(Some(language), cx);
 8086    });
 8087
 8088    cx.set_state(
 8089        &r#"
 8090            fn main() {
 8091                sampleˇ
 8092            }
 8093        "#
 8094        .unindent(),
 8095    );
 8096
 8097    cx.update_editor(|editor, window, cx| {
 8098        editor.handle_input("(", window, cx);
 8099    });
 8100    cx.assert_editor_state(
 8101        &"
 8102            fn main() {
 8103                sample(ˇ)
 8104            }
 8105        "
 8106        .unindent(),
 8107    );
 8108
 8109    let mocked_response = lsp::SignatureHelp {
 8110        signatures: vec![lsp::SignatureInformation {
 8111            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8112            documentation: None,
 8113            parameters: Some(vec![
 8114                lsp::ParameterInformation {
 8115                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8116                    documentation: None,
 8117                },
 8118                lsp::ParameterInformation {
 8119                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8120                    documentation: None,
 8121                },
 8122            ]),
 8123            active_parameter: None,
 8124        }],
 8125        active_signature: Some(0),
 8126        active_parameter: Some(0),
 8127    };
 8128    handle_signature_help_request(&mut cx, mocked_response).await;
 8129
 8130    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8131        .await;
 8132
 8133    cx.editor(|editor, _, _| {
 8134        let signature_help_state = editor.signature_help_state.popover().cloned();
 8135        assert_eq!(
 8136            signature_help_state.unwrap().label,
 8137            "param1: u8, param2: u8"
 8138        );
 8139    });
 8140}
 8141
 8142#[gpui::test]
 8143async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui::TestAppContext) {
 8144    init_test(cx, |_| {});
 8145
 8146    cx.update(|cx| {
 8147        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8148            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8149                settings.auto_signature_help = Some(false);
 8150                settings.show_signature_help_after_edits = Some(false);
 8151            });
 8152        });
 8153    });
 8154
 8155    let mut cx = EditorLspTestContext::new_rust(
 8156        lsp::ServerCapabilities {
 8157            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8158                ..Default::default()
 8159            }),
 8160            ..Default::default()
 8161        },
 8162        cx,
 8163    )
 8164    .await;
 8165
 8166    let language = Language::new(
 8167        LanguageConfig {
 8168            name: "Rust".into(),
 8169            brackets: BracketPairConfig {
 8170                pairs: vec![
 8171                    BracketPair {
 8172                        start: "{".to_string(),
 8173                        end: "}".to_string(),
 8174                        close: true,
 8175                        surround: true,
 8176                        newline: true,
 8177                    },
 8178                    BracketPair {
 8179                        start: "(".to_string(),
 8180                        end: ")".to_string(),
 8181                        close: true,
 8182                        surround: true,
 8183                        newline: true,
 8184                    },
 8185                    BracketPair {
 8186                        start: "/*".to_string(),
 8187                        end: " */".to_string(),
 8188                        close: true,
 8189                        surround: true,
 8190                        newline: true,
 8191                    },
 8192                    BracketPair {
 8193                        start: "[".to_string(),
 8194                        end: "]".to_string(),
 8195                        close: false,
 8196                        surround: false,
 8197                        newline: true,
 8198                    },
 8199                    BracketPair {
 8200                        start: "\"".to_string(),
 8201                        end: "\"".to_string(),
 8202                        close: true,
 8203                        surround: true,
 8204                        newline: false,
 8205                    },
 8206                    BracketPair {
 8207                        start: "<".to_string(),
 8208                        end: ">".to_string(),
 8209                        close: false,
 8210                        surround: true,
 8211                        newline: true,
 8212                    },
 8213                ],
 8214                ..Default::default()
 8215            },
 8216            autoclose_before: "})]".to_string(),
 8217            ..Default::default()
 8218        },
 8219        Some(tree_sitter_rust::LANGUAGE.into()),
 8220    );
 8221    let language = Arc::new(language);
 8222
 8223    cx.language_registry().add(language.clone());
 8224    cx.update_buffer(|buffer, cx| {
 8225        buffer.set_language(Some(language), cx);
 8226    });
 8227
 8228    // Ensure that signature_help is not called when no signature help is enabled.
 8229    cx.set_state(
 8230        &r#"
 8231            fn main() {
 8232                sampleˇ
 8233            }
 8234        "#
 8235        .unindent(),
 8236    );
 8237    cx.update_editor(|editor, window, cx| {
 8238        editor.handle_input("(", window, cx);
 8239    });
 8240    cx.assert_editor_state(
 8241        &"
 8242            fn main() {
 8243                sample(ˇ)
 8244            }
 8245        "
 8246        .unindent(),
 8247    );
 8248    cx.editor(|editor, _, _| {
 8249        assert!(editor.signature_help_state.task().is_none());
 8250    });
 8251
 8252    let mocked_response = lsp::SignatureHelp {
 8253        signatures: vec![lsp::SignatureInformation {
 8254            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8255            documentation: None,
 8256            parameters: Some(vec![
 8257                lsp::ParameterInformation {
 8258                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8259                    documentation: None,
 8260                },
 8261                lsp::ParameterInformation {
 8262                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8263                    documentation: None,
 8264                },
 8265            ]),
 8266            active_parameter: None,
 8267        }],
 8268        active_signature: Some(0),
 8269        active_parameter: Some(0),
 8270    };
 8271
 8272    // Ensure that signature_help is called when enabled afte edits
 8273    cx.update(|_, cx| {
 8274        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8275            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8276                settings.auto_signature_help = Some(false);
 8277                settings.show_signature_help_after_edits = Some(true);
 8278            });
 8279        });
 8280    });
 8281    cx.set_state(
 8282        &r#"
 8283            fn main() {
 8284                sampleˇ
 8285            }
 8286        "#
 8287        .unindent(),
 8288    );
 8289    cx.update_editor(|editor, window, cx| {
 8290        editor.handle_input("(", window, cx);
 8291    });
 8292    cx.assert_editor_state(
 8293        &"
 8294            fn main() {
 8295                sample(ˇ)
 8296            }
 8297        "
 8298        .unindent(),
 8299    );
 8300    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8301    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8302        .await;
 8303    cx.update_editor(|editor, _, _| {
 8304        let signature_help_state = editor.signature_help_state.popover().cloned();
 8305        assert!(signature_help_state.is_some());
 8306        assert_eq!(
 8307            signature_help_state.unwrap().label,
 8308            "param1: u8, param2: u8"
 8309        );
 8310        editor.signature_help_state = SignatureHelpState::default();
 8311    });
 8312
 8313    // Ensure that signature_help is called when auto signature help override is enabled
 8314    cx.update(|_, cx| {
 8315        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8316            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8317                settings.auto_signature_help = Some(true);
 8318                settings.show_signature_help_after_edits = Some(false);
 8319            });
 8320        });
 8321    });
 8322    cx.set_state(
 8323        &r#"
 8324            fn main() {
 8325                sampleˇ
 8326            }
 8327        "#
 8328        .unindent(),
 8329    );
 8330    cx.update_editor(|editor, window, cx| {
 8331        editor.handle_input("(", window, cx);
 8332    });
 8333    cx.assert_editor_state(
 8334        &"
 8335            fn main() {
 8336                sample(ˇ)
 8337            }
 8338        "
 8339        .unindent(),
 8340    );
 8341    handle_signature_help_request(&mut cx, mocked_response).await;
 8342    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8343        .await;
 8344    cx.editor(|editor, _, _| {
 8345        let signature_help_state = editor.signature_help_state.popover().cloned();
 8346        assert!(signature_help_state.is_some());
 8347        assert_eq!(
 8348            signature_help_state.unwrap().label,
 8349            "param1: u8, param2: u8"
 8350        );
 8351    });
 8352}
 8353
 8354#[gpui::test]
 8355async fn test_signature_help(cx: &mut gpui::TestAppContext) {
 8356    init_test(cx, |_| {});
 8357    cx.update(|cx| {
 8358        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8359            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8360                settings.auto_signature_help = Some(true);
 8361            });
 8362        });
 8363    });
 8364
 8365    let mut cx = EditorLspTestContext::new_rust(
 8366        lsp::ServerCapabilities {
 8367            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8368                ..Default::default()
 8369            }),
 8370            ..Default::default()
 8371        },
 8372        cx,
 8373    )
 8374    .await;
 8375
 8376    // A test that directly calls `show_signature_help`
 8377    cx.update_editor(|editor, window, cx| {
 8378        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8379    });
 8380
 8381    let mocked_response = lsp::SignatureHelp {
 8382        signatures: vec![lsp::SignatureInformation {
 8383            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8384            documentation: None,
 8385            parameters: Some(vec![
 8386                lsp::ParameterInformation {
 8387                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8388                    documentation: None,
 8389                },
 8390                lsp::ParameterInformation {
 8391                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8392                    documentation: None,
 8393                },
 8394            ]),
 8395            active_parameter: None,
 8396        }],
 8397        active_signature: Some(0),
 8398        active_parameter: Some(0),
 8399    };
 8400    handle_signature_help_request(&mut cx, mocked_response).await;
 8401
 8402    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8403        .await;
 8404
 8405    cx.editor(|editor, _, _| {
 8406        let signature_help_state = editor.signature_help_state.popover().cloned();
 8407        assert!(signature_help_state.is_some());
 8408        assert_eq!(
 8409            signature_help_state.unwrap().label,
 8410            "param1: u8, param2: u8"
 8411        );
 8412    });
 8413
 8414    // When exiting outside from inside the brackets, `signature_help` is closed.
 8415    cx.set_state(indoc! {"
 8416        fn main() {
 8417            sample(ˇ);
 8418        }
 8419
 8420        fn sample(param1: u8, param2: u8) {}
 8421    "});
 8422
 8423    cx.update_editor(|editor, window, cx| {
 8424        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8425    });
 8426
 8427    let mocked_response = lsp::SignatureHelp {
 8428        signatures: Vec::new(),
 8429        active_signature: None,
 8430        active_parameter: None,
 8431    };
 8432    handle_signature_help_request(&mut cx, mocked_response).await;
 8433
 8434    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8435        .await;
 8436
 8437    cx.editor(|editor, _, _| {
 8438        assert!(!editor.signature_help_state.is_shown());
 8439    });
 8440
 8441    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8442    cx.set_state(indoc! {"
 8443        fn main() {
 8444            sample(ˇ);
 8445        }
 8446
 8447        fn sample(param1: u8, param2: u8) {}
 8448    "});
 8449
 8450    let mocked_response = lsp::SignatureHelp {
 8451        signatures: vec![lsp::SignatureInformation {
 8452            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8453            documentation: None,
 8454            parameters: Some(vec![
 8455                lsp::ParameterInformation {
 8456                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8457                    documentation: None,
 8458                },
 8459                lsp::ParameterInformation {
 8460                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8461                    documentation: None,
 8462                },
 8463            ]),
 8464            active_parameter: None,
 8465        }],
 8466        active_signature: Some(0),
 8467        active_parameter: Some(0),
 8468    };
 8469    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8470    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8471        .await;
 8472    cx.editor(|editor, _, _| {
 8473        assert!(editor.signature_help_state.is_shown());
 8474    });
 8475
 8476    // Restore the popover with more parameter input
 8477    cx.set_state(indoc! {"
 8478        fn main() {
 8479            sample(param1, param2ˇ);
 8480        }
 8481
 8482        fn sample(param1: u8, param2: u8) {}
 8483    "});
 8484
 8485    let mocked_response = lsp::SignatureHelp {
 8486        signatures: vec![lsp::SignatureInformation {
 8487            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8488            documentation: None,
 8489            parameters: Some(vec![
 8490                lsp::ParameterInformation {
 8491                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8492                    documentation: None,
 8493                },
 8494                lsp::ParameterInformation {
 8495                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8496                    documentation: None,
 8497                },
 8498            ]),
 8499            active_parameter: None,
 8500        }],
 8501        active_signature: Some(0),
 8502        active_parameter: Some(1),
 8503    };
 8504    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8505    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8506        .await;
 8507
 8508    // When selecting a range, the popover is gone.
 8509    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8510    cx.update_editor(|editor, window, cx| {
 8511        editor.change_selections(None, window, cx, |s| {
 8512            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8513        })
 8514    });
 8515    cx.assert_editor_state(indoc! {"
 8516        fn main() {
 8517            sample(param1, «ˇparam2»);
 8518        }
 8519
 8520        fn sample(param1: u8, param2: u8) {}
 8521    "});
 8522    cx.editor(|editor, _, _| {
 8523        assert!(!editor.signature_help_state.is_shown());
 8524    });
 8525
 8526    // When unselecting again, the popover is back if within the brackets.
 8527    cx.update_editor(|editor, window, cx| {
 8528        editor.change_selections(None, window, cx, |s| {
 8529            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8530        })
 8531    });
 8532    cx.assert_editor_state(indoc! {"
 8533        fn main() {
 8534            sample(param1, ˇparam2);
 8535        }
 8536
 8537        fn sample(param1: u8, param2: u8) {}
 8538    "});
 8539    handle_signature_help_request(&mut cx, mocked_response).await;
 8540    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8541        .await;
 8542    cx.editor(|editor, _, _| {
 8543        assert!(editor.signature_help_state.is_shown());
 8544    });
 8545
 8546    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8547    cx.update_editor(|editor, window, cx| {
 8548        editor.change_selections(None, window, cx, |s| {
 8549            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8550            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8551        })
 8552    });
 8553    cx.assert_editor_state(indoc! {"
 8554        fn main() {
 8555            sample(param1, ˇparam2);
 8556        }
 8557
 8558        fn sample(param1: u8, param2: u8) {}
 8559    "});
 8560
 8561    let mocked_response = lsp::SignatureHelp {
 8562        signatures: vec![lsp::SignatureInformation {
 8563            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8564            documentation: None,
 8565            parameters: Some(vec![
 8566                lsp::ParameterInformation {
 8567                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8568                    documentation: None,
 8569                },
 8570                lsp::ParameterInformation {
 8571                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8572                    documentation: None,
 8573                },
 8574            ]),
 8575            active_parameter: None,
 8576        }],
 8577        active_signature: Some(0),
 8578        active_parameter: Some(1),
 8579    };
 8580    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8581    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8582        .await;
 8583    cx.update_editor(|editor, _, cx| {
 8584        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8585    });
 8586    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8587        .await;
 8588    cx.update_editor(|editor, window, cx| {
 8589        editor.change_selections(None, window, cx, |s| {
 8590            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8591        })
 8592    });
 8593    cx.assert_editor_state(indoc! {"
 8594        fn main() {
 8595            sample(param1, «ˇparam2»);
 8596        }
 8597
 8598        fn sample(param1: u8, param2: u8) {}
 8599    "});
 8600    cx.update_editor(|editor, window, cx| {
 8601        editor.change_selections(None, window, cx, |s| {
 8602            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8603        })
 8604    });
 8605    cx.assert_editor_state(indoc! {"
 8606        fn main() {
 8607            sample(param1, ˇparam2);
 8608        }
 8609
 8610        fn sample(param1: u8, param2: u8) {}
 8611    "});
 8612    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8613        .await;
 8614}
 8615
 8616#[gpui::test]
 8617async fn test_completion(cx: &mut gpui::TestAppContext) {
 8618    init_test(cx, |_| {});
 8619
 8620    let mut cx = EditorLspTestContext::new_rust(
 8621        lsp::ServerCapabilities {
 8622            completion_provider: Some(lsp::CompletionOptions {
 8623                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8624                resolve_provider: Some(true),
 8625                ..Default::default()
 8626            }),
 8627            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8628            ..Default::default()
 8629        },
 8630        cx,
 8631    )
 8632    .await;
 8633    let counter = Arc::new(AtomicUsize::new(0));
 8634
 8635    cx.set_state(indoc! {"
 8636        oneˇ
 8637        two
 8638        three
 8639    "});
 8640    cx.simulate_keystroke(".");
 8641    handle_completion_request(
 8642        &mut cx,
 8643        indoc! {"
 8644            one.|<>
 8645            two
 8646            three
 8647        "},
 8648        vec!["first_completion", "second_completion"],
 8649        counter.clone(),
 8650    )
 8651    .await;
 8652    cx.condition(|editor, _| editor.context_menu_visible())
 8653        .await;
 8654    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 8655
 8656    let _handler = handle_signature_help_request(
 8657        &mut cx,
 8658        lsp::SignatureHelp {
 8659            signatures: vec![lsp::SignatureInformation {
 8660                label: "test signature".to_string(),
 8661                documentation: None,
 8662                parameters: Some(vec![lsp::ParameterInformation {
 8663                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 8664                    documentation: None,
 8665                }]),
 8666                active_parameter: None,
 8667            }],
 8668            active_signature: None,
 8669            active_parameter: None,
 8670        },
 8671    );
 8672    cx.update_editor(|editor, window, cx| {
 8673        assert!(
 8674            !editor.signature_help_state.is_shown(),
 8675            "No signature help was called for"
 8676        );
 8677        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8678    });
 8679    cx.run_until_parked();
 8680    cx.update_editor(|editor, _, _| {
 8681        assert!(
 8682            !editor.signature_help_state.is_shown(),
 8683            "No signature help should be shown when completions menu is open"
 8684        );
 8685    });
 8686
 8687    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8688        editor.context_menu_next(&Default::default(), window, cx);
 8689        editor
 8690            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8691            .unwrap()
 8692    });
 8693    cx.assert_editor_state(indoc! {"
 8694        one.second_completionˇ
 8695        two
 8696        three
 8697    "});
 8698
 8699    handle_resolve_completion_request(
 8700        &mut cx,
 8701        Some(vec![
 8702            (
 8703                //This overlaps with the primary completion edit which is
 8704                //misbehavior from the LSP spec, test that we filter it out
 8705                indoc! {"
 8706                    one.second_ˇcompletion
 8707                    two
 8708                    threeˇ
 8709                "},
 8710                "overlapping additional edit",
 8711            ),
 8712            (
 8713                indoc! {"
 8714                    one.second_completion
 8715                    two
 8716                    threeˇ
 8717                "},
 8718                "\nadditional edit",
 8719            ),
 8720        ]),
 8721    )
 8722    .await;
 8723    apply_additional_edits.await.unwrap();
 8724    cx.assert_editor_state(indoc! {"
 8725        one.second_completionˇ
 8726        two
 8727        three
 8728        additional edit
 8729    "});
 8730
 8731    cx.set_state(indoc! {"
 8732        one.second_completion
 8733        twoˇ
 8734        threeˇ
 8735        additional edit
 8736    "});
 8737    cx.simulate_keystroke(" ");
 8738    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8739    cx.simulate_keystroke("s");
 8740    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8741
 8742    cx.assert_editor_state(indoc! {"
 8743        one.second_completion
 8744        two sˇ
 8745        three sˇ
 8746        additional edit
 8747    "});
 8748    handle_completion_request(
 8749        &mut cx,
 8750        indoc! {"
 8751            one.second_completion
 8752            two s
 8753            three <s|>
 8754            additional edit
 8755        "},
 8756        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8757        counter.clone(),
 8758    )
 8759    .await;
 8760    cx.condition(|editor, _| editor.context_menu_visible())
 8761        .await;
 8762    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 8763
 8764    cx.simulate_keystroke("i");
 8765
 8766    handle_completion_request(
 8767        &mut cx,
 8768        indoc! {"
 8769            one.second_completion
 8770            two si
 8771            three <si|>
 8772            additional edit
 8773        "},
 8774        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 8775        counter.clone(),
 8776    )
 8777    .await;
 8778    cx.condition(|editor, _| editor.context_menu_visible())
 8779        .await;
 8780    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 8781
 8782    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8783        editor
 8784            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8785            .unwrap()
 8786    });
 8787    cx.assert_editor_state(indoc! {"
 8788        one.second_completion
 8789        two sixth_completionˇ
 8790        three sixth_completionˇ
 8791        additional edit
 8792    "});
 8793
 8794    apply_additional_edits.await.unwrap();
 8795
 8796    update_test_language_settings(&mut cx, |settings| {
 8797        settings.defaults.show_completions_on_input = Some(false);
 8798    });
 8799    cx.set_state("editorˇ");
 8800    cx.simulate_keystroke(".");
 8801    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8802    cx.simulate_keystroke("c");
 8803    cx.simulate_keystroke("l");
 8804    cx.simulate_keystroke("o");
 8805    cx.assert_editor_state("editor.cloˇ");
 8806    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 8807    cx.update_editor(|editor, window, cx| {
 8808        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 8809    });
 8810    handle_completion_request(
 8811        &mut cx,
 8812        "editor.<clo|>",
 8813        vec!["close", "clobber"],
 8814        counter.clone(),
 8815    )
 8816    .await;
 8817    cx.condition(|editor, _| editor.context_menu_visible())
 8818        .await;
 8819    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 8820
 8821    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 8822        editor
 8823            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 8824            .unwrap()
 8825    });
 8826    cx.assert_editor_state("editor.closeˇ");
 8827    handle_resolve_completion_request(&mut cx, None).await;
 8828    apply_additional_edits.await.unwrap();
 8829}
 8830
 8831#[gpui::test]
 8832async fn test_multiline_completion(cx: &mut gpui::TestAppContext) {
 8833    init_test(cx, |_| {});
 8834
 8835    let fs = FakeFs::new(cx.executor());
 8836    fs.insert_tree(
 8837        path!("/a"),
 8838        json!({
 8839            "main.ts": "a",
 8840        }),
 8841    )
 8842    .await;
 8843
 8844    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8845    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8846    let typescript_language = Arc::new(Language::new(
 8847        LanguageConfig {
 8848            name: "TypeScript".into(),
 8849            matcher: LanguageMatcher {
 8850                path_suffixes: vec!["ts".to_string()],
 8851                ..LanguageMatcher::default()
 8852            },
 8853            line_comments: vec!["// ".into()],
 8854            ..LanguageConfig::default()
 8855        },
 8856        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8857    ));
 8858    language_registry.add(typescript_language.clone());
 8859    let mut fake_servers = language_registry.register_fake_lsp(
 8860        "TypeScript",
 8861        FakeLspAdapter {
 8862            capabilities: lsp::ServerCapabilities {
 8863                completion_provider: Some(lsp::CompletionOptions {
 8864                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8865                    ..lsp::CompletionOptions::default()
 8866                }),
 8867                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8868                ..lsp::ServerCapabilities::default()
 8869            },
 8870            // Emulate vtsls label generation
 8871            label_for_completion: Some(Box::new(|item, _| {
 8872                let text = if let Some(description) = item
 8873                    .label_details
 8874                    .as_ref()
 8875                    .and_then(|label_details| label_details.description.as_ref())
 8876                {
 8877                    format!("{} {}", item.label, description)
 8878                } else if let Some(detail) = &item.detail {
 8879                    format!("{} {}", item.label, detail)
 8880                } else {
 8881                    item.label.clone()
 8882                };
 8883                let len = text.len();
 8884                Some(language::CodeLabel {
 8885                    text,
 8886                    runs: Vec::new(),
 8887                    filter_range: 0..len,
 8888                })
 8889            })),
 8890            ..FakeLspAdapter::default()
 8891        },
 8892    );
 8893    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8894    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 8895    let worktree_id = workspace
 8896        .update(cx, |workspace, _window, cx| {
 8897            workspace.project().update(cx, |project, cx| {
 8898                project.worktrees(cx).next().unwrap().read(cx).id()
 8899            })
 8900        })
 8901        .unwrap();
 8902    let _buffer = project
 8903        .update(cx, |project, cx| {
 8904            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 8905        })
 8906        .await
 8907        .unwrap();
 8908    let editor = workspace
 8909        .update(cx, |workspace, window, cx| {
 8910            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 8911        })
 8912        .unwrap()
 8913        .await
 8914        .unwrap()
 8915        .downcast::<Editor>()
 8916        .unwrap();
 8917    let fake_server = fake_servers.next().await.unwrap();
 8918
 8919    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 8920    let multiline_label_2 = "a\nb\nc\n";
 8921    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 8922    let multiline_description = "d\ne\nf\n";
 8923    let multiline_detail_2 = "g\nh\ni\n";
 8924
 8925    let mut completion_handle =
 8926        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 8927            Ok(Some(lsp::CompletionResponse::Array(vec![
 8928                lsp::CompletionItem {
 8929                    label: multiline_label.to_string(),
 8930                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8931                        range: lsp::Range {
 8932                            start: lsp::Position {
 8933                                line: params.text_document_position.position.line,
 8934                                character: params.text_document_position.position.character,
 8935                            },
 8936                            end: lsp::Position {
 8937                                line: params.text_document_position.position.line,
 8938                                character: params.text_document_position.position.character,
 8939                            },
 8940                        },
 8941                        new_text: "new_text_1".to_string(),
 8942                    })),
 8943                    ..lsp::CompletionItem::default()
 8944                },
 8945                lsp::CompletionItem {
 8946                    label: "single line label 1".to_string(),
 8947                    detail: Some(multiline_detail.to_string()),
 8948                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8949                        range: lsp::Range {
 8950                            start: lsp::Position {
 8951                                line: params.text_document_position.position.line,
 8952                                character: params.text_document_position.position.character,
 8953                            },
 8954                            end: lsp::Position {
 8955                                line: params.text_document_position.position.line,
 8956                                character: params.text_document_position.position.character,
 8957                            },
 8958                        },
 8959                        new_text: "new_text_2".to_string(),
 8960                    })),
 8961                    ..lsp::CompletionItem::default()
 8962                },
 8963                lsp::CompletionItem {
 8964                    label: "single line label 2".to_string(),
 8965                    label_details: Some(lsp::CompletionItemLabelDetails {
 8966                        description: Some(multiline_description.to_string()),
 8967                        detail: None,
 8968                    }),
 8969                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8970                        range: lsp::Range {
 8971                            start: lsp::Position {
 8972                                line: params.text_document_position.position.line,
 8973                                character: params.text_document_position.position.character,
 8974                            },
 8975                            end: lsp::Position {
 8976                                line: params.text_document_position.position.line,
 8977                                character: params.text_document_position.position.character,
 8978                            },
 8979                        },
 8980                        new_text: "new_text_2".to_string(),
 8981                    })),
 8982                    ..lsp::CompletionItem::default()
 8983                },
 8984                lsp::CompletionItem {
 8985                    label: multiline_label_2.to_string(),
 8986                    detail: Some(multiline_detail_2.to_string()),
 8987                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 8988                        range: lsp::Range {
 8989                            start: lsp::Position {
 8990                                line: params.text_document_position.position.line,
 8991                                character: params.text_document_position.position.character,
 8992                            },
 8993                            end: lsp::Position {
 8994                                line: params.text_document_position.position.line,
 8995                                character: params.text_document_position.position.character,
 8996                            },
 8997                        },
 8998                        new_text: "new_text_3".to_string(),
 8999                    })),
 9000                    ..lsp::CompletionItem::default()
 9001                },
 9002                lsp::CompletionItem {
 9003                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9004                    detail: Some(
 9005                        "Details with many     spaces and \t but without newlines".to_string(),
 9006                    ),
 9007                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9008                        range: lsp::Range {
 9009                            start: lsp::Position {
 9010                                line: params.text_document_position.position.line,
 9011                                character: params.text_document_position.position.character,
 9012                            },
 9013                            end: lsp::Position {
 9014                                line: params.text_document_position.position.line,
 9015                                character: params.text_document_position.position.character,
 9016                            },
 9017                        },
 9018                        new_text: "new_text_4".to_string(),
 9019                    })),
 9020                    ..lsp::CompletionItem::default()
 9021                },
 9022            ])))
 9023        });
 9024
 9025    editor.update_in(cx, |editor, window, cx| {
 9026        cx.focus_self(window);
 9027        editor.move_to_end(&MoveToEnd, window, cx);
 9028        editor.handle_input(".", window, cx);
 9029    });
 9030    cx.run_until_parked();
 9031    completion_handle.next().await.unwrap();
 9032
 9033    editor.update(cx, |editor, _| {
 9034        assert!(editor.context_menu_visible());
 9035        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9036        {
 9037            let completion_labels = menu
 9038                .completions
 9039                .borrow()
 9040                .iter()
 9041                .map(|c| c.label.text.clone())
 9042                .collect::<Vec<_>>();
 9043            assert_eq!(
 9044                completion_labels,
 9045                &[
 9046                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9047                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9048                    "single line label 2 d e f ",
 9049                    "a b c g h i ",
 9050                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9051                ],
 9052                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9053            );
 9054
 9055            for completion in menu
 9056                .completions
 9057                .borrow()
 9058                .iter() {
 9059                    assert_eq!(
 9060                        completion.label.filter_range,
 9061                        0..completion.label.text.len(),
 9062                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9063                    );
 9064                }
 9065
 9066        } else {
 9067            panic!("expected completion menu to be open");
 9068        }
 9069    });
 9070}
 9071
 9072#[gpui::test]
 9073async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
 9074    init_test(cx, |_| {});
 9075    let mut cx = EditorLspTestContext::new_rust(
 9076        lsp::ServerCapabilities {
 9077            completion_provider: Some(lsp::CompletionOptions {
 9078                trigger_characters: Some(vec![".".to_string()]),
 9079                ..Default::default()
 9080            }),
 9081            ..Default::default()
 9082        },
 9083        cx,
 9084    )
 9085    .await;
 9086    cx.lsp
 9087        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9088            Ok(Some(lsp::CompletionResponse::Array(vec![
 9089                lsp::CompletionItem {
 9090                    label: "first".into(),
 9091                    ..Default::default()
 9092                },
 9093                lsp::CompletionItem {
 9094                    label: "last".into(),
 9095                    ..Default::default()
 9096                },
 9097            ])))
 9098        });
 9099    cx.set_state("variableˇ");
 9100    cx.simulate_keystroke(".");
 9101    cx.executor().run_until_parked();
 9102
 9103    cx.update_editor(|editor, _, _| {
 9104        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9105        {
 9106            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9107        } else {
 9108            panic!("expected completion menu to be open");
 9109        }
 9110    });
 9111
 9112    cx.update_editor(|editor, window, cx| {
 9113        editor.move_page_down(&MovePageDown::default(), window, cx);
 9114        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9115        {
 9116            assert!(
 9117                menu.selected_item == 1,
 9118                "expected PageDown to select the last item from the context menu"
 9119            );
 9120        } else {
 9121            panic!("expected completion menu to stay open after PageDown");
 9122        }
 9123    });
 9124
 9125    cx.update_editor(|editor, window, cx| {
 9126        editor.move_page_up(&MovePageUp::default(), window, cx);
 9127        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9128        {
 9129            assert!(
 9130                menu.selected_item == 0,
 9131                "expected PageUp to select the first item from the context menu"
 9132            );
 9133        } else {
 9134            panic!("expected completion menu to stay open after PageUp");
 9135        }
 9136    });
 9137}
 9138
 9139#[gpui::test]
 9140async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
 9141    init_test(cx, |_| {});
 9142    let mut cx = EditorLspTestContext::new_rust(
 9143        lsp::ServerCapabilities {
 9144            completion_provider: Some(lsp::CompletionOptions {
 9145                trigger_characters: Some(vec![".".to_string()]),
 9146                ..Default::default()
 9147            }),
 9148            ..Default::default()
 9149        },
 9150        cx,
 9151    )
 9152    .await;
 9153    cx.lsp
 9154        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9155            Ok(Some(lsp::CompletionResponse::Array(vec![
 9156                lsp::CompletionItem {
 9157                    label: "Range".into(),
 9158                    sort_text: Some("a".into()),
 9159                    ..Default::default()
 9160                },
 9161                lsp::CompletionItem {
 9162                    label: "r".into(),
 9163                    sort_text: Some("b".into()),
 9164                    ..Default::default()
 9165                },
 9166                lsp::CompletionItem {
 9167                    label: "ret".into(),
 9168                    sort_text: Some("c".into()),
 9169                    ..Default::default()
 9170                },
 9171                lsp::CompletionItem {
 9172                    label: "return".into(),
 9173                    sort_text: Some("d".into()),
 9174                    ..Default::default()
 9175                },
 9176                lsp::CompletionItem {
 9177                    label: "slice".into(),
 9178                    sort_text: Some("d".into()),
 9179                    ..Default::default()
 9180                },
 9181            ])))
 9182        });
 9183    cx.set_state("");
 9184    cx.executor().run_until_parked();
 9185    cx.update_editor(|editor, window, cx| {
 9186        editor.show_completions(
 9187            &ShowCompletions {
 9188                trigger: Some("r".into()),
 9189            },
 9190            window,
 9191            cx,
 9192        );
 9193    });
 9194    cx.executor().run_until_parked();
 9195
 9196    cx.update_editor(|editor, _, _| {
 9197        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9198        {
 9199            assert_eq!(
 9200                completion_menu_entries(&menu),
 9201                &["r", "ret", "Range", "return"]
 9202            );
 9203        } else {
 9204            panic!("expected completion menu to be open");
 9205        }
 9206    });
 9207}
 9208
 9209#[gpui::test]
 9210async fn test_no_duplicated_completion_requests(cx: &mut gpui::TestAppContext) {
 9211    init_test(cx, |_| {});
 9212
 9213    let mut cx = EditorLspTestContext::new_rust(
 9214        lsp::ServerCapabilities {
 9215            completion_provider: Some(lsp::CompletionOptions {
 9216                trigger_characters: Some(vec![".".to_string()]),
 9217                resolve_provider: Some(true),
 9218                ..Default::default()
 9219            }),
 9220            ..Default::default()
 9221        },
 9222        cx,
 9223    )
 9224    .await;
 9225
 9226    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9227    cx.simulate_keystroke(".");
 9228    let completion_item = lsp::CompletionItem {
 9229        label: "Some".into(),
 9230        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9231        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9232        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9233            kind: lsp::MarkupKind::Markdown,
 9234            value: "```rust\nSome(2)\n```".to_string(),
 9235        })),
 9236        deprecated: Some(false),
 9237        sort_text: Some("Some".to_string()),
 9238        filter_text: Some("Some".to_string()),
 9239        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9240        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9241            range: lsp::Range {
 9242                start: lsp::Position {
 9243                    line: 0,
 9244                    character: 22,
 9245                },
 9246                end: lsp::Position {
 9247                    line: 0,
 9248                    character: 22,
 9249                },
 9250            },
 9251            new_text: "Some(2)".to_string(),
 9252        })),
 9253        additional_text_edits: Some(vec![lsp::TextEdit {
 9254            range: lsp::Range {
 9255                start: lsp::Position {
 9256                    line: 0,
 9257                    character: 20,
 9258                },
 9259                end: lsp::Position {
 9260                    line: 0,
 9261                    character: 22,
 9262                },
 9263            },
 9264            new_text: "".to_string(),
 9265        }]),
 9266        ..Default::default()
 9267    };
 9268
 9269    let closure_completion_item = completion_item.clone();
 9270    let counter = Arc::new(AtomicUsize::new(0));
 9271    let counter_clone = counter.clone();
 9272    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9273        let task_completion_item = closure_completion_item.clone();
 9274        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9275        async move {
 9276            Ok(Some(lsp::CompletionResponse::Array(vec![
 9277                task_completion_item,
 9278            ])))
 9279        }
 9280    });
 9281
 9282    cx.condition(|editor, _| editor.context_menu_visible())
 9283        .await;
 9284    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9285    assert!(request.next().await.is_some());
 9286    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9287
 9288    cx.simulate_keystroke("S");
 9289    cx.simulate_keystroke("o");
 9290    cx.simulate_keystroke("m");
 9291    cx.condition(|editor, _| editor.context_menu_visible())
 9292        .await;
 9293    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9294    assert!(request.next().await.is_some());
 9295    assert!(request.next().await.is_some());
 9296    assert!(request.next().await.is_some());
 9297    request.close();
 9298    assert!(request.next().await.is_none());
 9299    assert_eq!(
 9300        counter.load(atomic::Ordering::Acquire),
 9301        4,
 9302        "With the completions menu open, only one LSP request should happen per input"
 9303    );
 9304}
 9305
 9306#[gpui::test]
 9307async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
 9308    init_test(cx, |_| {});
 9309    let mut cx = EditorTestContext::new(cx).await;
 9310    let language = Arc::new(Language::new(
 9311        LanguageConfig {
 9312            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9313            ..Default::default()
 9314        },
 9315        Some(tree_sitter_rust::LANGUAGE.into()),
 9316    ));
 9317    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9318
 9319    // If multiple selections intersect a line, the line is only toggled once.
 9320    cx.set_state(indoc! {"
 9321        fn a() {
 9322            «//b();
 9323            ˇ»// «c();
 9324            //ˇ»  d();
 9325        }
 9326    "});
 9327
 9328    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9329
 9330    cx.assert_editor_state(indoc! {"
 9331        fn a() {
 9332            «b();
 9333            c();
 9334            ˇ» d();
 9335        }
 9336    "});
 9337
 9338    // The comment prefix is inserted at the same column for every line in a
 9339    // selection.
 9340    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9341
 9342    cx.assert_editor_state(indoc! {"
 9343        fn a() {
 9344            // «b();
 9345            // c();
 9346            ˇ»//  d();
 9347        }
 9348    "});
 9349
 9350    // If a selection ends at the beginning of a line, that line is not toggled.
 9351    cx.set_selections_state(indoc! {"
 9352        fn a() {
 9353            // b();
 9354            «// c();
 9355        ˇ»    //  d();
 9356        }
 9357    "});
 9358
 9359    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9360
 9361    cx.assert_editor_state(indoc! {"
 9362        fn a() {
 9363            // b();
 9364            «c();
 9365        ˇ»    //  d();
 9366        }
 9367    "});
 9368
 9369    // If a selection span a single line and is empty, the line is toggled.
 9370    cx.set_state(indoc! {"
 9371        fn a() {
 9372            a();
 9373            b();
 9374        ˇ
 9375        }
 9376    "});
 9377
 9378    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9379
 9380    cx.assert_editor_state(indoc! {"
 9381        fn a() {
 9382            a();
 9383            b();
 9384        //•ˇ
 9385        }
 9386    "});
 9387
 9388    // If a selection span multiple lines, empty lines are not toggled.
 9389    cx.set_state(indoc! {"
 9390        fn a() {
 9391            «a();
 9392
 9393            c();ˇ»
 9394        }
 9395    "});
 9396
 9397    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9398
 9399    cx.assert_editor_state(indoc! {"
 9400        fn a() {
 9401            // «a();
 9402
 9403            // c();ˇ»
 9404        }
 9405    "});
 9406
 9407    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9408    cx.set_state(indoc! {"
 9409        fn a() {
 9410            «// a();
 9411            /// b();
 9412            //! c();ˇ»
 9413        }
 9414    "});
 9415
 9416    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9417
 9418    cx.assert_editor_state(indoc! {"
 9419        fn a() {
 9420            «a();
 9421            b();
 9422            c();ˇ»
 9423        }
 9424    "});
 9425}
 9426
 9427#[gpui::test]
 9428async fn test_toggle_comment_ignore_indent(cx: &mut gpui::TestAppContext) {
 9429    init_test(cx, |_| {});
 9430    let mut cx = EditorTestContext::new(cx).await;
 9431    let language = Arc::new(Language::new(
 9432        LanguageConfig {
 9433            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9434            ..Default::default()
 9435        },
 9436        Some(tree_sitter_rust::LANGUAGE.into()),
 9437    ));
 9438    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9439
 9440    let toggle_comments = &ToggleComments {
 9441        advance_downwards: false,
 9442        ignore_indent: true,
 9443    };
 9444
 9445    // If multiple selections intersect a line, the line is only toggled once.
 9446    cx.set_state(indoc! {"
 9447        fn a() {
 9448        //    «b();
 9449        //    c();
 9450        //    ˇ» d();
 9451        }
 9452    "});
 9453
 9454    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9455
 9456    cx.assert_editor_state(indoc! {"
 9457        fn a() {
 9458            «b();
 9459            c();
 9460            ˇ» d();
 9461        }
 9462    "});
 9463
 9464    // The comment prefix is inserted at the beginning of each line
 9465    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9466
 9467    cx.assert_editor_state(indoc! {"
 9468        fn a() {
 9469        //    «b();
 9470        //    c();
 9471        //    ˇ» d();
 9472        }
 9473    "});
 9474
 9475    // If a selection ends at the beginning of a line, that line is not toggled.
 9476    cx.set_selections_state(indoc! {"
 9477        fn a() {
 9478        //    b();
 9479        //    «c();
 9480        ˇ»//     d();
 9481        }
 9482    "});
 9483
 9484    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9485
 9486    cx.assert_editor_state(indoc! {"
 9487        fn a() {
 9488        //    b();
 9489            «c();
 9490        ˇ»//     d();
 9491        }
 9492    "});
 9493
 9494    // If a selection span a single line and is empty, the line is toggled.
 9495    cx.set_state(indoc! {"
 9496        fn a() {
 9497            a();
 9498            b();
 9499        ˇ
 9500        }
 9501    "});
 9502
 9503    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9504
 9505    cx.assert_editor_state(indoc! {"
 9506        fn a() {
 9507            a();
 9508            b();
 9509        //ˇ
 9510        }
 9511    "});
 9512
 9513    // If a selection span multiple lines, empty lines are not toggled.
 9514    cx.set_state(indoc! {"
 9515        fn a() {
 9516            «a();
 9517
 9518            c();ˇ»
 9519        }
 9520    "});
 9521
 9522    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9523
 9524    cx.assert_editor_state(indoc! {"
 9525        fn a() {
 9526        //    «a();
 9527
 9528        //    c();ˇ»
 9529        }
 9530    "});
 9531
 9532    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9533    cx.set_state(indoc! {"
 9534        fn a() {
 9535        //    «a();
 9536        ///    b();
 9537        //!    c();ˇ»
 9538        }
 9539    "});
 9540
 9541    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9542
 9543    cx.assert_editor_state(indoc! {"
 9544        fn a() {
 9545            «a();
 9546            b();
 9547            c();ˇ»
 9548        }
 9549    "});
 9550}
 9551
 9552#[gpui::test]
 9553async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
 9554    init_test(cx, |_| {});
 9555
 9556    let language = Arc::new(Language::new(
 9557        LanguageConfig {
 9558            line_comments: vec!["// ".into()],
 9559            ..Default::default()
 9560        },
 9561        Some(tree_sitter_rust::LANGUAGE.into()),
 9562    ));
 9563
 9564    let mut cx = EditorTestContext::new(cx).await;
 9565
 9566    cx.language_registry().add(language.clone());
 9567    cx.update_buffer(|buffer, cx| {
 9568        buffer.set_language(Some(language), cx);
 9569    });
 9570
 9571    let toggle_comments = &ToggleComments {
 9572        advance_downwards: true,
 9573        ignore_indent: false,
 9574    };
 9575
 9576    // Single cursor on one line -> advance
 9577    // Cursor moves horizontally 3 characters as well on non-blank line
 9578    cx.set_state(indoc!(
 9579        "fn a() {
 9580             ˇdog();
 9581             cat();
 9582        }"
 9583    ));
 9584    cx.update_editor(|editor, window, cx| {
 9585        editor.toggle_comments(toggle_comments, window, cx);
 9586    });
 9587    cx.assert_editor_state(indoc!(
 9588        "fn a() {
 9589             // dog();
 9590             catˇ();
 9591        }"
 9592    ));
 9593
 9594    // Single selection on one line -> don't advance
 9595    cx.set_state(indoc!(
 9596        "fn a() {
 9597             «dog()ˇ»;
 9598             cat();
 9599        }"
 9600    ));
 9601    cx.update_editor(|editor, window, cx| {
 9602        editor.toggle_comments(toggle_comments, window, cx);
 9603    });
 9604    cx.assert_editor_state(indoc!(
 9605        "fn a() {
 9606             // «dog()ˇ»;
 9607             cat();
 9608        }"
 9609    ));
 9610
 9611    // Multiple cursors on one line -> advance
 9612    cx.set_state(indoc!(
 9613        "fn a() {
 9614             ˇdˇog();
 9615             cat();
 9616        }"
 9617    ));
 9618    cx.update_editor(|editor, window, cx| {
 9619        editor.toggle_comments(toggle_comments, window, cx);
 9620    });
 9621    cx.assert_editor_state(indoc!(
 9622        "fn a() {
 9623             // dog();
 9624             catˇ(ˇ);
 9625        }"
 9626    ));
 9627
 9628    // Multiple cursors on one line, with selection -> don't advance
 9629    cx.set_state(indoc!(
 9630        "fn a() {
 9631             ˇdˇog«()ˇ»;
 9632             cat();
 9633        }"
 9634    ));
 9635    cx.update_editor(|editor, window, cx| {
 9636        editor.toggle_comments(toggle_comments, window, cx);
 9637    });
 9638    cx.assert_editor_state(indoc!(
 9639        "fn a() {
 9640             // ˇdˇog«()ˇ»;
 9641             cat();
 9642        }"
 9643    ));
 9644
 9645    // Single cursor on one line -> advance
 9646    // Cursor moves to column 0 on blank line
 9647    cx.set_state(indoc!(
 9648        "fn a() {
 9649             ˇdog();
 9650
 9651             cat();
 9652        }"
 9653    ));
 9654    cx.update_editor(|editor, window, cx| {
 9655        editor.toggle_comments(toggle_comments, window, cx);
 9656    });
 9657    cx.assert_editor_state(indoc!(
 9658        "fn a() {
 9659             // dog();
 9660        ˇ
 9661             cat();
 9662        }"
 9663    ));
 9664
 9665    // Single cursor on one line -> advance
 9666    // Cursor starts and ends at column 0
 9667    cx.set_state(indoc!(
 9668        "fn a() {
 9669         ˇ    dog();
 9670             cat();
 9671        }"
 9672    ));
 9673    cx.update_editor(|editor, window, cx| {
 9674        editor.toggle_comments(toggle_comments, window, cx);
 9675    });
 9676    cx.assert_editor_state(indoc!(
 9677        "fn a() {
 9678             // dog();
 9679         ˇ    cat();
 9680        }"
 9681    ));
 9682}
 9683
 9684#[gpui::test]
 9685async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 9686    init_test(cx, |_| {});
 9687
 9688    let mut cx = EditorTestContext::new(cx).await;
 9689
 9690    let html_language = Arc::new(
 9691        Language::new(
 9692            LanguageConfig {
 9693                name: "HTML".into(),
 9694                block_comment: Some(("<!-- ".into(), " -->".into())),
 9695                ..Default::default()
 9696            },
 9697            Some(tree_sitter_html::LANGUAGE.into()),
 9698        )
 9699        .with_injection_query(
 9700            r#"
 9701            (script_element
 9702                (raw_text) @injection.content
 9703                (#set! injection.language "javascript"))
 9704            "#,
 9705        )
 9706        .unwrap(),
 9707    );
 9708
 9709    let javascript_language = Arc::new(Language::new(
 9710        LanguageConfig {
 9711            name: "JavaScript".into(),
 9712            line_comments: vec!["// ".into()],
 9713            ..Default::default()
 9714        },
 9715        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 9716    ));
 9717
 9718    cx.language_registry().add(html_language.clone());
 9719    cx.language_registry().add(javascript_language.clone());
 9720    cx.update_buffer(|buffer, cx| {
 9721        buffer.set_language(Some(html_language), cx);
 9722    });
 9723
 9724    // Toggle comments for empty selections
 9725    cx.set_state(
 9726        &r#"
 9727            <p>A</p>ˇ
 9728            <p>B</p>ˇ
 9729            <p>C</p>ˇ
 9730        "#
 9731        .unindent(),
 9732    );
 9733    cx.update_editor(|editor, window, cx| {
 9734        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9735    });
 9736    cx.assert_editor_state(
 9737        &r#"
 9738            <!-- <p>A</p>ˇ -->
 9739            <!-- <p>B</p>ˇ -->
 9740            <!-- <p>C</p>ˇ -->
 9741        "#
 9742        .unindent(),
 9743    );
 9744    cx.update_editor(|editor, window, cx| {
 9745        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9746    });
 9747    cx.assert_editor_state(
 9748        &r#"
 9749            <p>A</p>ˇ
 9750            <p>B</p>ˇ
 9751            <p>C</p>ˇ
 9752        "#
 9753        .unindent(),
 9754    );
 9755
 9756    // Toggle comments for mixture of empty and non-empty selections, where
 9757    // multiple selections occupy a given line.
 9758    cx.set_state(
 9759        &r#"
 9760            <p>A«</p>
 9761            <p>ˇ»B</p>ˇ
 9762            <p>C«</p>
 9763            <p>ˇ»D</p>ˇ
 9764        "#
 9765        .unindent(),
 9766    );
 9767
 9768    cx.update_editor(|editor, window, cx| {
 9769        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9770    });
 9771    cx.assert_editor_state(
 9772        &r#"
 9773            <!-- <p>A«</p>
 9774            <p>ˇ»B</p>ˇ -->
 9775            <!-- <p>C«</p>
 9776            <p>ˇ»D</p>ˇ -->
 9777        "#
 9778        .unindent(),
 9779    );
 9780    cx.update_editor(|editor, window, cx| {
 9781        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9782    });
 9783    cx.assert_editor_state(
 9784        &r#"
 9785            <p>A«</p>
 9786            <p>ˇ»B</p>ˇ
 9787            <p>C«</p>
 9788            <p>ˇ»D</p>ˇ
 9789        "#
 9790        .unindent(),
 9791    );
 9792
 9793    // Toggle comments when different languages are active for different
 9794    // selections.
 9795    cx.set_state(
 9796        &r#"
 9797            ˇ<script>
 9798                ˇvar x = new Y();
 9799            ˇ</script>
 9800        "#
 9801        .unindent(),
 9802    );
 9803    cx.executor().run_until_parked();
 9804    cx.update_editor(|editor, window, cx| {
 9805        editor.toggle_comments(&ToggleComments::default(), window, cx)
 9806    });
 9807    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
 9808    // Uncommenting and commenting from this position brings in even more wrong artifacts.
 9809    cx.assert_editor_state(
 9810        &r#"
 9811            <!-- ˇ<script> -->
 9812                // ˇvar x = new Y();
 9813            <!-- ˇ</script> -->
 9814        "#
 9815        .unindent(),
 9816    );
 9817}
 9818
 9819#[gpui::test]
 9820fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
 9821    init_test(cx, |_| {});
 9822
 9823    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9824    let multibuffer = cx.new(|cx| {
 9825        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9826        multibuffer.push_excerpts(
 9827            buffer.clone(),
 9828            [
 9829                ExcerptRange {
 9830                    context: Point::new(0, 0)..Point::new(0, 4),
 9831                    primary: None,
 9832                },
 9833                ExcerptRange {
 9834                    context: Point::new(1, 0)..Point::new(1, 4),
 9835                    primary: None,
 9836                },
 9837            ],
 9838            cx,
 9839        );
 9840        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
 9841        multibuffer
 9842    });
 9843
 9844    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9845    editor.update_in(cx, |editor, window, cx| {
 9846        assert_eq!(editor.text(cx), "aaaa\nbbbb");
 9847        editor.change_selections(None, window, cx, |s| {
 9848            s.select_ranges([
 9849                Point::new(0, 0)..Point::new(0, 0),
 9850                Point::new(1, 0)..Point::new(1, 0),
 9851            ])
 9852        });
 9853
 9854        editor.handle_input("X", window, cx);
 9855        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
 9856        assert_eq!(
 9857            editor.selections.ranges(cx),
 9858            [
 9859                Point::new(0, 1)..Point::new(0, 1),
 9860                Point::new(1, 1)..Point::new(1, 1),
 9861            ]
 9862        );
 9863
 9864        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
 9865        editor.change_selections(None, window, cx, |s| {
 9866            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
 9867        });
 9868        editor.backspace(&Default::default(), window, cx);
 9869        assert_eq!(editor.text(cx), "Xa\nbbb");
 9870        assert_eq!(
 9871            editor.selections.ranges(cx),
 9872            [Point::new(1, 0)..Point::new(1, 0)]
 9873        );
 9874
 9875        editor.change_selections(None, window, cx, |s| {
 9876            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
 9877        });
 9878        editor.backspace(&Default::default(), window, cx);
 9879        assert_eq!(editor.text(cx), "X\nbb");
 9880        assert_eq!(
 9881            editor.selections.ranges(cx),
 9882            [Point::new(0, 1)..Point::new(0, 1)]
 9883        );
 9884    });
 9885}
 9886
 9887#[gpui::test]
 9888fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
 9889    init_test(cx, |_| {});
 9890
 9891    let markers = vec![('[', ']').into(), ('(', ')').into()];
 9892    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
 9893        indoc! {"
 9894            [aaaa
 9895            (bbbb]
 9896            cccc)",
 9897        },
 9898        markers.clone(),
 9899    );
 9900    let excerpt_ranges = markers.into_iter().map(|marker| {
 9901        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
 9902        ExcerptRange {
 9903            context,
 9904            primary: None,
 9905        }
 9906    });
 9907    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
 9908    let multibuffer = cx.new(|cx| {
 9909        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9910        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
 9911        multibuffer
 9912    });
 9913
 9914    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
 9915    editor.update_in(cx, |editor, window, cx| {
 9916        let (expected_text, selection_ranges) = marked_text_ranges(
 9917            indoc! {"
 9918                aaaa
 9919                bˇbbb
 9920                bˇbbˇb
 9921                cccc"
 9922            },
 9923            true,
 9924        );
 9925        assert_eq!(editor.text(cx), expected_text);
 9926        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
 9927
 9928        editor.handle_input("X", window, cx);
 9929
 9930        let (expected_text, expected_selections) = marked_text_ranges(
 9931            indoc! {"
 9932                aaaa
 9933                bXˇbbXb
 9934                bXˇbbXˇb
 9935                cccc"
 9936            },
 9937            false,
 9938        );
 9939        assert_eq!(editor.text(cx), expected_text);
 9940        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9941
 9942        editor.newline(&Newline, window, cx);
 9943        let (expected_text, expected_selections) = marked_text_ranges(
 9944            indoc! {"
 9945                aaaa
 9946                bX
 9947                ˇbbX
 9948                b
 9949                bX
 9950                ˇbbX
 9951                ˇb
 9952                cccc"
 9953            },
 9954            false,
 9955        );
 9956        assert_eq!(editor.text(cx), expected_text);
 9957        assert_eq!(editor.selections.ranges(cx), expected_selections);
 9958    });
 9959}
 9960
 9961#[gpui::test]
 9962fn test_refresh_selections(cx: &mut TestAppContext) {
 9963    init_test(cx, |_| {});
 9964
 9965    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
 9966    let mut excerpt1_id = None;
 9967    let multibuffer = cx.new(|cx| {
 9968        let mut multibuffer = MultiBuffer::new(ReadWrite);
 9969        excerpt1_id = multibuffer
 9970            .push_excerpts(
 9971                buffer.clone(),
 9972                [
 9973                    ExcerptRange {
 9974                        context: Point::new(0, 0)..Point::new(1, 4),
 9975                        primary: None,
 9976                    },
 9977                    ExcerptRange {
 9978                        context: Point::new(1, 0)..Point::new(2, 4),
 9979                        primary: None,
 9980                    },
 9981                ],
 9982                cx,
 9983            )
 9984            .into_iter()
 9985            .next();
 9986        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
 9987        multibuffer
 9988    });
 9989
 9990    let editor = cx.add_window(|window, cx| {
 9991        let mut editor = build_editor(multibuffer.clone(), window, cx);
 9992        let snapshot = editor.snapshot(window, cx);
 9993        editor.change_selections(None, window, cx, |s| {
 9994            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
 9995        });
 9996        editor.begin_selection(
 9997            Point::new(2, 1).to_display_point(&snapshot),
 9998            true,
 9999            1,
10000            window,
10001            cx,
10002        );
10003        assert_eq!(
10004            editor.selections.ranges(cx),
10005            [
10006                Point::new(1, 3)..Point::new(1, 3),
10007                Point::new(2, 1)..Point::new(2, 1),
10008            ]
10009        );
10010        editor
10011    });
10012
10013    // Refreshing selections is a no-op when excerpts haven't changed.
10014    _ = editor.update(cx, |editor, window, cx| {
10015        editor.change_selections(None, window, cx, |s| s.refresh());
10016        assert_eq!(
10017            editor.selections.ranges(cx),
10018            [
10019                Point::new(1, 3)..Point::new(1, 3),
10020                Point::new(2, 1)..Point::new(2, 1),
10021            ]
10022        );
10023    });
10024
10025    multibuffer.update(cx, |multibuffer, cx| {
10026        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10027    });
10028    _ = editor.update(cx, |editor, window, cx| {
10029        // Removing an excerpt causes the first selection to become degenerate.
10030        assert_eq!(
10031            editor.selections.ranges(cx),
10032            [
10033                Point::new(0, 0)..Point::new(0, 0),
10034                Point::new(0, 1)..Point::new(0, 1)
10035            ]
10036        );
10037
10038        // Refreshing selections will relocate the first selection to the original buffer
10039        // location.
10040        editor.change_selections(None, window, cx, |s| s.refresh());
10041        assert_eq!(
10042            editor.selections.ranges(cx),
10043            [
10044                Point::new(0, 1)..Point::new(0, 1),
10045                Point::new(0, 3)..Point::new(0, 3)
10046            ]
10047        );
10048        assert!(editor.selections.pending_anchor().is_some());
10049    });
10050}
10051
10052#[gpui::test]
10053fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10054    init_test(cx, |_| {});
10055
10056    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10057    let mut excerpt1_id = None;
10058    let multibuffer = cx.new(|cx| {
10059        let mut multibuffer = MultiBuffer::new(ReadWrite);
10060        excerpt1_id = multibuffer
10061            .push_excerpts(
10062                buffer.clone(),
10063                [
10064                    ExcerptRange {
10065                        context: Point::new(0, 0)..Point::new(1, 4),
10066                        primary: None,
10067                    },
10068                    ExcerptRange {
10069                        context: Point::new(1, 0)..Point::new(2, 4),
10070                        primary: None,
10071                    },
10072                ],
10073                cx,
10074            )
10075            .into_iter()
10076            .next();
10077        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10078        multibuffer
10079    });
10080
10081    let editor = cx.add_window(|window, cx| {
10082        let mut editor = build_editor(multibuffer.clone(), window, cx);
10083        let snapshot = editor.snapshot(window, cx);
10084        editor.begin_selection(
10085            Point::new(1, 3).to_display_point(&snapshot),
10086            false,
10087            1,
10088            window,
10089            cx,
10090        );
10091        assert_eq!(
10092            editor.selections.ranges(cx),
10093            [Point::new(1, 3)..Point::new(1, 3)]
10094        );
10095        editor
10096    });
10097
10098    multibuffer.update(cx, |multibuffer, cx| {
10099        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10100    });
10101    _ = editor.update(cx, |editor, window, cx| {
10102        assert_eq!(
10103            editor.selections.ranges(cx),
10104            [Point::new(0, 0)..Point::new(0, 0)]
10105        );
10106
10107        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10108        editor.change_selections(None, window, cx, |s| s.refresh());
10109        assert_eq!(
10110            editor.selections.ranges(cx),
10111            [Point::new(0, 3)..Point::new(0, 3)]
10112        );
10113        assert!(editor.selections.pending_anchor().is_some());
10114    });
10115}
10116
10117#[gpui::test]
10118async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10119    init_test(cx, |_| {});
10120
10121    let language = Arc::new(
10122        Language::new(
10123            LanguageConfig {
10124                brackets: BracketPairConfig {
10125                    pairs: vec![
10126                        BracketPair {
10127                            start: "{".to_string(),
10128                            end: "}".to_string(),
10129                            close: true,
10130                            surround: true,
10131                            newline: true,
10132                        },
10133                        BracketPair {
10134                            start: "/* ".to_string(),
10135                            end: " */".to_string(),
10136                            close: true,
10137                            surround: true,
10138                            newline: true,
10139                        },
10140                    ],
10141                    ..Default::default()
10142                },
10143                ..Default::default()
10144            },
10145            Some(tree_sitter_rust::LANGUAGE.into()),
10146        )
10147        .with_indents_query("")
10148        .unwrap(),
10149    );
10150
10151    let text = concat!(
10152        "{   }\n",     //
10153        "  x\n",       //
10154        "  /*   */\n", //
10155        "x\n",         //
10156        "{{} }\n",     //
10157    );
10158
10159    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10160    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10161    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10162    editor
10163        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10164        .await;
10165
10166    editor.update_in(cx, |editor, window, cx| {
10167        editor.change_selections(None, window, cx, |s| {
10168            s.select_display_ranges([
10169                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10170                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10171                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10172            ])
10173        });
10174        editor.newline(&Newline, window, cx);
10175
10176        assert_eq!(
10177            editor.buffer().read(cx).read(cx).text(),
10178            concat!(
10179                "{ \n",    // Suppress rustfmt
10180                "\n",      //
10181                "}\n",     //
10182                "  x\n",   //
10183                "  /* \n", //
10184                "  \n",    //
10185                "  */\n",  //
10186                "x\n",     //
10187                "{{} \n",  //
10188                "}\n",     //
10189            )
10190        );
10191    });
10192}
10193
10194#[gpui::test]
10195fn test_highlighted_ranges(cx: &mut TestAppContext) {
10196    init_test(cx, |_| {});
10197
10198    let editor = cx.add_window(|window, cx| {
10199        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10200        build_editor(buffer.clone(), window, cx)
10201    });
10202
10203    _ = editor.update(cx, |editor, window, cx| {
10204        struct Type1;
10205        struct Type2;
10206
10207        let buffer = editor.buffer.read(cx).snapshot(cx);
10208
10209        let anchor_range =
10210            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10211
10212        editor.highlight_background::<Type1>(
10213            &[
10214                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10215                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10216                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10217                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10218            ],
10219            |_| Hsla::red(),
10220            cx,
10221        );
10222        editor.highlight_background::<Type2>(
10223            &[
10224                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10225                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10226                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10227                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10228            ],
10229            |_| Hsla::green(),
10230            cx,
10231        );
10232
10233        let snapshot = editor.snapshot(window, cx);
10234        let mut highlighted_ranges = editor.background_highlights_in_range(
10235            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10236            &snapshot,
10237            cx.theme().colors(),
10238        );
10239        // Enforce a consistent ordering based on color without relying on the ordering of the
10240        // highlight's `TypeId` which is non-executor.
10241        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10242        assert_eq!(
10243            highlighted_ranges,
10244            &[
10245                (
10246                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10247                    Hsla::red(),
10248                ),
10249                (
10250                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10251                    Hsla::red(),
10252                ),
10253                (
10254                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10255                    Hsla::green(),
10256                ),
10257                (
10258                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10259                    Hsla::green(),
10260                ),
10261            ]
10262        );
10263        assert_eq!(
10264            editor.background_highlights_in_range(
10265                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10266                &snapshot,
10267                cx.theme().colors(),
10268            ),
10269            &[(
10270                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10271                Hsla::red(),
10272            )]
10273        );
10274    });
10275}
10276
10277#[gpui::test]
10278async fn test_following(cx: &mut gpui::TestAppContext) {
10279    init_test(cx, |_| {});
10280
10281    let fs = FakeFs::new(cx.executor());
10282    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10283
10284    let buffer = project.update(cx, |project, cx| {
10285        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10286        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10287    });
10288    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10289    let follower = cx.update(|cx| {
10290        cx.open_window(
10291            WindowOptions {
10292                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10293                    gpui::Point::new(px(0.), px(0.)),
10294                    gpui::Point::new(px(10.), px(80.)),
10295                ))),
10296                ..Default::default()
10297            },
10298            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10299        )
10300        .unwrap()
10301    });
10302
10303    let is_still_following = Rc::new(RefCell::new(true));
10304    let follower_edit_event_count = Rc::new(RefCell::new(0));
10305    let pending_update = Rc::new(RefCell::new(None));
10306    let leader_entity = leader.root(cx).unwrap();
10307    let follower_entity = follower.root(cx).unwrap();
10308    _ = follower.update(cx, {
10309        let update = pending_update.clone();
10310        let is_still_following = is_still_following.clone();
10311        let follower_edit_event_count = follower_edit_event_count.clone();
10312        |_, window, cx| {
10313            cx.subscribe_in(
10314                &leader_entity,
10315                window,
10316                move |_, leader, event, window, cx| {
10317                    leader.read(cx).add_event_to_update_proto(
10318                        event,
10319                        &mut update.borrow_mut(),
10320                        window,
10321                        cx,
10322                    );
10323                },
10324            )
10325            .detach();
10326
10327            cx.subscribe_in(
10328                &follower_entity,
10329                window,
10330                move |_, _, event: &EditorEvent, _window, _cx| {
10331                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10332                        *is_still_following.borrow_mut() = false;
10333                    }
10334
10335                    if let EditorEvent::BufferEdited = event {
10336                        *follower_edit_event_count.borrow_mut() += 1;
10337                    }
10338                },
10339            )
10340            .detach();
10341        }
10342    });
10343
10344    // Update the selections only
10345    _ = leader.update(cx, |leader, window, cx| {
10346        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10347    });
10348    follower
10349        .update(cx, |follower, window, cx| {
10350            follower.apply_update_proto(
10351                &project,
10352                pending_update.borrow_mut().take().unwrap(),
10353                window,
10354                cx,
10355            )
10356        })
10357        .unwrap()
10358        .await
10359        .unwrap();
10360    _ = follower.update(cx, |follower, _, cx| {
10361        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10362    });
10363    assert!(*is_still_following.borrow());
10364    assert_eq!(*follower_edit_event_count.borrow(), 0);
10365
10366    // Update the scroll position only
10367    _ = leader.update(cx, |leader, window, cx| {
10368        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10369    });
10370    follower
10371        .update(cx, |follower, window, cx| {
10372            follower.apply_update_proto(
10373                &project,
10374                pending_update.borrow_mut().take().unwrap(),
10375                window,
10376                cx,
10377            )
10378        })
10379        .unwrap()
10380        .await
10381        .unwrap();
10382    assert_eq!(
10383        follower
10384            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10385            .unwrap(),
10386        gpui::Point::new(1.5, 3.5)
10387    );
10388    assert!(*is_still_following.borrow());
10389    assert_eq!(*follower_edit_event_count.borrow(), 0);
10390
10391    // Update the selections and scroll position. The follower's scroll position is updated
10392    // via autoscroll, not via the leader's exact scroll position.
10393    _ = leader.update(cx, |leader, window, cx| {
10394        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10395        leader.request_autoscroll(Autoscroll::newest(), cx);
10396        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10397    });
10398    follower
10399        .update(cx, |follower, window, cx| {
10400            follower.apply_update_proto(
10401                &project,
10402                pending_update.borrow_mut().take().unwrap(),
10403                window,
10404                cx,
10405            )
10406        })
10407        .unwrap()
10408        .await
10409        .unwrap();
10410    _ = follower.update(cx, |follower, _, cx| {
10411        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10412        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10413    });
10414    assert!(*is_still_following.borrow());
10415
10416    // Creating a pending selection that precedes another selection
10417    _ = leader.update(cx, |leader, window, cx| {
10418        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10419        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10420    });
10421    follower
10422        .update(cx, |follower, window, cx| {
10423            follower.apply_update_proto(
10424                &project,
10425                pending_update.borrow_mut().take().unwrap(),
10426                window,
10427                cx,
10428            )
10429        })
10430        .unwrap()
10431        .await
10432        .unwrap();
10433    _ = follower.update(cx, |follower, _, cx| {
10434        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10435    });
10436    assert!(*is_still_following.borrow());
10437
10438    // Extend the pending selection so that it surrounds another selection
10439    _ = leader.update(cx, |leader, window, cx| {
10440        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10441    });
10442    follower
10443        .update(cx, |follower, window, cx| {
10444            follower.apply_update_proto(
10445                &project,
10446                pending_update.borrow_mut().take().unwrap(),
10447                window,
10448                cx,
10449            )
10450        })
10451        .unwrap()
10452        .await
10453        .unwrap();
10454    _ = follower.update(cx, |follower, _, cx| {
10455        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10456    });
10457
10458    // Scrolling locally breaks the follow
10459    _ = follower.update(cx, |follower, window, cx| {
10460        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10461        follower.set_scroll_anchor(
10462            ScrollAnchor {
10463                anchor: top_anchor,
10464                offset: gpui::Point::new(0.0, 0.5),
10465            },
10466            window,
10467            cx,
10468        );
10469    });
10470    assert!(!(*is_still_following.borrow()));
10471}
10472
10473#[gpui::test]
10474async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
10475    init_test(cx, |_| {});
10476
10477    let fs = FakeFs::new(cx.executor());
10478    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10479    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10480    let pane = workspace
10481        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10482        .unwrap();
10483
10484    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
10485
10486    let leader = pane.update_in(cx, |_, window, cx| {
10487        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
10488        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
10489    });
10490
10491    // Start following the editor when it has no excerpts.
10492    let mut state_message =
10493        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10494    let workspace_entity = workspace.root(cx).unwrap();
10495    let follower_1 = cx
10496        .update_window(*workspace.deref(), |_, window, cx| {
10497            Editor::from_state_proto(
10498                workspace_entity,
10499                ViewId {
10500                    creator: Default::default(),
10501                    id: 0,
10502                },
10503                &mut state_message,
10504                window,
10505                cx,
10506            )
10507        })
10508        .unwrap()
10509        .unwrap()
10510        .await
10511        .unwrap();
10512
10513    let update_message = Rc::new(RefCell::new(None));
10514    follower_1.update_in(cx, {
10515        let update = update_message.clone();
10516        |_, window, cx| {
10517            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
10518                leader.read(cx).add_event_to_update_proto(
10519                    event,
10520                    &mut update.borrow_mut(),
10521                    window,
10522                    cx,
10523                );
10524            })
10525            .detach();
10526        }
10527    });
10528
10529    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
10530        (
10531            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
10532            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
10533        )
10534    });
10535
10536    // Insert some excerpts.
10537    leader.update(cx, |leader, cx| {
10538        leader.buffer.update(cx, |multibuffer, cx| {
10539            let excerpt_ids = multibuffer.push_excerpts(
10540                buffer_1.clone(),
10541                [
10542                    ExcerptRange {
10543                        context: 1..6,
10544                        primary: None,
10545                    },
10546                    ExcerptRange {
10547                        context: 12..15,
10548                        primary: None,
10549                    },
10550                    ExcerptRange {
10551                        context: 0..3,
10552                        primary: None,
10553                    },
10554                ],
10555                cx,
10556            );
10557            multibuffer.insert_excerpts_after(
10558                excerpt_ids[0],
10559                buffer_2.clone(),
10560                [
10561                    ExcerptRange {
10562                        context: 8..12,
10563                        primary: None,
10564                    },
10565                    ExcerptRange {
10566                        context: 0..6,
10567                        primary: None,
10568                    },
10569                ],
10570                cx,
10571            );
10572        });
10573    });
10574
10575    // Apply the update of adding the excerpts.
10576    follower_1
10577        .update_in(cx, |follower, window, cx| {
10578            follower.apply_update_proto(
10579                &project,
10580                update_message.borrow().clone().unwrap(),
10581                window,
10582                cx,
10583            )
10584        })
10585        .await
10586        .unwrap();
10587    assert_eq!(
10588        follower_1.update(cx, |editor, cx| editor.text(cx)),
10589        leader.update(cx, |editor, cx| editor.text(cx))
10590    );
10591    update_message.borrow_mut().take();
10592
10593    // Start following separately after it already has excerpts.
10594    let mut state_message =
10595        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
10596    let workspace_entity = workspace.root(cx).unwrap();
10597    let follower_2 = cx
10598        .update_window(*workspace.deref(), |_, window, cx| {
10599            Editor::from_state_proto(
10600                workspace_entity,
10601                ViewId {
10602                    creator: Default::default(),
10603                    id: 0,
10604                },
10605                &mut state_message,
10606                window,
10607                cx,
10608            )
10609        })
10610        .unwrap()
10611        .unwrap()
10612        .await
10613        .unwrap();
10614    assert_eq!(
10615        follower_2.update(cx, |editor, cx| editor.text(cx)),
10616        leader.update(cx, |editor, cx| editor.text(cx))
10617    );
10618
10619    // Remove some excerpts.
10620    leader.update(cx, |leader, cx| {
10621        leader.buffer.update(cx, |multibuffer, cx| {
10622            let excerpt_ids = multibuffer.excerpt_ids();
10623            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
10624            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
10625        });
10626    });
10627
10628    // Apply the update of removing the excerpts.
10629    follower_1
10630        .update_in(cx, |follower, window, cx| {
10631            follower.apply_update_proto(
10632                &project,
10633                update_message.borrow().clone().unwrap(),
10634                window,
10635                cx,
10636            )
10637        })
10638        .await
10639        .unwrap();
10640    follower_2
10641        .update_in(cx, |follower, window, cx| {
10642            follower.apply_update_proto(
10643                &project,
10644                update_message.borrow().clone().unwrap(),
10645                window,
10646                cx,
10647            )
10648        })
10649        .await
10650        .unwrap();
10651    update_message.borrow_mut().take();
10652    assert_eq!(
10653        follower_1.update(cx, |editor, cx| editor.text(cx)),
10654        leader.update(cx, |editor, cx| editor.text(cx))
10655    );
10656}
10657
10658#[gpui::test]
10659async fn go_to_prev_overlapping_diagnostic(
10660    executor: BackgroundExecutor,
10661    cx: &mut gpui::TestAppContext,
10662) {
10663    init_test(cx, |_| {});
10664
10665    let mut cx = EditorTestContext::new(cx).await;
10666    let lsp_store =
10667        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10668
10669    cx.set_state(indoc! {"
10670        ˇfn func(abc def: i32) -> u32 {
10671        }
10672    "});
10673
10674    cx.update(|_, cx| {
10675        lsp_store.update(cx, |lsp_store, cx| {
10676            lsp_store
10677                .update_diagnostics(
10678                    LanguageServerId(0),
10679                    lsp::PublishDiagnosticsParams {
10680                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10681                        version: None,
10682                        diagnostics: vec![
10683                            lsp::Diagnostic {
10684                                range: lsp::Range::new(
10685                                    lsp::Position::new(0, 11),
10686                                    lsp::Position::new(0, 12),
10687                                ),
10688                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10689                                ..Default::default()
10690                            },
10691                            lsp::Diagnostic {
10692                                range: lsp::Range::new(
10693                                    lsp::Position::new(0, 12),
10694                                    lsp::Position::new(0, 15),
10695                                ),
10696                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10697                                ..Default::default()
10698                            },
10699                            lsp::Diagnostic {
10700                                range: lsp::Range::new(
10701                                    lsp::Position::new(0, 25),
10702                                    lsp::Position::new(0, 28),
10703                                ),
10704                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10705                                ..Default::default()
10706                            },
10707                        ],
10708                    },
10709                    &[],
10710                    cx,
10711                )
10712                .unwrap()
10713        });
10714    });
10715
10716    executor.run_until_parked();
10717
10718    cx.update_editor(|editor, window, cx| {
10719        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10720    });
10721
10722    cx.assert_editor_state(indoc! {"
10723        fn func(abc def: i32) -> ˇu32 {
10724        }
10725    "});
10726
10727    cx.update_editor(|editor, window, cx| {
10728        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10729    });
10730
10731    cx.assert_editor_state(indoc! {"
10732        fn func(abc ˇdef: i32) -> u32 {
10733        }
10734    "});
10735
10736    cx.update_editor(|editor, window, cx| {
10737        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10738    });
10739
10740    cx.assert_editor_state(indoc! {"
10741        fn func(abcˇ def: i32) -> u32 {
10742        }
10743    "});
10744
10745    cx.update_editor(|editor, window, cx| {
10746        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10747    });
10748
10749    cx.assert_editor_state(indoc! {"
10750        fn func(abc def: i32) -> ˇu32 {
10751        }
10752    "});
10753}
10754
10755#[gpui::test]
10756async fn cycle_through_same_place_diagnostics(
10757    executor: BackgroundExecutor,
10758    cx: &mut gpui::TestAppContext,
10759) {
10760    init_test(cx, |_| {});
10761
10762    let mut cx = EditorTestContext::new(cx).await;
10763    let lsp_store =
10764        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10765
10766    cx.set_state(indoc! {"
10767        ˇfn func(abc def: i32) -> u32 {
10768        }
10769    "});
10770
10771    cx.update(|_, cx| {
10772        lsp_store.update(cx, |lsp_store, cx| {
10773            lsp_store
10774                .update_diagnostics(
10775                    LanguageServerId(0),
10776                    lsp::PublishDiagnosticsParams {
10777                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10778                        version: None,
10779                        diagnostics: vec![
10780                            lsp::Diagnostic {
10781                                range: lsp::Range::new(
10782                                    lsp::Position::new(0, 11),
10783                                    lsp::Position::new(0, 12),
10784                                ),
10785                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10786                                ..Default::default()
10787                            },
10788                            lsp::Diagnostic {
10789                                range: lsp::Range::new(
10790                                    lsp::Position::new(0, 12),
10791                                    lsp::Position::new(0, 15),
10792                                ),
10793                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10794                                ..Default::default()
10795                            },
10796                            lsp::Diagnostic {
10797                                range: lsp::Range::new(
10798                                    lsp::Position::new(0, 12),
10799                                    lsp::Position::new(0, 15),
10800                                ),
10801                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10802                                ..Default::default()
10803                            },
10804                            lsp::Diagnostic {
10805                                range: lsp::Range::new(
10806                                    lsp::Position::new(0, 25),
10807                                    lsp::Position::new(0, 28),
10808                                ),
10809                                severity: Some(lsp::DiagnosticSeverity::ERROR),
10810                                ..Default::default()
10811                            },
10812                        ],
10813                    },
10814                    &[],
10815                    cx,
10816                )
10817                .unwrap()
10818        });
10819    });
10820    executor.run_until_parked();
10821
10822    //// Backward
10823
10824    // Fourth diagnostic
10825    cx.update_editor(|editor, window, cx| {
10826        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10827    });
10828    cx.assert_editor_state(indoc! {"
10829        fn func(abc def: i32) -> ˇu32 {
10830        }
10831    "});
10832
10833    // Third diagnostic
10834    cx.update_editor(|editor, window, cx| {
10835        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10836    });
10837    cx.assert_editor_state(indoc! {"
10838        fn func(abc ˇdef: i32) -> u32 {
10839        }
10840    "});
10841
10842    // Second diagnostic, same place
10843    cx.update_editor(|editor, window, cx| {
10844        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10845    });
10846    cx.assert_editor_state(indoc! {"
10847        fn func(abc ˇdef: i32) -> u32 {
10848        }
10849    "});
10850
10851    // First diagnostic
10852    cx.update_editor(|editor, window, cx| {
10853        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10854    });
10855    cx.assert_editor_state(indoc! {"
10856        fn func(abcˇ def: i32) -> u32 {
10857        }
10858    "});
10859
10860    // Wrapped over, fourth diagnostic
10861    cx.update_editor(|editor, window, cx| {
10862        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, window, cx);
10863    });
10864    cx.assert_editor_state(indoc! {"
10865        fn func(abc def: i32) -> ˇu32 {
10866        }
10867    "});
10868
10869    cx.update_editor(|editor, window, cx| {
10870        editor.move_to_beginning(&MoveToBeginning, window, cx);
10871    });
10872    cx.assert_editor_state(indoc! {"
10873        ˇfn func(abc def: i32) -> u32 {
10874        }
10875    "});
10876
10877    //// Forward
10878
10879    // First diagnostic
10880    cx.update_editor(|editor, window, cx| {
10881        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10882    });
10883    cx.assert_editor_state(indoc! {"
10884        fn func(abcˇ def: i32) -> u32 {
10885        }
10886    "});
10887
10888    // Second diagnostic
10889    cx.update_editor(|editor, window, cx| {
10890        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10891    });
10892    cx.assert_editor_state(indoc! {"
10893        fn func(abc ˇdef: i32) -> u32 {
10894        }
10895    "});
10896
10897    // Third diagnostic, same place
10898    cx.update_editor(|editor, window, cx| {
10899        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10900    });
10901    cx.assert_editor_state(indoc! {"
10902        fn func(abc ˇdef: i32) -> u32 {
10903        }
10904    "});
10905
10906    // Fourth diagnostic
10907    cx.update_editor(|editor, window, cx| {
10908        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10909    });
10910    cx.assert_editor_state(indoc! {"
10911        fn func(abc def: i32) -> ˇu32 {
10912        }
10913    "});
10914
10915    // Wrapped around, first diagnostic
10916    cx.update_editor(|editor, window, cx| {
10917        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
10918    });
10919    cx.assert_editor_state(indoc! {"
10920        fn func(abcˇ def: i32) -> u32 {
10921        }
10922    "});
10923}
10924
10925#[gpui::test]
10926async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
10927    init_test(cx, |_| {});
10928
10929    let mut cx = EditorTestContext::new(cx).await;
10930
10931    cx.set_state(indoc! {"
10932        fn func(abˇc def: i32) -> u32 {
10933        }
10934    "});
10935    let lsp_store =
10936        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
10937
10938    cx.update(|_, cx| {
10939        lsp_store.update(cx, |lsp_store, cx| {
10940            lsp_store.update_diagnostics(
10941                LanguageServerId(0),
10942                lsp::PublishDiagnosticsParams {
10943                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
10944                    version: None,
10945                    diagnostics: vec![lsp::Diagnostic {
10946                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
10947                        severity: Some(lsp::DiagnosticSeverity::ERROR),
10948                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
10949                        ..Default::default()
10950                    }],
10951                },
10952                &[],
10953                cx,
10954            )
10955        })
10956    }).unwrap();
10957    cx.run_until_parked();
10958    cx.update_editor(|editor, window, cx| {
10959        hover_popover::hover(editor, &Default::default(), window, cx)
10960    });
10961    cx.run_until_parked();
10962    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
10963}
10964
10965#[gpui::test]
10966async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
10967    init_test(cx, |_| {});
10968
10969    let mut cx = EditorTestContext::new(cx).await;
10970
10971    let diff_base = r#"
10972        use some::mod;
10973
10974        const A: u32 = 42;
10975
10976        fn main() {
10977            println!("hello");
10978
10979            println!("world");
10980        }
10981        "#
10982    .unindent();
10983
10984    // Edits are modified, removed, modified, added
10985    cx.set_state(
10986        &r#"
10987        use some::modified;
10988
10989        ˇ
10990        fn main() {
10991            println!("hello there");
10992
10993            println!("around the");
10994            println!("world");
10995        }
10996        "#
10997        .unindent(),
10998    );
10999
11000    cx.set_diff_base(&diff_base);
11001    executor.run_until_parked();
11002
11003    cx.update_editor(|editor, window, cx| {
11004        //Wrap around the bottom of the buffer
11005        for _ in 0..3 {
11006            editor.go_to_next_hunk(&GoToHunk, window, cx);
11007        }
11008    });
11009
11010    cx.assert_editor_state(
11011        &r#"
11012        ˇuse some::modified;
11013
11014
11015        fn main() {
11016            println!("hello there");
11017
11018            println!("around the");
11019            println!("world");
11020        }
11021        "#
11022        .unindent(),
11023    );
11024
11025    cx.update_editor(|editor, window, cx| {
11026        //Wrap around the top of the buffer
11027        for _ in 0..2 {
11028            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11029        }
11030    });
11031
11032    cx.assert_editor_state(
11033        &r#"
11034        use some::modified;
11035
11036
11037        fn main() {
11038        ˇ    println!("hello there");
11039
11040            println!("around the");
11041            println!("world");
11042        }
11043        "#
11044        .unindent(),
11045    );
11046
11047    cx.update_editor(|editor, window, cx| {
11048        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11049    });
11050
11051    cx.assert_editor_state(
11052        &r#"
11053        use some::modified;
11054
11055        ˇ
11056        fn main() {
11057            println!("hello there");
11058
11059            println!("around the");
11060            println!("world");
11061        }
11062        "#
11063        .unindent(),
11064    );
11065
11066    cx.update_editor(|editor, window, cx| {
11067        editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11068    });
11069
11070    cx.assert_editor_state(
11071        &r#"
11072        ˇuse some::modified;
11073
11074
11075        fn main() {
11076            println!("hello there");
11077
11078            println!("around the");
11079            println!("world");
11080        }
11081        "#
11082        .unindent(),
11083    );
11084
11085    cx.update_editor(|editor, window, cx| {
11086        for _ in 0..2 {
11087            editor.go_to_prev_hunk(&GoToPrevHunk, window, cx);
11088        }
11089    });
11090
11091    cx.assert_editor_state(
11092        &r#"
11093        use some::modified;
11094
11095
11096        fn main() {
11097        ˇ    println!("hello there");
11098
11099            println!("around the");
11100            println!("world");
11101        }
11102        "#
11103        .unindent(),
11104    );
11105
11106    cx.update_editor(|editor, window, cx| {
11107        editor.fold(&Fold, window, cx);
11108    });
11109
11110    cx.update_editor(|editor, window, cx| {
11111        editor.go_to_next_hunk(&GoToHunk, window, cx);
11112    });
11113
11114    cx.assert_editor_state(
11115        &r#"
11116        ˇuse some::modified;
11117
11118
11119        fn main() {
11120            println!("hello there");
11121
11122            println!("around the");
11123            println!("world");
11124        }
11125        "#
11126        .unindent(),
11127    );
11128}
11129
11130#[test]
11131fn test_split_words() {
11132    fn split(text: &str) -> Vec<&str> {
11133        split_words(text).collect()
11134    }
11135
11136    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11137    assert_eq!(split("hello_world"), &["hello_", "world"]);
11138    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11139    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11140    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11141    assert_eq!(split("helloworld"), &["helloworld"]);
11142
11143    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11144}
11145
11146#[gpui::test]
11147async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
11148    init_test(cx, |_| {});
11149
11150    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11151    let mut assert = |before, after| {
11152        let _state_context = cx.set_state(before);
11153        cx.run_until_parked();
11154        cx.update_editor(|editor, window, cx| {
11155            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11156        });
11157        cx.assert_editor_state(after);
11158    };
11159
11160    // Outside bracket jumps to outside of matching bracket
11161    assert("console.logˇ(var);", "console.log(var)ˇ;");
11162    assert("console.log(var)ˇ;", "console.logˇ(var);");
11163
11164    // Inside bracket jumps to inside of matching bracket
11165    assert("console.log(ˇvar);", "console.log(varˇ);");
11166    assert("console.log(varˇ);", "console.log(ˇvar);");
11167
11168    // When outside a bracket and inside, favor jumping to the inside bracket
11169    assert(
11170        "console.log('foo', [1, 2, 3]ˇ);",
11171        "console.log(ˇ'foo', [1, 2, 3]);",
11172    );
11173    assert(
11174        "console.log(ˇ'foo', [1, 2, 3]);",
11175        "console.log('foo', [1, 2, 3]ˇ);",
11176    );
11177
11178    // Bias forward if two options are equally likely
11179    assert(
11180        "let result = curried_fun()ˇ();",
11181        "let result = curried_fun()()ˇ;",
11182    );
11183
11184    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11185    assert(
11186        indoc! {"
11187            function test() {
11188                console.log('test')ˇ
11189            }"},
11190        indoc! {"
11191            function test() {
11192                console.logˇ('test')
11193            }"},
11194    );
11195}
11196
11197#[gpui::test]
11198async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
11199    init_test(cx, |_| {});
11200
11201    let fs = FakeFs::new(cx.executor());
11202    fs.insert_tree(
11203        path!("/a"),
11204        json!({
11205            "main.rs": "fn main() { let a = 5; }",
11206            "other.rs": "// Test file",
11207        }),
11208    )
11209    .await;
11210    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11211
11212    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11213    language_registry.add(Arc::new(Language::new(
11214        LanguageConfig {
11215            name: "Rust".into(),
11216            matcher: LanguageMatcher {
11217                path_suffixes: vec!["rs".to_string()],
11218                ..Default::default()
11219            },
11220            brackets: BracketPairConfig {
11221                pairs: vec![BracketPair {
11222                    start: "{".to_string(),
11223                    end: "}".to_string(),
11224                    close: true,
11225                    surround: true,
11226                    newline: true,
11227                }],
11228                disabled_scopes_by_bracket_ix: Vec::new(),
11229            },
11230            ..Default::default()
11231        },
11232        Some(tree_sitter_rust::LANGUAGE.into()),
11233    )));
11234    let mut fake_servers = language_registry.register_fake_lsp(
11235        "Rust",
11236        FakeLspAdapter {
11237            capabilities: lsp::ServerCapabilities {
11238                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11239                    first_trigger_character: "{".to_string(),
11240                    more_trigger_character: None,
11241                }),
11242                ..Default::default()
11243            },
11244            ..Default::default()
11245        },
11246    );
11247
11248    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11249
11250    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11251
11252    let worktree_id = workspace
11253        .update(cx, |workspace, _, cx| {
11254            workspace.project().update(cx, |project, cx| {
11255                project.worktrees(cx).next().unwrap().read(cx).id()
11256            })
11257        })
11258        .unwrap();
11259
11260    let buffer = project
11261        .update(cx, |project, cx| {
11262            project.open_local_buffer(path!("/a/main.rs"), cx)
11263        })
11264        .await
11265        .unwrap();
11266    let editor_handle = workspace
11267        .update(cx, |workspace, window, cx| {
11268            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11269        })
11270        .unwrap()
11271        .await
11272        .unwrap()
11273        .downcast::<Editor>()
11274        .unwrap();
11275
11276    cx.executor().start_waiting();
11277    let fake_server = fake_servers.next().await.unwrap();
11278
11279    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11280        assert_eq!(
11281            params.text_document_position.text_document.uri,
11282            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11283        );
11284        assert_eq!(
11285            params.text_document_position.position,
11286            lsp::Position::new(0, 21),
11287        );
11288
11289        Ok(Some(vec![lsp::TextEdit {
11290            new_text: "]".to_string(),
11291            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11292        }]))
11293    });
11294
11295    editor_handle.update_in(cx, |editor, window, cx| {
11296        window.focus(&editor.focus_handle(cx));
11297        editor.change_selections(None, window, cx, |s| {
11298            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11299        });
11300        editor.handle_input("{", window, cx);
11301    });
11302
11303    cx.executor().run_until_parked();
11304
11305    buffer.update(cx, |buffer, _| {
11306        assert_eq!(
11307            buffer.text(),
11308            "fn main() { let a = {5}; }",
11309            "No extra braces from on type formatting should appear in the buffer"
11310        )
11311    });
11312}
11313
11314#[gpui::test]
11315async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
11316    init_test(cx, |_| {});
11317
11318    let fs = FakeFs::new(cx.executor());
11319    fs.insert_tree(
11320        path!("/a"),
11321        json!({
11322            "main.rs": "fn main() { let a = 5; }",
11323            "other.rs": "// Test file",
11324        }),
11325    )
11326    .await;
11327
11328    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11329
11330    let server_restarts = Arc::new(AtomicUsize::new(0));
11331    let closure_restarts = Arc::clone(&server_restarts);
11332    let language_server_name = "test language server";
11333    let language_name: LanguageName = "Rust".into();
11334
11335    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11336    language_registry.add(Arc::new(Language::new(
11337        LanguageConfig {
11338            name: language_name.clone(),
11339            matcher: LanguageMatcher {
11340                path_suffixes: vec!["rs".to_string()],
11341                ..Default::default()
11342            },
11343            ..Default::default()
11344        },
11345        Some(tree_sitter_rust::LANGUAGE.into()),
11346    )));
11347    let mut fake_servers = language_registry.register_fake_lsp(
11348        "Rust",
11349        FakeLspAdapter {
11350            name: language_server_name,
11351            initialization_options: Some(json!({
11352                "testOptionValue": true
11353            })),
11354            initializer: Some(Box::new(move |fake_server| {
11355                let task_restarts = Arc::clone(&closure_restarts);
11356                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11357                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11358                    futures::future::ready(Ok(()))
11359                });
11360            })),
11361            ..Default::default()
11362        },
11363    );
11364
11365    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11366    let _buffer = project
11367        .update(cx, |project, cx| {
11368            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11369        })
11370        .await
11371        .unwrap();
11372    let _fake_server = fake_servers.next().await.unwrap();
11373    update_test_language_settings(cx, |language_settings| {
11374        language_settings.languages.insert(
11375            language_name.clone(),
11376            LanguageSettingsContent {
11377                tab_size: NonZeroU32::new(8),
11378                ..Default::default()
11379            },
11380        );
11381    });
11382    cx.executor().run_until_parked();
11383    assert_eq!(
11384        server_restarts.load(atomic::Ordering::Acquire),
11385        0,
11386        "Should not restart LSP server on an unrelated change"
11387    );
11388
11389    update_test_project_settings(cx, |project_settings| {
11390        project_settings.lsp.insert(
11391            "Some other server name".into(),
11392            LspSettings {
11393                binary: None,
11394                settings: None,
11395                initialization_options: Some(json!({
11396                    "some other init value": false
11397                })),
11398            },
11399        );
11400    });
11401    cx.executor().run_until_parked();
11402    assert_eq!(
11403        server_restarts.load(atomic::Ordering::Acquire),
11404        0,
11405        "Should not restart LSP server on an unrelated LSP settings change"
11406    );
11407
11408    update_test_project_settings(cx, |project_settings| {
11409        project_settings.lsp.insert(
11410            language_server_name.into(),
11411            LspSettings {
11412                binary: None,
11413                settings: None,
11414                initialization_options: Some(json!({
11415                    "anotherInitValue": false
11416                })),
11417            },
11418        );
11419    });
11420    cx.executor().run_until_parked();
11421    assert_eq!(
11422        server_restarts.load(atomic::Ordering::Acquire),
11423        1,
11424        "Should restart LSP server on a related LSP settings change"
11425    );
11426
11427    update_test_project_settings(cx, |project_settings| {
11428        project_settings.lsp.insert(
11429            language_server_name.into(),
11430            LspSettings {
11431                binary: None,
11432                settings: None,
11433                initialization_options: Some(json!({
11434                    "anotherInitValue": false
11435                })),
11436            },
11437        );
11438    });
11439    cx.executor().run_until_parked();
11440    assert_eq!(
11441        server_restarts.load(atomic::Ordering::Acquire),
11442        1,
11443        "Should not restart LSP server on a related LSP settings change that is the same"
11444    );
11445
11446    update_test_project_settings(cx, |project_settings| {
11447        project_settings.lsp.insert(
11448            language_server_name.into(),
11449            LspSettings {
11450                binary: None,
11451                settings: None,
11452                initialization_options: None,
11453            },
11454        );
11455    });
11456    cx.executor().run_until_parked();
11457    assert_eq!(
11458        server_restarts.load(atomic::Ordering::Acquire),
11459        2,
11460        "Should restart LSP server on another related LSP settings change"
11461    );
11462}
11463
11464#[gpui::test]
11465async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
11466    init_test(cx, |_| {});
11467
11468    let mut cx = EditorLspTestContext::new_rust(
11469        lsp::ServerCapabilities {
11470            completion_provider: Some(lsp::CompletionOptions {
11471                trigger_characters: Some(vec![".".to_string()]),
11472                resolve_provider: Some(true),
11473                ..Default::default()
11474            }),
11475            ..Default::default()
11476        },
11477        cx,
11478    )
11479    .await;
11480
11481    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11482    cx.simulate_keystroke(".");
11483    let completion_item = lsp::CompletionItem {
11484        label: "some".into(),
11485        kind: Some(lsp::CompletionItemKind::SNIPPET),
11486        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11487        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11488            kind: lsp::MarkupKind::Markdown,
11489            value: "```rust\nSome(2)\n```".to_string(),
11490        })),
11491        deprecated: Some(false),
11492        sort_text: Some("fffffff2".to_string()),
11493        filter_text: Some("some".to_string()),
11494        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11495        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11496            range: lsp::Range {
11497                start: lsp::Position {
11498                    line: 0,
11499                    character: 22,
11500                },
11501                end: lsp::Position {
11502                    line: 0,
11503                    character: 22,
11504                },
11505            },
11506            new_text: "Some(2)".to_string(),
11507        })),
11508        additional_text_edits: Some(vec![lsp::TextEdit {
11509            range: lsp::Range {
11510                start: lsp::Position {
11511                    line: 0,
11512                    character: 20,
11513                },
11514                end: lsp::Position {
11515                    line: 0,
11516                    character: 22,
11517                },
11518            },
11519            new_text: "".to_string(),
11520        }]),
11521        ..Default::default()
11522    };
11523
11524    let closure_completion_item = completion_item.clone();
11525    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11526        let task_completion_item = closure_completion_item.clone();
11527        async move {
11528            Ok(Some(lsp::CompletionResponse::Array(vec![
11529                task_completion_item,
11530            ])))
11531        }
11532    });
11533
11534    request.next().await;
11535
11536    cx.condition(|editor, _| editor.context_menu_visible())
11537        .await;
11538    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
11539        editor
11540            .confirm_completion(&ConfirmCompletion::default(), window, cx)
11541            .unwrap()
11542    });
11543    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
11544
11545    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
11546        let task_completion_item = completion_item.clone();
11547        async move { Ok(task_completion_item) }
11548    })
11549    .next()
11550    .await
11551    .unwrap();
11552    apply_additional_edits.await.unwrap();
11553    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
11554}
11555
11556#[gpui::test]
11557async fn test_completions_resolve_updates_labels_if_filter_text_matches(
11558    cx: &mut gpui::TestAppContext,
11559) {
11560    init_test(cx, |_| {});
11561
11562    let mut cx = EditorLspTestContext::new_rust(
11563        lsp::ServerCapabilities {
11564            completion_provider: Some(lsp::CompletionOptions {
11565                trigger_characters: Some(vec![".".to_string()]),
11566                resolve_provider: Some(true),
11567                ..Default::default()
11568            }),
11569            ..Default::default()
11570        },
11571        cx,
11572    )
11573    .await;
11574
11575    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11576    cx.simulate_keystroke(".");
11577
11578    let item1 = lsp::CompletionItem {
11579        label: "method id()".to_string(),
11580        filter_text: Some("id".to_string()),
11581        detail: None,
11582        documentation: None,
11583        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11584            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11585            new_text: ".id".to_string(),
11586        })),
11587        ..lsp::CompletionItem::default()
11588    };
11589
11590    let item2 = lsp::CompletionItem {
11591        label: "other".to_string(),
11592        filter_text: Some("other".to_string()),
11593        detail: None,
11594        documentation: None,
11595        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11596            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11597            new_text: ".other".to_string(),
11598        })),
11599        ..lsp::CompletionItem::default()
11600    };
11601
11602    let item1 = item1.clone();
11603    cx.handle_request::<lsp::request::Completion, _, _>({
11604        let item1 = item1.clone();
11605        move |_, _, _| {
11606            let item1 = item1.clone();
11607            let item2 = item2.clone();
11608            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
11609        }
11610    })
11611    .next()
11612    .await;
11613
11614    cx.condition(|editor, _| editor.context_menu_visible())
11615        .await;
11616    cx.update_editor(|editor, _, _| {
11617        let context_menu = editor.context_menu.borrow_mut();
11618        let context_menu = context_menu
11619            .as_ref()
11620            .expect("Should have the context menu deployed");
11621        match context_menu {
11622            CodeContextMenu::Completions(completions_menu) => {
11623                let completions = completions_menu.completions.borrow_mut();
11624                assert_eq!(
11625                    completions
11626                        .iter()
11627                        .map(|completion| &completion.label.text)
11628                        .collect::<Vec<_>>(),
11629                    vec!["method id()", "other"]
11630                )
11631            }
11632            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11633        }
11634    });
11635
11636    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
11637        let item1 = item1.clone();
11638        move |_, item_to_resolve, _| {
11639            let item1 = item1.clone();
11640            async move {
11641                if item1 == item_to_resolve {
11642                    Ok(lsp::CompletionItem {
11643                        label: "method id()".to_string(),
11644                        filter_text: Some("id".to_string()),
11645                        detail: Some("Now resolved!".to_string()),
11646                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
11647                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11648                            range: lsp::Range::new(
11649                                lsp::Position::new(0, 22),
11650                                lsp::Position::new(0, 22),
11651                            ),
11652                            new_text: ".id".to_string(),
11653                        })),
11654                        ..lsp::CompletionItem::default()
11655                    })
11656                } else {
11657                    Ok(item_to_resolve)
11658                }
11659            }
11660        }
11661    })
11662    .next()
11663    .await
11664    .unwrap();
11665    cx.run_until_parked();
11666
11667    cx.update_editor(|editor, window, cx| {
11668        editor.context_menu_next(&Default::default(), window, cx);
11669    });
11670
11671    cx.update_editor(|editor, _, _| {
11672        let context_menu = editor.context_menu.borrow_mut();
11673        let context_menu = context_menu
11674            .as_ref()
11675            .expect("Should have the context menu deployed");
11676        match context_menu {
11677            CodeContextMenu::Completions(completions_menu) => {
11678                let completions = completions_menu.completions.borrow_mut();
11679                assert_eq!(
11680                    completions
11681                        .iter()
11682                        .map(|completion| &completion.label.text)
11683                        .collect::<Vec<_>>(),
11684                    vec!["method id() Now resolved!", "other"],
11685                    "Should update first completion label, but not second as the filter text did not match."
11686                );
11687            }
11688            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11689        }
11690    });
11691}
11692
11693#[gpui::test]
11694async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
11695    init_test(cx, |_| {});
11696
11697    let mut cx = EditorLspTestContext::new_rust(
11698        lsp::ServerCapabilities {
11699            completion_provider: Some(lsp::CompletionOptions {
11700                trigger_characters: Some(vec![".".to_string()]),
11701                resolve_provider: Some(true),
11702                ..Default::default()
11703            }),
11704            ..Default::default()
11705        },
11706        cx,
11707    )
11708    .await;
11709
11710    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11711    cx.simulate_keystroke(".");
11712
11713    let unresolved_item_1 = lsp::CompletionItem {
11714        label: "id".to_string(),
11715        filter_text: Some("id".to_string()),
11716        detail: None,
11717        documentation: None,
11718        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11719            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11720            new_text: ".id".to_string(),
11721        })),
11722        ..lsp::CompletionItem::default()
11723    };
11724    let resolved_item_1 = lsp::CompletionItem {
11725        additional_text_edits: Some(vec![lsp::TextEdit {
11726            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11727            new_text: "!!".to_string(),
11728        }]),
11729        ..unresolved_item_1.clone()
11730    };
11731    let unresolved_item_2 = lsp::CompletionItem {
11732        label: "other".to_string(),
11733        filter_text: Some("other".to_string()),
11734        detail: None,
11735        documentation: None,
11736        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11737            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11738            new_text: ".other".to_string(),
11739        })),
11740        ..lsp::CompletionItem::default()
11741    };
11742    let resolved_item_2 = lsp::CompletionItem {
11743        additional_text_edits: Some(vec![lsp::TextEdit {
11744            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
11745            new_text: "??".to_string(),
11746        }]),
11747        ..unresolved_item_2.clone()
11748    };
11749
11750    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
11751    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
11752    cx.lsp
11753        .server
11754        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11755            let unresolved_item_1 = unresolved_item_1.clone();
11756            let resolved_item_1 = resolved_item_1.clone();
11757            let unresolved_item_2 = unresolved_item_2.clone();
11758            let resolved_item_2 = resolved_item_2.clone();
11759            let resolve_requests_1 = resolve_requests_1.clone();
11760            let resolve_requests_2 = resolve_requests_2.clone();
11761            move |unresolved_request, _| {
11762                let unresolved_item_1 = unresolved_item_1.clone();
11763                let resolved_item_1 = resolved_item_1.clone();
11764                let unresolved_item_2 = unresolved_item_2.clone();
11765                let resolved_item_2 = resolved_item_2.clone();
11766                let resolve_requests_1 = resolve_requests_1.clone();
11767                let resolve_requests_2 = resolve_requests_2.clone();
11768                async move {
11769                    if unresolved_request == unresolved_item_1 {
11770                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
11771                        Ok(resolved_item_1.clone())
11772                    } else if unresolved_request == unresolved_item_2 {
11773                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
11774                        Ok(resolved_item_2.clone())
11775                    } else {
11776                        panic!("Unexpected completion item {unresolved_request:?}")
11777                    }
11778                }
11779            }
11780        })
11781        .detach();
11782
11783    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11784        let unresolved_item_1 = unresolved_item_1.clone();
11785        let unresolved_item_2 = unresolved_item_2.clone();
11786        async move {
11787            Ok(Some(lsp::CompletionResponse::Array(vec![
11788                unresolved_item_1,
11789                unresolved_item_2,
11790            ])))
11791        }
11792    })
11793    .next()
11794    .await;
11795
11796    cx.condition(|editor, _| editor.context_menu_visible())
11797        .await;
11798    cx.update_editor(|editor, _, _| {
11799        let context_menu = editor.context_menu.borrow_mut();
11800        let context_menu = context_menu
11801            .as_ref()
11802            .expect("Should have the context menu deployed");
11803        match context_menu {
11804            CodeContextMenu::Completions(completions_menu) => {
11805                let completions = completions_menu.completions.borrow_mut();
11806                assert_eq!(
11807                    completions
11808                        .iter()
11809                        .map(|completion| &completion.label.text)
11810                        .collect::<Vec<_>>(),
11811                    vec!["id", "other"]
11812                )
11813            }
11814            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
11815        }
11816    });
11817    cx.run_until_parked();
11818
11819    cx.update_editor(|editor, window, cx| {
11820        editor.context_menu_next(&ContextMenuNext, window, cx);
11821    });
11822    cx.run_until_parked();
11823    cx.update_editor(|editor, window, cx| {
11824        editor.context_menu_prev(&ContextMenuPrev, window, cx);
11825    });
11826    cx.run_until_parked();
11827    cx.update_editor(|editor, window, cx| {
11828        editor.context_menu_next(&ContextMenuNext, window, cx);
11829    });
11830    cx.run_until_parked();
11831    cx.update_editor(|editor, window, cx| {
11832        editor
11833            .compose_completion(&ComposeCompletion::default(), window, cx)
11834            .expect("No task returned")
11835    })
11836    .await
11837    .expect("Completion failed");
11838    cx.run_until_parked();
11839
11840    cx.update_editor(|editor, _, cx| {
11841        assert_eq!(
11842            resolve_requests_1.load(atomic::Ordering::Acquire),
11843            1,
11844            "Should always resolve once despite multiple selections"
11845        );
11846        assert_eq!(
11847            resolve_requests_2.load(atomic::Ordering::Acquire),
11848            1,
11849            "Should always resolve once after multiple selections and applying the completion"
11850        );
11851        assert_eq!(
11852            editor.text(cx),
11853            "fn main() { let a = ??.other; }",
11854            "Should use resolved data when applying the completion"
11855        );
11856    });
11857}
11858
11859#[gpui::test]
11860async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
11861    init_test(cx, |_| {});
11862
11863    let item_0 = lsp::CompletionItem {
11864        label: "abs".into(),
11865        insert_text: Some("abs".into()),
11866        data: Some(json!({ "very": "special"})),
11867        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
11868        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11869            lsp::InsertReplaceEdit {
11870                new_text: "abs".to_string(),
11871                insert: lsp::Range::default(),
11872                replace: lsp::Range::default(),
11873            },
11874        )),
11875        ..lsp::CompletionItem::default()
11876    };
11877    let items = iter::once(item_0.clone())
11878        .chain((11..51).map(|i| lsp::CompletionItem {
11879            label: format!("item_{}", i),
11880            insert_text: Some(format!("item_{}", i)),
11881            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
11882            ..lsp::CompletionItem::default()
11883        }))
11884        .collect::<Vec<_>>();
11885
11886    let default_commit_characters = vec!["?".to_string()];
11887    let default_data = json!({ "default": "data"});
11888    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
11889    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
11890    let default_edit_range = lsp::Range {
11891        start: lsp::Position {
11892            line: 0,
11893            character: 5,
11894        },
11895        end: lsp::Position {
11896            line: 0,
11897            character: 5,
11898        },
11899    };
11900
11901    let item_0_out = lsp::CompletionItem {
11902        commit_characters: Some(default_commit_characters.clone()),
11903        insert_text_format: Some(default_insert_text_format),
11904        ..item_0
11905    };
11906    let items_out = iter::once(item_0_out)
11907        .chain(items[1..].iter().map(|item| lsp::CompletionItem {
11908            commit_characters: Some(default_commit_characters.clone()),
11909            data: Some(default_data.clone()),
11910            insert_text_mode: Some(default_insert_text_mode),
11911            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11912                range: default_edit_range,
11913                new_text: item.label.clone(),
11914            })),
11915            ..item.clone()
11916        }))
11917        .collect::<Vec<lsp::CompletionItem>>();
11918
11919    let mut cx = EditorLspTestContext::new_rust(
11920        lsp::ServerCapabilities {
11921            completion_provider: Some(lsp::CompletionOptions {
11922                trigger_characters: Some(vec![".".to_string()]),
11923                resolve_provider: Some(true),
11924                ..Default::default()
11925            }),
11926            ..Default::default()
11927        },
11928        cx,
11929    )
11930    .await;
11931
11932    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
11933    cx.simulate_keystroke(".");
11934
11935    let completion_data = default_data.clone();
11936    let completion_characters = default_commit_characters.clone();
11937    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
11938        let default_data = completion_data.clone();
11939        let default_commit_characters = completion_characters.clone();
11940        let items = items.clone();
11941        async move {
11942            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
11943                items,
11944                item_defaults: Some(lsp::CompletionListItemDefaults {
11945                    data: Some(default_data.clone()),
11946                    commit_characters: Some(default_commit_characters.clone()),
11947                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
11948                        default_edit_range,
11949                    )),
11950                    insert_text_format: Some(default_insert_text_format),
11951                    insert_text_mode: Some(default_insert_text_mode),
11952                }),
11953                ..lsp::CompletionList::default()
11954            })))
11955        }
11956    })
11957    .next()
11958    .await;
11959
11960    let resolved_items = Arc::new(Mutex::new(Vec::new()));
11961    cx.lsp
11962        .server
11963        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
11964            let closure_resolved_items = resolved_items.clone();
11965            move |item_to_resolve, _| {
11966                let closure_resolved_items = closure_resolved_items.clone();
11967                async move {
11968                    closure_resolved_items.lock().push(item_to_resolve.clone());
11969                    Ok(item_to_resolve)
11970                }
11971            }
11972        })
11973        .detach();
11974
11975    cx.condition(|editor, _| editor.context_menu_visible())
11976        .await;
11977    cx.run_until_parked();
11978    cx.update_editor(|editor, _, _| {
11979        let menu = editor.context_menu.borrow_mut();
11980        match menu.as_ref().expect("should have the completions menu") {
11981            CodeContextMenu::Completions(completions_menu) => {
11982                assert_eq!(
11983                    completions_menu
11984                        .entries
11985                        .borrow()
11986                        .iter()
11987                        .map(|mat| mat.string.clone())
11988                        .collect::<Vec<String>>(),
11989                    items_out
11990                        .iter()
11991                        .map(|completion| completion.label.clone())
11992                        .collect::<Vec<String>>()
11993                );
11994            }
11995            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
11996        }
11997    });
11998    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
11999    // with 4 from the end.
12000    assert_eq!(
12001        *resolved_items.lock(),
12002        [
12003            &items_out[0..16],
12004            &items_out[items_out.len() - 4..items_out.len()]
12005        ]
12006        .concat()
12007        .iter()
12008        .cloned()
12009        .collect::<Vec<lsp::CompletionItem>>()
12010    );
12011    resolved_items.lock().clear();
12012
12013    cx.update_editor(|editor, window, cx| {
12014        editor.context_menu_prev(&ContextMenuPrev, window, cx);
12015    });
12016    cx.run_until_parked();
12017    // Completions that have already been resolved are skipped.
12018    assert_eq!(
12019        *resolved_items.lock(),
12020        items_out[items_out.len() - 16..items_out.len() - 4]
12021            .iter()
12022            .cloned()
12023            .collect::<Vec<lsp::CompletionItem>>()
12024    );
12025    resolved_items.lock().clear();
12026}
12027
12028#[gpui::test]
12029async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
12030    init_test(cx, |_| {});
12031
12032    let mut cx = EditorLspTestContext::new(
12033        Language::new(
12034            LanguageConfig {
12035                matcher: LanguageMatcher {
12036                    path_suffixes: vec!["jsx".into()],
12037                    ..Default::default()
12038                },
12039                overrides: [(
12040                    "element".into(),
12041                    LanguageConfigOverride {
12042                        word_characters: Override::Set(['-'].into_iter().collect()),
12043                        ..Default::default()
12044                    },
12045                )]
12046                .into_iter()
12047                .collect(),
12048                ..Default::default()
12049            },
12050            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12051        )
12052        .with_override_query("(jsx_self_closing_element) @element")
12053        .unwrap(),
12054        lsp::ServerCapabilities {
12055            completion_provider: Some(lsp::CompletionOptions {
12056                trigger_characters: Some(vec![":".to_string()]),
12057                ..Default::default()
12058            }),
12059            ..Default::default()
12060        },
12061        cx,
12062    )
12063    .await;
12064
12065    cx.lsp
12066        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12067            Ok(Some(lsp::CompletionResponse::Array(vec![
12068                lsp::CompletionItem {
12069                    label: "bg-blue".into(),
12070                    ..Default::default()
12071                },
12072                lsp::CompletionItem {
12073                    label: "bg-red".into(),
12074                    ..Default::default()
12075                },
12076                lsp::CompletionItem {
12077                    label: "bg-yellow".into(),
12078                    ..Default::default()
12079                },
12080            ])))
12081        });
12082
12083    cx.set_state(r#"<p class="bgˇ" />"#);
12084
12085    // Trigger completion when typing a dash, because the dash is an extra
12086    // word character in the 'element' scope, which contains the cursor.
12087    cx.simulate_keystroke("-");
12088    cx.executor().run_until_parked();
12089    cx.update_editor(|editor, _, _| {
12090        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12091        {
12092            assert_eq!(
12093                completion_menu_entries(&menu),
12094                &["bg-red", "bg-blue", "bg-yellow"]
12095            );
12096        } else {
12097            panic!("expected completion menu to be open");
12098        }
12099    });
12100
12101    cx.simulate_keystroke("l");
12102    cx.executor().run_until_parked();
12103    cx.update_editor(|editor, _, _| {
12104        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12105        {
12106            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12107        } else {
12108            panic!("expected completion menu to be open");
12109        }
12110    });
12111
12112    // When filtering completions, consider the character after the '-' to
12113    // be the start of a subword.
12114    cx.set_state(r#"<p class="yelˇ" />"#);
12115    cx.simulate_keystroke("l");
12116    cx.executor().run_until_parked();
12117    cx.update_editor(|editor, _, _| {
12118        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12119        {
12120            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12121        } else {
12122            panic!("expected completion menu to be open");
12123        }
12124    });
12125}
12126
12127fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12128    let entries = menu.entries.borrow();
12129    entries.iter().map(|mat| mat.string.clone()).collect()
12130}
12131
12132#[gpui::test]
12133async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
12134    init_test(cx, |settings| {
12135        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12136            FormatterList(vec![Formatter::Prettier].into()),
12137        ))
12138    });
12139
12140    let fs = FakeFs::new(cx.executor());
12141    fs.insert_file(path!("/file.ts"), Default::default()).await;
12142
12143    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12144    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12145
12146    language_registry.add(Arc::new(Language::new(
12147        LanguageConfig {
12148            name: "TypeScript".into(),
12149            matcher: LanguageMatcher {
12150                path_suffixes: vec!["ts".to_string()],
12151                ..Default::default()
12152            },
12153            ..Default::default()
12154        },
12155        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12156    )));
12157    update_test_language_settings(cx, |settings| {
12158        settings.defaults.prettier = Some(PrettierSettings {
12159            allowed: true,
12160            ..PrettierSettings::default()
12161        });
12162    });
12163
12164    let test_plugin = "test_plugin";
12165    let _ = language_registry.register_fake_lsp(
12166        "TypeScript",
12167        FakeLspAdapter {
12168            prettier_plugins: vec![test_plugin],
12169            ..Default::default()
12170        },
12171    );
12172
12173    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12174    let buffer = project
12175        .update(cx, |project, cx| {
12176            project.open_local_buffer(path!("/file.ts"), cx)
12177        })
12178        .await
12179        .unwrap();
12180
12181    let buffer_text = "one\ntwo\nthree\n";
12182    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12183    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12184    editor.update_in(cx, |editor, window, cx| {
12185        editor.set_text(buffer_text, window, cx)
12186    });
12187
12188    editor
12189        .update_in(cx, |editor, window, cx| {
12190            editor.perform_format(
12191                project.clone(),
12192                FormatTrigger::Manual,
12193                FormatTarget::Buffers,
12194                window,
12195                cx,
12196            )
12197        })
12198        .unwrap()
12199        .await;
12200    assert_eq!(
12201        editor.update(cx, |editor, cx| editor.text(cx)),
12202        buffer_text.to_string() + prettier_format_suffix,
12203        "Test prettier formatting was not applied to the original buffer text",
12204    );
12205
12206    update_test_language_settings(cx, |settings| {
12207        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12208    });
12209    let format = editor.update_in(cx, |editor, window, cx| {
12210        editor.perform_format(
12211            project.clone(),
12212            FormatTrigger::Manual,
12213            FormatTarget::Buffers,
12214            window,
12215            cx,
12216        )
12217    });
12218    format.await.unwrap();
12219    assert_eq!(
12220        editor.update(cx, |editor, cx| editor.text(cx)),
12221        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12222        "Autoformatting (via test prettier) was not applied to the original buffer text",
12223    );
12224}
12225
12226#[gpui::test]
12227async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
12228    init_test(cx, |_| {});
12229    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12230    let base_text = indoc! {r#"
12231        struct Row;
12232        struct Row1;
12233        struct Row2;
12234
12235        struct Row4;
12236        struct Row5;
12237        struct Row6;
12238
12239        struct Row8;
12240        struct Row9;
12241        struct Row10;"#};
12242
12243    // When addition hunks are not adjacent to carets, no hunk revert is performed
12244    assert_hunk_revert(
12245        indoc! {r#"struct Row;
12246                   struct Row1;
12247                   struct Row1.1;
12248                   struct Row1.2;
12249                   struct Row2;ˇ
12250
12251                   struct Row4;
12252                   struct Row5;
12253                   struct Row6;
12254
12255                   struct Row8;
12256                   ˇstruct Row9;
12257                   struct Row9.1;
12258                   struct Row9.2;
12259                   struct Row9.3;
12260                   struct Row10;"#},
12261        vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
12262        indoc! {r#"struct Row;
12263                   struct Row1;
12264                   struct Row1.1;
12265                   struct Row1.2;
12266                   struct Row2;ˇ
12267
12268                   struct Row4;
12269                   struct Row5;
12270                   struct Row6;
12271
12272                   struct Row8;
12273                   ˇstruct Row9;
12274                   struct Row9.1;
12275                   struct Row9.2;
12276                   struct Row9.3;
12277                   struct Row10;"#},
12278        base_text,
12279        &mut cx,
12280    );
12281    // Same for selections
12282    assert_hunk_revert(
12283        indoc! {r#"struct Row;
12284                   struct Row1;
12285                   struct Row2;
12286                   struct Row2.1;
12287                   struct Row2.2;
12288                   «ˇ
12289                   struct Row4;
12290                   struct» Row5;
12291                   «struct Row6;
12292                   ˇ»
12293                   struct Row9.1;
12294                   struct Row9.2;
12295                   struct Row9.3;
12296                   struct Row8;
12297                   struct Row9;
12298                   struct Row10;"#},
12299        vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
12300        indoc! {r#"struct Row;
12301                   struct Row1;
12302                   struct Row2;
12303                   struct Row2.1;
12304                   struct Row2.2;
12305                   «ˇ
12306                   struct Row4;
12307                   struct» Row5;
12308                   «struct Row6;
12309                   ˇ»
12310                   struct Row9.1;
12311                   struct Row9.2;
12312                   struct Row9.3;
12313                   struct Row8;
12314                   struct Row9;
12315                   struct Row10;"#},
12316        base_text,
12317        &mut cx,
12318    );
12319
12320    // When carets and selections intersect the addition hunks, those are reverted.
12321    // Adjacent carets got merged.
12322    assert_hunk_revert(
12323        indoc! {r#"struct Row;
12324                   ˇ// something on the top
12325                   struct Row1;
12326                   struct Row2;
12327                   struct Roˇw3.1;
12328                   struct Row2.2;
12329                   struct Row2.3;ˇ
12330
12331                   struct Row4;
12332                   struct ˇRow5.1;
12333                   struct Row5.2;
12334                   struct «Rowˇ»5.3;
12335                   struct Row5;
12336                   struct Row6;
12337                   ˇ
12338                   struct Row9.1;
12339                   struct «Rowˇ»9.2;
12340                   struct «ˇRow»9.3;
12341                   struct Row8;
12342                   struct Row9;
12343                   «ˇ// something on bottom»
12344                   struct Row10;"#},
12345        vec![
12346            DiffHunkStatus::added(),
12347            DiffHunkStatus::added(),
12348            DiffHunkStatus::added(),
12349            DiffHunkStatus::added(),
12350            DiffHunkStatus::added(),
12351        ],
12352        indoc! {r#"struct Row;
12353                   ˇstruct Row1;
12354                   struct Row2;
12355                   ˇ
12356                   struct Row4;
12357                   ˇstruct Row5;
12358                   struct Row6;
12359                   ˇ
12360                   ˇstruct Row8;
12361                   struct Row9;
12362                   ˇstruct Row10;"#},
12363        base_text,
12364        &mut cx,
12365    );
12366}
12367
12368#[gpui::test]
12369async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
12370    init_test(cx, |_| {});
12371    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12372    let base_text = indoc! {r#"
12373        struct Row;
12374        struct Row1;
12375        struct Row2;
12376
12377        struct Row4;
12378        struct Row5;
12379        struct Row6;
12380
12381        struct Row8;
12382        struct Row9;
12383        struct Row10;"#};
12384
12385    // Modification hunks behave the same as the addition ones.
12386    assert_hunk_revert(
12387        indoc! {r#"struct Row;
12388                   struct Row1;
12389                   struct Row33;
12390                   ˇ
12391                   struct Row4;
12392                   struct Row5;
12393                   struct Row6;
12394                   ˇ
12395                   struct Row99;
12396                   struct Row9;
12397                   struct Row10;"#},
12398        vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
12399        indoc! {r#"struct Row;
12400                   struct Row1;
12401                   struct Row33;
12402                   ˇ
12403                   struct Row4;
12404                   struct Row5;
12405                   struct Row6;
12406                   ˇ
12407                   struct Row99;
12408                   struct Row9;
12409                   struct Row10;"#},
12410        base_text,
12411        &mut cx,
12412    );
12413    assert_hunk_revert(
12414        indoc! {r#"struct Row;
12415                   struct Row1;
12416                   struct Row33;
12417                   «ˇ
12418                   struct Row4;
12419                   struct» Row5;
12420                   «struct Row6;
12421                   ˇ»
12422                   struct Row99;
12423                   struct Row9;
12424                   struct Row10;"#},
12425        vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
12426        indoc! {r#"struct Row;
12427                   struct Row1;
12428                   struct Row33;
12429                   «ˇ
12430                   struct Row4;
12431                   struct» Row5;
12432                   «struct Row6;
12433                   ˇ»
12434                   struct Row99;
12435                   struct Row9;
12436                   struct Row10;"#},
12437        base_text,
12438        &mut cx,
12439    );
12440
12441    assert_hunk_revert(
12442        indoc! {r#"ˇstruct Row1.1;
12443                   struct Row1;
12444                   «ˇstr»uct Row22;
12445
12446                   struct ˇRow44;
12447                   struct Row5;
12448                   struct «Rˇ»ow66;ˇ
12449
12450                   «struˇ»ct Row88;
12451                   struct Row9;
12452                   struct Row1011;ˇ"#},
12453        vec![
12454            DiffHunkStatus::modified(),
12455            DiffHunkStatus::modified(),
12456            DiffHunkStatus::modified(),
12457            DiffHunkStatus::modified(),
12458            DiffHunkStatus::modified(),
12459            DiffHunkStatus::modified(),
12460        ],
12461        indoc! {r#"struct Row;
12462                   ˇstruct Row1;
12463                   struct Row2;
12464                   ˇ
12465                   struct Row4;
12466                   ˇstruct Row5;
12467                   struct Row6;
12468                   ˇ
12469                   struct Row8;
12470                   ˇstruct Row9;
12471                   struct Row10;ˇ"#},
12472        base_text,
12473        &mut cx,
12474    );
12475}
12476
12477#[gpui::test]
12478async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12479    init_test(cx, |_| {});
12480    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12481    let base_text = indoc! {r#"
12482        one
12483
12484        two
12485        three
12486        "#};
12487
12488    cx.set_diff_base(base_text);
12489    cx.set_state("\nˇ\n");
12490    cx.executor().run_until_parked();
12491    cx.update_editor(|editor, _window, cx| {
12492        editor.expand_selected_diff_hunks(cx);
12493    });
12494    cx.executor().run_until_parked();
12495    cx.update_editor(|editor, window, cx| {
12496        editor.backspace(&Default::default(), window, cx);
12497    });
12498    cx.run_until_parked();
12499    cx.assert_state_with_diff(
12500        indoc! {r#"
12501
12502        - two
12503        - threeˇ
12504        +
12505        "#}
12506        .to_string(),
12507    );
12508}
12509
12510#[gpui::test]
12511async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12512    init_test(cx, |_| {});
12513    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12514    let base_text = indoc! {r#"struct Row;
12515struct Row1;
12516struct Row2;
12517
12518struct Row4;
12519struct Row5;
12520struct Row6;
12521
12522struct Row8;
12523struct Row9;
12524struct Row10;"#};
12525
12526    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12527    assert_hunk_revert(
12528        indoc! {r#"struct Row;
12529                   struct Row2;
12530
12531                   ˇstruct Row4;
12532                   struct Row5;
12533                   struct Row6;
12534                   ˇ
12535                   struct Row8;
12536                   struct Row10;"#},
12537        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12538        indoc! {r#"struct Row;
12539                   struct Row2;
12540
12541                   ˇstruct Row4;
12542                   struct Row5;
12543                   struct Row6;
12544                   ˇ
12545                   struct Row8;
12546                   struct Row10;"#},
12547        base_text,
12548        &mut cx,
12549    );
12550    assert_hunk_revert(
12551        indoc! {r#"struct Row;
12552                   struct Row2;
12553
12554                   «ˇstruct Row4;
12555                   struct» Row5;
12556                   «struct Row6;
12557                   ˇ»
12558                   struct Row8;
12559                   struct Row10;"#},
12560        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12561        indoc! {r#"struct Row;
12562                   struct Row2;
12563
12564                   «ˇstruct Row4;
12565                   struct» Row5;
12566                   «struct Row6;
12567                   ˇ»
12568                   struct Row8;
12569                   struct Row10;"#},
12570        base_text,
12571        &mut cx,
12572    );
12573
12574    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12575    assert_hunk_revert(
12576        indoc! {r#"struct Row;
12577                   ˇstruct Row2;
12578
12579                   struct Row4;
12580                   struct Row5;
12581                   struct Row6;
12582
12583                   struct Row8;ˇ
12584                   struct Row10;"#},
12585        vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
12586        indoc! {r#"struct Row;
12587                   struct Row1;
12588                   ˇstruct Row2;
12589
12590                   struct Row4;
12591                   struct Row5;
12592                   struct Row6;
12593
12594                   struct Row8;ˇ
12595                   struct Row9;
12596                   struct Row10;"#},
12597        base_text,
12598        &mut cx,
12599    );
12600    assert_hunk_revert(
12601        indoc! {r#"struct Row;
12602                   struct Row2«ˇ;
12603                   struct Row4;
12604                   struct» Row5;
12605                   «struct Row6;
12606
12607                   struct Row8;ˇ»
12608                   struct Row10;"#},
12609        vec![
12610            DiffHunkStatus::removed(),
12611            DiffHunkStatus::removed(),
12612            DiffHunkStatus::removed(),
12613        ],
12614        indoc! {r#"struct Row;
12615                   struct Row1;
12616                   struct Row2«ˇ;
12617
12618                   struct Row4;
12619                   struct» Row5;
12620                   «struct Row6;
12621
12622                   struct Row8;ˇ»
12623                   struct Row9;
12624                   struct Row10;"#},
12625        base_text,
12626        &mut cx,
12627    );
12628}
12629
12630#[gpui::test]
12631async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12632    init_test(cx, |_| {});
12633
12634    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12635    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12636    let base_text_3 =
12637        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12638
12639    let text_1 = edit_first_char_of_every_line(base_text_1);
12640    let text_2 = edit_first_char_of_every_line(base_text_2);
12641    let text_3 = edit_first_char_of_every_line(base_text_3);
12642
12643    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12644    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12645    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12646
12647    let multibuffer = cx.new(|cx| {
12648        let mut multibuffer = MultiBuffer::new(ReadWrite);
12649        multibuffer.push_excerpts(
12650            buffer_1.clone(),
12651            [
12652                ExcerptRange {
12653                    context: Point::new(0, 0)..Point::new(3, 0),
12654                    primary: None,
12655                },
12656                ExcerptRange {
12657                    context: Point::new(5, 0)..Point::new(7, 0),
12658                    primary: None,
12659                },
12660                ExcerptRange {
12661                    context: Point::new(9, 0)..Point::new(10, 4),
12662                    primary: None,
12663                },
12664            ],
12665            cx,
12666        );
12667        multibuffer.push_excerpts(
12668            buffer_2.clone(),
12669            [
12670                ExcerptRange {
12671                    context: Point::new(0, 0)..Point::new(3, 0),
12672                    primary: None,
12673                },
12674                ExcerptRange {
12675                    context: Point::new(5, 0)..Point::new(7, 0),
12676                    primary: None,
12677                },
12678                ExcerptRange {
12679                    context: Point::new(9, 0)..Point::new(10, 4),
12680                    primary: None,
12681                },
12682            ],
12683            cx,
12684        );
12685        multibuffer.push_excerpts(
12686            buffer_3.clone(),
12687            [
12688                ExcerptRange {
12689                    context: Point::new(0, 0)..Point::new(3, 0),
12690                    primary: None,
12691                },
12692                ExcerptRange {
12693                    context: Point::new(5, 0)..Point::new(7, 0),
12694                    primary: None,
12695                },
12696                ExcerptRange {
12697                    context: Point::new(9, 0)..Point::new(10, 4),
12698                    primary: None,
12699                },
12700            ],
12701            cx,
12702        );
12703        multibuffer
12704    });
12705
12706    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12707    editor.update_in(cx, |editor, _window, cx| {
12708        for (buffer, diff_base) in [
12709            (buffer_1.clone(), base_text_1),
12710            (buffer_2.clone(), base_text_2),
12711            (buffer_3.clone(), base_text_3),
12712        ] {
12713            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12714            editor
12715                .buffer
12716                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12717        }
12718    });
12719    cx.executor().run_until_parked();
12720
12721    editor.update_in(cx, |editor, window, cx| {
12722        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}");
12723        editor.select_all(&SelectAll, window, cx);
12724        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12725    });
12726    cx.executor().run_until_parked();
12727
12728    // When all ranges are selected, all buffer hunks are reverted.
12729    editor.update(cx, |editor, cx| {
12730        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");
12731    });
12732    buffer_1.update(cx, |buffer, _| {
12733        assert_eq!(buffer.text(), base_text_1);
12734    });
12735    buffer_2.update(cx, |buffer, _| {
12736        assert_eq!(buffer.text(), base_text_2);
12737    });
12738    buffer_3.update(cx, |buffer, _| {
12739        assert_eq!(buffer.text(), base_text_3);
12740    });
12741
12742    editor.update_in(cx, |editor, window, cx| {
12743        editor.undo(&Default::default(), window, cx);
12744    });
12745
12746    editor.update_in(cx, |editor, window, cx| {
12747        editor.change_selections(None, window, cx, |s| {
12748            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12749        });
12750        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12751    });
12752
12753    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12754    // but not affect buffer_2 and its related excerpts.
12755    editor.update(cx, |editor, cx| {
12756        assert_eq!(
12757            editor.text(cx),
12758            "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}"
12759        );
12760    });
12761    buffer_1.update(cx, |buffer, _| {
12762        assert_eq!(buffer.text(), base_text_1);
12763    });
12764    buffer_2.update(cx, |buffer, _| {
12765        assert_eq!(
12766            buffer.text(),
12767            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12768        );
12769    });
12770    buffer_3.update(cx, |buffer, _| {
12771        assert_eq!(
12772            buffer.text(),
12773            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12774        );
12775    });
12776
12777    fn edit_first_char_of_every_line(text: &str) -> String {
12778        text.split('\n')
12779            .map(|line| format!("X{}", &line[1..]))
12780            .collect::<Vec<_>>()
12781            .join("\n")
12782    }
12783}
12784
12785#[gpui::test]
12786async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12787    init_test(cx, |_| {});
12788
12789    let cols = 4;
12790    let rows = 10;
12791    let sample_text_1 = sample_text(rows, cols, 'a');
12792    assert_eq!(
12793        sample_text_1,
12794        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12795    );
12796    let sample_text_2 = sample_text(rows, cols, 'l');
12797    assert_eq!(
12798        sample_text_2,
12799        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12800    );
12801    let sample_text_3 = sample_text(rows, cols, 'v');
12802    assert_eq!(
12803        sample_text_3,
12804        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12805    );
12806
12807    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12808    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12809    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12810
12811    let multi_buffer = cx.new(|cx| {
12812        let mut multibuffer = MultiBuffer::new(ReadWrite);
12813        multibuffer.push_excerpts(
12814            buffer_1.clone(),
12815            [
12816                ExcerptRange {
12817                    context: Point::new(0, 0)..Point::new(3, 0),
12818                    primary: None,
12819                },
12820                ExcerptRange {
12821                    context: Point::new(5, 0)..Point::new(7, 0),
12822                    primary: None,
12823                },
12824                ExcerptRange {
12825                    context: Point::new(9, 0)..Point::new(10, 4),
12826                    primary: None,
12827                },
12828            ],
12829            cx,
12830        );
12831        multibuffer.push_excerpts(
12832            buffer_2.clone(),
12833            [
12834                ExcerptRange {
12835                    context: Point::new(0, 0)..Point::new(3, 0),
12836                    primary: None,
12837                },
12838                ExcerptRange {
12839                    context: Point::new(5, 0)..Point::new(7, 0),
12840                    primary: None,
12841                },
12842                ExcerptRange {
12843                    context: Point::new(9, 0)..Point::new(10, 4),
12844                    primary: None,
12845                },
12846            ],
12847            cx,
12848        );
12849        multibuffer.push_excerpts(
12850            buffer_3.clone(),
12851            [
12852                ExcerptRange {
12853                    context: Point::new(0, 0)..Point::new(3, 0),
12854                    primary: None,
12855                },
12856                ExcerptRange {
12857                    context: Point::new(5, 0)..Point::new(7, 0),
12858                    primary: None,
12859                },
12860                ExcerptRange {
12861                    context: Point::new(9, 0)..Point::new(10, 4),
12862                    primary: None,
12863                },
12864            ],
12865            cx,
12866        );
12867        multibuffer
12868    });
12869
12870    let fs = FakeFs::new(cx.executor());
12871    fs.insert_tree(
12872        "/a",
12873        json!({
12874            "main.rs": sample_text_1,
12875            "other.rs": sample_text_2,
12876            "lib.rs": sample_text_3,
12877        }),
12878    )
12879    .await;
12880    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12881    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12882    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12883    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12884        Editor::new(
12885            EditorMode::Full,
12886            multi_buffer,
12887            Some(project.clone()),
12888            true,
12889            window,
12890            cx,
12891        )
12892    });
12893    let multibuffer_item_id = workspace
12894        .update(cx, |workspace, window, cx| {
12895            assert!(
12896                workspace.active_item(cx).is_none(),
12897                "active item should be None before the first item is added"
12898            );
12899            workspace.add_item_to_active_pane(
12900                Box::new(multi_buffer_editor.clone()),
12901                None,
12902                true,
12903                window,
12904                cx,
12905            );
12906            let active_item = workspace
12907                .active_item(cx)
12908                .expect("should have an active item after adding the multi buffer");
12909            assert!(
12910                !active_item.is_singleton(cx),
12911                "A multi buffer was expected to active after adding"
12912            );
12913            active_item.item_id()
12914        })
12915        .unwrap();
12916    cx.executor().run_until_parked();
12917
12918    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12919        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12920            s.select_ranges(Some(1..2))
12921        });
12922        editor.open_excerpts(&OpenExcerpts, window, cx);
12923    });
12924    cx.executor().run_until_parked();
12925    let first_item_id = workspace
12926        .update(cx, |workspace, window, cx| {
12927            let active_item = workspace
12928                .active_item(cx)
12929                .expect("should have an active item after navigating into the 1st buffer");
12930            let first_item_id = active_item.item_id();
12931            assert_ne!(
12932                first_item_id, multibuffer_item_id,
12933                "Should navigate into the 1st buffer and activate it"
12934            );
12935            assert!(
12936                active_item.is_singleton(cx),
12937                "New active item should be a singleton buffer"
12938            );
12939            assert_eq!(
12940                active_item
12941                    .act_as::<Editor>(cx)
12942                    .expect("should have navigated into an editor for the 1st buffer")
12943                    .read(cx)
12944                    .text(cx),
12945                sample_text_1
12946            );
12947
12948            workspace
12949                .go_back(workspace.active_pane().downgrade(), window, cx)
12950                .detach_and_log_err(cx);
12951
12952            first_item_id
12953        })
12954        .unwrap();
12955    cx.executor().run_until_parked();
12956    workspace
12957        .update(cx, |workspace, _, cx| {
12958            let active_item = workspace
12959                .active_item(cx)
12960                .expect("should have an active item after navigating back");
12961            assert_eq!(
12962                active_item.item_id(),
12963                multibuffer_item_id,
12964                "Should navigate back to the multi buffer"
12965            );
12966            assert!(!active_item.is_singleton(cx));
12967        })
12968        .unwrap();
12969
12970    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12971        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12972            s.select_ranges(Some(39..40))
12973        });
12974        editor.open_excerpts(&OpenExcerpts, window, cx);
12975    });
12976    cx.executor().run_until_parked();
12977    let second_item_id = workspace
12978        .update(cx, |workspace, window, cx| {
12979            let active_item = workspace
12980                .active_item(cx)
12981                .expect("should have an active item after navigating into the 2nd buffer");
12982            let second_item_id = active_item.item_id();
12983            assert_ne!(
12984                second_item_id, multibuffer_item_id,
12985                "Should navigate away from the multibuffer"
12986            );
12987            assert_ne!(
12988                second_item_id, first_item_id,
12989                "Should navigate into the 2nd buffer and activate it"
12990            );
12991            assert!(
12992                active_item.is_singleton(cx),
12993                "New active item should be a singleton buffer"
12994            );
12995            assert_eq!(
12996                active_item
12997                    .act_as::<Editor>(cx)
12998                    .expect("should have navigated into an editor")
12999                    .read(cx)
13000                    .text(cx),
13001                sample_text_2
13002            );
13003
13004            workspace
13005                .go_back(workspace.active_pane().downgrade(), window, cx)
13006                .detach_and_log_err(cx);
13007
13008            second_item_id
13009        })
13010        .unwrap();
13011    cx.executor().run_until_parked();
13012    workspace
13013        .update(cx, |workspace, _, cx| {
13014            let active_item = workspace
13015                .active_item(cx)
13016                .expect("should have an active item after navigating back from the 2nd buffer");
13017            assert_eq!(
13018                active_item.item_id(),
13019                multibuffer_item_id,
13020                "Should navigate back from the 2nd buffer to the multi buffer"
13021            );
13022            assert!(!active_item.is_singleton(cx));
13023        })
13024        .unwrap();
13025
13026    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13027        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13028            s.select_ranges(Some(70..70))
13029        });
13030        editor.open_excerpts(&OpenExcerpts, window, cx);
13031    });
13032    cx.executor().run_until_parked();
13033    workspace
13034        .update(cx, |workspace, window, cx| {
13035            let active_item = workspace
13036                .active_item(cx)
13037                .expect("should have an active item after navigating into the 3rd buffer");
13038            let third_item_id = active_item.item_id();
13039            assert_ne!(
13040                third_item_id, multibuffer_item_id,
13041                "Should navigate into the 3rd buffer and activate it"
13042            );
13043            assert_ne!(third_item_id, first_item_id);
13044            assert_ne!(third_item_id, second_item_id);
13045            assert!(
13046                active_item.is_singleton(cx),
13047                "New active item should be a singleton buffer"
13048            );
13049            assert_eq!(
13050                active_item
13051                    .act_as::<Editor>(cx)
13052                    .expect("should have navigated into an editor")
13053                    .read(cx)
13054                    .text(cx),
13055                sample_text_3
13056            );
13057
13058            workspace
13059                .go_back(workspace.active_pane().downgrade(), window, cx)
13060                .detach_and_log_err(cx);
13061        })
13062        .unwrap();
13063    cx.executor().run_until_parked();
13064    workspace
13065        .update(cx, |workspace, _, cx| {
13066            let active_item = workspace
13067                .active_item(cx)
13068                .expect("should have an active item after navigating back from the 3rd buffer");
13069            assert_eq!(
13070                active_item.item_id(),
13071                multibuffer_item_id,
13072                "Should navigate back from the 3rd buffer to the multi buffer"
13073            );
13074            assert!(!active_item.is_singleton(cx));
13075        })
13076        .unwrap();
13077}
13078
13079#[gpui::test]
13080async fn test_toggle_selected_diff_hunks(
13081    executor: BackgroundExecutor,
13082    cx: &mut gpui::TestAppContext,
13083) {
13084    init_test(cx, |_| {});
13085
13086    let mut cx = EditorTestContext::new(cx).await;
13087
13088    let diff_base = r#"
13089        use some::mod;
13090
13091        const A: u32 = 42;
13092
13093        fn main() {
13094            println!("hello");
13095
13096            println!("world");
13097        }
13098        "#
13099    .unindent();
13100
13101    cx.set_state(
13102        &r#"
13103        use some::modified;
13104
13105        ˇ
13106        fn main() {
13107            println!("hello there");
13108
13109            println!("around the");
13110            println!("world");
13111        }
13112        "#
13113        .unindent(),
13114    );
13115
13116    cx.set_diff_base(&diff_base);
13117    executor.run_until_parked();
13118
13119    cx.update_editor(|editor, window, cx| {
13120        editor.go_to_next_hunk(&GoToHunk, window, cx);
13121        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13122    });
13123    executor.run_until_parked();
13124    cx.assert_state_with_diff(
13125        r#"
13126          use some::modified;
13127
13128
13129          fn main() {
13130        -     println!("hello");
13131        + ˇ    println!("hello there");
13132
13133              println!("around the");
13134              println!("world");
13135          }
13136        "#
13137        .unindent(),
13138    );
13139
13140    cx.update_editor(|editor, window, cx| {
13141        for _ in 0..2 {
13142            editor.go_to_next_hunk(&GoToHunk, window, cx);
13143            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13144        }
13145    });
13146    executor.run_until_parked();
13147    cx.assert_state_with_diff(
13148        r#"
13149        - use some::mod;
13150        + ˇuse some::modified;
13151
13152
13153          fn main() {
13154        -     println!("hello");
13155        +     println!("hello there");
13156
13157        +     println!("around the");
13158              println!("world");
13159          }
13160        "#
13161        .unindent(),
13162    );
13163
13164    cx.update_editor(|editor, window, cx| {
13165        editor.go_to_next_hunk(&GoToHunk, window, cx);
13166        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13167    });
13168    executor.run_until_parked();
13169    cx.assert_state_with_diff(
13170        r#"
13171        - use some::mod;
13172        + use some::modified;
13173
13174        - const A: u32 = 42;
13175          ˇ
13176          fn main() {
13177        -     println!("hello");
13178        +     println!("hello there");
13179
13180        +     println!("around the");
13181              println!("world");
13182          }
13183        "#
13184        .unindent(),
13185    );
13186
13187    cx.update_editor(|editor, window, cx| {
13188        editor.cancel(&Cancel, window, cx);
13189    });
13190
13191    cx.assert_state_with_diff(
13192        r#"
13193          use some::modified;
13194
13195          ˇ
13196          fn main() {
13197              println!("hello there");
13198
13199              println!("around the");
13200              println!("world");
13201          }
13202        "#
13203        .unindent(),
13204    );
13205}
13206
13207#[gpui::test]
13208async fn test_diff_base_change_with_expanded_diff_hunks(
13209    executor: BackgroundExecutor,
13210    cx: &mut gpui::TestAppContext,
13211) {
13212    init_test(cx, |_| {});
13213
13214    let mut cx = EditorTestContext::new(cx).await;
13215
13216    let diff_base = r#"
13217        use some::mod1;
13218        use some::mod2;
13219
13220        const A: u32 = 42;
13221        const B: u32 = 42;
13222        const C: u32 = 42;
13223
13224        fn main() {
13225            println!("hello");
13226
13227            println!("world");
13228        }
13229        "#
13230    .unindent();
13231
13232    cx.set_state(
13233        &r#"
13234        use some::mod2;
13235
13236        const A: u32 = 42;
13237        const C: u32 = 42;
13238
13239        fn main(ˇ) {
13240            //println!("hello");
13241
13242            println!("world");
13243            //
13244            //
13245        }
13246        "#
13247        .unindent(),
13248    );
13249
13250    cx.set_diff_base(&diff_base);
13251    executor.run_until_parked();
13252
13253    cx.update_editor(|editor, window, cx| {
13254        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13255    });
13256    executor.run_until_parked();
13257    cx.assert_state_with_diff(
13258        r#"
13259        - use some::mod1;
13260          use some::mod2;
13261
13262          const A: u32 = 42;
13263        - const B: u32 = 42;
13264          const C: u32 = 42;
13265
13266          fn main(ˇ) {
13267        -     println!("hello");
13268        +     //println!("hello");
13269
13270              println!("world");
13271        +     //
13272        +     //
13273          }
13274        "#
13275        .unindent(),
13276    );
13277
13278    cx.set_diff_base("new diff base!");
13279    executor.run_until_parked();
13280    cx.assert_state_with_diff(
13281        r#"
13282        - new diff base!
13283        + use some::mod2;
13284        +
13285        + const A: u32 = 42;
13286        + const C: u32 = 42;
13287        +
13288        + fn main(ˇ) {
13289        +     //println!("hello");
13290        +
13291        +     println!("world");
13292        +     //
13293        +     //
13294        + }
13295        "#
13296        .unindent(),
13297    );
13298}
13299
13300#[gpui::test]
13301async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13302    init_test(cx, |_| {});
13303
13304    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13305    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13306    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13307    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13308    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13309    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13310
13311    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13312    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13313    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13314
13315    let multi_buffer = cx.new(|cx| {
13316        let mut multibuffer = MultiBuffer::new(ReadWrite);
13317        multibuffer.push_excerpts(
13318            buffer_1.clone(),
13319            [
13320                ExcerptRange {
13321                    context: Point::new(0, 0)..Point::new(3, 0),
13322                    primary: None,
13323                },
13324                ExcerptRange {
13325                    context: Point::new(5, 0)..Point::new(7, 0),
13326                    primary: None,
13327                },
13328                ExcerptRange {
13329                    context: Point::new(9, 0)..Point::new(10, 3),
13330                    primary: None,
13331                },
13332            ],
13333            cx,
13334        );
13335        multibuffer.push_excerpts(
13336            buffer_2.clone(),
13337            [
13338                ExcerptRange {
13339                    context: Point::new(0, 0)..Point::new(3, 0),
13340                    primary: None,
13341                },
13342                ExcerptRange {
13343                    context: Point::new(5, 0)..Point::new(7, 0),
13344                    primary: None,
13345                },
13346                ExcerptRange {
13347                    context: Point::new(9, 0)..Point::new(10, 3),
13348                    primary: None,
13349                },
13350            ],
13351            cx,
13352        );
13353        multibuffer.push_excerpts(
13354            buffer_3.clone(),
13355            [
13356                ExcerptRange {
13357                    context: Point::new(0, 0)..Point::new(3, 0),
13358                    primary: None,
13359                },
13360                ExcerptRange {
13361                    context: Point::new(5, 0)..Point::new(7, 0),
13362                    primary: None,
13363                },
13364                ExcerptRange {
13365                    context: Point::new(9, 0)..Point::new(10, 3),
13366                    primary: None,
13367                },
13368            ],
13369            cx,
13370        );
13371        multibuffer
13372    });
13373
13374    let editor = cx.add_window(|window, cx| {
13375        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13376    });
13377    editor
13378        .update(cx, |editor, _window, cx| {
13379            for (buffer, diff_base) in [
13380                (buffer_1.clone(), file_1_old),
13381                (buffer_2.clone(), file_2_old),
13382                (buffer_3.clone(), file_3_old),
13383            ] {
13384                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13385                editor
13386                    .buffer
13387                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13388            }
13389        })
13390        .unwrap();
13391
13392    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13393    cx.run_until_parked();
13394
13395    cx.assert_editor_state(
13396        &"
13397            ˇaaa
13398            ccc
13399            ddd
13400
13401            ggg
13402            hhh
13403
13404
13405            lll
13406            mmm
13407            NNN
13408
13409            qqq
13410            rrr
13411
13412            uuu
13413            111
13414            222
13415            333
13416
13417            666
13418            777
13419
13420            000
13421            !!!"
13422        .unindent(),
13423    );
13424
13425    cx.update_editor(|editor, window, cx| {
13426        editor.select_all(&SelectAll, window, cx);
13427        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13428    });
13429    cx.executor().run_until_parked();
13430
13431    cx.assert_state_with_diff(
13432        "
13433            «aaa
13434          - bbb
13435            ccc
13436            ddd
13437
13438            ggg
13439            hhh
13440
13441
13442            lll
13443            mmm
13444          - nnn
13445          + NNN
13446
13447            qqq
13448            rrr
13449
13450            uuu
13451            111
13452            222
13453            333
13454
13455          + 666
13456            777
13457
13458            000
13459            !!!ˇ»"
13460            .unindent(),
13461    );
13462}
13463
13464#[gpui::test]
13465async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13466    init_test(cx, |_| {});
13467
13468    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13469    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13470
13471    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13472    let multi_buffer = cx.new(|cx| {
13473        let mut multibuffer = MultiBuffer::new(ReadWrite);
13474        multibuffer.push_excerpts(
13475            buffer.clone(),
13476            [
13477                ExcerptRange {
13478                    context: Point::new(0, 0)..Point::new(2, 0),
13479                    primary: None,
13480                },
13481                ExcerptRange {
13482                    context: Point::new(4, 0)..Point::new(7, 0),
13483                    primary: None,
13484                },
13485                ExcerptRange {
13486                    context: Point::new(9, 0)..Point::new(10, 0),
13487                    primary: None,
13488                },
13489            ],
13490            cx,
13491        );
13492        multibuffer
13493    });
13494
13495    let editor = cx.add_window(|window, cx| {
13496        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13497    });
13498    editor
13499        .update(cx, |editor, _window, cx| {
13500            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13501            editor
13502                .buffer
13503                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13504        })
13505        .unwrap();
13506
13507    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13508    cx.run_until_parked();
13509
13510    cx.update_editor(|editor, window, cx| {
13511        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13512    });
13513    cx.executor().run_until_parked();
13514
13515    // When the start of a hunk coincides with the start of its excerpt,
13516    // the hunk is expanded. When the start of a a hunk is earlier than
13517    // the start of its excerpt, the hunk is not expanded.
13518    cx.assert_state_with_diff(
13519        "
13520            ˇaaa
13521          - bbb
13522          + BBB
13523
13524          - ddd
13525          - eee
13526          + DDD
13527          + EEE
13528            fff
13529
13530            iii
13531        "
13532        .unindent(),
13533    );
13534}
13535
13536#[gpui::test]
13537async fn test_edits_around_expanded_insertion_hunks(
13538    executor: BackgroundExecutor,
13539    cx: &mut gpui::TestAppContext,
13540) {
13541    init_test(cx, |_| {});
13542
13543    let mut cx = EditorTestContext::new(cx).await;
13544
13545    let diff_base = r#"
13546        use some::mod1;
13547        use some::mod2;
13548
13549        const A: u32 = 42;
13550
13551        fn main() {
13552            println!("hello");
13553
13554            println!("world");
13555        }
13556        "#
13557    .unindent();
13558    executor.run_until_parked();
13559    cx.set_state(
13560        &r#"
13561        use some::mod1;
13562        use some::mod2;
13563
13564        const A: u32 = 42;
13565        const B: u32 = 42;
13566        const C: u32 = 42;
13567        ˇ
13568
13569        fn main() {
13570            println!("hello");
13571
13572            println!("world");
13573        }
13574        "#
13575        .unindent(),
13576    );
13577
13578    cx.set_diff_base(&diff_base);
13579    executor.run_until_parked();
13580
13581    cx.update_editor(|editor, window, cx| {
13582        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13583    });
13584    executor.run_until_parked();
13585
13586    cx.assert_state_with_diff(
13587        r#"
13588        use some::mod1;
13589        use some::mod2;
13590
13591        const A: u32 = 42;
13592      + const B: u32 = 42;
13593      + const C: u32 = 42;
13594      + ˇ
13595
13596        fn main() {
13597            println!("hello");
13598
13599            println!("world");
13600        }
13601      "#
13602        .unindent(),
13603    );
13604
13605    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13606    executor.run_until_parked();
13607
13608    cx.assert_state_with_diff(
13609        r#"
13610        use some::mod1;
13611        use some::mod2;
13612
13613        const A: u32 = 42;
13614      + const B: u32 = 42;
13615      + const C: u32 = 42;
13616      + const D: u32 = 42;
13617      + ˇ
13618
13619        fn main() {
13620            println!("hello");
13621
13622            println!("world");
13623        }
13624      "#
13625        .unindent(),
13626    );
13627
13628    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13629    executor.run_until_parked();
13630
13631    cx.assert_state_with_diff(
13632        r#"
13633        use some::mod1;
13634        use some::mod2;
13635
13636        const A: u32 = 42;
13637      + const B: u32 = 42;
13638      + const C: u32 = 42;
13639      + const D: u32 = 42;
13640      + const E: u32 = 42;
13641      + ˇ
13642
13643        fn main() {
13644            println!("hello");
13645
13646            println!("world");
13647        }
13648      "#
13649        .unindent(),
13650    );
13651
13652    cx.update_editor(|editor, window, cx| {
13653        editor.delete_line(&DeleteLine, window, cx);
13654    });
13655    executor.run_until_parked();
13656
13657    cx.assert_state_with_diff(
13658        r#"
13659        use some::mod1;
13660        use some::mod2;
13661
13662        const A: u32 = 42;
13663      + const B: u32 = 42;
13664      + const C: u32 = 42;
13665      + const D: u32 = 42;
13666      + const E: u32 = 42;
13667        ˇ
13668        fn main() {
13669            println!("hello");
13670
13671            println!("world");
13672        }
13673      "#
13674        .unindent(),
13675    );
13676
13677    cx.update_editor(|editor, window, cx| {
13678        editor.move_up(&MoveUp, window, cx);
13679        editor.delete_line(&DeleteLine, window, cx);
13680        editor.move_up(&MoveUp, window, cx);
13681        editor.delete_line(&DeleteLine, window, cx);
13682        editor.move_up(&MoveUp, window, cx);
13683        editor.delete_line(&DeleteLine, window, cx);
13684    });
13685    executor.run_until_parked();
13686    cx.assert_state_with_diff(
13687        r#"
13688        use some::mod1;
13689        use some::mod2;
13690
13691        const A: u32 = 42;
13692      + const B: u32 = 42;
13693        ˇ
13694        fn main() {
13695            println!("hello");
13696
13697            println!("world");
13698        }
13699      "#
13700        .unindent(),
13701    );
13702
13703    cx.update_editor(|editor, window, cx| {
13704        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13705        editor.delete_line(&DeleteLine, window, cx);
13706    });
13707    executor.run_until_parked();
13708    cx.assert_state_with_diff(
13709        r#"
13710        ˇ
13711        fn main() {
13712            println!("hello");
13713
13714            println!("world");
13715        }
13716      "#
13717        .unindent(),
13718    );
13719}
13720
13721#[gpui::test]
13722async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13723    init_test(cx, |_| {});
13724
13725    let mut cx = EditorTestContext::new(cx).await;
13726    cx.set_diff_base(indoc! { "
13727        one
13728        two
13729        three
13730        four
13731        five
13732        "
13733    });
13734    cx.set_state(indoc! { "
13735        one
13736        ˇthree
13737        five
13738    "});
13739    cx.run_until_parked();
13740    cx.update_editor(|editor, window, cx| {
13741        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13742    });
13743    cx.assert_state_with_diff(
13744        indoc! { "
13745        one
13746      - two
13747        ˇthree
13748      - four
13749        five
13750    "}
13751        .to_string(),
13752    );
13753    cx.update_editor(|editor, window, cx| {
13754        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13755    });
13756
13757    cx.assert_state_with_diff(
13758        indoc! { "
13759        one
13760        ˇthree
13761        five
13762    "}
13763        .to_string(),
13764    );
13765
13766    cx.set_state(indoc! { "
13767        one
13768        ˇTWO
13769        three
13770        four
13771        five
13772    "});
13773    cx.run_until_parked();
13774    cx.update_editor(|editor, window, cx| {
13775        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13776    });
13777
13778    cx.assert_state_with_diff(
13779        indoc! { "
13780            one
13781          - two
13782          + ˇTWO
13783            three
13784            four
13785            five
13786        "}
13787        .to_string(),
13788    );
13789    cx.update_editor(|editor, window, cx| {
13790        editor.move_up(&Default::default(), window, cx);
13791        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13792    });
13793    cx.assert_state_with_diff(
13794        indoc! { "
13795            one
13796            ˇTWO
13797            three
13798            four
13799            five
13800        "}
13801        .to_string(),
13802    );
13803}
13804
13805#[gpui::test]
13806async fn test_edits_around_expanded_deletion_hunks(
13807    executor: BackgroundExecutor,
13808    cx: &mut gpui::TestAppContext,
13809) {
13810    init_test(cx, |_| {});
13811
13812    let mut cx = EditorTestContext::new(cx).await;
13813
13814    let diff_base = r#"
13815        use some::mod1;
13816        use some::mod2;
13817
13818        const A: u32 = 42;
13819        const B: u32 = 42;
13820        const C: u32 = 42;
13821
13822
13823        fn main() {
13824            println!("hello");
13825
13826            println!("world");
13827        }
13828    "#
13829    .unindent();
13830    executor.run_until_parked();
13831    cx.set_state(
13832        &r#"
13833        use some::mod1;
13834        use some::mod2;
13835
13836        ˇconst B: u32 = 42;
13837        const C: u32 = 42;
13838
13839
13840        fn main() {
13841            println!("hello");
13842
13843            println!("world");
13844        }
13845        "#
13846        .unindent(),
13847    );
13848
13849    cx.set_diff_base(&diff_base);
13850    executor.run_until_parked();
13851
13852    cx.update_editor(|editor, window, cx| {
13853        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13854    });
13855    executor.run_until_parked();
13856
13857    cx.assert_state_with_diff(
13858        r#"
13859        use some::mod1;
13860        use some::mod2;
13861
13862      - const A: u32 = 42;
13863        ˇconst B: u32 = 42;
13864        const C: u32 = 42;
13865
13866
13867        fn main() {
13868            println!("hello");
13869
13870            println!("world");
13871        }
13872      "#
13873        .unindent(),
13874    );
13875
13876    cx.update_editor(|editor, window, cx| {
13877        editor.delete_line(&DeleteLine, window, cx);
13878    });
13879    executor.run_until_parked();
13880    cx.assert_state_with_diff(
13881        r#"
13882        use some::mod1;
13883        use some::mod2;
13884
13885      - const A: u32 = 42;
13886      - const B: u32 = 42;
13887        ˇconst C: u32 = 42;
13888
13889
13890        fn main() {
13891            println!("hello");
13892
13893            println!("world");
13894        }
13895      "#
13896        .unindent(),
13897    );
13898
13899    cx.update_editor(|editor, window, cx| {
13900        editor.delete_line(&DeleteLine, window, cx);
13901    });
13902    executor.run_until_parked();
13903    cx.assert_state_with_diff(
13904        r#"
13905        use some::mod1;
13906        use some::mod2;
13907
13908      - const A: u32 = 42;
13909      - const B: u32 = 42;
13910      - const C: u32 = 42;
13911        ˇ
13912
13913        fn main() {
13914            println!("hello");
13915
13916            println!("world");
13917        }
13918      "#
13919        .unindent(),
13920    );
13921
13922    cx.update_editor(|editor, window, cx| {
13923        editor.handle_input("replacement", window, cx);
13924    });
13925    executor.run_until_parked();
13926    cx.assert_state_with_diff(
13927        r#"
13928        use some::mod1;
13929        use some::mod2;
13930
13931      - const A: u32 = 42;
13932      - const B: u32 = 42;
13933      - const C: u32 = 42;
13934      -
13935      + replacementˇ
13936
13937        fn main() {
13938            println!("hello");
13939
13940            println!("world");
13941        }
13942      "#
13943        .unindent(),
13944    );
13945}
13946
13947#[gpui::test]
13948async fn test_backspace_after_deletion_hunk(
13949    executor: BackgroundExecutor,
13950    cx: &mut gpui::TestAppContext,
13951) {
13952    init_test(cx, |_| {});
13953
13954    let mut cx = EditorTestContext::new(cx).await;
13955
13956    let base_text = r#"
13957        one
13958        two
13959        three
13960        four
13961        five
13962    "#
13963    .unindent();
13964    executor.run_until_parked();
13965    cx.set_state(
13966        &r#"
13967        one
13968        two
13969        fˇour
13970        five
13971        "#
13972        .unindent(),
13973    );
13974
13975    cx.set_diff_base(&base_text);
13976    executor.run_until_parked();
13977
13978    cx.update_editor(|editor, window, cx| {
13979        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13980    });
13981    executor.run_until_parked();
13982
13983    cx.assert_state_with_diff(
13984        r#"
13985          one
13986          two
13987        - three
13988          fˇour
13989          five
13990        "#
13991        .unindent(),
13992    );
13993
13994    cx.update_editor(|editor, window, cx| {
13995        editor.backspace(&Backspace, window, cx);
13996        editor.backspace(&Backspace, window, cx);
13997    });
13998    executor.run_until_parked();
13999    cx.assert_state_with_diff(
14000        r#"
14001          one
14002          two
14003        - threeˇ
14004        - four
14005        + our
14006          five
14007        "#
14008        .unindent(),
14009    );
14010}
14011
14012#[gpui::test]
14013async fn test_edit_after_expanded_modification_hunk(
14014    executor: BackgroundExecutor,
14015    cx: &mut gpui::TestAppContext,
14016) {
14017    init_test(cx, |_| {});
14018
14019    let mut cx = EditorTestContext::new(cx).await;
14020
14021    let diff_base = r#"
14022        use some::mod1;
14023        use some::mod2;
14024
14025        const A: u32 = 42;
14026        const B: u32 = 42;
14027        const C: u32 = 42;
14028        const D: u32 = 42;
14029
14030
14031        fn main() {
14032            println!("hello");
14033
14034            println!("world");
14035        }"#
14036    .unindent();
14037
14038    cx.set_state(
14039        &r#"
14040        use some::mod1;
14041        use some::mod2;
14042
14043        const A: u32 = 42;
14044        const B: u32 = 42;
14045        const C: u32 = 43ˇ
14046        const D: u32 = 42;
14047
14048
14049        fn main() {
14050            println!("hello");
14051
14052            println!("world");
14053        }"#
14054        .unindent(),
14055    );
14056
14057    cx.set_diff_base(&diff_base);
14058    executor.run_until_parked();
14059    cx.update_editor(|editor, window, cx| {
14060        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
14061    });
14062    executor.run_until_parked();
14063
14064    cx.assert_state_with_diff(
14065        r#"
14066        use some::mod1;
14067        use some::mod2;
14068
14069        const A: u32 = 42;
14070        const B: u32 = 42;
14071      - const C: u32 = 42;
14072      + const C: u32 = 43ˇ
14073        const D: u32 = 42;
14074
14075
14076        fn main() {
14077            println!("hello");
14078
14079            println!("world");
14080        }"#
14081        .unindent(),
14082    );
14083
14084    cx.update_editor(|editor, window, cx| {
14085        editor.handle_input("\nnew_line\n", window, cx);
14086    });
14087    executor.run_until_parked();
14088
14089    cx.assert_state_with_diff(
14090        r#"
14091        use some::mod1;
14092        use some::mod2;
14093
14094        const A: u32 = 42;
14095        const B: u32 = 42;
14096      - const C: u32 = 42;
14097      + const C: u32 = 43
14098      + new_line
14099      + ˇ
14100        const D: u32 = 42;
14101
14102
14103        fn main() {
14104            println!("hello");
14105
14106            println!("world");
14107        }"#
14108        .unindent(),
14109    );
14110}
14111
14112#[gpui::test]
14113async fn test_stage_and_unstage_added_file_hunk(
14114    executor: BackgroundExecutor,
14115    cx: &mut gpui::TestAppContext,
14116) {
14117    init_test(cx, |_| {});
14118
14119    let mut cx = EditorTestContext::new(cx).await;
14120    cx.update_editor(|editor, _, cx| {
14121        editor.set_expand_all_diff_hunks(cx);
14122    });
14123
14124    let working_copy = r#"
14125            ˇfn main() {
14126                println!("hello, world!");
14127            }
14128        "#
14129    .unindent();
14130
14131    cx.set_state(&working_copy);
14132    executor.run_until_parked();
14133
14134    cx.assert_state_with_diff(
14135        r#"
14136            + ˇfn main() {
14137            +     println!("hello, world!");
14138            + }
14139        "#
14140        .unindent(),
14141    );
14142    cx.assert_index_text(None);
14143
14144    cx.update_editor(|editor, window, cx| {
14145        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14146    });
14147    executor.run_until_parked();
14148    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14149    cx.assert_state_with_diff(
14150        r#"
14151            + ˇfn main() {
14152            +     println!("hello, world!");
14153            + }
14154        "#
14155        .unindent(),
14156    );
14157
14158    cx.update_editor(|editor, window, cx| {
14159        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14160    });
14161    executor.run_until_parked();
14162    cx.assert_index_text(None);
14163}
14164
14165async fn setup_indent_guides_editor(
14166    text: &str,
14167    cx: &mut gpui::TestAppContext,
14168) -> (BufferId, EditorTestContext) {
14169    init_test(cx, |_| {});
14170
14171    let mut cx = EditorTestContext::new(cx).await;
14172
14173    let buffer_id = cx.update_editor(|editor, window, cx| {
14174        editor.set_text(text, window, cx);
14175        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14176
14177        buffer_ids[0]
14178    });
14179
14180    (buffer_id, cx)
14181}
14182
14183fn assert_indent_guides(
14184    range: Range<u32>,
14185    expected: Vec<IndentGuide>,
14186    active_indices: Option<Vec<usize>>,
14187    cx: &mut EditorTestContext,
14188) {
14189    let indent_guides = cx.update_editor(|editor, window, cx| {
14190        let snapshot = editor.snapshot(window, cx).display_snapshot;
14191        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14192            editor,
14193            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14194            true,
14195            &snapshot,
14196            cx,
14197        );
14198
14199        indent_guides.sort_by(|a, b| {
14200            a.depth.cmp(&b.depth).then(
14201                a.start_row
14202                    .cmp(&b.start_row)
14203                    .then(a.end_row.cmp(&b.end_row)),
14204            )
14205        });
14206        indent_guides
14207    });
14208
14209    if let Some(expected) = active_indices {
14210        let active_indices = cx.update_editor(|editor, window, cx| {
14211            let snapshot = editor.snapshot(window, cx).display_snapshot;
14212            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14213        });
14214
14215        assert_eq!(
14216            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14217            expected,
14218            "Active indent guide indices do not match"
14219        );
14220    }
14221
14222    assert_eq!(indent_guides, expected, "Indent guides do not match");
14223}
14224
14225fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14226    IndentGuide {
14227        buffer_id,
14228        start_row: MultiBufferRow(start_row),
14229        end_row: MultiBufferRow(end_row),
14230        depth,
14231        tab_size: 4,
14232        settings: IndentGuideSettings {
14233            enabled: true,
14234            line_width: 1,
14235            active_line_width: 1,
14236            ..Default::default()
14237        },
14238    }
14239}
14240
14241#[gpui::test]
14242async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14243    let (buffer_id, mut cx) = setup_indent_guides_editor(
14244        &"
14245    fn main() {
14246        let a = 1;
14247    }"
14248        .unindent(),
14249        cx,
14250    )
14251    .await;
14252
14253    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14254}
14255
14256#[gpui::test]
14257async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
14258    let (buffer_id, mut cx) = setup_indent_guides_editor(
14259        &"
14260    fn main() {
14261        let a = 1;
14262        let b = 2;
14263    }"
14264        .unindent(),
14265        cx,
14266    )
14267    .await;
14268
14269    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14270}
14271
14272#[gpui::test]
14273async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
14274    let (buffer_id, mut cx) = setup_indent_guides_editor(
14275        &"
14276    fn main() {
14277        let a = 1;
14278        if a == 3 {
14279            let b = 2;
14280        } else {
14281            let c = 3;
14282        }
14283    }"
14284        .unindent(),
14285        cx,
14286    )
14287    .await;
14288
14289    assert_indent_guides(
14290        0..8,
14291        vec![
14292            indent_guide(buffer_id, 1, 6, 0),
14293            indent_guide(buffer_id, 3, 3, 1),
14294            indent_guide(buffer_id, 5, 5, 1),
14295        ],
14296        None,
14297        &mut cx,
14298    );
14299}
14300
14301#[gpui::test]
14302async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
14303    let (buffer_id, mut cx) = setup_indent_guides_editor(
14304        &"
14305    fn main() {
14306        let a = 1;
14307            let b = 2;
14308        let c = 3;
14309    }"
14310        .unindent(),
14311        cx,
14312    )
14313    .await;
14314
14315    assert_indent_guides(
14316        0..5,
14317        vec![
14318            indent_guide(buffer_id, 1, 3, 0),
14319            indent_guide(buffer_id, 2, 2, 1),
14320        ],
14321        None,
14322        &mut cx,
14323    );
14324}
14325
14326#[gpui::test]
14327async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14328    let (buffer_id, mut cx) = setup_indent_guides_editor(
14329        &"
14330        fn main() {
14331            let a = 1;
14332
14333            let c = 3;
14334        }"
14335        .unindent(),
14336        cx,
14337    )
14338    .await;
14339
14340    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14341}
14342
14343#[gpui::test]
14344async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14345    let (buffer_id, mut cx) = setup_indent_guides_editor(
14346        &"
14347        fn main() {
14348            let a = 1;
14349
14350            let c = 3;
14351
14352            if a == 3 {
14353                let b = 2;
14354            } else {
14355                let c = 3;
14356            }
14357        }"
14358        .unindent(),
14359        cx,
14360    )
14361    .await;
14362
14363    assert_indent_guides(
14364        0..11,
14365        vec![
14366            indent_guide(buffer_id, 1, 9, 0),
14367            indent_guide(buffer_id, 6, 6, 1),
14368            indent_guide(buffer_id, 8, 8, 1),
14369        ],
14370        None,
14371        &mut cx,
14372    );
14373}
14374
14375#[gpui::test]
14376async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14377    let (buffer_id, mut cx) = setup_indent_guides_editor(
14378        &"
14379        fn main() {
14380            let a = 1;
14381
14382            let c = 3;
14383
14384            if a == 3 {
14385                let b = 2;
14386            } else {
14387                let c = 3;
14388            }
14389        }"
14390        .unindent(),
14391        cx,
14392    )
14393    .await;
14394
14395    assert_indent_guides(
14396        1..11,
14397        vec![
14398            indent_guide(buffer_id, 1, 9, 0),
14399            indent_guide(buffer_id, 6, 6, 1),
14400            indent_guide(buffer_id, 8, 8, 1),
14401        ],
14402        None,
14403        &mut cx,
14404    );
14405}
14406
14407#[gpui::test]
14408async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14409    let (buffer_id, mut cx) = setup_indent_guides_editor(
14410        &"
14411        fn main() {
14412            let a = 1;
14413
14414            let c = 3;
14415
14416            if a == 3 {
14417                let b = 2;
14418            } else {
14419                let c = 3;
14420            }
14421        }"
14422        .unindent(),
14423        cx,
14424    )
14425    .await;
14426
14427    assert_indent_guides(
14428        1..10,
14429        vec![
14430            indent_guide(buffer_id, 1, 9, 0),
14431            indent_guide(buffer_id, 6, 6, 1),
14432            indent_guide(buffer_id, 8, 8, 1),
14433        ],
14434        None,
14435        &mut cx,
14436    );
14437}
14438
14439#[gpui::test]
14440async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14441    let (buffer_id, mut cx) = setup_indent_guides_editor(
14442        &"
14443        block1
14444            block2
14445                block3
14446                    block4
14447            block2
14448        block1
14449        block1"
14450            .unindent(),
14451        cx,
14452    )
14453    .await;
14454
14455    assert_indent_guides(
14456        1..10,
14457        vec![
14458            indent_guide(buffer_id, 1, 4, 0),
14459            indent_guide(buffer_id, 2, 3, 1),
14460            indent_guide(buffer_id, 3, 3, 2),
14461        ],
14462        None,
14463        &mut cx,
14464    );
14465}
14466
14467#[gpui::test]
14468async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14469    let (buffer_id, mut cx) = setup_indent_guides_editor(
14470        &"
14471        block1
14472            block2
14473                block3
14474
14475        block1
14476        block1"
14477            .unindent(),
14478        cx,
14479    )
14480    .await;
14481
14482    assert_indent_guides(
14483        0..6,
14484        vec![
14485            indent_guide(buffer_id, 1, 2, 0),
14486            indent_guide(buffer_id, 2, 2, 1),
14487        ],
14488        None,
14489        &mut cx,
14490    );
14491}
14492
14493#[gpui::test]
14494async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14495    let (buffer_id, mut cx) = setup_indent_guides_editor(
14496        &"
14497        block1
14498
14499
14500
14501            block2
14502        "
14503        .unindent(),
14504        cx,
14505    )
14506    .await;
14507
14508    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14509}
14510
14511#[gpui::test]
14512async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14513    let (buffer_id, mut cx) = setup_indent_guides_editor(
14514        &"
14515        def a:
14516        \tb = 3
14517        \tif True:
14518        \t\tc = 4
14519        \t\td = 5
14520        \tprint(b)
14521        "
14522        .unindent(),
14523        cx,
14524    )
14525    .await;
14526
14527    assert_indent_guides(
14528        0..6,
14529        vec![
14530            indent_guide(buffer_id, 1, 6, 0),
14531            indent_guide(buffer_id, 3, 4, 1),
14532        ],
14533        None,
14534        &mut cx,
14535    );
14536}
14537
14538#[gpui::test]
14539async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14540    let (buffer_id, mut cx) = setup_indent_guides_editor(
14541        &"
14542    fn main() {
14543        let a = 1;
14544    }"
14545        .unindent(),
14546        cx,
14547    )
14548    .await;
14549
14550    cx.update_editor(|editor, window, cx| {
14551        editor.change_selections(None, window, cx, |s| {
14552            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14553        });
14554    });
14555
14556    assert_indent_guides(
14557        0..3,
14558        vec![indent_guide(buffer_id, 1, 1, 0)],
14559        Some(vec![0]),
14560        &mut cx,
14561    );
14562}
14563
14564#[gpui::test]
14565async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14566    let (buffer_id, mut cx) = setup_indent_guides_editor(
14567        &"
14568    fn main() {
14569        if 1 == 2 {
14570            let a = 1;
14571        }
14572    }"
14573        .unindent(),
14574        cx,
14575    )
14576    .await;
14577
14578    cx.update_editor(|editor, window, cx| {
14579        editor.change_selections(None, window, cx, |s| {
14580            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14581        });
14582    });
14583
14584    assert_indent_guides(
14585        0..4,
14586        vec![
14587            indent_guide(buffer_id, 1, 3, 0),
14588            indent_guide(buffer_id, 2, 2, 1),
14589        ],
14590        Some(vec![1]),
14591        &mut cx,
14592    );
14593
14594    cx.update_editor(|editor, window, cx| {
14595        editor.change_selections(None, window, cx, |s| {
14596            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14597        });
14598    });
14599
14600    assert_indent_guides(
14601        0..4,
14602        vec![
14603            indent_guide(buffer_id, 1, 3, 0),
14604            indent_guide(buffer_id, 2, 2, 1),
14605        ],
14606        Some(vec![1]),
14607        &mut cx,
14608    );
14609
14610    cx.update_editor(|editor, window, cx| {
14611        editor.change_selections(None, window, cx, |s| {
14612            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14613        });
14614    });
14615
14616    assert_indent_guides(
14617        0..4,
14618        vec![
14619            indent_guide(buffer_id, 1, 3, 0),
14620            indent_guide(buffer_id, 2, 2, 1),
14621        ],
14622        Some(vec![0]),
14623        &mut cx,
14624    );
14625}
14626
14627#[gpui::test]
14628async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14629    let (buffer_id, mut cx) = setup_indent_guides_editor(
14630        &"
14631    fn main() {
14632        let a = 1;
14633
14634        let b = 2;
14635    }"
14636        .unindent(),
14637        cx,
14638    )
14639    .await;
14640
14641    cx.update_editor(|editor, window, cx| {
14642        editor.change_selections(None, window, cx, |s| {
14643            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14644        });
14645    });
14646
14647    assert_indent_guides(
14648        0..5,
14649        vec![indent_guide(buffer_id, 1, 3, 0)],
14650        Some(vec![0]),
14651        &mut cx,
14652    );
14653}
14654
14655#[gpui::test]
14656async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14657    let (buffer_id, mut cx) = setup_indent_guides_editor(
14658        &"
14659    def m:
14660        a = 1
14661        pass"
14662            .unindent(),
14663        cx,
14664    )
14665    .await;
14666
14667    cx.update_editor(|editor, window, cx| {
14668        editor.change_selections(None, window, cx, |s| {
14669            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14670        });
14671    });
14672
14673    assert_indent_guides(
14674        0..3,
14675        vec![indent_guide(buffer_id, 1, 2, 0)],
14676        Some(vec![0]),
14677        &mut cx,
14678    );
14679}
14680
14681#[gpui::test]
14682async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14683    init_test(cx, |_| {});
14684    let mut cx = EditorTestContext::new(cx).await;
14685    let text = indoc! {
14686        "
14687        impl A {
14688            fn b() {
14689                0;
14690                3;
14691                5;
14692                6;
14693                7;
14694            }
14695        }
14696        "
14697    };
14698    let base_text = indoc! {
14699        "
14700        impl A {
14701            fn b() {
14702                0;
14703                1;
14704                2;
14705                3;
14706                4;
14707            }
14708            fn c() {
14709                5;
14710                6;
14711                7;
14712            }
14713        }
14714        "
14715    };
14716
14717    cx.update_editor(|editor, window, cx| {
14718        editor.set_text(text, window, cx);
14719
14720        editor.buffer().update(cx, |multibuffer, cx| {
14721            let buffer = multibuffer.as_singleton().unwrap();
14722            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14723
14724            multibuffer.set_all_diff_hunks_expanded(cx);
14725            multibuffer.add_diff(diff, cx);
14726
14727            buffer.read(cx).remote_id()
14728        })
14729    });
14730    cx.run_until_parked();
14731
14732    cx.assert_state_with_diff(
14733        indoc! { "
14734          impl A {
14735              fn b() {
14736                  0;
14737        -         1;
14738        -         2;
14739                  3;
14740        -         4;
14741        -     }
14742        -     fn c() {
14743                  5;
14744                  6;
14745                  7;
14746              }
14747          }
14748          ˇ"
14749        }
14750        .to_string(),
14751    );
14752
14753    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14754        editor
14755            .snapshot(window, cx)
14756            .buffer_snapshot
14757            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14758            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14759            .collect::<Vec<_>>()
14760    });
14761    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14762    assert_eq!(
14763        actual_guides,
14764        vec![
14765            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14766            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14767            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14768        ]
14769    );
14770}
14771
14772#[gpui::test]
14773fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14774    init_test(cx, |_| {});
14775
14776    let editor = cx.add_window(|window, cx| {
14777        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14778        build_editor(buffer, window, cx)
14779    });
14780
14781    let render_args = Arc::new(Mutex::new(None));
14782    let snapshot = editor
14783        .update(cx, |editor, window, cx| {
14784            let snapshot = editor.buffer().read(cx).snapshot(cx);
14785            let range =
14786                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14787
14788            struct RenderArgs {
14789                row: MultiBufferRow,
14790                folded: bool,
14791                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14792            }
14793
14794            let crease = Crease::inline(
14795                range,
14796                FoldPlaceholder::test(),
14797                {
14798                    let toggle_callback = render_args.clone();
14799                    move |row, folded, callback, _window, _cx| {
14800                        *toggle_callback.lock() = Some(RenderArgs {
14801                            row,
14802                            folded,
14803                            callback,
14804                        });
14805                        div()
14806                    }
14807                },
14808                |_row, _folded, _window, _cx| div(),
14809            );
14810
14811            editor.insert_creases(Some(crease), cx);
14812            let snapshot = editor.snapshot(window, cx);
14813            let _div = snapshot.render_crease_toggle(
14814                MultiBufferRow(1),
14815                false,
14816                cx.entity().clone(),
14817                window,
14818                cx,
14819            );
14820            snapshot
14821        })
14822        .unwrap();
14823
14824    let render_args = render_args.lock().take().unwrap();
14825    assert_eq!(render_args.row, MultiBufferRow(1));
14826    assert!(!render_args.folded);
14827    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14828
14829    cx.update_window(*editor, |_, window, cx| {
14830        (render_args.callback)(true, window, cx)
14831    })
14832    .unwrap();
14833    let snapshot = editor
14834        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14835        .unwrap();
14836    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14837
14838    cx.update_window(*editor, |_, window, cx| {
14839        (render_args.callback)(false, window, cx)
14840    })
14841    .unwrap();
14842    let snapshot = editor
14843        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14844        .unwrap();
14845    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14846}
14847
14848#[gpui::test]
14849async fn test_input_text(cx: &mut gpui::TestAppContext) {
14850    init_test(cx, |_| {});
14851    let mut cx = EditorTestContext::new(cx).await;
14852
14853    cx.set_state(
14854        &r#"ˇone
14855        two
14856
14857        three
14858        fourˇ
14859        five
14860
14861        siˇx"#
14862            .unindent(),
14863    );
14864
14865    cx.dispatch_action(HandleInput(String::new()));
14866    cx.assert_editor_state(
14867        &r#"ˇone
14868        two
14869
14870        three
14871        fourˇ
14872        five
14873
14874        siˇx"#
14875            .unindent(),
14876    );
14877
14878    cx.dispatch_action(HandleInput("AAAA".to_string()));
14879    cx.assert_editor_state(
14880        &r#"AAAAˇone
14881        two
14882
14883        three
14884        fourAAAAˇ
14885        five
14886
14887        siAAAAˇx"#
14888            .unindent(),
14889    );
14890}
14891
14892#[gpui::test]
14893async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14894    init_test(cx, |_| {});
14895
14896    let mut cx = EditorTestContext::new(cx).await;
14897    cx.set_state(
14898        r#"let foo = 1;
14899let foo = 2;
14900let foo = 3;
14901let fooˇ = 4;
14902let foo = 5;
14903let foo = 6;
14904let foo = 7;
14905let foo = 8;
14906let foo = 9;
14907let foo = 10;
14908let foo = 11;
14909let foo = 12;
14910let foo = 13;
14911let foo = 14;
14912let foo = 15;"#,
14913    );
14914
14915    cx.update_editor(|e, window, cx| {
14916        assert_eq!(
14917            e.next_scroll_position,
14918            NextScrollCursorCenterTopBottom::Center,
14919            "Default next scroll direction is center",
14920        );
14921
14922        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14923        assert_eq!(
14924            e.next_scroll_position,
14925            NextScrollCursorCenterTopBottom::Top,
14926            "After center, next scroll direction should be top",
14927        );
14928
14929        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14930        assert_eq!(
14931            e.next_scroll_position,
14932            NextScrollCursorCenterTopBottom::Bottom,
14933            "After top, next scroll direction should be bottom",
14934        );
14935
14936        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14937        assert_eq!(
14938            e.next_scroll_position,
14939            NextScrollCursorCenterTopBottom::Center,
14940            "After bottom, scrolling should start over",
14941        );
14942
14943        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14944        assert_eq!(
14945            e.next_scroll_position,
14946            NextScrollCursorCenterTopBottom::Top,
14947            "Scrolling continues if retriggered fast enough"
14948        );
14949    });
14950
14951    cx.executor()
14952        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14953    cx.executor().run_until_parked();
14954    cx.update_editor(|e, _, _| {
14955        assert_eq!(
14956            e.next_scroll_position,
14957            NextScrollCursorCenterTopBottom::Center,
14958            "If scrolling is not triggered fast enough, it should reset"
14959        );
14960    });
14961}
14962
14963#[gpui::test]
14964async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14965    init_test(cx, |_| {});
14966    let mut cx = EditorLspTestContext::new_rust(
14967        lsp::ServerCapabilities {
14968            definition_provider: Some(lsp::OneOf::Left(true)),
14969            references_provider: Some(lsp::OneOf::Left(true)),
14970            ..lsp::ServerCapabilities::default()
14971        },
14972        cx,
14973    )
14974    .await;
14975
14976    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14977        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14978            move |params, _| async move {
14979                if empty_go_to_definition {
14980                    Ok(None)
14981                } else {
14982                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14983                        uri: params.text_document_position_params.text_document.uri,
14984                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
14985                    })))
14986                }
14987            },
14988        );
14989        let references =
14990            cx.lsp
14991                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
14992                    Ok(Some(vec![lsp::Location {
14993                        uri: params.text_document_position.text_document.uri,
14994                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
14995                    }]))
14996                });
14997        (go_to_definition, references)
14998    };
14999
15000    cx.set_state(
15001        &r#"fn one() {
15002            let mut a = ˇtwo();
15003        }
15004
15005        fn two() {}"#
15006            .unindent(),
15007    );
15008    set_up_lsp_handlers(false, &mut cx);
15009    let navigated = cx
15010        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15011        .await
15012        .expect("Failed to navigate to definition");
15013    assert_eq!(
15014        navigated,
15015        Navigated::Yes,
15016        "Should have navigated to definition from the GetDefinition response"
15017    );
15018    cx.assert_editor_state(
15019        &r#"fn one() {
15020            let mut a = two();
15021        }
15022
15023        fn «twoˇ»() {}"#
15024            .unindent(),
15025    );
15026
15027    let editors = cx.update_workspace(|workspace, _, cx| {
15028        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15029    });
15030    cx.update_editor(|_, _, test_editor_cx| {
15031        assert_eq!(
15032            editors.len(),
15033            1,
15034            "Initially, only one, test, editor should be open in the workspace"
15035        );
15036        assert_eq!(
15037            test_editor_cx.entity(),
15038            editors.last().expect("Asserted len is 1").clone()
15039        );
15040    });
15041
15042    set_up_lsp_handlers(true, &mut cx);
15043    let navigated = cx
15044        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15045        .await
15046        .expect("Failed to navigate to lookup references");
15047    assert_eq!(
15048        navigated,
15049        Navigated::Yes,
15050        "Should have navigated to references as a fallback after empty GoToDefinition response"
15051    );
15052    // We should not change the selections in the existing file,
15053    // if opening another milti buffer with the references
15054    cx.assert_editor_state(
15055        &r#"fn one() {
15056            let mut a = two();
15057        }
15058
15059        fn «twoˇ»() {}"#
15060            .unindent(),
15061    );
15062    let editors = cx.update_workspace(|workspace, _, cx| {
15063        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15064    });
15065    cx.update_editor(|_, _, test_editor_cx| {
15066        assert_eq!(
15067            editors.len(),
15068            2,
15069            "After falling back to references search, we open a new editor with the results"
15070        );
15071        let references_fallback_text = editors
15072            .into_iter()
15073            .find(|new_editor| *new_editor != test_editor_cx.entity())
15074            .expect("Should have one non-test editor now")
15075            .read(test_editor_cx)
15076            .text(test_editor_cx);
15077        assert_eq!(
15078            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15079            "Should use the range from the references response and not the GoToDefinition one"
15080        );
15081    });
15082}
15083
15084#[gpui::test]
15085async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
15086    init_test(cx, |_| {});
15087
15088    let language = Arc::new(Language::new(
15089        LanguageConfig::default(),
15090        Some(tree_sitter_rust::LANGUAGE.into()),
15091    ));
15092
15093    let text = r#"
15094        #[cfg(test)]
15095        mod tests() {
15096            #[test]
15097            fn runnable_1() {
15098                let a = 1;
15099            }
15100
15101            #[test]
15102            fn runnable_2() {
15103                let a = 1;
15104                let b = 2;
15105            }
15106        }
15107    "#
15108    .unindent();
15109
15110    let fs = FakeFs::new(cx.executor());
15111    fs.insert_file("/file.rs", Default::default()).await;
15112
15113    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15114    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15115    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15116    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15117    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15118
15119    let editor = cx.new_window_entity(|window, cx| {
15120        Editor::new(
15121            EditorMode::Full,
15122            multi_buffer,
15123            Some(project.clone()),
15124            true,
15125            window,
15126            cx,
15127        )
15128    });
15129
15130    editor.update_in(cx, |editor, window, cx| {
15131        editor.tasks.insert(
15132            (buffer.read(cx).remote_id(), 3),
15133            RunnableTasks {
15134                templates: vec![],
15135                offset: MultiBufferOffset(43),
15136                column: 0,
15137                extra_variables: HashMap::default(),
15138                context_range: BufferOffset(43)..BufferOffset(85),
15139            },
15140        );
15141        editor.tasks.insert(
15142            (buffer.read(cx).remote_id(), 8),
15143            RunnableTasks {
15144                templates: vec![],
15145                offset: MultiBufferOffset(86),
15146                column: 0,
15147                extra_variables: HashMap::default(),
15148                context_range: BufferOffset(86)..BufferOffset(191),
15149            },
15150        );
15151
15152        // Test finding task when cursor is inside function body
15153        editor.change_selections(None, window, cx, |s| {
15154            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15155        });
15156        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15157        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15158
15159        // Test finding task when cursor is on function name
15160        editor.change_selections(None, window, cx, |s| {
15161            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15162        });
15163        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15164        assert_eq!(row, 8, "Should find task when cursor is on function name");
15165    });
15166}
15167
15168#[gpui::test]
15169async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
15170    init_test(cx, |_| {});
15171
15172    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15173    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15174    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15175
15176    let fs = FakeFs::new(cx.executor());
15177    fs.insert_tree(
15178        path!("/a"),
15179        json!({
15180            "first.rs": sample_text_1,
15181            "second.rs": sample_text_2,
15182            "third.rs": sample_text_3,
15183        }),
15184    )
15185    .await;
15186    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15187    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15188    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15189    let worktree = project.update(cx, |project, cx| {
15190        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15191        assert_eq!(worktrees.len(), 1);
15192        worktrees.pop().unwrap()
15193    });
15194    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15195
15196    let buffer_1 = project
15197        .update(cx, |project, cx| {
15198            project.open_buffer((worktree_id, "first.rs"), cx)
15199        })
15200        .await
15201        .unwrap();
15202    let buffer_2 = project
15203        .update(cx, |project, cx| {
15204            project.open_buffer((worktree_id, "second.rs"), cx)
15205        })
15206        .await
15207        .unwrap();
15208    let buffer_3 = project
15209        .update(cx, |project, cx| {
15210            project.open_buffer((worktree_id, "third.rs"), cx)
15211        })
15212        .await
15213        .unwrap();
15214
15215    let multi_buffer = cx.new(|cx| {
15216        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15217        multi_buffer.push_excerpts(
15218            buffer_1.clone(),
15219            [
15220                ExcerptRange {
15221                    context: Point::new(0, 0)..Point::new(3, 0),
15222                    primary: None,
15223                },
15224                ExcerptRange {
15225                    context: Point::new(5, 0)..Point::new(7, 0),
15226                    primary: None,
15227                },
15228                ExcerptRange {
15229                    context: Point::new(9, 0)..Point::new(10, 4),
15230                    primary: None,
15231                },
15232            ],
15233            cx,
15234        );
15235        multi_buffer.push_excerpts(
15236            buffer_2.clone(),
15237            [
15238                ExcerptRange {
15239                    context: Point::new(0, 0)..Point::new(3, 0),
15240                    primary: None,
15241                },
15242                ExcerptRange {
15243                    context: Point::new(5, 0)..Point::new(7, 0),
15244                    primary: None,
15245                },
15246                ExcerptRange {
15247                    context: Point::new(9, 0)..Point::new(10, 4),
15248                    primary: None,
15249                },
15250            ],
15251            cx,
15252        );
15253        multi_buffer.push_excerpts(
15254            buffer_3.clone(),
15255            [
15256                ExcerptRange {
15257                    context: Point::new(0, 0)..Point::new(3, 0),
15258                    primary: None,
15259                },
15260                ExcerptRange {
15261                    context: Point::new(5, 0)..Point::new(7, 0),
15262                    primary: None,
15263                },
15264                ExcerptRange {
15265                    context: Point::new(9, 0)..Point::new(10, 4),
15266                    primary: None,
15267                },
15268            ],
15269            cx,
15270        );
15271        multi_buffer
15272    });
15273    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15274        Editor::new(
15275            EditorMode::Full,
15276            multi_buffer,
15277            Some(project.clone()),
15278            true,
15279            window,
15280            cx,
15281        )
15282    });
15283
15284    let full_text = "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n";
15285    assert_eq!(
15286        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15287        full_text,
15288    );
15289
15290    multi_buffer_editor.update(cx, |editor, cx| {
15291        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15292    });
15293    assert_eq!(
15294        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15295        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15296        "After folding the first buffer, its text should not be displayed"
15297    );
15298
15299    multi_buffer_editor.update(cx, |editor, cx| {
15300        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15301    });
15302    assert_eq!(
15303        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15304        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15305        "After folding the second buffer, its text should not be displayed"
15306    );
15307
15308    multi_buffer_editor.update(cx, |editor, cx| {
15309        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15310    });
15311    assert_eq!(
15312        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15313        "\n\n\n\n\n",
15314        "After folding the third buffer, its text should not be displayed"
15315    );
15316
15317    // Emulate selection inside the fold logic, that should work
15318    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15319        editor
15320            .snapshot(window, cx)
15321            .next_line_boundary(Point::new(0, 4));
15322    });
15323
15324    multi_buffer_editor.update(cx, |editor, cx| {
15325        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15326    });
15327    assert_eq!(
15328        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15329        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15330        "After unfolding the second buffer, its text should be displayed"
15331    );
15332
15333    multi_buffer_editor.update(cx, |editor, cx| {
15334        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15335    });
15336    assert_eq!(
15337        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15338        "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15339        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15340    );
15341
15342    multi_buffer_editor.update(cx, |editor, cx| {
15343        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15344    });
15345    assert_eq!(
15346        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15347        full_text,
15348        "After unfolding the all buffers, all original text should be displayed"
15349    );
15350}
15351
15352#[gpui::test]
15353async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15354    init_test(cx, |_| {});
15355
15356    let sample_text_1 = "1111\n2222\n3333".to_string();
15357    let sample_text_2 = "4444\n5555\n6666".to_string();
15358    let sample_text_3 = "7777\n8888\n9999".to_string();
15359
15360    let fs = FakeFs::new(cx.executor());
15361    fs.insert_tree(
15362        path!("/a"),
15363        json!({
15364            "first.rs": sample_text_1,
15365            "second.rs": sample_text_2,
15366            "third.rs": sample_text_3,
15367        }),
15368    )
15369    .await;
15370    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15371    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15372    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15373    let worktree = project.update(cx, |project, cx| {
15374        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15375        assert_eq!(worktrees.len(), 1);
15376        worktrees.pop().unwrap()
15377    });
15378    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15379
15380    let buffer_1 = project
15381        .update(cx, |project, cx| {
15382            project.open_buffer((worktree_id, "first.rs"), cx)
15383        })
15384        .await
15385        .unwrap();
15386    let buffer_2 = project
15387        .update(cx, |project, cx| {
15388            project.open_buffer((worktree_id, "second.rs"), cx)
15389        })
15390        .await
15391        .unwrap();
15392    let buffer_3 = project
15393        .update(cx, |project, cx| {
15394            project.open_buffer((worktree_id, "third.rs"), cx)
15395        })
15396        .await
15397        .unwrap();
15398
15399    let multi_buffer = cx.new(|cx| {
15400        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15401        multi_buffer.push_excerpts(
15402            buffer_1.clone(),
15403            [ExcerptRange {
15404                context: Point::new(0, 0)..Point::new(3, 0),
15405                primary: None,
15406            }],
15407            cx,
15408        );
15409        multi_buffer.push_excerpts(
15410            buffer_2.clone(),
15411            [ExcerptRange {
15412                context: Point::new(0, 0)..Point::new(3, 0),
15413                primary: None,
15414            }],
15415            cx,
15416        );
15417        multi_buffer.push_excerpts(
15418            buffer_3.clone(),
15419            [ExcerptRange {
15420                context: Point::new(0, 0)..Point::new(3, 0),
15421                primary: None,
15422            }],
15423            cx,
15424        );
15425        multi_buffer
15426    });
15427
15428    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15429        Editor::new(
15430            EditorMode::Full,
15431            multi_buffer,
15432            Some(project.clone()),
15433            true,
15434            window,
15435            cx,
15436        )
15437    });
15438
15439    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15440    assert_eq!(
15441        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15442        full_text,
15443    );
15444
15445    multi_buffer_editor.update(cx, |editor, cx| {
15446        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15447    });
15448    assert_eq!(
15449        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15450        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15451        "After folding the first buffer, its text should not be displayed"
15452    );
15453
15454    multi_buffer_editor.update(cx, |editor, cx| {
15455        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15456    });
15457
15458    assert_eq!(
15459        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15460        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15461        "After folding the second buffer, its text should not be displayed"
15462    );
15463
15464    multi_buffer_editor.update(cx, |editor, cx| {
15465        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15466    });
15467    assert_eq!(
15468        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15469        "\n\n\n\n\n",
15470        "After folding the third buffer, its text should not be displayed"
15471    );
15472
15473    multi_buffer_editor.update(cx, |editor, cx| {
15474        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15475    });
15476    assert_eq!(
15477        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15478        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15479        "After unfolding the second buffer, its text should be displayed"
15480    );
15481
15482    multi_buffer_editor.update(cx, |editor, cx| {
15483        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15484    });
15485    assert_eq!(
15486        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15487        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15488        "After unfolding the first buffer, its text should be displayed"
15489    );
15490
15491    multi_buffer_editor.update(cx, |editor, cx| {
15492        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15493    });
15494    assert_eq!(
15495        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15496        full_text,
15497        "After unfolding all buffers, all original text should be displayed"
15498    );
15499}
15500
15501#[gpui::test]
15502async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15503    init_test(cx, |_| {});
15504
15505    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15506
15507    let fs = FakeFs::new(cx.executor());
15508    fs.insert_tree(
15509        path!("/a"),
15510        json!({
15511            "main.rs": sample_text,
15512        }),
15513    )
15514    .await;
15515    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15516    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15517    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15518    let worktree = project.update(cx, |project, cx| {
15519        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15520        assert_eq!(worktrees.len(), 1);
15521        worktrees.pop().unwrap()
15522    });
15523    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15524
15525    let buffer_1 = project
15526        .update(cx, |project, cx| {
15527            project.open_buffer((worktree_id, "main.rs"), cx)
15528        })
15529        .await
15530        .unwrap();
15531
15532    let multi_buffer = cx.new(|cx| {
15533        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15534        multi_buffer.push_excerpts(
15535            buffer_1.clone(),
15536            [ExcerptRange {
15537                context: Point::new(0, 0)
15538                    ..Point::new(
15539                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15540                        0,
15541                    ),
15542                primary: None,
15543            }],
15544            cx,
15545        );
15546        multi_buffer
15547    });
15548    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15549        Editor::new(
15550            EditorMode::Full,
15551            multi_buffer,
15552            Some(project.clone()),
15553            true,
15554            window,
15555            cx,
15556        )
15557    });
15558
15559    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15560    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15561        enum TestHighlight {}
15562        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15563        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15564        editor.highlight_text::<TestHighlight>(
15565            vec![highlight_range.clone()],
15566            HighlightStyle::color(Hsla::green()),
15567            cx,
15568        );
15569        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15570    });
15571
15572    let full_text = format!("\n\n\n{sample_text}\n");
15573    assert_eq!(
15574        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15575        full_text,
15576    );
15577}
15578
15579#[gpui::test]
15580async fn test_inline_completion_text(cx: &mut TestAppContext) {
15581    init_test(cx, |_| {});
15582
15583    // Simple insertion
15584    assert_highlighted_edits(
15585        "Hello, world!",
15586        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15587        true,
15588        cx,
15589        |highlighted_edits, cx| {
15590            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15591            assert_eq!(highlighted_edits.highlights.len(), 1);
15592            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15593            assert_eq!(
15594                highlighted_edits.highlights[0].1.background_color,
15595                Some(cx.theme().status().created_background)
15596            );
15597        },
15598    )
15599    .await;
15600
15601    // Replacement
15602    assert_highlighted_edits(
15603        "This is a test.",
15604        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15605        false,
15606        cx,
15607        |highlighted_edits, cx| {
15608            assert_eq!(highlighted_edits.text, "That is a test.");
15609            assert_eq!(highlighted_edits.highlights.len(), 1);
15610            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15611            assert_eq!(
15612                highlighted_edits.highlights[0].1.background_color,
15613                Some(cx.theme().status().created_background)
15614            );
15615        },
15616    )
15617    .await;
15618
15619    // Multiple edits
15620    assert_highlighted_edits(
15621        "Hello, world!",
15622        vec![
15623            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15624            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15625        ],
15626        false,
15627        cx,
15628        |highlighted_edits, cx| {
15629            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15630            assert_eq!(highlighted_edits.highlights.len(), 2);
15631            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15632            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15633            assert_eq!(
15634                highlighted_edits.highlights[0].1.background_color,
15635                Some(cx.theme().status().created_background)
15636            );
15637            assert_eq!(
15638                highlighted_edits.highlights[1].1.background_color,
15639                Some(cx.theme().status().created_background)
15640            );
15641        },
15642    )
15643    .await;
15644
15645    // Multiple lines with edits
15646    assert_highlighted_edits(
15647        "First line\nSecond line\nThird line\nFourth line",
15648        vec![
15649            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15650            (
15651                Point::new(2, 0)..Point::new(2, 10),
15652                "New third line".to_string(),
15653            ),
15654            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15655        ],
15656        false,
15657        cx,
15658        |highlighted_edits, cx| {
15659            assert_eq!(
15660                highlighted_edits.text,
15661                "Second modified\nNew third line\nFourth updated line"
15662            );
15663            assert_eq!(highlighted_edits.highlights.len(), 3);
15664            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15665            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15666            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15667            for highlight in &highlighted_edits.highlights {
15668                assert_eq!(
15669                    highlight.1.background_color,
15670                    Some(cx.theme().status().created_background)
15671                );
15672            }
15673        },
15674    )
15675    .await;
15676}
15677
15678#[gpui::test]
15679async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15680    init_test(cx, |_| {});
15681
15682    // Deletion
15683    assert_highlighted_edits(
15684        "Hello, world!",
15685        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15686        true,
15687        cx,
15688        |highlighted_edits, cx| {
15689            assert_eq!(highlighted_edits.text, "Hello, world!");
15690            assert_eq!(highlighted_edits.highlights.len(), 1);
15691            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15692            assert_eq!(
15693                highlighted_edits.highlights[0].1.background_color,
15694                Some(cx.theme().status().deleted_background)
15695            );
15696        },
15697    )
15698    .await;
15699
15700    // Insertion
15701    assert_highlighted_edits(
15702        "Hello, world!",
15703        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15704        true,
15705        cx,
15706        |highlighted_edits, cx| {
15707            assert_eq!(highlighted_edits.highlights.len(), 1);
15708            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15709            assert_eq!(
15710                highlighted_edits.highlights[0].1.background_color,
15711                Some(cx.theme().status().created_background)
15712            );
15713        },
15714    )
15715    .await;
15716}
15717
15718async fn assert_highlighted_edits(
15719    text: &str,
15720    edits: Vec<(Range<Point>, String)>,
15721    include_deletions: bool,
15722    cx: &mut TestAppContext,
15723    assertion_fn: impl Fn(HighlightedText, &App),
15724) {
15725    let window = cx.add_window(|window, cx| {
15726        let buffer = MultiBuffer::build_simple(text, cx);
15727        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15728    });
15729    let cx = &mut VisualTestContext::from_window(*window, cx);
15730
15731    let (buffer, snapshot) = window
15732        .update(cx, |editor, _window, cx| {
15733            (
15734                editor.buffer().clone(),
15735                editor.buffer().read(cx).snapshot(cx),
15736            )
15737        })
15738        .unwrap();
15739
15740    let edits = edits
15741        .into_iter()
15742        .map(|(range, edit)| {
15743            (
15744                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15745                edit,
15746            )
15747        })
15748        .collect::<Vec<_>>();
15749
15750    let text_anchor_edits = edits
15751        .clone()
15752        .into_iter()
15753        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15754        .collect::<Vec<_>>();
15755
15756    let edit_preview = window
15757        .update(cx, |_, _window, cx| {
15758            buffer
15759                .read(cx)
15760                .as_singleton()
15761                .unwrap()
15762                .read(cx)
15763                .preview_edits(text_anchor_edits.into(), cx)
15764        })
15765        .unwrap()
15766        .await;
15767
15768    cx.update(|_window, cx| {
15769        let highlighted_edits = inline_completion_edit_text(
15770            &snapshot.as_singleton().unwrap().2,
15771            &edits,
15772            &edit_preview,
15773            include_deletions,
15774            cx,
15775        );
15776        assertion_fn(highlighted_edits, cx)
15777    });
15778}
15779
15780#[gpui::test]
15781async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15782    init_test(cx, |_| {});
15783    let capabilities = lsp::ServerCapabilities {
15784        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15785            prepare_provider: Some(true),
15786            work_done_progress_options: Default::default(),
15787        })),
15788        ..Default::default()
15789    };
15790    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15791
15792    cx.set_state(indoc! {"
15793        struct Fˇoo {}
15794    "});
15795
15796    cx.update_editor(|editor, _, cx| {
15797        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15798        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15799        editor.highlight_background::<DocumentHighlightRead>(
15800            &[highlight_range],
15801            |c| c.editor_document_highlight_read_background,
15802            cx,
15803        );
15804    });
15805
15806    let mut prepare_rename_handler =
15807        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15808            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15809                start: lsp::Position {
15810                    line: 0,
15811                    character: 7,
15812                },
15813                end: lsp::Position {
15814                    line: 0,
15815                    character: 10,
15816                },
15817            })))
15818        });
15819    let prepare_rename_task = cx
15820        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15821        .expect("Prepare rename was not started");
15822    prepare_rename_handler.next().await.unwrap();
15823    prepare_rename_task.await.expect("Prepare rename failed");
15824
15825    let mut rename_handler =
15826        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15827            let edit = lsp::TextEdit {
15828                range: lsp::Range {
15829                    start: lsp::Position {
15830                        line: 0,
15831                        character: 7,
15832                    },
15833                    end: lsp::Position {
15834                        line: 0,
15835                        character: 10,
15836                    },
15837                },
15838                new_text: "FooRenamed".to_string(),
15839            };
15840            Ok(Some(lsp::WorkspaceEdit::new(
15841                // Specify the same edit twice
15842                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15843            )))
15844        });
15845    let rename_task = cx
15846        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15847        .expect("Confirm rename was not started");
15848    rename_handler.next().await.unwrap();
15849    rename_task.await.expect("Confirm rename failed");
15850    cx.run_until_parked();
15851
15852    // Despite two edits, only one is actually applied as those are identical
15853    cx.assert_editor_state(indoc! {"
15854        struct FooRenamedˇ {}
15855    "});
15856}
15857
15858#[gpui::test]
15859async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15860    init_test(cx, |_| {});
15861    // These capabilities indicate that the server does not support prepare rename.
15862    let capabilities = lsp::ServerCapabilities {
15863        rename_provider: Some(lsp::OneOf::Left(true)),
15864        ..Default::default()
15865    };
15866    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15867
15868    cx.set_state(indoc! {"
15869        struct Fˇoo {}
15870    "});
15871
15872    cx.update_editor(|editor, _window, cx| {
15873        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15874        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15875        editor.highlight_background::<DocumentHighlightRead>(
15876            &[highlight_range],
15877            |c| c.editor_document_highlight_read_background,
15878            cx,
15879        );
15880    });
15881
15882    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15883        .expect("Prepare rename was not started")
15884        .await
15885        .expect("Prepare rename failed");
15886
15887    let mut rename_handler =
15888        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15889            let edit = lsp::TextEdit {
15890                range: lsp::Range {
15891                    start: lsp::Position {
15892                        line: 0,
15893                        character: 7,
15894                    },
15895                    end: lsp::Position {
15896                        line: 0,
15897                        character: 10,
15898                    },
15899                },
15900                new_text: "FooRenamed".to_string(),
15901            };
15902            Ok(Some(lsp::WorkspaceEdit::new(
15903                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15904            )))
15905        });
15906    let rename_task = cx
15907        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15908        .expect("Confirm rename was not started");
15909    rename_handler.next().await.unwrap();
15910    rename_task.await.expect("Confirm rename failed");
15911    cx.run_until_parked();
15912
15913    // Correct range is renamed, as `surrounding_word` is used to find it.
15914    cx.assert_editor_state(indoc! {"
15915        struct FooRenamedˇ {}
15916    "});
15917}
15918
15919fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15920    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15921    point..point
15922}
15923
15924fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15925    let (text, ranges) = marked_text_ranges(marked_text, true);
15926    assert_eq!(editor.text(cx), text);
15927    assert_eq!(
15928        editor.selections.ranges(cx),
15929        ranges,
15930        "Assert selections are {}",
15931        marked_text
15932    );
15933}
15934
15935pub fn handle_signature_help_request(
15936    cx: &mut EditorLspTestContext,
15937    mocked_response: lsp::SignatureHelp,
15938) -> impl Future<Output = ()> {
15939    let mut request =
15940        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15941            let mocked_response = mocked_response.clone();
15942            async move { Ok(Some(mocked_response)) }
15943        });
15944
15945    async move {
15946        request.next().await;
15947    }
15948}
15949
15950/// Handle completion request passing a marked string specifying where the completion
15951/// should be triggered from using '|' character, what range should be replaced, and what completions
15952/// should be returned using '<' and '>' to delimit the range
15953pub fn handle_completion_request(
15954    cx: &mut EditorLspTestContext,
15955    marked_string: &str,
15956    completions: Vec<&'static str>,
15957    counter: Arc<AtomicUsize>,
15958) -> impl Future<Output = ()> {
15959    let complete_from_marker: TextRangeMarker = '|'.into();
15960    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15961    let (_, mut marked_ranges) = marked_text_ranges_by(
15962        marked_string,
15963        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15964    );
15965
15966    let complete_from_position =
15967        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15968    let replace_range =
15969        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15970
15971    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15972        let completions = completions.clone();
15973        counter.fetch_add(1, atomic::Ordering::Release);
15974        async move {
15975            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15976            assert_eq!(
15977                params.text_document_position.position,
15978                complete_from_position
15979            );
15980            Ok(Some(lsp::CompletionResponse::Array(
15981                completions
15982                    .iter()
15983                    .map(|completion_text| lsp::CompletionItem {
15984                        label: completion_text.to_string(),
15985                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
15986                            range: replace_range,
15987                            new_text: completion_text.to_string(),
15988                        })),
15989                        ..Default::default()
15990                    })
15991                    .collect(),
15992            )))
15993        }
15994    });
15995
15996    async move {
15997        request.next().await;
15998    }
15999}
16000
16001fn handle_resolve_completion_request(
16002    cx: &mut EditorLspTestContext,
16003    edits: Option<Vec<(&'static str, &'static str)>>,
16004) -> impl Future<Output = ()> {
16005    let edits = edits.map(|edits| {
16006        edits
16007            .iter()
16008            .map(|(marked_string, new_text)| {
16009                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16010                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16011                lsp::TextEdit::new(replace_range, new_text.to_string())
16012            })
16013            .collect::<Vec<_>>()
16014    });
16015
16016    let mut request =
16017        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16018            let edits = edits.clone();
16019            async move {
16020                Ok(lsp::CompletionItem {
16021                    additional_text_edits: edits,
16022                    ..Default::default()
16023                })
16024            }
16025        });
16026
16027    async move {
16028        request.next().await;
16029    }
16030}
16031
16032pub(crate) fn update_test_language_settings(
16033    cx: &mut TestAppContext,
16034    f: impl Fn(&mut AllLanguageSettingsContent),
16035) {
16036    cx.update(|cx| {
16037        SettingsStore::update_global(cx, |store, cx| {
16038            store.update_user_settings::<AllLanguageSettings>(cx, f);
16039        });
16040    });
16041}
16042
16043pub(crate) fn update_test_project_settings(
16044    cx: &mut TestAppContext,
16045    f: impl Fn(&mut ProjectSettings),
16046) {
16047    cx.update(|cx| {
16048        SettingsStore::update_global(cx, |store, cx| {
16049            store.update_user_settings::<ProjectSettings>(cx, f);
16050        });
16051    });
16052}
16053
16054pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16055    cx.update(|cx| {
16056        assets::Assets.load_test_fonts(cx);
16057        let store = SettingsStore::test(cx);
16058        cx.set_global(store);
16059        theme::init(theme::LoadThemes::JustBase, cx);
16060        release_channel::init(SemanticVersion::default(), cx);
16061        client::init_settings(cx);
16062        language::init(cx);
16063        Project::init_settings(cx);
16064        workspace::init_settings(cx);
16065        crate::init(cx);
16066    });
16067
16068    update_test_language_settings(cx, f);
16069}
16070
16071#[track_caller]
16072fn assert_hunk_revert(
16073    not_reverted_text_with_selections: &str,
16074    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16075    expected_reverted_text_with_selections: &str,
16076    base_text: &str,
16077    cx: &mut EditorLspTestContext,
16078) {
16079    cx.set_state(not_reverted_text_with_selections);
16080    cx.set_diff_base(base_text);
16081    cx.executor().run_until_parked();
16082
16083    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16084        let snapshot = editor.snapshot(window, cx);
16085        let reverted_hunk_statuses = snapshot
16086            .buffer_snapshot
16087            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16088            .map(|hunk| hunk.status())
16089            .collect::<Vec<_>>();
16090
16091        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
16092        reverted_hunk_statuses
16093    });
16094    cx.executor().run_until_parked();
16095    cx.assert_editor_state(expected_reverted_text_with_selections);
16096    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16097}