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_none(), DiffHunkStatus::added_none()],
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_none(), DiffHunkStatus::added_none()],
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_none(),
12347            DiffHunkStatus::added_none(),
12348            DiffHunkStatus::added_none(),
12349            DiffHunkStatus::added_none(),
12350            DiffHunkStatus::added_none(),
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![
12399            DiffHunkStatus::modified_none(),
12400            DiffHunkStatus::modified_none(),
12401        ],
12402        indoc! {r#"struct Row;
12403                   struct Row1;
12404                   struct Row33;
12405                   ˇ
12406                   struct Row4;
12407                   struct Row5;
12408                   struct Row6;
12409                   ˇ
12410                   struct Row99;
12411                   struct Row9;
12412                   struct Row10;"#},
12413        base_text,
12414        &mut cx,
12415    );
12416    assert_hunk_revert(
12417        indoc! {r#"struct Row;
12418                   struct Row1;
12419                   struct Row33;
12420                   «ˇ
12421                   struct Row4;
12422                   struct» Row5;
12423                   «struct Row6;
12424                   ˇ»
12425                   struct Row99;
12426                   struct Row9;
12427                   struct Row10;"#},
12428        vec![
12429            DiffHunkStatus::modified_none(),
12430            DiffHunkStatus::modified_none(),
12431        ],
12432        indoc! {r#"struct Row;
12433                   struct Row1;
12434                   struct Row33;
12435                   «ˇ
12436                   struct Row4;
12437                   struct» Row5;
12438                   «struct Row6;
12439                   ˇ»
12440                   struct Row99;
12441                   struct Row9;
12442                   struct Row10;"#},
12443        base_text,
12444        &mut cx,
12445    );
12446
12447    assert_hunk_revert(
12448        indoc! {r#"ˇstruct Row1.1;
12449                   struct Row1;
12450                   «ˇstr»uct Row22;
12451
12452                   struct ˇRow44;
12453                   struct Row5;
12454                   struct «Rˇ»ow66;ˇ
12455
12456                   «struˇ»ct Row88;
12457                   struct Row9;
12458                   struct Row1011;ˇ"#},
12459        vec![
12460            DiffHunkStatus::modified_none(),
12461            DiffHunkStatus::modified_none(),
12462            DiffHunkStatus::modified_none(),
12463            DiffHunkStatus::modified_none(),
12464            DiffHunkStatus::modified_none(),
12465            DiffHunkStatus::modified_none(),
12466        ],
12467        indoc! {r#"struct Row;
12468                   ˇstruct Row1;
12469                   struct Row2;
12470                   ˇ
12471                   struct Row4;
12472                   ˇstruct Row5;
12473                   struct Row6;
12474                   ˇ
12475                   struct Row8;
12476                   ˇstruct Row9;
12477                   struct Row10;ˇ"#},
12478        base_text,
12479        &mut cx,
12480    );
12481}
12482
12483#[gpui::test]
12484async fn test_deleting_over_diff_hunk(cx: &mut gpui::TestAppContext) {
12485    init_test(cx, |_| {});
12486    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12487    let base_text = indoc! {r#"
12488        one
12489
12490        two
12491        three
12492        "#};
12493
12494    cx.set_diff_base(base_text);
12495    cx.set_state("\nˇ\n");
12496    cx.executor().run_until_parked();
12497    cx.update_editor(|editor, _window, cx| {
12498        editor.expand_selected_diff_hunks(cx);
12499    });
12500    cx.executor().run_until_parked();
12501    cx.update_editor(|editor, window, cx| {
12502        editor.backspace(&Default::default(), window, cx);
12503    });
12504    cx.run_until_parked();
12505    cx.assert_state_with_diff(
12506        indoc! {r#"
12507
12508        - two
12509        - threeˇ
12510        +
12511        "#}
12512        .to_string(),
12513    );
12514}
12515
12516#[gpui::test]
12517async fn test_deletion_reverts(cx: &mut gpui::TestAppContext) {
12518    init_test(cx, |_| {});
12519    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12520    let base_text = indoc! {r#"struct Row;
12521struct Row1;
12522struct Row2;
12523
12524struct Row4;
12525struct Row5;
12526struct Row6;
12527
12528struct Row8;
12529struct Row9;
12530struct Row10;"#};
12531
12532    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
12533    assert_hunk_revert(
12534        indoc! {r#"struct Row;
12535                   struct Row2;
12536
12537                   ˇstruct Row4;
12538                   struct Row5;
12539                   struct Row6;
12540                   ˇ
12541                   struct Row8;
12542                   struct Row10;"#},
12543        vec![
12544            DiffHunkStatus::deleted_none(),
12545            DiffHunkStatus::deleted_none(),
12546        ],
12547        indoc! {r#"struct Row;
12548                   struct Row2;
12549
12550                   ˇstruct Row4;
12551                   struct Row5;
12552                   struct Row6;
12553                   ˇ
12554                   struct Row8;
12555                   struct Row10;"#},
12556        base_text,
12557        &mut cx,
12558    );
12559    assert_hunk_revert(
12560        indoc! {r#"struct Row;
12561                   struct Row2;
12562
12563                   «ˇstruct Row4;
12564                   struct» Row5;
12565                   «struct Row6;
12566                   ˇ»
12567                   struct Row8;
12568                   struct Row10;"#},
12569        vec![
12570            DiffHunkStatus::deleted_none(),
12571            DiffHunkStatus::deleted_none(),
12572        ],
12573        indoc! {r#"struct Row;
12574                   struct Row2;
12575
12576                   «ˇstruct Row4;
12577                   struct» Row5;
12578                   «struct Row6;
12579                   ˇ»
12580                   struct Row8;
12581                   struct Row10;"#},
12582        base_text,
12583        &mut cx,
12584    );
12585
12586    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
12587    assert_hunk_revert(
12588        indoc! {r#"struct Row;
12589                   ˇstruct Row2;
12590
12591                   struct Row4;
12592                   struct Row5;
12593                   struct Row6;
12594
12595                   struct Row8;ˇ
12596                   struct Row10;"#},
12597        vec![
12598            DiffHunkStatus::deleted_none(),
12599            DiffHunkStatus::deleted_none(),
12600        ],
12601        indoc! {r#"struct Row;
12602                   struct Row1;
12603                   ˇstruct Row2;
12604
12605                   struct Row4;
12606                   struct Row5;
12607                   struct Row6;
12608
12609                   struct Row8;ˇ
12610                   struct Row9;
12611                   struct Row10;"#},
12612        base_text,
12613        &mut cx,
12614    );
12615    assert_hunk_revert(
12616        indoc! {r#"struct Row;
12617                   struct Row2«ˇ;
12618                   struct Row4;
12619                   struct» Row5;
12620                   «struct Row6;
12621
12622                   struct Row8;ˇ»
12623                   struct Row10;"#},
12624        vec![
12625            DiffHunkStatus::deleted_none(),
12626            DiffHunkStatus::deleted_none(),
12627            DiffHunkStatus::deleted_none(),
12628        ],
12629        indoc! {r#"struct Row;
12630                   struct Row1;
12631                   struct Row2«ˇ;
12632
12633                   struct Row4;
12634                   struct» Row5;
12635                   «struct Row6;
12636
12637                   struct Row8;ˇ»
12638                   struct Row9;
12639                   struct Row10;"#},
12640        base_text,
12641        &mut cx,
12642    );
12643}
12644
12645#[gpui::test]
12646async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
12647    init_test(cx, |_| {});
12648
12649    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
12650    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
12651    let base_text_3 =
12652        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
12653
12654    let text_1 = edit_first_char_of_every_line(base_text_1);
12655    let text_2 = edit_first_char_of_every_line(base_text_2);
12656    let text_3 = edit_first_char_of_every_line(base_text_3);
12657
12658    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
12659    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
12660    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
12661
12662    let multibuffer = cx.new(|cx| {
12663        let mut multibuffer = MultiBuffer::new(ReadWrite);
12664        multibuffer.push_excerpts(
12665            buffer_1.clone(),
12666            [
12667                ExcerptRange {
12668                    context: Point::new(0, 0)..Point::new(3, 0),
12669                    primary: None,
12670                },
12671                ExcerptRange {
12672                    context: Point::new(5, 0)..Point::new(7, 0),
12673                    primary: None,
12674                },
12675                ExcerptRange {
12676                    context: Point::new(9, 0)..Point::new(10, 4),
12677                    primary: None,
12678                },
12679            ],
12680            cx,
12681        );
12682        multibuffer.push_excerpts(
12683            buffer_2.clone(),
12684            [
12685                ExcerptRange {
12686                    context: Point::new(0, 0)..Point::new(3, 0),
12687                    primary: None,
12688                },
12689                ExcerptRange {
12690                    context: Point::new(5, 0)..Point::new(7, 0),
12691                    primary: None,
12692                },
12693                ExcerptRange {
12694                    context: Point::new(9, 0)..Point::new(10, 4),
12695                    primary: None,
12696                },
12697            ],
12698            cx,
12699        );
12700        multibuffer.push_excerpts(
12701            buffer_3.clone(),
12702            [
12703                ExcerptRange {
12704                    context: Point::new(0, 0)..Point::new(3, 0),
12705                    primary: None,
12706                },
12707                ExcerptRange {
12708                    context: Point::new(5, 0)..Point::new(7, 0),
12709                    primary: None,
12710                },
12711                ExcerptRange {
12712                    context: Point::new(9, 0)..Point::new(10, 4),
12713                    primary: None,
12714                },
12715            ],
12716            cx,
12717        );
12718        multibuffer
12719    });
12720
12721    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12722    editor.update_in(cx, |editor, _window, cx| {
12723        for (buffer, diff_base) in [
12724            (buffer_1.clone(), base_text_1),
12725            (buffer_2.clone(), base_text_2),
12726            (buffer_3.clone(), base_text_3),
12727        ] {
12728            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
12729            editor
12730                .buffer
12731                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
12732        }
12733    });
12734    cx.executor().run_until_parked();
12735
12736    editor.update_in(cx, |editor, window, cx| {
12737        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}");
12738        editor.select_all(&SelectAll, window, cx);
12739        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12740    });
12741    cx.executor().run_until_parked();
12742
12743    // When all ranges are selected, all buffer hunks are reverted.
12744    editor.update(cx, |editor, cx| {
12745        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");
12746    });
12747    buffer_1.update(cx, |buffer, _| {
12748        assert_eq!(buffer.text(), base_text_1);
12749    });
12750    buffer_2.update(cx, |buffer, _| {
12751        assert_eq!(buffer.text(), base_text_2);
12752    });
12753    buffer_3.update(cx, |buffer, _| {
12754        assert_eq!(buffer.text(), base_text_3);
12755    });
12756
12757    editor.update_in(cx, |editor, window, cx| {
12758        editor.undo(&Default::default(), window, cx);
12759    });
12760
12761    editor.update_in(cx, |editor, window, cx| {
12762        editor.change_selections(None, window, cx, |s| {
12763            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
12764        });
12765        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
12766    });
12767
12768    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
12769    // but not affect buffer_2 and its related excerpts.
12770    editor.update(cx, |editor, cx| {
12771        assert_eq!(
12772            editor.text(cx),
12773            "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}"
12774        );
12775    });
12776    buffer_1.update(cx, |buffer, _| {
12777        assert_eq!(buffer.text(), base_text_1);
12778    });
12779    buffer_2.update(cx, |buffer, _| {
12780        assert_eq!(
12781            buffer.text(),
12782            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
12783        );
12784    });
12785    buffer_3.update(cx, |buffer, _| {
12786        assert_eq!(
12787            buffer.text(),
12788            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
12789        );
12790    });
12791
12792    fn edit_first_char_of_every_line(text: &str) -> String {
12793        text.split('\n')
12794            .map(|line| format!("X{}", &line[1..]))
12795            .collect::<Vec<_>>()
12796            .join("\n")
12797    }
12798}
12799
12800#[gpui::test]
12801async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
12802    init_test(cx, |_| {});
12803
12804    let cols = 4;
12805    let rows = 10;
12806    let sample_text_1 = sample_text(rows, cols, 'a');
12807    assert_eq!(
12808        sample_text_1,
12809        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
12810    );
12811    let sample_text_2 = sample_text(rows, cols, 'l');
12812    assert_eq!(
12813        sample_text_2,
12814        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
12815    );
12816    let sample_text_3 = sample_text(rows, cols, 'v');
12817    assert_eq!(
12818        sample_text_3,
12819        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
12820    );
12821
12822    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
12823    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
12824    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
12825
12826    let multi_buffer = cx.new(|cx| {
12827        let mut multibuffer = MultiBuffer::new(ReadWrite);
12828        multibuffer.push_excerpts(
12829            buffer_1.clone(),
12830            [
12831                ExcerptRange {
12832                    context: Point::new(0, 0)..Point::new(3, 0),
12833                    primary: None,
12834                },
12835                ExcerptRange {
12836                    context: Point::new(5, 0)..Point::new(7, 0),
12837                    primary: None,
12838                },
12839                ExcerptRange {
12840                    context: Point::new(9, 0)..Point::new(10, 4),
12841                    primary: None,
12842                },
12843            ],
12844            cx,
12845        );
12846        multibuffer.push_excerpts(
12847            buffer_2.clone(),
12848            [
12849                ExcerptRange {
12850                    context: Point::new(0, 0)..Point::new(3, 0),
12851                    primary: None,
12852                },
12853                ExcerptRange {
12854                    context: Point::new(5, 0)..Point::new(7, 0),
12855                    primary: None,
12856                },
12857                ExcerptRange {
12858                    context: Point::new(9, 0)..Point::new(10, 4),
12859                    primary: None,
12860                },
12861            ],
12862            cx,
12863        );
12864        multibuffer.push_excerpts(
12865            buffer_3.clone(),
12866            [
12867                ExcerptRange {
12868                    context: Point::new(0, 0)..Point::new(3, 0),
12869                    primary: None,
12870                },
12871                ExcerptRange {
12872                    context: Point::new(5, 0)..Point::new(7, 0),
12873                    primary: None,
12874                },
12875                ExcerptRange {
12876                    context: Point::new(9, 0)..Point::new(10, 4),
12877                    primary: None,
12878                },
12879            ],
12880            cx,
12881        );
12882        multibuffer
12883    });
12884
12885    let fs = FakeFs::new(cx.executor());
12886    fs.insert_tree(
12887        "/a",
12888        json!({
12889            "main.rs": sample_text_1,
12890            "other.rs": sample_text_2,
12891            "lib.rs": sample_text_3,
12892        }),
12893    )
12894    .await;
12895    let project = Project::test(fs, ["/a".as_ref()], cx).await;
12896    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12897    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12898    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
12899        Editor::new(
12900            EditorMode::Full,
12901            multi_buffer,
12902            Some(project.clone()),
12903            true,
12904            window,
12905            cx,
12906        )
12907    });
12908    let multibuffer_item_id = workspace
12909        .update(cx, |workspace, window, cx| {
12910            assert!(
12911                workspace.active_item(cx).is_none(),
12912                "active item should be None before the first item is added"
12913            );
12914            workspace.add_item_to_active_pane(
12915                Box::new(multi_buffer_editor.clone()),
12916                None,
12917                true,
12918                window,
12919                cx,
12920            );
12921            let active_item = workspace
12922                .active_item(cx)
12923                .expect("should have an active item after adding the multi buffer");
12924            assert!(
12925                !active_item.is_singleton(cx),
12926                "A multi buffer was expected to active after adding"
12927            );
12928            active_item.item_id()
12929        })
12930        .unwrap();
12931    cx.executor().run_until_parked();
12932
12933    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12934        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12935            s.select_ranges(Some(1..2))
12936        });
12937        editor.open_excerpts(&OpenExcerpts, window, cx);
12938    });
12939    cx.executor().run_until_parked();
12940    let first_item_id = workspace
12941        .update(cx, |workspace, window, cx| {
12942            let active_item = workspace
12943                .active_item(cx)
12944                .expect("should have an active item after navigating into the 1st buffer");
12945            let first_item_id = active_item.item_id();
12946            assert_ne!(
12947                first_item_id, multibuffer_item_id,
12948                "Should navigate into the 1st buffer and activate it"
12949            );
12950            assert!(
12951                active_item.is_singleton(cx),
12952                "New active item should be a singleton buffer"
12953            );
12954            assert_eq!(
12955                active_item
12956                    .act_as::<Editor>(cx)
12957                    .expect("should have navigated into an editor for the 1st buffer")
12958                    .read(cx)
12959                    .text(cx),
12960                sample_text_1
12961            );
12962
12963            workspace
12964                .go_back(workspace.active_pane().downgrade(), window, cx)
12965                .detach_and_log_err(cx);
12966
12967            first_item_id
12968        })
12969        .unwrap();
12970    cx.executor().run_until_parked();
12971    workspace
12972        .update(cx, |workspace, _, cx| {
12973            let active_item = workspace
12974                .active_item(cx)
12975                .expect("should have an active item after navigating back");
12976            assert_eq!(
12977                active_item.item_id(),
12978                multibuffer_item_id,
12979                "Should navigate back to the multi buffer"
12980            );
12981            assert!(!active_item.is_singleton(cx));
12982        })
12983        .unwrap();
12984
12985    multi_buffer_editor.update_in(cx, |editor, window, cx| {
12986        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
12987            s.select_ranges(Some(39..40))
12988        });
12989        editor.open_excerpts(&OpenExcerpts, window, cx);
12990    });
12991    cx.executor().run_until_parked();
12992    let second_item_id = workspace
12993        .update(cx, |workspace, window, cx| {
12994            let active_item = workspace
12995                .active_item(cx)
12996                .expect("should have an active item after navigating into the 2nd buffer");
12997            let second_item_id = active_item.item_id();
12998            assert_ne!(
12999                second_item_id, multibuffer_item_id,
13000                "Should navigate away from the multibuffer"
13001            );
13002            assert_ne!(
13003                second_item_id, first_item_id,
13004                "Should navigate into the 2nd buffer and activate it"
13005            );
13006            assert!(
13007                active_item.is_singleton(cx),
13008                "New active item should be a singleton buffer"
13009            );
13010            assert_eq!(
13011                active_item
13012                    .act_as::<Editor>(cx)
13013                    .expect("should have navigated into an editor")
13014                    .read(cx)
13015                    .text(cx),
13016                sample_text_2
13017            );
13018
13019            workspace
13020                .go_back(workspace.active_pane().downgrade(), window, cx)
13021                .detach_and_log_err(cx);
13022
13023            second_item_id
13024        })
13025        .unwrap();
13026    cx.executor().run_until_parked();
13027    workspace
13028        .update(cx, |workspace, _, cx| {
13029            let active_item = workspace
13030                .active_item(cx)
13031                .expect("should have an active item after navigating back from the 2nd buffer");
13032            assert_eq!(
13033                active_item.item_id(),
13034                multibuffer_item_id,
13035                "Should navigate back from the 2nd buffer to the multi buffer"
13036            );
13037            assert!(!active_item.is_singleton(cx));
13038        })
13039        .unwrap();
13040
13041    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13042        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13043            s.select_ranges(Some(70..70))
13044        });
13045        editor.open_excerpts(&OpenExcerpts, window, cx);
13046    });
13047    cx.executor().run_until_parked();
13048    workspace
13049        .update(cx, |workspace, window, cx| {
13050            let active_item = workspace
13051                .active_item(cx)
13052                .expect("should have an active item after navigating into the 3rd buffer");
13053            let third_item_id = active_item.item_id();
13054            assert_ne!(
13055                third_item_id, multibuffer_item_id,
13056                "Should navigate into the 3rd buffer and activate it"
13057            );
13058            assert_ne!(third_item_id, first_item_id);
13059            assert_ne!(third_item_id, second_item_id);
13060            assert!(
13061                active_item.is_singleton(cx),
13062                "New active item should be a singleton buffer"
13063            );
13064            assert_eq!(
13065                active_item
13066                    .act_as::<Editor>(cx)
13067                    .expect("should have navigated into an editor")
13068                    .read(cx)
13069                    .text(cx),
13070                sample_text_3
13071            );
13072
13073            workspace
13074                .go_back(workspace.active_pane().downgrade(), window, cx)
13075                .detach_and_log_err(cx);
13076        })
13077        .unwrap();
13078    cx.executor().run_until_parked();
13079    workspace
13080        .update(cx, |workspace, _, cx| {
13081            let active_item = workspace
13082                .active_item(cx)
13083                .expect("should have an active item after navigating back from the 3rd buffer");
13084            assert_eq!(
13085                active_item.item_id(),
13086                multibuffer_item_id,
13087                "Should navigate back from the 3rd buffer to the multi buffer"
13088            );
13089            assert!(!active_item.is_singleton(cx));
13090        })
13091        .unwrap();
13092}
13093
13094#[gpui::test]
13095async fn test_toggle_selected_diff_hunks(
13096    executor: BackgroundExecutor,
13097    cx: &mut gpui::TestAppContext,
13098) {
13099    init_test(cx, |_| {});
13100
13101    let mut cx = EditorTestContext::new(cx).await;
13102
13103    let diff_base = r#"
13104        use some::mod;
13105
13106        const A: u32 = 42;
13107
13108        fn main() {
13109            println!("hello");
13110
13111            println!("world");
13112        }
13113        "#
13114    .unindent();
13115
13116    cx.set_state(
13117        &r#"
13118        use some::modified;
13119
13120        ˇ
13121        fn main() {
13122            println!("hello there");
13123
13124            println!("around the");
13125            println!("world");
13126        }
13127        "#
13128        .unindent(),
13129    );
13130
13131    cx.set_diff_base(&diff_base);
13132    executor.run_until_parked();
13133
13134    cx.update_editor(|editor, window, cx| {
13135        editor.go_to_next_hunk(&GoToHunk, window, cx);
13136        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13137    });
13138    executor.run_until_parked();
13139    cx.assert_state_with_diff(
13140        r#"
13141          use some::modified;
13142
13143
13144          fn main() {
13145        -     println!("hello");
13146        + ˇ    println!("hello there");
13147
13148              println!("around the");
13149              println!("world");
13150          }
13151        "#
13152        .unindent(),
13153    );
13154
13155    cx.update_editor(|editor, window, cx| {
13156        for _ in 0..2 {
13157            editor.go_to_next_hunk(&GoToHunk, window, cx);
13158            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13159        }
13160    });
13161    executor.run_until_parked();
13162    cx.assert_state_with_diff(
13163        r#"
13164        - use some::mod;
13165        + ˇuse some::modified;
13166
13167
13168          fn main() {
13169        -     println!("hello");
13170        +     println!("hello there");
13171
13172        +     println!("around the");
13173              println!("world");
13174          }
13175        "#
13176        .unindent(),
13177    );
13178
13179    cx.update_editor(|editor, window, cx| {
13180        editor.go_to_next_hunk(&GoToHunk, window, cx);
13181        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13182    });
13183    executor.run_until_parked();
13184    cx.assert_state_with_diff(
13185        r#"
13186        - use some::mod;
13187        + use some::modified;
13188
13189        - const A: u32 = 42;
13190          ˇ
13191          fn main() {
13192        -     println!("hello");
13193        +     println!("hello there");
13194
13195        +     println!("around the");
13196              println!("world");
13197          }
13198        "#
13199        .unindent(),
13200    );
13201
13202    cx.update_editor(|editor, window, cx| {
13203        editor.cancel(&Cancel, window, cx);
13204    });
13205
13206    cx.assert_state_with_diff(
13207        r#"
13208          use some::modified;
13209
13210          ˇ
13211          fn main() {
13212              println!("hello there");
13213
13214              println!("around the");
13215              println!("world");
13216          }
13217        "#
13218        .unindent(),
13219    );
13220}
13221
13222#[gpui::test]
13223async fn test_diff_base_change_with_expanded_diff_hunks(
13224    executor: BackgroundExecutor,
13225    cx: &mut gpui::TestAppContext,
13226) {
13227    init_test(cx, |_| {});
13228
13229    let mut cx = EditorTestContext::new(cx).await;
13230
13231    let diff_base = r#"
13232        use some::mod1;
13233        use some::mod2;
13234
13235        const A: u32 = 42;
13236        const B: u32 = 42;
13237        const C: u32 = 42;
13238
13239        fn main() {
13240            println!("hello");
13241
13242            println!("world");
13243        }
13244        "#
13245    .unindent();
13246
13247    cx.set_state(
13248        &r#"
13249        use some::mod2;
13250
13251        const A: u32 = 42;
13252        const C: u32 = 42;
13253
13254        fn main(ˇ) {
13255            //println!("hello");
13256
13257            println!("world");
13258            //
13259            //
13260        }
13261        "#
13262        .unindent(),
13263    );
13264
13265    cx.set_diff_base(&diff_base);
13266    executor.run_until_parked();
13267
13268    cx.update_editor(|editor, window, cx| {
13269        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13270    });
13271    executor.run_until_parked();
13272    cx.assert_state_with_diff(
13273        r#"
13274        - use some::mod1;
13275          use some::mod2;
13276
13277          const A: u32 = 42;
13278        - const B: u32 = 42;
13279          const C: u32 = 42;
13280
13281          fn main(ˇ) {
13282        -     println!("hello");
13283        +     //println!("hello");
13284
13285              println!("world");
13286        +     //
13287        +     //
13288          }
13289        "#
13290        .unindent(),
13291    );
13292
13293    cx.set_diff_base("new diff base!");
13294    executor.run_until_parked();
13295    cx.assert_state_with_diff(
13296        r#"
13297        - new diff base!
13298        + use some::mod2;
13299        +
13300        + const A: u32 = 42;
13301        + const C: u32 = 42;
13302        +
13303        + fn main(ˇ) {
13304        +     //println!("hello");
13305        +
13306        +     println!("world");
13307        +     //
13308        +     //
13309        + }
13310        "#
13311        .unindent(),
13312    );
13313}
13314
13315#[gpui::test]
13316async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext) {
13317    init_test(cx, |_| {});
13318
13319    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13320    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13321    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13322    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13323    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13324    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13325
13326    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13327    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13328    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13329
13330    let multi_buffer = cx.new(|cx| {
13331        let mut multibuffer = MultiBuffer::new(ReadWrite);
13332        multibuffer.push_excerpts(
13333            buffer_1.clone(),
13334            [
13335                ExcerptRange {
13336                    context: Point::new(0, 0)..Point::new(3, 0),
13337                    primary: None,
13338                },
13339                ExcerptRange {
13340                    context: Point::new(5, 0)..Point::new(7, 0),
13341                    primary: None,
13342                },
13343                ExcerptRange {
13344                    context: Point::new(9, 0)..Point::new(10, 3),
13345                    primary: None,
13346                },
13347            ],
13348            cx,
13349        );
13350        multibuffer.push_excerpts(
13351            buffer_2.clone(),
13352            [
13353                ExcerptRange {
13354                    context: Point::new(0, 0)..Point::new(3, 0),
13355                    primary: None,
13356                },
13357                ExcerptRange {
13358                    context: Point::new(5, 0)..Point::new(7, 0),
13359                    primary: None,
13360                },
13361                ExcerptRange {
13362                    context: Point::new(9, 0)..Point::new(10, 3),
13363                    primary: None,
13364                },
13365            ],
13366            cx,
13367        );
13368        multibuffer.push_excerpts(
13369            buffer_3.clone(),
13370            [
13371                ExcerptRange {
13372                    context: Point::new(0, 0)..Point::new(3, 0),
13373                    primary: None,
13374                },
13375                ExcerptRange {
13376                    context: Point::new(5, 0)..Point::new(7, 0),
13377                    primary: None,
13378                },
13379                ExcerptRange {
13380                    context: Point::new(9, 0)..Point::new(10, 3),
13381                    primary: None,
13382                },
13383            ],
13384            cx,
13385        );
13386        multibuffer
13387    });
13388
13389    let editor = cx.add_window(|window, cx| {
13390        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13391    });
13392    editor
13393        .update(cx, |editor, _window, cx| {
13394            for (buffer, diff_base) in [
13395                (buffer_1.clone(), file_1_old),
13396                (buffer_2.clone(), file_2_old),
13397                (buffer_3.clone(), file_3_old),
13398            ] {
13399                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13400                editor
13401                    .buffer
13402                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13403            }
13404        })
13405        .unwrap();
13406
13407    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13408    cx.run_until_parked();
13409
13410    cx.assert_editor_state(
13411        &"
13412            ˇaaa
13413            ccc
13414            ddd
13415
13416            ggg
13417            hhh
13418
13419
13420            lll
13421            mmm
13422            NNN
13423
13424            qqq
13425            rrr
13426
13427            uuu
13428            111
13429            222
13430            333
13431
13432            666
13433            777
13434
13435            000
13436            !!!"
13437        .unindent(),
13438    );
13439
13440    cx.update_editor(|editor, window, cx| {
13441        editor.select_all(&SelectAll, window, cx);
13442        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13443    });
13444    cx.executor().run_until_parked();
13445
13446    cx.assert_state_with_diff(
13447        "
13448            «aaa
13449          - bbb
13450            ccc
13451            ddd
13452
13453            ggg
13454            hhh
13455
13456
13457            lll
13458            mmm
13459          - nnn
13460          + NNN
13461
13462            qqq
13463            rrr
13464
13465            uuu
13466            111
13467            222
13468            333
13469
13470          + 666
13471            777
13472
13473            000
13474            !!!ˇ»"
13475            .unindent(),
13476    );
13477}
13478
13479#[gpui::test]
13480async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext) {
13481    init_test(cx, |_| {});
13482
13483    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
13484    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
13485
13486    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
13487    let multi_buffer = cx.new(|cx| {
13488        let mut multibuffer = MultiBuffer::new(ReadWrite);
13489        multibuffer.push_excerpts(
13490            buffer.clone(),
13491            [
13492                ExcerptRange {
13493                    context: Point::new(0, 0)..Point::new(2, 0),
13494                    primary: None,
13495                },
13496                ExcerptRange {
13497                    context: Point::new(4, 0)..Point::new(7, 0),
13498                    primary: None,
13499                },
13500                ExcerptRange {
13501                    context: Point::new(9, 0)..Point::new(10, 0),
13502                    primary: None,
13503                },
13504            ],
13505            cx,
13506        );
13507        multibuffer
13508    });
13509
13510    let editor = cx.add_window(|window, cx| {
13511        Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx)
13512    });
13513    editor
13514        .update(cx, |editor, _window, cx| {
13515            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
13516            editor
13517                .buffer
13518                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
13519        })
13520        .unwrap();
13521
13522    let mut cx = EditorTestContext::for_editor(editor, cx).await;
13523    cx.run_until_parked();
13524
13525    cx.update_editor(|editor, window, cx| {
13526        editor.expand_all_diff_hunks(&Default::default(), window, cx)
13527    });
13528    cx.executor().run_until_parked();
13529
13530    // When the start of a hunk coincides with the start of its excerpt,
13531    // the hunk is expanded. When the start of a a hunk is earlier than
13532    // the start of its excerpt, the hunk is not expanded.
13533    cx.assert_state_with_diff(
13534        "
13535            ˇaaa
13536          - bbb
13537          + BBB
13538
13539          - ddd
13540          - eee
13541          + DDD
13542          + EEE
13543            fff
13544
13545            iii
13546        "
13547        .unindent(),
13548    );
13549}
13550
13551#[gpui::test]
13552async fn test_edits_around_expanded_insertion_hunks(
13553    executor: BackgroundExecutor,
13554    cx: &mut gpui::TestAppContext,
13555) {
13556    init_test(cx, |_| {});
13557
13558    let mut cx = EditorTestContext::new(cx).await;
13559
13560    let diff_base = r#"
13561        use some::mod1;
13562        use some::mod2;
13563
13564        const A: u32 = 42;
13565
13566        fn main() {
13567            println!("hello");
13568
13569            println!("world");
13570        }
13571        "#
13572    .unindent();
13573    executor.run_until_parked();
13574    cx.set_state(
13575        &r#"
13576        use some::mod1;
13577        use some::mod2;
13578
13579        const A: u32 = 42;
13580        const B: u32 = 42;
13581        const C: u32 = 42;
13582        ˇ
13583
13584        fn main() {
13585            println!("hello");
13586
13587            println!("world");
13588        }
13589        "#
13590        .unindent(),
13591    );
13592
13593    cx.set_diff_base(&diff_base);
13594    executor.run_until_parked();
13595
13596    cx.update_editor(|editor, window, cx| {
13597        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13598    });
13599    executor.run_until_parked();
13600
13601    cx.assert_state_with_diff(
13602        r#"
13603        use some::mod1;
13604        use some::mod2;
13605
13606        const A: u32 = 42;
13607      + const B: u32 = 42;
13608      + const C: u32 = 42;
13609      + ˇ
13610
13611        fn main() {
13612            println!("hello");
13613
13614            println!("world");
13615        }
13616      "#
13617        .unindent(),
13618    );
13619
13620    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
13621    executor.run_until_parked();
13622
13623    cx.assert_state_with_diff(
13624        r#"
13625        use some::mod1;
13626        use some::mod2;
13627
13628        const A: u32 = 42;
13629      + const B: u32 = 42;
13630      + const C: u32 = 42;
13631      + const D: u32 = 42;
13632      + ˇ
13633
13634        fn main() {
13635            println!("hello");
13636
13637            println!("world");
13638        }
13639      "#
13640        .unindent(),
13641    );
13642
13643    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
13644    executor.run_until_parked();
13645
13646    cx.assert_state_with_diff(
13647        r#"
13648        use some::mod1;
13649        use some::mod2;
13650
13651        const A: u32 = 42;
13652      + const B: u32 = 42;
13653      + const C: u32 = 42;
13654      + const D: u32 = 42;
13655      + const E: u32 = 42;
13656      + ˇ
13657
13658        fn main() {
13659            println!("hello");
13660
13661            println!("world");
13662        }
13663      "#
13664        .unindent(),
13665    );
13666
13667    cx.update_editor(|editor, window, cx| {
13668        editor.delete_line(&DeleteLine, window, cx);
13669    });
13670    executor.run_until_parked();
13671
13672    cx.assert_state_with_diff(
13673        r#"
13674        use some::mod1;
13675        use some::mod2;
13676
13677        const A: u32 = 42;
13678      + const B: u32 = 42;
13679      + const C: u32 = 42;
13680      + const D: u32 = 42;
13681      + const E: u32 = 42;
13682        ˇ
13683        fn main() {
13684            println!("hello");
13685
13686            println!("world");
13687        }
13688      "#
13689        .unindent(),
13690    );
13691
13692    cx.update_editor(|editor, window, cx| {
13693        editor.move_up(&MoveUp, window, cx);
13694        editor.delete_line(&DeleteLine, window, cx);
13695        editor.move_up(&MoveUp, window, cx);
13696        editor.delete_line(&DeleteLine, window, cx);
13697        editor.move_up(&MoveUp, window, cx);
13698        editor.delete_line(&DeleteLine, window, cx);
13699    });
13700    executor.run_until_parked();
13701    cx.assert_state_with_diff(
13702        r#"
13703        use some::mod1;
13704        use some::mod2;
13705
13706        const A: u32 = 42;
13707      + const B: u32 = 42;
13708        ˇ
13709        fn main() {
13710            println!("hello");
13711
13712            println!("world");
13713        }
13714      "#
13715        .unindent(),
13716    );
13717
13718    cx.update_editor(|editor, window, cx| {
13719        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
13720        editor.delete_line(&DeleteLine, window, cx);
13721    });
13722    executor.run_until_parked();
13723    cx.assert_state_with_diff(
13724        r#"
13725        ˇ
13726        fn main() {
13727            println!("hello");
13728
13729            println!("world");
13730        }
13731      "#
13732        .unindent(),
13733    );
13734}
13735
13736#[gpui::test]
13737async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
13738    init_test(cx, |_| {});
13739
13740    let mut cx = EditorTestContext::new(cx).await;
13741    cx.set_diff_base(indoc! { "
13742        one
13743        two
13744        three
13745        four
13746        five
13747        "
13748    });
13749    cx.set_state(indoc! { "
13750        one
13751        ˇthree
13752        five
13753    "});
13754    cx.run_until_parked();
13755    cx.update_editor(|editor, window, cx| {
13756        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13757    });
13758    cx.assert_state_with_diff(
13759        indoc! { "
13760        one
13761      - two
13762        ˇthree
13763      - four
13764        five
13765    "}
13766        .to_string(),
13767    );
13768    cx.update_editor(|editor, window, cx| {
13769        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13770    });
13771
13772    cx.assert_state_with_diff(
13773        indoc! { "
13774        one
13775        ˇthree
13776        five
13777    "}
13778        .to_string(),
13779    );
13780
13781    cx.set_state(indoc! { "
13782        one
13783        ˇTWO
13784        three
13785        four
13786        five
13787    "});
13788    cx.run_until_parked();
13789    cx.update_editor(|editor, window, cx| {
13790        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13791    });
13792
13793    cx.assert_state_with_diff(
13794        indoc! { "
13795            one
13796          - two
13797          + ˇTWO
13798            three
13799            four
13800            five
13801        "}
13802        .to_string(),
13803    );
13804    cx.update_editor(|editor, window, cx| {
13805        editor.move_up(&Default::default(), window, cx);
13806        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
13807    });
13808    cx.assert_state_with_diff(
13809        indoc! { "
13810            one
13811            ˇTWO
13812            three
13813            four
13814            five
13815        "}
13816        .to_string(),
13817    );
13818}
13819
13820#[gpui::test]
13821async fn test_edits_around_expanded_deletion_hunks(
13822    executor: BackgroundExecutor,
13823    cx: &mut gpui::TestAppContext,
13824) {
13825    init_test(cx, |_| {});
13826
13827    let mut cx = EditorTestContext::new(cx).await;
13828
13829    let diff_base = r#"
13830        use some::mod1;
13831        use some::mod2;
13832
13833        const A: u32 = 42;
13834        const B: u32 = 42;
13835        const C: u32 = 42;
13836
13837
13838        fn main() {
13839            println!("hello");
13840
13841            println!("world");
13842        }
13843    "#
13844    .unindent();
13845    executor.run_until_parked();
13846    cx.set_state(
13847        &r#"
13848        use some::mod1;
13849        use some::mod2;
13850
13851        ˇconst B: u32 = 42;
13852        const C: u32 = 42;
13853
13854
13855        fn main() {
13856            println!("hello");
13857
13858            println!("world");
13859        }
13860        "#
13861        .unindent(),
13862    );
13863
13864    cx.set_diff_base(&diff_base);
13865    executor.run_until_parked();
13866
13867    cx.update_editor(|editor, window, cx| {
13868        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13869    });
13870    executor.run_until_parked();
13871
13872    cx.assert_state_with_diff(
13873        r#"
13874        use some::mod1;
13875        use some::mod2;
13876
13877      - const A: u32 = 42;
13878        ˇconst B: u32 = 42;
13879        const C: u32 = 42;
13880
13881
13882        fn main() {
13883            println!("hello");
13884
13885            println!("world");
13886        }
13887      "#
13888        .unindent(),
13889    );
13890
13891    cx.update_editor(|editor, window, cx| {
13892        editor.delete_line(&DeleteLine, window, cx);
13893    });
13894    executor.run_until_parked();
13895    cx.assert_state_with_diff(
13896        r#"
13897        use some::mod1;
13898        use some::mod2;
13899
13900      - const A: u32 = 42;
13901      - const B: u32 = 42;
13902        ˇconst C: u32 = 42;
13903
13904
13905        fn main() {
13906            println!("hello");
13907
13908            println!("world");
13909        }
13910      "#
13911        .unindent(),
13912    );
13913
13914    cx.update_editor(|editor, window, cx| {
13915        editor.delete_line(&DeleteLine, window, cx);
13916    });
13917    executor.run_until_parked();
13918    cx.assert_state_with_diff(
13919        r#"
13920        use some::mod1;
13921        use some::mod2;
13922
13923      - const A: u32 = 42;
13924      - const B: u32 = 42;
13925      - const C: u32 = 42;
13926        ˇ
13927
13928        fn main() {
13929            println!("hello");
13930
13931            println!("world");
13932        }
13933      "#
13934        .unindent(),
13935    );
13936
13937    cx.update_editor(|editor, window, cx| {
13938        editor.handle_input("replacement", window, cx);
13939    });
13940    executor.run_until_parked();
13941    cx.assert_state_with_diff(
13942        r#"
13943        use some::mod1;
13944        use some::mod2;
13945
13946      - const A: u32 = 42;
13947      - const B: u32 = 42;
13948      - const C: u32 = 42;
13949      -
13950      + replacementˇ
13951
13952        fn main() {
13953            println!("hello");
13954
13955            println!("world");
13956        }
13957      "#
13958        .unindent(),
13959    );
13960}
13961
13962#[gpui::test]
13963async fn test_backspace_after_deletion_hunk(
13964    executor: BackgroundExecutor,
13965    cx: &mut gpui::TestAppContext,
13966) {
13967    init_test(cx, |_| {});
13968
13969    let mut cx = EditorTestContext::new(cx).await;
13970
13971    let base_text = r#"
13972        one
13973        two
13974        three
13975        four
13976        five
13977    "#
13978    .unindent();
13979    executor.run_until_parked();
13980    cx.set_state(
13981        &r#"
13982        one
13983        two
13984        fˇour
13985        five
13986        "#
13987        .unindent(),
13988    );
13989
13990    cx.set_diff_base(&base_text);
13991    executor.run_until_parked();
13992
13993    cx.update_editor(|editor, window, cx| {
13994        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
13995    });
13996    executor.run_until_parked();
13997
13998    cx.assert_state_with_diff(
13999        r#"
14000          one
14001          two
14002        - three
14003          fˇour
14004          five
14005        "#
14006        .unindent(),
14007    );
14008
14009    cx.update_editor(|editor, window, cx| {
14010        editor.backspace(&Backspace, window, cx);
14011        editor.backspace(&Backspace, window, cx);
14012    });
14013    executor.run_until_parked();
14014    cx.assert_state_with_diff(
14015        r#"
14016          one
14017          two
14018        - threeˇ
14019        - four
14020        + our
14021          five
14022        "#
14023        .unindent(),
14024    );
14025}
14026
14027#[gpui::test]
14028async fn test_edit_after_expanded_modification_hunk(
14029    executor: BackgroundExecutor,
14030    cx: &mut gpui::TestAppContext,
14031) {
14032    init_test(cx, |_| {});
14033
14034    let mut cx = EditorTestContext::new(cx).await;
14035
14036    let diff_base = r#"
14037        use some::mod1;
14038        use some::mod2;
14039
14040        const A: u32 = 42;
14041        const B: u32 = 42;
14042        const C: u32 = 42;
14043        const D: u32 = 42;
14044
14045
14046        fn main() {
14047            println!("hello");
14048
14049            println!("world");
14050        }"#
14051    .unindent();
14052
14053    cx.set_state(
14054        &r#"
14055        use some::mod1;
14056        use some::mod2;
14057
14058        const A: u32 = 42;
14059        const B: u32 = 42;
14060        const C: u32 = 43ˇ
14061        const D: u32 = 42;
14062
14063
14064        fn main() {
14065            println!("hello");
14066
14067            println!("world");
14068        }"#
14069        .unindent(),
14070    );
14071
14072    cx.set_diff_base(&diff_base);
14073    executor.run_until_parked();
14074    cx.update_editor(|editor, window, cx| {
14075        editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
14076    });
14077    executor.run_until_parked();
14078
14079    cx.assert_state_with_diff(
14080        r#"
14081        use some::mod1;
14082        use some::mod2;
14083
14084        const A: u32 = 42;
14085        const B: u32 = 42;
14086      - const C: u32 = 42;
14087      + const C: u32 = 43ˇ
14088        const D: u32 = 42;
14089
14090
14091        fn main() {
14092            println!("hello");
14093
14094            println!("world");
14095        }"#
14096        .unindent(),
14097    );
14098
14099    cx.update_editor(|editor, window, cx| {
14100        editor.handle_input("\nnew_line\n", window, cx);
14101    });
14102    executor.run_until_parked();
14103
14104    cx.assert_state_with_diff(
14105        r#"
14106        use some::mod1;
14107        use some::mod2;
14108
14109        const A: u32 = 42;
14110        const B: u32 = 42;
14111      - const C: u32 = 42;
14112      + const C: u32 = 43
14113      + new_line
14114      + ˇ
14115        const D: u32 = 42;
14116
14117
14118        fn main() {
14119            println!("hello");
14120
14121            println!("world");
14122        }"#
14123        .unindent(),
14124    );
14125}
14126
14127#[gpui::test]
14128async fn test_stage_and_unstage_added_file_hunk(
14129    executor: BackgroundExecutor,
14130    cx: &mut gpui::TestAppContext,
14131) {
14132    init_test(cx, |_| {});
14133
14134    let mut cx = EditorTestContext::new(cx).await;
14135    cx.update_editor(|editor, _, cx| {
14136        editor.set_expand_all_diff_hunks(cx);
14137    });
14138
14139    let working_copy = r#"
14140            ˇfn main() {
14141                println!("hello, world!");
14142            }
14143        "#
14144    .unindent();
14145
14146    cx.set_state(&working_copy);
14147    executor.run_until_parked();
14148
14149    cx.assert_state_with_diff(
14150        r#"
14151            + ˇfn main() {
14152            +     println!("hello, world!");
14153            + }
14154        "#
14155        .unindent(),
14156    );
14157    cx.assert_index_text(None);
14158
14159    cx.update_editor(|editor, window, cx| {
14160        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14161    });
14162    executor.run_until_parked();
14163    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14164    cx.assert_state_with_diff(
14165        r#"
14166            + ˇfn main() {
14167            +     println!("hello, world!");
14168            + }
14169        "#
14170        .unindent(),
14171    );
14172
14173    cx.update_editor(|editor, window, cx| {
14174        editor.toggle_staged_selected_diff_hunks(&ToggleStagedSelectedDiffHunks, window, cx);
14175    });
14176    executor.run_until_parked();
14177    cx.assert_index_text(None);
14178}
14179
14180async fn setup_indent_guides_editor(
14181    text: &str,
14182    cx: &mut gpui::TestAppContext,
14183) -> (BufferId, EditorTestContext) {
14184    init_test(cx, |_| {});
14185
14186    let mut cx = EditorTestContext::new(cx).await;
14187
14188    let buffer_id = cx.update_editor(|editor, window, cx| {
14189        editor.set_text(text, window, cx);
14190        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14191
14192        buffer_ids[0]
14193    });
14194
14195    (buffer_id, cx)
14196}
14197
14198fn assert_indent_guides(
14199    range: Range<u32>,
14200    expected: Vec<IndentGuide>,
14201    active_indices: Option<Vec<usize>>,
14202    cx: &mut EditorTestContext,
14203) {
14204    let indent_guides = cx.update_editor(|editor, window, cx| {
14205        let snapshot = editor.snapshot(window, cx).display_snapshot;
14206        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14207            editor,
14208            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14209            true,
14210            &snapshot,
14211            cx,
14212        );
14213
14214        indent_guides.sort_by(|a, b| {
14215            a.depth.cmp(&b.depth).then(
14216                a.start_row
14217                    .cmp(&b.start_row)
14218                    .then(a.end_row.cmp(&b.end_row)),
14219            )
14220        });
14221        indent_guides
14222    });
14223
14224    if let Some(expected) = active_indices {
14225        let active_indices = cx.update_editor(|editor, window, cx| {
14226            let snapshot = editor.snapshot(window, cx).display_snapshot;
14227            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14228        });
14229
14230        assert_eq!(
14231            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14232            expected,
14233            "Active indent guide indices do not match"
14234        );
14235    }
14236
14237    assert_eq!(indent_guides, expected, "Indent guides do not match");
14238}
14239
14240fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14241    IndentGuide {
14242        buffer_id,
14243        start_row: MultiBufferRow(start_row),
14244        end_row: MultiBufferRow(end_row),
14245        depth,
14246        tab_size: 4,
14247        settings: IndentGuideSettings {
14248            enabled: true,
14249            line_width: 1,
14250            active_line_width: 1,
14251            ..Default::default()
14252        },
14253    }
14254}
14255
14256#[gpui::test]
14257async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14258    let (buffer_id, mut cx) = setup_indent_guides_editor(
14259        &"
14260    fn main() {
14261        let a = 1;
14262    }"
14263        .unindent(),
14264        cx,
14265    )
14266    .await;
14267
14268    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14269}
14270
14271#[gpui::test]
14272async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
14273    let (buffer_id, mut cx) = setup_indent_guides_editor(
14274        &"
14275    fn main() {
14276        let a = 1;
14277        let b = 2;
14278    }"
14279        .unindent(),
14280        cx,
14281    )
14282    .await;
14283
14284    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14285}
14286
14287#[gpui::test]
14288async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
14289    let (buffer_id, mut cx) = setup_indent_guides_editor(
14290        &"
14291    fn main() {
14292        let a = 1;
14293        if a == 3 {
14294            let b = 2;
14295        } else {
14296            let c = 3;
14297        }
14298    }"
14299        .unindent(),
14300        cx,
14301    )
14302    .await;
14303
14304    assert_indent_guides(
14305        0..8,
14306        vec![
14307            indent_guide(buffer_id, 1, 6, 0),
14308            indent_guide(buffer_id, 3, 3, 1),
14309            indent_guide(buffer_id, 5, 5, 1),
14310        ],
14311        None,
14312        &mut cx,
14313    );
14314}
14315
14316#[gpui::test]
14317async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
14318    let (buffer_id, mut cx) = setup_indent_guides_editor(
14319        &"
14320    fn main() {
14321        let a = 1;
14322            let b = 2;
14323        let c = 3;
14324    }"
14325        .unindent(),
14326        cx,
14327    )
14328    .await;
14329
14330    assert_indent_guides(
14331        0..5,
14332        vec![
14333            indent_guide(buffer_id, 1, 3, 0),
14334            indent_guide(buffer_id, 2, 2, 1),
14335        ],
14336        None,
14337        &mut cx,
14338    );
14339}
14340
14341#[gpui::test]
14342async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
14343    let (buffer_id, mut cx) = setup_indent_guides_editor(
14344        &"
14345        fn main() {
14346            let a = 1;
14347
14348            let c = 3;
14349        }"
14350        .unindent(),
14351        cx,
14352    )
14353    .await;
14354
14355    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14356}
14357
14358#[gpui::test]
14359async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
14360    let (buffer_id, mut cx) = setup_indent_guides_editor(
14361        &"
14362        fn main() {
14363            let a = 1;
14364
14365            let c = 3;
14366
14367            if a == 3 {
14368                let b = 2;
14369            } else {
14370                let c = 3;
14371            }
14372        }"
14373        .unindent(),
14374        cx,
14375    )
14376    .await;
14377
14378    assert_indent_guides(
14379        0..11,
14380        vec![
14381            indent_guide(buffer_id, 1, 9, 0),
14382            indent_guide(buffer_id, 6, 6, 1),
14383            indent_guide(buffer_id, 8, 8, 1),
14384        ],
14385        None,
14386        &mut cx,
14387    );
14388}
14389
14390#[gpui::test]
14391async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
14392    let (buffer_id, mut cx) = setup_indent_guides_editor(
14393        &"
14394        fn main() {
14395            let a = 1;
14396
14397            let c = 3;
14398
14399            if a == 3 {
14400                let b = 2;
14401            } else {
14402                let c = 3;
14403            }
14404        }"
14405        .unindent(),
14406        cx,
14407    )
14408    .await;
14409
14410    assert_indent_guides(
14411        1..11,
14412        vec![
14413            indent_guide(buffer_id, 1, 9, 0),
14414            indent_guide(buffer_id, 6, 6, 1),
14415            indent_guide(buffer_id, 8, 8, 1),
14416        ],
14417        None,
14418        &mut cx,
14419    );
14420}
14421
14422#[gpui::test]
14423async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
14424    let (buffer_id, mut cx) = setup_indent_guides_editor(
14425        &"
14426        fn main() {
14427            let a = 1;
14428
14429            let c = 3;
14430
14431            if a == 3 {
14432                let b = 2;
14433            } else {
14434                let c = 3;
14435            }
14436        }"
14437        .unindent(),
14438        cx,
14439    )
14440    .await;
14441
14442    assert_indent_guides(
14443        1..10,
14444        vec![
14445            indent_guide(buffer_id, 1, 9, 0),
14446            indent_guide(buffer_id, 6, 6, 1),
14447            indent_guide(buffer_id, 8, 8, 1),
14448        ],
14449        None,
14450        &mut cx,
14451    );
14452}
14453
14454#[gpui::test]
14455async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
14456    let (buffer_id, mut cx) = setup_indent_guides_editor(
14457        &"
14458        block1
14459            block2
14460                block3
14461                    block4
14462            block2
14463        block1
14464        block1"
14465            .unindent(),
14466        cx,
14467    )
14468    .await;
14469
14470    assert_indent_guides(
14471        1..10,
14472        vec![
14473            indent_guide(buffer_id, 1, 4, 0),
14474            indent_guide(buffer_id, 2, 3, 1),
14475            indent_guide(buffer_id, 3, 3, 2),
14476        ],
14477        None,
14478        &mut cx,
14479    );
14480}
14481
14482#[gpui::test]
14483async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
14484    let (buffer_id, mut cx) = setup_indent_guides_editor(
14485        &"
14486        block1
14487            block2
14488                block3
14489
14490        block1
14491        block1"
14492            .unindent(),
14493        cx,
14494    )
14495    .await;
14496
14497    assert_indent_guides(
14498        0..6,
14499        vec![
14500            indent_guide(buffer_id, 1, 2, 0),
14501            indent_guide(buffer_id, 2, 2, 1),
14502        ],
14503        None,
14504        &mut cx,
14505    );
14506}
14507
14508#[gpui::test]
14509async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
14510    let (buffer_id, mut cx) = setup_indent_guides_editor(
14511        &"
14512        block1
14513
14514
14515
14516            block2
14517        "
14518        .unindent(),
14519        cx,
14520    )
14521    .await;
14522
14523    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14524}
14525
14526#[gpui::test]
14527async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
14528    let (buffer_id, mut cx) = setup_indent_guides_editor(
14529        &"
14530        def a:
14531        \tb = 3
14532        \tif True:
14533        \t\tc = 4
14534        \t\td = 5
14535        \tprint(b)
14536        "
14537        .unindent(),
14538        cx,
14539    )
14540    .await;
14541
14542    assert_indent_guides(
14543        0..6,
14544        vec![
14545            indent_guide(buffer_id, 1, 6, 0),
14546            indent_guide(buffer_id, 3, 4, 1),
14547        ],
14548        None,
14549        &mut cx,
14550    );
14551}
14552
14553#[gpui::test]
14554async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
14555    let (buffer_id, mut cx) = setup_indent_guides_editor(
14556        &"
14557    fn main() {
14558        let a = 1;
14559    }"
14560        .unindent(),
14561        cx,
14562    )
14563    .await;
14564
14565    cx.update_editor(|editor, window, cx| {
14566        editor.change_selections(None, window, cx, |s| {
14567            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14568        });
14569    });
14570
14571    assert_indent_guides(
14572        0..3,
14573        vec![indent_guide(buffer_id, 1, 1, 0)],
14574        Some(vec![0]),
14575        &mut cx,
14576    );
14577}
14578
14579#[gpui::test]
14580async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
14581    let (buffer_id, mut cx) = setup_indent_guides_editor(
14582        &"
14583    fn main() {
14584        if 1 == 2 {
14585            let a = 1;
14586        }
14587    }"
14588        .unindent(),
14589        cx,
14590    )
14591    .await;
14592
14593    cx.update_editor(|editor, window, cx| {
14594        editor.change_selections(None, window, cx, |s| {
14595            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14596        });
14597    });
14598
14599    assert_indent_guides(
14600        0..4,
14601        vec![
14602            indent_guide(buffer_id, 1, 3, 0),
14603            indent_guide(buffer_id, 2, 2, 1),
14604        ],
14605        Some(vec![1]),
14606        &mut cx,
14607    );
14608
14609    cx.update_editor(|editor, window, cx| {
14610        editor.change_selections(None, window, cx, |s| {
14611            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14612        });
14613    });
14614
14615    assert_indent_guides(
14616        0..4,
14617        vec![
14618            indent_guide(buffer_id, 1, 3, 0),
14619            indent_guide(buffer_id, 2, 2, 1),
14620        ],
14621        Some(vec![1]),
14622        &mut cx,
14623    );
14624
14625    cx.update_editor(|editor, window, cx| {
14626        editor.change_selections(None, window, cx, |s| {
14627            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
14628        });
14629    });
14630
14631    assert_indent_guides(
14632        0..4,
14633        vec![
14634            indent_guide(buffer_id, 1, 3, 0),
14635            indent_guide(buffer_id, 2, 2, 1),
14636        ],
14637        Some(vec![0]),
14638        &mut cx,
14639    );
14640}
14641
14642#[gpui::test]
14643async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
14644    let (buffer_id, mut cx) = setup_indent_guides_editor(
14645        &"
14646    fn main() {
14647        let a = 1;
14648
14649        let b = 2;
14650    }"
14651        .unindent(),
14652        cx,
14653    )
14654    .await;
14655
14656    cx.update_editor(|editor, window, cx| {
14657        editor.change_selections(None, window, cx, |s| {
14658            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
14659        });
14660    });
14661
14662    assert_indent_guides(
14663        0..5,
14664        vec![indent_guide(buffer_id, 1, 3, 0)],
14665        Some(vec![0]),
14666        &mut cx,
14667    );
14668}
14669
14670#[gpui::test]
14671async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
14672    let (buffer_id, mut cx) = setup_indent_guides_editor(
14673        &"
14674    def m:
14675        a = 1
14676        pass"
14677            .unindent(),
14678        cx,
14679    )
14680    .await;
14681
14682    cx.update_editor(|editor, window, cx| {
14683        editor.change_selections(None, window, cx, |s| {
14684            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
14685        });
14686    });
14687
14688    assert_indent_guides(
14689        0..3,
14690        vec![indent_guide(buffer_id, 1, 2, 0)],
14691        Some(vec![0]),
14692        &mut cx,
14693    );
14694}
14695
14696#[gpui::test]
14697async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContext) {
14698    init_test(cx, |_| {});
14699    let mut cx = EditorTestContext::new(cx).await;
14700    let text = indoc! {
14701        "
14702        impl A {
14703            fn b() {
14704                0;
14705                3;
14706                5;
14707                6;
14708                7;
14709            }
14710        }
14711        "
14712    };
14713    let base_text = indoc! {
14714        "
14715        impl A {
14716            fn b() {
14717                0;
14718                1;
14719                2;
14720                3;
14721                4;
14722            }
14723            fn c() {
14724                5;
14725                6;
14726                7;
14727            }
14728        }
14729        "
14730    };
14731
14732    cx.update_editor(|editor, window, cx| {
14733        editor.set_text(text, window, cx);
14734
14735        editor.buffer().update(cx, |multibuffer, cx| {
14736            let buffer = multibuffer.as_singleton().unwrap();
14737            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
14738
14739            multibuffer.set_all_diff_hunks_expanded(cx);
14740            multibuffer.add_diff(diff, cx);
14741
14742            buffer.read(cx).remote_id()
14743        })
14744    });
14745    cx.run_until_parked();
14746
14747    cx.assert_state_with_diff(
14748        indoc! { "
14749          impl A {
14750              fn b() {
14751                  0;
14752        -         1;
14753        -         2;
14754                  3;
14755        -         4;
14756        -     }
14757        -     fn c() {
14758                  5;
14759                  6;
14760                  7;
14761              }
14762          }
14763          ˇ"
14764        }
14765        .to_string(),
14766    );
14767
14768    let mut actual_guides = cx.update_editor(|editor, window, cx| {
14769        editor
14770            .snapshot(window, cx)
14771            .buffer_snapshot
14772            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
14773            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
14774            .collect::<Vec<_>>()
14775    });
14776    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
14777    assert_eq!(
14778        actual_guides,
14779        vec![
14780            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
14781            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
14782            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
14783        ]
14784    );
14785}
14786
14787#[gpui::test]
14788fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
14789    init_test(cx, |_| {});
14790
14791    let editor = cx.add_window(|window, cx| {
14792        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
14793        build_editor(buffer, window, cx)
14794    });
14795
14796    let render_args = Arc::new(Mutex::new(None));
14797    let snapshot = editor
14798        .update(cx, |editor, window, cx| {
14799            let snapshot = editor.buffer().read(cx).snapshot(cx);
14800            let range =
14801                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
14802
14803            struct RenderArgs {
14804                row: MultiBufferRow,
14805                folded: bool,
14806                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
14807            }
14808
14809            let crease = Crease::inline(
14810                range,
14811                FoldPlaceholder::test(),
14812                {
14813                    let toggle_callback = render_args.clone();
14814                    move |row, folded, callback, _window, _cx| {
14815                        *toggle_callback.lock() = Some(RenderArgs {
14816                            row,
14817                            folded,
14818                            callback,
14819                        });
14820                        div()
14821                    }
14822                },
14823                |_row, _folded, _window, _cx| div(),
14824            );
14825
14826            editor.insert_creases(Some(crease), cx);
14827            let snapshot = editor.snapshot(window, cx);
14828            let _div = snapshot.render_crease_toggle(
14829                MultiBufferRow(1),
14830                false,
14831                cx.entity().clone(),
14832                window,
14833                cx,
14834            );
14835            snapshot
14836        })
14837        .unwrap();
14838
14839    let render_args = render_args.lock().take().unwrap();
14840    assert_eq!(render_args.row, MultiBufferRow(1));
14841    assert!(!render_args.folded);
14842    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14843
14844    cx.update_window(*editor, |_, window, cx| {
14845        (render_args.callback)(true, window, cx)
14846    })
14847    .unwrap();
14848    let snapshot = editor
14849        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14850        .unwrap();
14851    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
14852
14853    cx.update_window(*editor, |_, window, cx| {
14854        (render_args.callback)(false, window, cx)
14855    })
14856    .unwrap();
14857    let snapshot = editor
14858        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
14859        .unwrap();
14860    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
14861}
14862
14863#[gpui::test]
14864async fn test_input_text(cx: &mut gpui::TestAppContext) {
14865    init_test(cx, |_| {});
14866    let mut cx = EditorTestContext::new(cx).await;
14867
14868    cx.set_state(
14869        &r#"ˇone
14870        two
14871
14872        three
14873        fourˇ
14874        five
14875
14876        siˇx"#
14877            .unindent(),
14878    );
14879
14880    cx.dispatch_action(HandleInput(String::new()));
14881    cx.assert_editor_state(
14882        &r#"ˇone
14883        two
14884
14885        three
14886        fourˇ
14887        five
14888
14889        siˇx"#
14890            .unindent(),
14891    );
14892
14893    cx.dispatch_action(HandleInput("AAAA".to_string()));
14894    cx.assert_editor_state(
14895        &r#"AAAAˇone
14896        two
14897
14898        three
14899        fourAAAAˇ
14900        five
14901
14902        siAAAAˇx"#
14903            .unindent(),
14904    );
14905}
14906
14907#[gpui::test]
14908async fn test_scroll_cursor_center_top_bottom(cx: &mut gpui::TestAppContext) {
14909    init_test(cx, |_| {});
14910
14911    let mut cx = EditorTestContext::new(cx).await;
14912    cx.set_state(
14913        r#"let foo = 1;
14914let foo = 2;
14915let foo = 3;
14916let fooˇ = 4;
14917let foo = 5;
14918let foo = 6;
14919let foo = 7;
14920let foo = 8;
14921let foo = 9;
14922let foo = 10;
14923let foo = 11;
14924let foo = 12;
14925let foo = 13;
14926let foo = 14;
14927let foo = 15;"#,
14928    );
14929
14930    cx.update_editor(|e, window, cx| {
14931        assert_eq!(
14932            e.next_scroll_position,
14933            NextScrollCursorCenterTopBottom::Center,
14934            "Default next scroll direction is center",
14935        );
14936
14937        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14938        assert_eq!(
14939            e.next_scroll_position,
14940            NextScrollCursorCenterTopBottom::Top,
14941            "After center, next scroll direction should be top",
14942        );
14943
14944        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14945        assert_eq!(
14946            e.next_scroll_position,
14947            NextScrollCursorCenterTopBottom::Bottom,
14948            "After top, next scroll direction should be bottom",
14949        );
14950
14951        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14952        assert_eq!(
14953            e.next_scroll_position,
14954            NextScrollCursorCenterTopBottom::Center,
14955            "After bottom, scrolling should start over",
14956        );
14957
14958        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
14959        assert_eq!(
14960            e.next_scroll_position,
14961            NextScrollCursorCenterTopBottom::Top,
14962            "Scrolling continues if retriggered fast enough"
14963        );
14964    });
14965
14966    cx.executor()
14967        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
14968    cx.executor().run_until_parked();
14969    cx.update_editor(|e, _, _| {
14970        assert_eq!(
14971            e.next_scroll_position,
14972            NextScrollCursorCenterTopBottom::Center,
14973            "If scrolling is not triggered fast enough, it should reset"
14974        );
14975    });
14976}
14977
14978#[gpui::test]
14979async fn test_goto_definition_with_find_all_references_fallback(cx: &mut gpui::TestAppContext) {
14980    init_test(cx, |_| {});
14981    let mut cx = EditorLspTestContext::new_rust(
14982        lsp::ServerCapabilities {
14983            definition_provider: Some(lsp::OneOf::Left(true)),
14984            references_provider: Some(lsp::OneOf::Left(true)),
14985            ..lsp::ServerCapabilities::default()
14986        },
14987        cx,
14988    )
14989    .await;
14990
14991    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
14992        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
14993            move |params, _| async move {
14994                if empty_go_to_definition {
14995                    Ok(None)
14996                } else {
14997                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
14998                        uri: params.text_document_position_params.text_document.uri,
14999                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15000                    })))
15001                }
15002            },
15003        );
15004        let references =
15005            cx.lsp
15006                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15007                    Ok(Some(vec![lsp::Location {
15008                        uri: params.text_document_position.text_document.uri,
15009                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15010                    }]))
15011                });
15012        (go_to_definition, references)
15013    };
15014
15015    cx.set_state(
15016        &r#"fn one() {
15017            let mut a = ˇtwo();
15018        }
15019
15020        fn two() {}"#
15021            .unindent(),
15022    );
15023    set_up_lsp_handlers(false, &mut cx);
15024    let navigated = cx
15025        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15026        .await
15027        .expect("Failed to navigate to definition");
15028    assert_eq!(
15029        navigated,
15030        Navigated::Yes,
15031        "Should have navigated to definition from the GetDefinition response"
15032    );
15033    cx.assert_editor_state(
15034        &r#"fn one() {
15035            let mut a = two();
15036        }
15037
15038        fn «twoˇ»() {}"#
15039            .unindent(),
15040    );
15041
15042    let editors = cx.update_workspace(|workspace, _, cx| {
15043        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15044    });
15045    cx.update_editor(|_, _, test_editor_cx| {
15046        assert_eq!(
15047            editors.len(),
15048            1,
15049            "Initially, only one, test, editor should be open in the workspace"
15050        );
15051        assert_eq!(
15052            test_editor_cx.entity(),
15053            editors.last().expect("Asserted len is 1").clone()
15054        );
15055    });
15056
15057    set_up_lsp_handlers(true, &mut cx);
15058    let navigated = cx
15059        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
15060        .await
15061        .expect("Failed to navigate to lookup references");
15062    assert_eq!(
15063        navigated,
15064        Navigated::Yes,
15065        "Should have navigated to references as a fallback after empty GoToDefinition response"
15066    );
15067    // We should not change the selections in the existing file,
15068    // if opening another milti buffer with the references
15069    cx.assert_editor_state(
15070        &r#"fn one() {
15071            let mut a = two();
15072        }
15073
15074        fn «twoˇ»() {}"#
15075            .unindent(),
15076    );
15077    let editors = cx.update_workspace(|workspace, _, cx| {
15078        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
15079    });
15080    cx.update_editor(|_, _, test_editor_cx| {
15081        assert_eq!(
15082            editors.len(),
15083            2,
15084            "After falling back to references search, we open a new editor with the results"
15085        );
15086        let references_fallback_text = editors
15087            .into_iter()
15088            .find(|new_editor| *new_editor != test_editor_cx.entity())
15089            .expect("Should have one non-test editor now")
15090            .read(test_editor_cx)
15091            .text(test_editor_cx);
15092        assert_eq!(
15093            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
15094            "Should use the range from the references response and not the GoToDefinition one"
15095        );
15096    });
15097}
15098
15099#[gpui::test]
15100async fn test_find_enclosing_node_with_task(cx: &mut gpui::TestAppContext) {
15101    init_test(cx, |_| {});
15102
15103    let language = Arc::new(Language::new(
15104        LanguageConfig::default(),
15105        Some(tree_sitter_rust::LANGUAGE.into()),
15106    ));
15107
15108    let text = r#"
15109        #[cfg(test)]
15110        mod tests() {
15111            #[test]
15112            fn runnable_1() {
15113                let a = 1;
15114            }
15115
15116            #[test]
15117            fn runnable_2() {
15118                let a = 1;
15119                let b = 2;
15120            }
15121        }
15122    "#
15123    .unindent();
15124
15125    let fs = FakeFs::new(cx.executor());
15126    fs.insert_file("/file.rs", Default::default()).await;
15127
15128    let project = Project::test(fs, ["/a".as_ref()], cx).await;
15129    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15130    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15131    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
15132    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
15133
15134    let editor = cx.new_window_entity(|window, cx| {
15135        Editor::new(
15136            EditorMode::Full,
15137            multi_buffer,
15138            Some(project.clone()),
15139            true,
15140            window,
15141            cx,
15142        )
15143    });
15144
15145    editor.update_in(cx, |editor, window, cx| {
15146        editor.tasks.insert(
15147            (buffer.read(cx).remote_id(), 3),
15148            RunnableTasks {
15149                templates: vec![],
15150                offset: MultiBufferOffset(43),
15151                column: 0,
15152                extra_variables: HashMap::default(),
15153                context_range: BufferOffset(43)..BufferOffset(85),
15154            },
15155        );
15156        editor.tasks.insert(
15157            (buffer.read(cx).remote_id(), 8),
15158            RunnableTasks {
15159                templates: vec![],
15160                offset: MultiBufferOffset(86),
15161                column: 0,
15162                extra_variables: HashMap::default(),
15163                context_range: BufferOffset(86)..BufferOffset(191),
15164            },
15165        );
15166
15167        // Test finding task when cursor is inside function body
15168        editor.change_selections(None, window, cx, |s| {
15169            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
15170        });
15171        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15172        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
15173
15174        // Test finding task when cursor is on function name
15175        editor.change_selections(None, window, cx, |s| {
15176            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
15177        });
15178        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
15179        assert_eq!(row, 8, "Should find task when cursor is on function name");
15180    });
15181}
15182
15183#[gpui::test]
15184async fn test_multi_buffer_folding(cx: &mut gpui::TestAppContext) {
15185    init_test(cx, |_| {});
15186
15187    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15188    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
15189    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
15190
15191    let fs = FakeFs::new(cx.executor());
15192    fs.insert_tree(
15193        path!("/a"),
15194        json!({
15195            "first.rs": sample_text_1,
15196            "second.rs": sample_text_2,
15197            "third.rs": sample_text_3,
15198        }),
15199    )
15200    .await;
15201    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15202    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15203    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15204    let worktree = project.update(cx, |project, cx| {
15205        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15206        assert_eq!(worktrees.len(), 1);
15207        worktrees.pop().unwrap()
15208    });
15209    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15210
15211    let buffer_1 = project
15212        .update(cx, |project, cx| {
15213            project.open_buffer((worktree_id, "first.rs"), cx)
15214        })
15215        .await
15216        .unwrap();
15217    let buffer_2 = project
15218        .update(cx, |project, cx| {
15219            project.open_buffer((worktree_id, "second.rs"), cx)
15220        })
15221        .await
15222        .unwrap();
15223    let buffer_3 = project
15224        .update(cx, |project, cx| {
15225            project.open_buffer((worktree_id, "third.rs"), cx)
15226        })
15227        .await
15228        .unwrap();
15229
15230    let multi_buffer = cx.new(|cx| {
15231        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15232        multi_buffer.push_excerpts(
15233            buffer_1.clone(),
15234            [
15235                ExcerptRange {
15236                    context: Point::new(0, 0)..Point::new(3, 0),
15237                    primary: None,
15238                },
15239                ExcerptRange {
15240                    context: Point::new(5, 0)..Point::new(7, 0),
15241                    primary: None,
15242                },
15243                ExcerptRange {
15244                    context: Point::new(9, 0)..Point::new(10, 4),
15245                    primary: None,
15246                },
15247            ],
15248            cx,
15249        );
15250        multi_buffer.push_excerpts(
15251            buffer_2.clone(),
15252            [
15253                ExcerptRange {
15254                    context: Point::new(0, 0)..Point::new(3, 0),
15255                    primary: None,
15256                },
15257                ExcerptRange {
15258                    context: Point::new(5, 0)..Point::new(7, 0),
15259                    primary: None,
15260                },
15261                ExcerptRange {
15262                    context: Point::new(9, 0)..Point::new(10, 4),
15263                    primary: None,
15264                },
15265            ],
15266            cx,
15267        );
15268        multi_buffer.push_excerpts(
15269            buffer_3.clone(),
15270            [
15271                ExcerptRange {
15272                    context: Point::new(0, 0)..Point::new(3, 0),
15273                    primary: None,
15274                },
15275                ExcerptRange {
15276                    context: Point::new(5, 0)..Point::new(7, 0),
15277                    primary: None,
15278                },
15279                ExcerptRange {
15280                    context: Point::new(9, 0)..Point::new(10, 4),
15281                    primary: None,
15282                },
15283            ],
15284            cx,
15285        );
15286        multi_buffer
15287    });
15288    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15289        Editor::new(
15290            EditorMode::Full,
15291            multi_buffer,
15292            Some(project.clone()),
15293            true,
15294            window,
15295            cx,
15296        )
15297    });
15298
15299    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";
15300    assert_eq!(
15301        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15302        full_text,
15303    );
15304
15305    multi_buffer_editor.update(cx, |editor, cx| {
15306        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15307    });
15308    assert_eq!(
15309        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15310        "\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",
15311        "After folding the first buffer, its text should not be displayed"
15312    );
15313
15314    multi_buffer_editor.update(cx, |editor, cx| {
15315        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15316    });
15317    assert_eq!(
15318        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15319        "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n",
15320        "After folding the second buffer, its text should not be displayed"
15321    );
15322
15323    multi_buffer_editor.update(cx, |editor, cx| {
15324        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15325    });
15326    assert_eq!(
15327        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15328        "\n\n\n\n\n",
15329        "After folding the third buffer, its text should not be displayed"
15330    );
15331
15332    // Emulate selection inside the fold logic, that should work
15333    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15334        editor
15335            .snapshot(window, cx)
15336            .next_line_boundary(Point::new(0, 4));
15337    });
15338
15339    multi_buffer_editor.update(cx, |editor, cx| {
15340        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15341    });
15342    assert_eq!(
15343        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15344        "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n",
15345        "After unfolding the second buffer, its text should be displayed"
15346    );
15347
15348    multi_buffer_editor.update(cx, |editor, cx| {
15349        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15350    });
15351    assert_eq!(
15352        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15353        "\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",
15354        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
15355    );
15356
15357    multi_buffer_editor.update(cx, |editor, cx| {
15358        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15359    });
15360    assert_eq!(
15361        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15362        full_text,
15363        "After unfolding the all buffers, all original text should be displayed"
15364    );
15365}
15366
15367#[gpui::test]
15368async fn test_multi_buffer_single_excerpts_folding(cx: &mut gpui::TestAppContext) {
15369    init_test(cx, |_| {});
15370
15371    let sample_text_1 = "1111\n2222\n3333".to_string();
15372    let sample_text_2 = "4444\n5555\n6666".to_string();
15373    let sample_text_3 = "7777\n8888\n9999".to_string();
15374
15375    let fs = FakeFs::new(cx.executor());
15376    fs.insert_tree(
15377        path!("/a"),
15378        json!({
15379            "first.rs": sample_text_1,
15380            "second.rs": sample_text_2,
15381            "third.rs": sample_text_3,
15382        }),
15383    )
15384    .await;
15385    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15386    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15387    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15388    let worktree = project.update(cx, |project, cx| {
15389        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15390        assert_eq!(worktrees.len(), 1);
15391        worktrees.pop().unwrap()
15392    });
15393    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15394
15395    let buffer_1 = project
15396        .update(cx, |project, cx| {
15397            project.open_buffer((worktree_id, "first.rs"), cx)
15398        })
15399        .await
15400        .unwrap();
15401    let buffer_2 = project
15402        .update(cx, |project, cx| {
15403            project.open_buffer((worktree_id, "second.rs"), cx)
15404        })
15405        .await
15406        .unwrap();
15407    let buffer_3 = project
15408        .update(cx, |project, cx| {
15409            project.open_buffer((worktree_id, "third.rs"), cx)
15410        })
15411        .await
15412        .unwrap();
15413
15414    let multi_buffer = cx.new(|cx| {
15415        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15416        multi_buffer.push_excerpts(
15417            buffer_1.clone(),
15418            [ExcerptRange {
15419                context: Point::new(0, 0)..Point::new(3, 0),
15420                primary: None,
15421            }],
15422            cx,
15423        );
15424        multi_buffer.push_excerpts(
15425            buffer_2.clone(),
15426            [ExcerptRange {
15427                context: Point::new(0, 0)..Point::new(3, 0),
15428                primary: None,
15429            }],
15430            cx,
15431        );
15432        multi_buffer.push_excerpts(
15433            buffer_3.clone(),
15434            [ExcerptRange {
15435                context: Point::new(0, 0)..Point::new(3, 0),
15436                primary: None,
15437            }],
15438            cx,
15439        );
15440        multi_buffer
15441    });
15442
15443    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15444        Editor::new(
15445            EditorMode::Full,
15446            multi_buffer,
15447            Some(project.clone()),
15448            true,
15449            window,
15450            cx,
15451        )
15452    });
15453
15454    let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n";
15455    assert_eq!(
15456        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15457        full_text,
15458    );
15459
15460    multi_buffer_editor.update(cx, |editor, cx| {
15461        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
15462    });
15463    assert_eq!(
15464        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15465        "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n",
15466        "After folding the first buffer, its text should not be displayed"
15467    );
15468
15469    multi_buffer_editor.update(cx, |editor, cx| {
15470        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
15471    });
15472
15473    assert_eq!(
15474        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15475        "\n\n\n\n\n\n\n7777\n8888\n9999\n",
15476        "After folding the second buffer, its text should not be displayed"
15477    );
15478
15479    multi_buffer_editor.update(cx, |editor, cx| {
15480        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
15481    });
15482    assert_eq!(
15483        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15484        "\n\n\n\n\n",
15485        "After folding the third buffer, its text should not be displayed"
15486    );
15487
15488    multi_buffer_editor.update(cx, |editor, cx| {
15489        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
15490    });
15491    assert_eq!(
15492        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15493        "\n\n\n\n\n4444\n5555\n6666\n\n\n",
15494        "After unfolding the second buffer, its text should be displayed"
15495    );
15496
15497    multi_buffer_editor.update(cx, |editor, cx| {
15498        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
15499    });
15500    assert_eq!(
15501        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15502        "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n",
15503        "After unfolding the first buffer, its text should be displayed"
15504    );
15505
15506    multi_buffer_editor.update(cx, |editor, cx| {
15507        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
15508    });
15509    assert_eq!(
15510        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15511        full_text,
15512        "After unfolding all buffers, all original text should be displayed"
15513    );
15514}
15515
15516#[gpui::test]
15517async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppContext) {
15518    init_test(cx, |_| {});
15519
15520    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
15521
15522    let fs = FakeFs::new(cx.executor());
15523    fs.insert_tree(
15524        path!("/a"),
15525        json!({
15526            "main.rs": sample_text,
15527        }),
15528    )
15529    .await;
15530    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
15531    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
15532    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
15533    let worktree = project.update(cx, |project, cx| {
15534        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
15535        assert_eq!(worktrees.len(), 1);
15536        worktrees.pop().unwrap()
15537    });
15538    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
15539
15540    let buffer_1 = project
15541        .update(cx, |project, cx| {
15542            project.open_buffer((worktree_id, "main.rs"), cx)
15543        })
15544        .await
15545        .unwrap();
15546
15547    let multi_buffer = cx.new(|cx| {
15548        let mut multi_buffer = MultiBuffer::new(ReadWrite);
15549        multi_buffer.push_excerpts(
15550            buffer_1.clone(),
15551            [ExcerptRange {
15552                context: Point::new(0, 0)
15553                    ..Point::new(
15554                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
15555                        0,
15556                    ),
15557                primary: None,
15558            }],
15559            cx,
15560        );
15561        multi_buffer
15562    });
15563    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
15564        Editor::new(
15565            EditorMode::Full,
15566            multi_buffer,
15567            Some(project.clone()),
15568            true,
15569            window,
15570            cx,
15571        )
15572    });
15573
15574    let selection_range = Point::new(1, 0)..Point::new(2, 0);
15575    multi_buffer_editor.update_in(cx, |editor, window, cx| {
15576        enum TestHighlight {}
15577        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
15578        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
15579        editor.highlight_text::<TestHighlight>(
15580            vec![highlight_range.clone()],
15581            HighlightStyle::color(Hsla::green()),
15582            cx,
15583        );
15584        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
15585    });
15586
15587    let full_text = format!("\n\n\n{sample_text}\n");
15588    assert_eq!(
15589        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
15590        full_text,
15591    );
15592}
15593
15594#[gpui::test]
15595async fn test_inline_completion_text(cx: &mut TestAppContext) {
15596    init_test(cx, |_| {});
15597
15598    // Simple insertion
15599    assert_highlighted_edits(
15600        "Hello, world!",
15601        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
15602        true,
15603        cx,
15604        |highlighted_edits, cx| {
15605            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
15606            assert_eq!(highlighted_edits.highlights.len(), 1);
15607            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
15608            assert_eq!(
15609                highlighted_edits.highlights[0].1.background_color,
15610                Some(cx.theme().status().created_background)
15611            );
15612        },
15613    )
15614    .await;
15615
15616    // Replacement
15617    assert_highlighted_edits(
15618        "This is a test.",
15619        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
15620        false,
15621        cx,
15622        |highlighted_edits, cx| {
15623            assert_eq!(highlighted_edits.text, "That is a test.");
15624            assert_eq!(highlighted_edits.highlights.len(), 1);
15625            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
15626            assert_eq!(
15627                highlighted_edits.highlights[0].1.background_color,
15628                Some(cx.theme().status().created_background)
15629            );
15630        },
15631    )
15632    .await;
15633
15634    // Multiple edits
15635    assert_highlighted_edits(
15636        "Hello, world!",
15637        vec![
15638            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
15639            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
15640        ],
15641        false,
15642        cx,
15643        |highlighted_edits, cx| {
15644            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
15645            assert_eq!(highlighted_edits.highlights.len(), 2);
15646            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
15647            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
15648            assert_eq!(
15649                highlighted_edits.highlights[0].1.background_color,
15650                Some(cx.theme().status().created_background)
15651            );
15652            assert_eq!(
15653                highlighted_edits.highlights[1].1.background_color,
15654                Some(cx.theme().status().created_background)
15655            );
15656        },
15657    )
15658    .await;
15659
15660    // Multiple lines with edits
15661    assert_highlighted_edits(
15662        "First line\nSecond line\nThird line\nFourth line",
15663        vec![
15664            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
15665            (
15666                Point::new(2, 0)..Point::new(2, 10),
15667                "New third line".to_string(),
15668            ),
15669            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
15670        ],
15671        false,
15672        cx,
15673        |highlighted_edits, cx| {
15674            assert_eq!(
15675                highlighted_edits.text,
15676                "Second modified\nNew third line\nFourth updated line"
15677            );
15678            assert_eq!(highlighted_edits.highlights.len(), 3);
15679            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
15680            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
15681            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
15682            for highlight in &highlighted_edits.highlights {
15683                assert_eq!(
15684                    highlight.1.background_color,
15685                    Some(cx.theme().status().created_background)
15686                );
15687            }
15688        },
15689    )
15690    .await;
15691}
15692
15693#[gpui::test]
15694async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
15695    init_test(cx, |_| {});
15696
15697    // Deletion
15698    assert_highlighted_edits(
15699        "Hello, world!",
15700        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
15701        true,
15702        cx,
15703        |highlighted_edits, cx| {
15704            assert_eq!(highlighted_edits.text, "Hello, world!");
15705            assert_eq!(highlighted_edits.highlights.len(), 1);
15706            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
15707            assert_eq!(
15708                highlighted_edits.highlights[0].1.background_color,
15709                Some(cx.theme().status().deleted_background)
15710            );
15711        },
15712    )
15713    .await;
15714
15715    // Insertion
15716    assert_highlighted_edits(
15717        "Hello, world!",
15718        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
15719        true,
15720        cx,
15721        |highlighted_edits, cx| {
15722            assert_eq!(highlighted_edits.highlights.len(), 1);
15723            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
15724            assert_eq!(
15725                highlighted_edits.highlights[0].1.background_color,
15726                Some(cx.theme().status().created_background)
15727            );
15728        },
15729    )
15730    .await;
15731}
15732
15733async fn assert_highlighted_edits(
15734    text: &str,
15735    edits: Vec<(Range<Point>, String)>,
15736    include_deletions: bool,
15737    cx: &mut TestAppContext,
15738    assertion_fn: impl Fn(HighlightedText, &App),
15739) {
15740    let window = cx.add_window(|window, cx| {
15741        let buffer = MultiBuffer::build_simple(text, cx);
15742        Editor::new(EditorMode::Full, buffer, None, true, window, cx)
15743    });
15744    let cx = &mut VisualTestContext::from_window(*window, cx);
15745
15746    let (buffer, snapshot) = window
15747        .update(cx, |editor, _window, cx| {
15748            (
15749                editor.buffer().clone(),
15750                editor.buffer().read(cx).snapshot(cx),
15751            )
15752        })
15753        .unwrap();
15754
15755    let edits = edits
15756        .into_iter()
15757        .map(|(range, edit)| {
15758            (
15759                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
15760                edit,
15761            )
15762        })
15763        .collect::<Vec<_>>();
15764
15765    let text_anchor_edits = edits
15766        .clone()
15767        .into_iter()
15768        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
15769        .collect::<Vec<_>>();
15770
15771    let edit_preview = window
15772        .update(cx, |_, _window, cx| {
15773            buffer
15774                .read(cx)
15775                .as_singleton()
15776                .unwrap()
15777                .read(cx)
15778                .preview_edits(text_anchor_edits.into(), cx)
15779        })
15780        .unwrap()
15781        .await;
15782
15783    cx.update(|_window, cx| {
15784        let highlighted_edits = inline_completion_edit_text(
15785            &snapshot.as_singleton().unwrap().2,
15786            &edits,
15787            &edit_preview,
15788            include_deletions,
15789            cx,
15790        );
15791        assertion_fn(highlighted_edits, cx)
15792    });
15793}
15794
15795#[gpui::test]
15796async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
15797    init_test(cx, |_| {});
15798    let capabilities = lsp::ServerCapabilities {
15799        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
15800            prepare_provider: Some(true),
15801            work_done_progress_options: Default::default(),
15802        })),
15803        ..Default::default()
15804    };
15805    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15806
15807    cx.set_state(indoc! {"
15808        struct Fˇoo {}
15809    "});
15810
15811    cx.update_editor(|editor, _, cx| {
15812        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15813        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15814        editor.highlight_background::<DocumentHighlightRead>(
15815            &[highlight_range],
15816            |c| c.editor_document_highlight_read_background,
15817            cx,
15818        );
15819    });
15820
15821    let mut prepare_rename_handler =
15822        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
15823            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
15824                start: lsp::Position {
15825                    line: 0,
15826                    character: 7,
15827                },
15828                end: lsp::Position {
15829                    line: 0,
15830                    character: 10,
15831                },
15832            })))
15833        });
15834    let prepare_rename_task = cx
15835        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15836        .expect("Prepare rename was not started");
15837    prepare_rename_handler.next().await.unwrap();
15838    prepare_rename_task.await.expect("Prepare rename failed");
15839
15840    let mut rename_handler =
15841        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15842            let edit = lsp::TextEdit {
15843                range: lsp::Range {
15844                    start: lsp::Position {
15845                        line: 0,
15846                        character: 7,
15847                    },
15848                    end: lsp::Position {
15849                        line: 0,
15850                        character: 10,
15851                    },
15852                },
15853                new_text: "FooRenamed".to_string(),
15854            };
15855            Ok(Some(lsp::WorkspaceEdit::new(
15856                // Specify the same edit twice
15857                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
15858            )))
15859        });
15860    let rename_task = cx
15861        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15862        .expect("Confirm rename was not started");
15863    rename_handler.next().await.unwrap();
15864    rename_task.await.expect("Confirm rename failed");
15865    cx.run_until_parked();
15866
15867    // Despite two edits, only one is actually applied as those are identical
15868    cx.assert_editor_state(indoc! {"
15869        struct FooRenamedˇ {}
15870    "});
15871}
15872
15873#[gpui::test]
15874async fn test_rename_without_prepare(cx: &mut gpui::TestAppContext) {
15875    init_test(cx, |_| {});
15876    // These capabilities indicate that the server does not support prepare rename.
15877    let capabilities = lsp::ServerCapabilities {
15878        rename_provider: Some(lsp::OneOf::Left(true)),
15879        ..Default::default()
15880    };
15881    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
15882
15883    cx.set_state(indoc! {"
15884        struct Fˇoo {}
15885    "});
15886
15887    cx.update_editor(|editor, _window, cx| {
15888        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
15889        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
15890        editor.highlight_background::<DocumentHighlightRead>(
15891            &[highlight_range],
15892            |c| c.editor_document_highlight_read_background,
15893            cx,
15894        );
15895    });
15896
15897    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
15898        .expect("Prepare rename was not started")
15899        .await
15900        .expect("Prepare rename failed");
15901
15902    let mut rename_handler =
15903        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
15904            let edit = lsp::TextEdit {
15905                range: lsp::Range {
15906                    start: lsp::Position {
15907                        line: 0,
15908                        character: 7,
15909                    },
15910                    end: lsp::Position {
15911                        line: 0,
15912                        character: 10,
15913                    },
15914                },
15915                new_text: "FooRenamed".to_string(),
15916            };
15917            Ok(Some(lsp::WorkspaceEdit::new(
15918                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
15919            )))
15920        });
15921    let rename_task = cx
15922        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
15923        .expect("Confirm rename was not started");
15924    rename_handler.next().await.unwrap();
15925    rename_task.await.expect("Confirm rename failed");
15926    cx.run_until_parked();
15927
15928    // Correct range is renamed, as `surrounding_word` is used to find it.
15929    cx.assert_editor_state(indoc! {"
15930        struct FooRenamedˇ {}
15931    "});
15932}
15933
15934fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
15935    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
15936    point..point
15937}
15938
15939fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
15940    let (text, ranges) = marked_text_ranges(marked_text, true);
15941    assert_eq!(editor.text(cx), text);
15942    assert_eq!(
15943        editor.selections.ranges(cx),
15944        ranges,
15945        "Assert selections are {}",
15946        marked_text
15947    );
15948}
15949
15950pub fn handle_signature_help_request(
15951    cx: &mut EditorLspTestContext,
15952    mocked_response: lsp::SignatureHelp,
15953) -> impl Future<Output = ()> {
15954    let mut request =
15955        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
15956            let mocked_response = mocked_response.clone();
15957            async move { Ok(Some(mocked_response)) }
15958        });
15959
15960    async move {
15961        request.next().await;
15962    }
15963}
15964
15965/// Handle completion request passing a marked string specifying where the completion
15966/// should be triggered from using '|' character, what range should be replaced, and what completions
15967/// should be returned using '<' and '>' to delimit the range
15968pub fn handle_completion_request(
15969    cx: &mut EditorLspTestContext,
15970    marked_string: &str,
15971    completions: Vec<&'static str>,
15972    counter: Arc<AtomicUsize>,
15973) -> impl Future<Output = ()> {
15974    let complete_from_marker: TextRangeMarker = '|'.into();
15975    let replace_range_marker: TextRangeMarker = ('<', '>').into();
15976    let (_, mut marked_ranges) = marked_text_ranges_by(
15977        marked_string,
15978        vec![complete_from_marker.clone(), replace_range_marker.clone()],
15979    );
15980
15981    let complete_from_position =
15982        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
15983    let replace_range =
15984        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
15985
15986    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
15987        let completions = completions.clone();
15988        counter.fetch_add(1, atomic::Ordering::Release);
15989        async move {
15990            assert_eq!(params.text_document_position.text_document.uri, url.clone());
15991            assert_eq!(
15992                params.text_document_position.position,
15993                complete_from_position
15994            );
15995            Ok(Some(lsp::CompletionResponse::Array(
15996                completions
15997                    .iter()
15998                    .map(|completion_text| lsp::CompletionItem {
15999                        label: completion_text.to_string(),
16000                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
16001                            range: replace_range,
16002                            new_text: completion_text.to_string(),
16003                        })),
16004                        ..Default::default()
16005                    })
16006                    .collect(),
16007            )))
16008        }
16009    });
16010
16011    async move {
16012        request.next().await;
16013    }
16014}
16015
16016fn handle_resolve_completion_request(
16017    cx: &mut EditorLspTestContext,
16018    edits: Option<Vec<(&'static str, &'static str)>>,
16019) -> impl Future<Output = ()> {
16020    let edits = edits.map(|edits| {
16021        edits
16022            .iter()
16023            .map(|(marked_string, new_text)| {
16024                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
16025                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
16026                lsp::TextEdit::new(replace_range, new_text.to_string())
16027            })
16028            .collect::<Vec<_>>()
16029    });
16030
16031    let mut request =
16032        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
16033            let edits = edits.clone();
16034            async move {
16035                Ok(lsp::CompletionItem {
16036                    additional_text_edits: edits,
16037                    ..Default::default()
16038                })
16039            }
16040        });
16041
16042    async move {
16043        request.next().await;
16044    }
16045}
16046
16047pub(crate) fn update_test_language_settings(
16048    cx: &mut TestAppContext,
16049    f: impl Fn(&mut AllLanguageSettingsContent),
16050) {
16051    cx.update(|cx| {
16052        SettingsStore::update_global(cx, |store, cx| {
16053            store.update_user_settings::<AllLanguageSettings>(cx, f);
16054        });
16055    });
16056}
16057
16058pub(crate) fn update_test_project_settings(
16059    cx: &mut TestAppContext,
16060    f: impl Fn(&mut ProjectSettings),
16061) {
16062    cx.update(|cx| {
16063        SettingsStore::update_global(cx, |store, cx| {
16064            store.update_user_settings::<ProjectSettings>(cx, f);
16065        });
16066    });
16067}
16068
16069pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
16070    cx.update(|cx| {
16071        assets::Assets.load_test_fonts(cx);
16072        let store = SettingsStore::test(cx);
16073        cx.set_global(store);
16074        theme::init(theme::LoadThemes::JustBase, cx);
16075        release_channel::init(SemanticVersion::default(), cx);
16076        client::init_settings(cx);
16077        language::init(cx);
16078        Project::init_settings(cx);
16079        workspace::init_settings(cx);
16080        crate::init(cx);
16081    });
16082
16083    update_test_language_settings(cx, f);
16084}
16085
16086#[track_caller]
16087fn assert_hunk_revert(
16088    not_reverted_text_with_selections: &str,
16089    expected_hunk_statuses_before: Vec<DiffHunkStatus>,
16090    expected_reverted_text_with_selections: &str,
16091    base_text: &str,
16092    cx: &mut EditorLspTestContext,
16093) {
16094    cx.set_state(not_reverted_text_with_selections);
16095    cx.set_diff_base(base_text);
16096    cx.executor().run_until_parked();
16097
16098    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
16099        let snapshot = editor.snapshot(window, cx);
16100        let reverted_hunk_statuses = snapshot
16101            .buffer_snapshot
16102            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
16103            .map(|hunk| hunk.status())
16104            .collect::<Vec<_>>();
16105
16106        editor.revert_selected_hunks(&RevertSelectedHunks, window, cx);
16107        reverted_hunk_statuses
16108    });
16109    cx.executor().run_until_parked();
16110    cx.assert_editor_state(expected_reverted_text_with_selections);
16111    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
16112}